From 9ace95e66b6f242232d87e72888046b251d22aa8 Mon Sep 17 00:00:00 2001
From: Matthias Konitzny <konitzny@ibr.cs.tu-bs.de>
Date: Wed, 4 Aug 2021 17:02:21 +0200
Subject: [PATCH] More refactoring on the overlays. Beginning to write code
 comments.

---
 display/graph.js                              |   8 +-
 display/helpers.js                            |  11 +-
 display/infooverlay.js                        | 187 ------------------
 .../linkselection.js}                         |  23 ++-
 display/overlays/neighbors.js                 | 120 +++++++++++
 display/overlays/nodeinfo.js                  | 107 ++++++++++
 6 files changed, 261 insertions(+), 195 deletions(-)
 delete mode 100644 display/infooverlay.js
 rename display/{linkoverlay.js => overlays/linkselection.js} (70%)
 create mode 100644 display/overlays/neighbors.js
 create mode 100644 display/overlays/nodeinfo.js

diff --git a/display/graph.js b/display/graph.js
index dfa2183..f10a9ba 100644
--- a/display/graph.js
+++ b/display/graph.js
@@ -1,7 +1,7 @@
 import * as Config from "../config";
 import * as Helpers from "./helpers";
-import { InfoOverlay } from "./infooverlay";
-import { LinkOverlay } from "./linkoverlay";
+import { NodeInfoOverlay } from "./overlays/nodeinfo";
+import { LinkSelectionOverlay } from "./overlays/linkselection";
 
 import * as THREE from "three";
 import ForceGraph3D from "3d-force-graph";
@@ -357,6 +357,6 @@ function createFullScreenButton() {
 
 const dataUrl = Config.PLUGIN_PATH + "datasets/aud1.json";
 const G = new Graph(dataUrl);
-const linkoverlay = new LinkOverlay(G);
-const infooverlay = new InfoOverlay(G);
+const linkoverlay = new LinkSelectionOverlay(G);
+const infooverlay = new NodeInfoOverlay(G);
 G.infooverlay = infooverlay;
diff --git a/display/helpers.js b/display/helpers.js
index 4469d40..459ec16 100644
--- a/display/helpers.js
+++ b/display/helpers.js
@@ -1,4 +1,4 @@
-export { getWidth, getHeight, getCanvasDivNode };
+export { getWidth, getHeight, getCanvasDivNode, createDiv };
 
 function getWidth() {
     return document.getElementById("3d-graph").offsetWidth;
@@ -12,3 +12,12 @@ function getCanvasDivNode() {
     const domNode = document.getElementById("3d-graph");
     return domNode.firstChild.firstChild.firstChild;
 }
+
+function createDiv(className, parent) {
+    const node = document.createElement("div");
+    node.className = className;
+    if (typeof parent !== "undefined") {
+        parent.appendChild(node);
+    }
+    return node;
+}
diff --git a/display/infooverlay.js b/display/infooverlay.js
deleted file mode 100644
index a821327..0000000
--- a/display/infooverlay.js
+++ /dev/null
@@ -1,187 +0,0 @@
-import * as Helpers from "./helpers";
-import jQuery from "jquery";
-import * as Config from "../config";
-
-export { InfoOverlay };
-
-class InfoOverlay {
-    constructor(graph) {
-        this.graph = graph;
-
-        this.activeTabNav = null;
-        this.activeTabContent = null;
-
-        this.tabContentPages = {};
-    }
-
-    create() {
-        const overlayDiv = this.createOverlayMainDiv();
-        this.createOverlayElements(overlayDiv);
-        this.createBottomMenu(overlayDiv);
-
-        jQuery("#infoOverlayCloseButton").click(function () {
-            jQuery("#infoOverlay").slideUp("fast");
-        });
-    }
-
-    createBottomMenu(overlayNode) {
-        const bottomContainerDiv = document.createElement("div");
-        bottomContainerDiv.className = "bottom-container";
-        overlayNode.appendChild(bottomContainerDiv);
-
-        const bottomContainerNavDiv = document.createElement("div");
-        bottomContainerNavDiv.className = "bottom-container-nav";
-        bottomContainerDiv.appendChild(bottomContainerNavDiv);
-
-        const bottomContainerLinkDiv = document.createElement("div");
-        bottomContainerLinkDiv.className = "bottom-container-links";
-        bottomContainerDiv.appendChild(bottomContainerLinkDiv);
-
-        for (const [cls, color] of Object.entries(this.graph.edgeColors)) {
-            const navTab = document.createElement("div");
-            navTab.className = "bottom-container-nav-tab";
-            navTab.innerText = cls.slice(0, 3);
-            navTab.style = "background-color: " + color;
-            navTab.edgeType = cls;
-            bottomContainerNavDiv.appendChild(navTab);
-            jQuery(navTab).click((event) => this.openTab(event));
-
-            const tabContent = document.createElement("div");
-            tabContent.className = "bottom-container-tab-content";
-            tabContent.id = cls + "_id_content";
-            tabContent.style = "background-color: " + color;
-            bottomContainerLinkDiv.appendChild(tabContent);
-            this.tabContentPages[cls] = tabContent;
-        }
-
-        this.activeTabNav = bottomContainerNavDiv.firstChild;
-        this.activeTabContent = bottomContainerLinkDiv.firstChild;
-
-        this.activeTabContent.classList.add("active-tab-content");
-        this.activeTabNav.classList.add("active-tab-nav");
-        this.activeTabNav.innerText = this.activeTabNav.edgeType;
-    }
-
-    openTab(event) {
-        const navTab = event.target;
-        const cls = navTab.edgeType;
-
-        this.activeTabNav.classList.remove("active-tab-nav");
-        this.activeTabNav.innerText = this.activeTabNav.innerText.slice(0, 3);
-        navTab.classList.add("active-tab-nav");
-        navTab.innerText = cls;
-
-        this.activeTabContent.classList.remove("active-tab-content");
-        this.tabContentPages[cls].classList.add("active-tab-content");
-
-        this.activeTabNav = navTab;
-        this.activeTabContent = this.tabContentPages[cls];
-    }
-
-    createOverlayMainDiv() {
-        const sceneNode = Helpers.getCanvasDivNode();
-        const overlayNode = document.createElement("div");
-        overlayNode.id = "infoOverlay";
-        overlayNode.className = "detail-view";
-        //overlayNode.innerText = 'Hello there!';
-        sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]);
-        return overlayNode;
-    }
-
-    createOverlayElements(overlayNode) {
-        const close = document.createElement("div");
-        close.innerHTML = "<p>&#10006;</p>";
-        close.id = "infoOverlayCloseButton";
-        close.className = "close-button";
-        overlayNode.appendChild(close);
-
-        const infoArea = document.createElement("div");
-        infoArea.className = "detail-view-info-area";
-        overlayNode.appendChild(infoArea);
-
-        const topArea = document.createElement("div");
-        topArea.className = "detail-view-top-area";
-        infoArea.appendChild(topArea);
-
-        const nodeImage = document.createElement("img");
-        nodeImage.id = "infoOverlayImage";
-        nodeImage.src = Config.PLUGIN_PATH + "datasets/images/default.jpg";
-        nodeImage.className = "detail-view-image";
-        topArea.appendChild(nodeImage);
-
-        const headline = document.createElement("h2");
-        headline.id = "infoOverlayHeadline";
-        headline.innerText = "Default Text";
-        headline.className = "detail-view-headline";
-        topArea.appendChild(headline);
-
-        const textArea = document.createElement("div");
-        textArea.className = "detail-view-text-area";
-        infoArea.appendChild(textArea);
-
-        const description = document.createElement("p");
-        description.id = "infoOverlayDescription";
-        description.innerText = "Default Text";
-        description.setAttribute("overflow-y", "scroll");
-        textArea.appendChild(description);
-    }
-
-    updateInfoOverlay(node) {
-        jQuery("#infoOverlayHeadline").text(node.name);
-        if ("image" in node) {
-            jQuery("#infoOverlayImage").attr(
-                "src",
-                Config.PLUGIN_PATH + "datasets/images/" + node.image
-            );
-        } else {
-            jQuery("#infoOverlayImage").attr(
-                "src",
-                Config.PLUGIN_PATH + "datasets/images/default.jpg"
-            );
-        }
-
-        if ("description" in node) {
-            jQuery("#infoOverlayDescription").text(node.description);
-        } else {
-            jQuery("#infoOverlayDescription").text("Default Text");
-        }
-
-        this.updateTabs(node);
-
-        jQuery("#infoOverlay").slideDown("fast");
-    }
-
-    clearTabContentPages() {
-        for (const page of Object.values(this.tabContentPages)) {
-            jQuery(page).empty();
-        }
-    }
-
-    createReference(target) {
-        const linkDiv = document.createElement("div");
-        linkDiv.className = "link-img";
-
-        if ("image" in target) {
-            const linkImage = document.createElement("img");
-            linkImage.src =
-                Config.PLUGIN_PATH + "datasets/images/" + target.image;
-            linkDiv.appendChild(linkImage);
-        }
-
-        jQuery(linkDiv).on("click", () => {
-            this.graph.focusOnNode(target);
-            this.updateInfoOverlay(target);
-        });
-        return linkDiv;
-    }
-
-    updateTabs(node) {
-        this.clearTabContentPages();
-
-        for (const link of node.links) {
-            const target = link.source == node ? link.target : link.source;
-            const reference = this.createReference(target);
-            this.tabContentPages[link.type].appendChild(reference);
-        }
-    }
-}
diff --git a/display/linkoverlay.js b/display/overlays/linkselection.js
similarity index 70%
rename from display/linkoverlay.js
rename to display/overlays/linkselection.js
index 912089a..1a27d21 100644
--- a/display/linkoverlay.js
+++ b/display/overlays/linkselection.js
@@ -1,13 +1,25 @@
-import * as Helpers from "./helpers";
+import * as Helpers from "../helpers";
 import jQuery from "jquery";
 
-export { LinkOverlay };
+export { LinkSelectionOverlay };
 
-class LinkOverlay {
+/**
+ * Represents an overlay showing the meaning of different edge colors.
+ * Offers the ability to toggle certain edge types.
+ */
+class LinkSelectionOverlay {
+    /**
+     * Creates the link overlay for a given graph object.
+     * @param {Graph} graph The graph object.
+     */
     constructor(graph) {
         this.graph = graph;
     }
 
+    /**
+     * Creates the overlay based on the edges and nodes of the graph object.
+     * Must be called after the graph has been initialized.
+     */
     create() {
         const sceneNode = Helpers.getCanvasDivNode();
         const overlayNode = document.createElement("div");
@@ -39,6 +51,11 @@ class LinkOverlay {
         }
     }
 
+    /**
+     * Event handler for the click event of the link type divs.
+     * Toggles the visibility of certain edge types.
+     * @param {MouseEvent} event
+     */
     toggleLinkVisibility(event) {
         const target = event.currentTarget;
         this.graph.toggleLinkVisibility(target.edgeType);
diff --git a/display/overlays/neighbors.js b/display/overlays/neighbors.js
new file mode 100644
index 0000000..0499788
--- /dev/null
+++ b/display/overlays/neighbors.js
@@ -0,0 +1,120 @@
+import * as Helpers from "../helpers";
+import jQuery from "jquery";
+import * as Config from "../../config";
+
+export { NodeNeighborOverlay };
+
+class NodeNeighborOverlay {
+    constructor(graph, parentNode, infoOverlay) {
+        this.graph = graph;
+        this.parentNode = parentNode;
+        this.infoOverlay = infoOverlay;
+
+        this.activeTabNav = null;
+        this.activeTabContent = null;
+
+        this.tabContentPages = {};
+    }
+
+    create() {
+        const bottomContainerDiv = Helpers.createDiv(
+            "bottom-container",
+            this.parentNode
+        );
+        const bottomContainerNavDiv = Helpers.createDiv(
+            "bottom-container-nav",
+            bottomContainerDiv
+        );
+        const bottomContainerLinkDiv = Helpers.createDiv(
+            "bottom-container-links",
+            bottomContainerDiv
+        );
+
+        for (const [cls, color] of Object.entries(this.graph.edgeColors)) {
+            const navTab = Helpers.createDiv(
+                "bottom-container-nav-tab",
+                bottomContainerNavDiv
+            );
+            navTab.innerText = cls.slice(0, 3);
+            navTab.style.backgroundColor = color;
+            navTab.edgeType = cls; // Attach the edge type to the DOM object to retrieve it during click events
+            jQuery(navTab).click((event) => this.openTab(event));
+
+            const tabContent = Helpers.createDiv(
+                "bottom-container-tab-content",
+                bottomContainerLinkDiv
+            );
+            tabContent.style.backgroundColor = color;
+            this.tabContentPages[cls] = tabContent;
+        }
+        this.initializeActive(bottomContainerNavDiv, bottomContainerLinkDiv);
+    }
+
+    /**
+     * Initializes the activeTabNav and activeTabContent variables to a random edge type.
+     * @param {Element} bottomContainerNavDiv
+     * @param {Element} bottomContainerLinkDiv
+     */
+    initializeActive(bottomContainerNavDiv, bottomContainerLinkDiv) {
+        this.activeTabNav = bottomContainerNavDiv.firstChild;
+        this.activeTabContent = bottomContainerLinkDiv.firstChild;
+
+        this.activeTabContent.classList.add("active-tab-content");
+        this.activeTabNav.classList.add("active-tab-nav");
+        this.activeTabNav.innerText = this.activeTabNav.edgeType;
+    }
+
+    /**
+     * Click event handler for the tab headers of the bottom menu.
+     * @param event
+     */
+    openTab(event) {
+        const navTab = event.target;
+        const cls = navTab.edgeType;
+
+        this.activeTabNav.classList.remove("active-tab-nav");
+        this.activeTabNav.innerText = this.activeTabNav.innerText.slice(0, 3);
+        navTab.classList.add("active-tab-nav");
+        navTab.innerText = cls;
+
+        this.activeTabContent.classList.remove("active-tab-content");
+        this.tabContentPages[cls].classList.add("active-tab-content");
+
+        this.activeTabNav = navTab;
+        this.activeTabContent = this.tabContentPages[cls];
+    }
+
+    clearTabContentPages() {
+        for (const page of Object.values(this.tabContentPages)) {
+            jQuery(page).empty();
+        }
+    }
+
+    createReference(target) {
+        const linkDiv = document.createElement("div");
+        linkDiv.className = "link-img";
+
+        if ("image" in target) {
+            const linkImage = document.createElement("img");
+            linkImage.src =
+                Config.PLUGIN_PATH + "datasets/images/" + target.image;
+            linkDiv.appendChild(linkImage);
+        }
+
+        jQuery(linkDiv).on("click", () => {
+            this.graph.focusOnNode(target);
+            this.infoOverlay.updateInfoOverlay(target);
+        });
+        return linkDiv;
+    }
+
+    updateTabs(node) {
+        this.clearTabContentPages();
+
+        for (const link of node.links) {
+            const target = link.source == node ? link.target : link.source;
+            const reference = this.createReference(target);
+            this.tabContentPages[link.type].appendChild(reference);
+        }
+    }
+}
diff --git a/display/overlays/nodeinfo.js b/display/overlays/nodeinfo.js
new file mode 100644
index 0000000..18bd679
--- /dev/null
+++ b/display/overlays/nodeinfo.js
@@ -0,0 +1,107 @@
+import jQuery from "jquery";
+
+import { NodeNeighborOverlay } from "./neighbors";
+import * as Helpers from "../helpers";
+import * as Config from "../../config";
+
+export { NodeInfoOverlay };
+
+/**
+ * An overlay displaying the information which is connected with each node.
+ */
+class NodeInfoOverlay {
+    /**
+     * Constructs a new info overlay for the given graph object.
+     * @param {Graph} graph
+     */
+    constructor(graph) {
+        this.graph = graph;
+        this.bottomMenu = null;
+    }
+
+    /**
+     * Creates the visible elements of the overlay.
+     * Must be called after the graph has been initialized.
+     */
+    create() {
+        const overlayDiv = this.createOverlayMainDiv();
+        this.createOverlayElements(overlayDiv);
+        this.bottomMenu = new NodeNeighborOverlay(this.graph, overlayDiv, this);
+        this.bottomMenu.create();
+
+        jQuery("#infoOverlayCloseButton").click(function () {
+            jQuery("#infoOverlay").slideUp("fast");
+        });
+    }
+
+    createOverlayMainDiv() {
+        const sceneNode = Helpers.getCanvasDivNode();
+        const overlayNode = document.createElement("div");
+        overlayNode.id = "infoOverlay";
+        overlayNode.className = "detail-view";
+        sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]);
+        return overlayNode;
+    }
+
+    createOverlayElements(overlayNode) {
+        const close = document.createElement("div");
+        close.innerHTML = "<p>&#10006;</p>";
+        close.id = "infoOverlayCloseButton";
+        close.className = "close-button";
+        overlayNode.appendChild(close);
+
+        const infoArea = document.createElement("div");
+        infoArea.className = "detail-view-info-area";
+        overlayNode.appendChild(infoArea);
+
+        const topArea = document.createElement("div");
+        topArea.className = "detail-view-top-area";
+        infoArea.appendChild(topArea);
+
+        const nodeImage = document.createElement("img");
+        nodeImage.id = "infoOverlayImage";
+        nodeImage.src = Config.PLUGIN_PATH + "datasets/images/default.jpg";
+        nodeImage.className = "detail-view-image";
+        topArea.appendChild(nodeImage);
+
+        const headline = document.createElement("h2");
+        headline.id = "infoOverlayHeadline";
+        headline.innerText = "Default Text";
+        headline.className = "detail-view-headline";
+        topArea.appendChild(headline);
+
+        const textArea = document.createElement("div");
+        textArea.className = "detail-view-text-area";
+        infoArea.appendChild(textArea);
+
+        const description = document.createElement("p");
+        description.id = "infoOverlayDescription";
+        description.innerText = "Default Text";
+        description.setAttribute("overflow-y", "scroll");
+        textArea.appendChild(description);
+    }
+
+    updateInfoOverlay(node) {
+        jQuery("#infoOverlayHeadline").text(node.name);
+        if ("image" in node) {
+            jQuery("#infoOverlayImage").attr(
+                "src",
+                Config.PLUGIN_PATH + "datasets/images/" + node.image
+            );
+        } else {
+            jQuery("#infoOverlayImage").attr(
+                "src",
+                Config.PLUGIN_PATH + "datasets/images/default.jpg"
+            );
+        }
+
+        if ("description" in node) {
+            jQuery("#infoOverlayDescription").text(node.description);
+        } else {
+            jQuery("#infoOverlayDescription").text("Default Text");
+        }
+
+        this.bottomMenu.updateTabs(node);
+        jQuery("#infoOverlay").slideDown("fast");
+    }
+}
-- 
GitLab