Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/messages.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 44 additions & 45 deletions graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,49 +49,41 @@ function layoutNodes() {
cy_layout.run();
}

const BUTTON_ICONS = {
"source": "github.svg",
"documentation": "book.svg",
"homepage": "home.svg",
"download": "download.svg",
"chat": "messages.svg",
"issue tracker": "check-circle.svg",
"forum": "users.svg",
"examples": "code.svg",
"tutorial": "user.svg",
"installation": "package.svg",
"email": "mail.svg"
}

const BUTTON_ROWS = [
["homepage", "download", "source"],
["documentation", "installation", "tutorial", "examples"],
["forum", "issue tracker", "chat", "email"]
];

function urlButton(type, url) {
const button = document.createElement("button");
let icon = "";
switch (type) {
case "source":
iconFile = "github.svg";
break;
case "documentation":
iconFile = "book.svg";
break;
case "homepage":
iconFile = "home.svg";
break;
case "download":
iconFile = "download.svg";
break;
case "issue tracker":
iconFile = "check-circle.svg";
break;
case "forum":
iconFile = "users.svg";
break;
case "examples":
iconFile = "code.svg";
break;
case "tutorial":
iconFile = "user.svg";
break;
case "installation":
iconFile = "package.svg";
break;
case "email":
iconFile = "mail.svg";
break;
default:
iconFile = "link.svg";
}
let iconFile = BUTTON_ICONS[type];
button.type = "button"
button.classList.add('btn', 'btn-info', 'm-1');
icon = `<img aria-hidden='true' focusable='false' class='icon' src='assets/${iconFile}'></img>`;
button.classList.add('btn', 'btn-sm', 'm-1');
let icon = `<img aria-hidden='true' focusable='false' class='icon' src='assets/${iconFile}'></img>`;
button.innerHTML = icon + " " + type;
button.onclick = function() {
window.open(url, "_blank");
if (url !== undefined) {
button.classList.add('btn-info');
button.onclick = function() {
window.open(url, "_blank");
}
} else {
button.classList.add('btn-secondary');
button.disabled = true;
}
return button;
}
Expand Down Expand Up @@ -139,13 +131,20 @@ function showNodeDetails(node) {
showDetails(null, null);
} else {
showDetails(node.data(), node.outgoers("edge").map((edge) => {
return {target: edge.target().id(), label: edge.data("label"), source: edge.source().id()};
}));
return { type: "outgoing", target: edge.target().id(), label: edge.data("label"), source: edge.source().id() };
}).concat(
node.incomers("edge").map((edge) => {
return { type: "incoming", target: edge.target().id(), label: edge.data("label"), source: edge.source().id() }
})
)
);
}
}

function highlightEdge(edge) {
const details = document.getElementById("details");
const details_top = document.getElementById("details_top");
const details_bottom = document.getElementById("details_bottom");
details_bottom.innerHTML = "";
const headerElement = document.createElement("h2");
headerElement.innerHTML = edge.id();

Expand All @@ -159,16 +158,16 @@ function highlightEdge(edge) {
targetLink.addEventListener("click",function(e) { edge.unselect(); edge.target().select(); });
targetLink.innerHTML = edge.target().id();

details.innerHTML = "";
details_top.innerHTML = "";

const paragraph = document.createElement("p");
paragraph.appendChild(sourceLink);
const label = document.createElement("i");
label.innerHTML = " " + edge.data("label") + " ";
paragraph.appendChild(label);
paragraph.appendChild(targetLink);
details.appendChild(headerElement);
details.appendChild(paragraph);
details_top.appendChild(headerElement);
details_top.appendChild(paragraph);
// Only show the edge and the connected nodes
cy.elements().forEach(n => n.style("opacity", 0.2));
edge.style("opacity", 1);
Expand Down
5 changes: 4 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ <h5 class="offcanvas-title" id="filter_pane_label">Highlight simulators</h5>
<div class="col col-8" style="height: 80vh" id="cy"><br /></div>

<!-- details pane -->
<div class="col col-4" style="height: 80vh; overflow: scroll" id="details"></div>
<div class="col col-4 d-flex flex-column justify-content-between" style="height: 80vh; overflow: scroll" id="details">
<div id="details_top" style="overflow-y: scroll; max-height: 80vh"></div>
<div id="details_bottom"></div>
</div>
</div>
</div>

Expand Down
164 changes: 85 additions & 79 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,108 +8,114 @@ var TOOL_DESCRIPTIONS = {};
const selected = [];

// If params are null, show a default message
function showDetails(data, outgoers) {
function showDetails(data, connected) {
// Show details about the simulator
const details = document.getElementById("details");
const details_top = document.getElementById("details_top");
const details_bottom = document.getElementById("details_bottom");
// Basic description
if (data === null) {
details.innerHTML = "<br />";
details.innerHTML += "<h2>Using this resource</h2>";
details.innerHTML += "<ul>";
details.innerHTML += "<li>Use the 'Toggle Filters' button to activate the simulation engine filter.</li>";
details.innerHTML += "<li>Select what simulation engines you would like to show in the graph.</li>";
details.innerHTML += "<li>Select a node/edge to see its ecosystem in the graph.</li>";
details.innerHTML += "<li>Double click/tap on a node/edge to see details of the tool.</li>";
details.innerHTML += "<li>Click outside to unselect nodes.</li>";
details.innerHTML += "</ul>";
details.innerHTML += "<h3 class='mt-3'>Contributing</h2>";
details.innerHTML += `<p>Contributions are welcome! If you have anything to add or correct in the data,
details_top.innerHTML = "<br />";
details_top.innerHTML += "<h2>Using this resource</h2>";
details_top.innerHTML += "<ul>";
details_top.innerHTML += "<li>Use the 'Toggle Filters' button to activate the simulation engine filter.</li>";
details_top.innerHTML += "<li>Select what simulation engines you would like to show in the graph.</li>";
details_top.innerHTML += "<li>Select a node/edge to see its ecosystem in the graph.</li>";
details_top.innerHTML += "<li>Double click/tap on a node/edge to see details of the tool.</li>";
details_top.innerHTML += "<li>Click outside to unselect nodes.</li>";
details_top.innerHTML += "</ul>";
details_top.innerHTML += "<h3 class='mt-3'>Contributing</h2>";
details_top.innerHTML += `<p>Contributions are welcome! If you have anything to add or correct in the data,
please follow the link at the end of the tool's details view to edit the data on GitHub.
You can also open an <a href='${REPO_URL}/issues'>issue on the GitHub repository</a>.</p>`;
details.innerHTML += "<h3 class='mt-3'>List of simulators</h2>";
details.innerHTML += "<div class='d-flex'>";
details_bottom.innerHTML = "<div class='d-flex'>";
details_bottom.innerHTML += "<h3 class='mt-3'>List of simulators</h2>";
for (const sim of SIMULATORS) {
const quoted_sim = `[id='${sim}']`;
details.innerHTML += `<button class='btn btn-secondary m-1' onclick="cy.nodes('#simulators').unselect(); let node = cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'); node.select(); showNodeDetails(node);">${TOOL_DESCRIPTIONS[sim].short_name}</button>`;
details_bottom.innerHTML += `<button class='btn btn-secondary m-1' onclick="cy.nodes('#simulators').unselect(); let node = cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'); node.select(); showNodeDetails(node);">${TOOL_DESCRIPTIONS[sim].short_name}</button>`;
}
details.innerHTML += "</div>";

details_bottom.innerHTML += "</div>";
window.history.pushState({}, "", window.location.href.split("?")[0]);
return;
}
else {
if (data["features"].includes("simulator")) {
const quoted_sim = `[id='${data.id}']`;
details.innerHTML = `<div class='d-flex justify-content-between align-items-center'>
<h2>${data["full_name"]}</h2>
<button class='btn btn-outline-primary align-middle' title='Center ${data["short_name"]} in the graph' onclick="highlightNode(cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'));">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bullseye" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
<path d="M8 13A5 5 0 1 1 8 3a5 5 0 0 1 0 10m0 1A6 6 0 1 0 8 2a6 6 0 0 0 0 12"/>
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8"/>
<path d="M9.5 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
</svg>
</button>
</div>`;
} else {
details.innerHTML = `<h2>${data["full_name"]}</h2>`;
}
details.innerHTML += "<p>" + data["description"] + "</p>";
details_top.innerHTML = "";
details_bottom.innerHTML = "";
let description = document.createElement("div");
if (data["features"].includes("simulator")) {
const quoted_sim = `[id='${data.id}']`;
description.innerHTML = `<div class='d-flex justify-content-between align-items-center'>
<h2>${data["full_name"]}</h2>
<button class='btn btn-outline-primary align-middle' title='Center ${data["short_name"]} in the graph' onclick="highlightNode(cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'));">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bullseye" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
<path d="M8 13A5 5 0 1 1 8 3a5 5 0 0 1 0 10m0 1A6 6 0 1 0 8 2a6 6 0 0 0 0 12"/>
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8"/>
<path d="M9.5 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
</svg>
</button>
</div>`;
} else {
description.innerHTML = `<h2>${data["full_name"]}</h2>`;
}
description.innerHTML += "<p>" + data["description"] + "</p>";
// Relations
if (outgoers !== null) {
if (outgoers.length > 0) {
details.innerHTML += "<h3>Relations</h3>";
if (connected !== null) {
if (connected.length > 0) {
description.innerHTML += "<h3>Relations</h3>";
const list = document.createElement("ul");
for (let edge of outgoers) {
for (let edge of connected) {
if (edge["source"] === "simulators") {
continue;
}
const listItem = document.createElement("li");
const targetLink = document.createElement("a");
// targetLink.href = "#";
// targetLink.addEventListener("click",function(e) { node.unselect(); edge.target().select(); });
targetLink.innerHTML = edge["target"];
const label = document.createElement("i");
targetLink.href = "#";
if (edge["type"] === "outgoing") {
targetId = edge["target"];
} else {
targetId = edge["source"];
}
targetLink.addEventListener("click",function(e) {
console.log("Clicked on " + targetLink.innerHTML);
cy.nodes("[id='" + data.id + "']").unselect();
cy.nodes("[id='" + targetLink.innerHTML + "']").select();
});
targetLink.innerHTML = targetId;
const simName = document.createElement("i");
simName.innerHTML = data["short_name"];
const label = document.createElement("span");
label.innerHTML = " " + edge["label"] + " ";
listItem.appendChild(label);
listItem.appendChild(targetLink);

if (edge["type"] === "outgoing") {
listItem.append(simName);
listItem.append(label);
listItem.appendChild(targetLink);
} else {
listItem.appendChild(targetLink);
listItem.append(label);
listItem.append(simName);
}
list.appendChild(listItem);
}
details.appendChild(list);
description.appendChild(list);
}
details_top.appendChild(description);
// URLs
link_heading = document.createElement("h3");
link_heading.innerHTML = "Links";
details.append(link_heading);
if (data["urls"] !== undefined) {
for (let [text, url] of Object.entries(data["urls"])) {
details.appendChild(urlButton(text, url));
let tool_links = data["urls"];
details_bottom.appendChild(link_heading);
for (let row_idx=0; row_idx < BUTTON_ROWS.length; row_idx++) {
let row = document.createElement("div");
row.classList.add("row");
// Go through elements in BUTTON_ROWS
for (const button_type of BUTTON_ROWS[row_idx]) {
let col = document.createElement("div");
col.classList.add("col-auto");
let button = urlButton(button_type, tool_links[button_type]);
col.appendChild(button);
row.appendChild(col);
}
details_bottom.appendChild(row);
}
// Back to simulators
back_p = document.createElement("p");
back_p.classList.add("mt-3");
back_button = document.createElement("a");
back_button.href = "#";
back_button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466"/>
</svg>&nbsp;Back to simulators`;
back_button.classList.add("btn", "btn-secondary");
back_button.onclick = function() { cy.nodes(`[id = '${data.id}']`).unselect(); cy.nodes("#simulators").select(); unhighlightNode(); };
back_p.appendChild(back_button);
details.appendChild(back_p);
// Edit footer
edit_p = document.createElement("p");
edit_p.classList.add("mt-3", "text-end");
edit_link = document.createElement("a");
edit_link.classList.add("link-secondary");
edit_link.href = `${REPO_URL}/edit/${GIT_BRANCH}/${DATA_FOLDER}/${data["short_name"].replaceAll(" ", "-")}.yaml`;
edit_link.innerHTML = "Edit this description on GitHub&nbsp;";
edit_link.innerHTML += `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
</svg>`;
edit_link.target = "_blank";
edit_p.appendChild(edit_link);
details.appendChild(edit_p);

}
// hide filter pane
const filterPane = new bootstrap.Offcanvas('#filter_pane');
Expand Down