<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.8/css/bootstrap.min.css"
      integrity="sha512-2bBQCjcnw658Lho4nlXJcc6WkV/UxpE/sAokbXPxQNGqmNdQrWqtw26Ns9kFF/yG792pKR1Sx8/Y1Lf1XN4GKA=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
      />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.13.1/font/bootstrap-icons.min.css"
      integrity="sha512-t7Few9xlddEmgd3oKZQahkNI4dS6l80+eGEzFQiqtyVYdvcSG2D3Iub77R20BdotfRPA9caaRkg1tyaJiPmO0g=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
      />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/vis-network/10.0.2/dist/dist/vis-network.min.css"
      integrity="sha512-GSpw80rwo8kTr/5IPf9mhy5Ze8smoCCJ9fDJceVf6UAA5EUk9mOa/h/rug+PcDyCkdkR1mA+Gb3ot2GyHimFkw=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
      />
    <link
      rel="stylesheet"
      href="https://cdn.datatables.net/2.2.2/css/dataTables.dataTables.css"
      />

    <style>
/* fonts, icons */
@import url("https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700&display=swap");

/* structural */
html, h1, h2, h3, h4, h5, p, span, cite, figcaption, button, input, select, textarea {
    font-family: "Atkinson Hyperlegible", sans-serif;
}

body {
    color: hsl(0, 0%, 40%);
}

p {
    font-weight: normal;
    font-size: 1.1em;
    line-height: 1.3em;
    margin: 1.2em 0 1.2em 0;
}

a {
    color: hsl(55, 17%, 49%);
    background-color: white;
    font-weight: bold;
}

a:visited {
    color: hsl(55, 17%, 49%);
    background-color: white;
}

a:hover {
    color: white;
    background-color: hsl(55, 17%, 49%);
}

a {
    text-decoration: none !important;
}


#mynetwork {
    width: 100%;
    height: 900px;
    background-color: #ffffff;
    border: 1px solid lightgray;
    position: relative;
    float: left;
}
             
#loadingBar {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 900px;
    background-color: rgba(200,200,200,0.8);
    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -ms-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;
    opacity: 1;
}

#bar {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 20px;
    height: 20px;
    margin: auto auto auto auto;
    border-radius: 11px;
    border: 2px solid rgba(30,30,30,0.05);
    background: rgb(0, 173, 246); /* old browsers */
    box-shadow: 2px 0px 4px rgba(0,0,0,0.4);
}

#border {
    position: absolute;
    top: 10px;
    left: 10px;
    width: 500px;
    height: 23px;
    margin: auto auto auto auto;
    box-shadow: 0px 0px 4px rgba(0,0,0,0.2);
    border-radius: 10px;
}

#text {
    position: absolute;
    top: 8px;
    left: 530px;
    width: 30px;
    height: 50px;
    margin: auto auto auto auto;
    font-size: 22px;
    color: #000000;
}

div.outerBorder {
    position: relative;
    top: 400px;
    width: 600px;
    height: 44px;
    margin: auto auto auto auto;
    border: 8px solid rgba(0,0,0,0.1);
    background: rgb(252,252,252); /* old browsers */
    background: -moz-linear-gradient(top,  rgba(252,252,252,1) 0%, rgba(237,237,237,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(252,252,252,1)), color-stop(100%,rgba(237,237,237,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  rgba(252,252,252,1) 0%,rgba(237,237,237,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  rgba(252,252,252,1) 0%,rgba(237,237,237,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  rgba(252,252,252,1) 0%,rgba(237,237,237,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom,  rgba(252,252,252,1) 0%,rgba(237,237,237,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fcfcfc', endColorstr='#ededed',GradientType=0 ); /* IE6-9 */
    border-radius: 72px;
    box-shadow: 0px 0px 10px rgba(0,0,0,0.2);
}
             
#config {
    float: left;
    width: 400px;
    height: 600px;
}
    </style>

  </head>

  <body>
    <div id="main" class="container-fluid">
      <h1>{{ domain.name }}</h1>
      <nav>
	<div class="nav nav-tabs" id="nav-tab" role="tablist">
	  <button class="nav-link active" id="nav-graph-tab" data-bs-toggle="tab" data-bs-target="#nav-graph" type="button" role="tab" aria-controls="nav-graph" aria-selected="true"><strong>graph</strong></button>
	  <button class="nav-link" id="nav-ent-tab" data-bs-toggle="tab" data-bs-target="#nav-ent" type="button" role="tab" aria-controls="nav-ent" aria-selected="false"><strong>entities</strong></button>
	  <button class="nav-link" id="nav-rel-tab" data-bs-toggle="tab" data-bs-target="#nav-rel" type="button" role="tab" aria-controls="nav-rel" aria-selected="false"><strong>relations</strong></button>
	</div>
      </nav>
      <div class="tab-content" id="nav-tabContent">

	<!-- graph -->
	<div class="tab-pane fade show active" id="nav-graph" role="tabpanel" aria-labelledby="nav-graph-tab" tabindex="0">
	  <div class="container">
	    <details>
	      <summary>options</summary>
	      <div id="config"></div>
	    </details>

	    <div class="card" style="width: 100%">
	      <div id="mynetwork" class="card-body"></div>
	    </div>

	    <div id="loadingBar">
	      <div class="outerBorder">
		<div id="text">0%</div>
		<div id="border">
		  <div id="bar"></div>
		</div>
	      </div>
	    </div>
	  </div>
	</div>

	<!-- entities -->
	<div class="tab-pane fade show active" id="nav-ent" role="tabpanel" aria-labelledby="nav-ent-tab" tabindex="1">
	  <div class="container">
	    <p>ENTITIES</p>
	  </div>
	</div>

	<!-- relations -->
	<div class="tab-pane fade show active" id="nav-rel" role="tabpanel" aria-labelledby="nav-rel-tab" tabindex="2">
	  <div class="container">
	    <p>RELATIONS</p>
	  </div>
	</div>

      </div>
    </div>
        
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.8/js/bootstrap.bundle.min.js"
      integrity="sha512-HvOjJrdwNpDbkGJIG2ZNqDlVqMo77qbs4Me4cah0HoDrfhrbA+8SBlZn1KrvAQw7cILLPFJvdwIgphzQmMm+Pw=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
      ></script>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/vis-network/10.0.2/dist/vis-network.min.js"
      integrity="sha512-5qYRU42HLweh0Ehlsu9bVWc13gwZviSNGsnfx+PqGRQRM4NltzGzb8dO3WY20CTsbkTBzhyKlso9cfYz2A5lOQ=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
      ></script>

    <script>
const NODES = {{ nodes }};
const EDGES = {{ edges }};

// initialize global variables
var allNodes;
var allEdges;

var nodeColors;
var originalNodes;
var network;

var filter = {
    "item": "",
    "property": "",
    "value": [],
};

var options = {
    "configure": {
        "enabled": true,
        "filter": [
	    "physics"
        ]
    },
    "edges": {
        "color": {
	    "inherit": true
        },
        "smooth": {
	    "enabled": true,
	    "type": "dynamic"
        }
    },
    "interaction": {
        "dragNodes": true,
        "hideEdgesOnDrag": false,
        "hideNodesOnDrag": false
    },
    "physics": {
        "enabled": true,
        "stabilization": {
	    "enabled": true,
	    "fit": true,
	    "iterations": 1000,
	    "onlyDynamicEdges": false,
	    "updateInterval": 50
        },
	"barnesHut": {
	    "gravitationalConstant": -2800,
	    "centralGravity": 0.65,
	    "springLength": 90,
	    "springConstant": 0.285,
	    "damping": 0.54
	},
	"minVelocity": 0.75
    }
};


function neighbourhoodHighlight (params) {
    allNodes = nodes.get({ returnType: "Object" });

    // if something is selected:
    if (params.nodes.length > 0) {
	var highlightActive = true;

	var i, j;
	var selectedNode = params.nodes[0];
	var degrees = 2;

	// mark all nodes as hard to read.
	for (let nodeId in allNodes) {
	    // nodeColors[nodeId] = allNodes[nodeId].color;
	    allNodes[nodeId].color = "rgba(200,200,200,0.5)";

	    if (allNodes[nodeId].hiddenLabel === undefined) {
		allNodes[nodeId].hiddenLabel = allNodes[nodeId].label;
		allNodes[nodeId].label = undefined;
	    }
	}

	var connectedNodes = network.getConnectedNodes(selectedNode);
	var allConnectedNodes = [];

	// get the second degree nodes
	for (i = 1; i < degrees; i++) {
	    for (j = 0; j < connectedNodes.length; j++) {
		allConnectedNodes = allConnectedNodes.concat(
		    network.getConnectedNodes(connectedNodes[j])
		);
	    }
	}

	// all second degree nodes get a different color and their label back
	for (i = 0; i < allConnectedNodes.length; i++) {
	    allNodes[allConnectedNodes[i]].color = "rgba(150,150,150,0.75)";

	    if (allNodes[allConnectedNodes[i]].hiddenLabel !== undefined) {
		allNodes[allConnectedNodes[i]].label = allNodes[allConnectedNodes[i]].hiddenLabel;
		allNodes[allConnectedNodes[i]].hiddenLabel = undefined;
	    }
	}

	// all first degree nodes get their own color and their label back
	for (i = 0; i < connectedNodes.length; i++) {
	    allNodes[connectedNodes[i]].color = nodeColors[connectedNodes[i]];

	    if (allNodes[connectedNodes[i]].hiddenLabel !== undefined) {
		allNodes[connectedNodes[i]].label = allNodes[connectedNodes[i]].hiddenLabel;
		allNodes[connectedNodes[i]].hiddenLabel = undefined;
	    }
	}

	// the main node gets its own color and its label back.
	// allNodes[selectedNode].color = undefined;
	allNodes[selectedNode].color = nodeColors[selectedNode];

	if (allNodes[selectedNode].hiddenLabel !== undefined) {
	    allNodes[selectedNode].label = allNodes[selectedNode].hiddenLabel;
	    allNodes[selectedNode].hiddenLabel = undefined;
	}
    }
    else if (highlightActive === true) {
	// reset all nodes
	for (let nodeId in allNodes) {
	    allNodes[nodeId].color = nodeColors[nodeId];
	    // delete allNodes[nodeId].color;

	    if (allNodes[nodeId].hiddenLabel !== undefined) {
		allNodes[nodeId].label = allNodes[nodeId].hiddenLabel;
		allNodes[nodeId].hiddenLabel = undefined;
	    }
	}

	highlightActive = false;
    }

    // transform the object into an array
    var updateArray = [];

    if (params.nodes.length > 0) {
	for (let nodeId in allNodes) {
	    if (allNodes.hasOwnProperty(nodeId)) {
		updateArray.push(allNodes[nodeId]);
	    }
	}

	nodes.update(updateArray);
    }
    else {
	for (let nodeId in allNodes) {
	    if (allNodes.hasOwnProperty(nodeId)) {
		updateArray.push(allNodes[nodeId]);
	    }
	}

	nodes.update(updateArray);
    }
}


function filterHighlight (params) {
    allNodes = nodes.get({ returnType: "Object" });

    // if something is selected:
    if (params.nodes.length > 0) {
	var filterActive = true;
	let selectedNodes = params.nodes;

	// hiding all nodes and saving the label
	for (let nodeId in allNodes) {
	    allNodes[nodeId].hidden = true;

	    if (allNodes[nodeId].savedLabel === undefined) {
		allNodes[nodeId].savedLabel = allNodes[nodeId].label;
		allNodes[nodeId].label = undefined;
	    }
	}

	for (let i = 0; i < selectedNodes.length; i++) {
	    allNodes[selectedNodes[i]].hidden = false;

	    if (allNodes[selectedNodes[i]].savedLabel !== undefined) {
		allNodes[selectedNodes[i]].label = allNodes[selectedNodes[i]].savedLabel;
		allNodes[selectedNodes[i]].savedLabel = undefined;
	    }
	}

    }
    else if (filterActive === true) {
	// reset all nodes
	for (let nodeId in allNodes) {
	    allNodes[nodeId].hidden = false;

	    if (allNodes[nodeId].savedLabel !== undefined) {
		allNodes[nodeId].label = allNodes[nodeId].savedLabel;
		allNodes[nodeId].savedLabel = undefined;
	    }
	}

	filterActive = false;
    }

    // transform the object into an array
    var updateArray = [];

    if (params.nodes.length > 0) {
	for (let nodeId in allNodes) {
	    if (allNodes.hasOwnProperty(nodeId)) {
		updateArray.push(allNodes[nodeId]);
	    }
	}

	nodes.update(updateArray);
    }
    else {
	for (let nodeId in allNodes) {
	    if (allNodes.hasOwnProperty(nodeId)) {
		updateArray.push(allNodes[nodeId]);
	    }
	}

	nodes.update(updateArray);
    }
}


function selectNode (nodes) {
    network.selectNodes(nodes);
    neighbourhoodHighlight({ nodes: nodes });

    return nodes;
}


function selectNodes (nodes) {
    network.selectNodes(nodes);
    filterHighlight({nodes: nodes});

    return nodes;
}


function highlightFilter (filter) {
    let selectedNodes = []
    let selectedProp = filter['property']

    if (filter['item'] === 'node') {
	let allNodes = nodes.get({ returnType: "Object" });

	for (let nodeId in allNodes) {
	    if (allNodes[nodeId][selectedProp] && filter['value'].includes((allNodes[nodeId][selectedProp]).toString())) {
		selectedNodes.push(nodeId)
	    }
	}
    }
    else if (filter['item'] === 'edge'){
	let allEdges = edges.get({returnType: 'object'});

	// check if the selected property exists for selected edge and
	// select the nodes connected to the edge
	for (let edge in allEdges) {
	    if (allEdges[edge][selectedProp] && filter['value'].includes((allEdges[edge][selectedProp]).toString())) {
		selectedNodes.push(allEdges[edge]['from'])
		selectedNodes.push(allEdges[edge]['to'])
	    }
	}
    }

    selectNodes(selectedNodes)
}


// this method is responsible for drawing the graph, returns the drawn network
function drawGraph () {
    var container = document.getElementById('mynetwork');

    // parse the nodes and edges defined in Python
    var nodes = new vis.DataSet(NODES);
    var edges = new vis.DataSet(EDGES);
		  
    nodeColors = {};
    allNodes = nodes.get({ returnType: "Object" });

    for (nodeId in allNodes) {
        nodeColors[nodeId] = allNodes[nodeId].color;
    }

    allEdges = edges.get({ returnType: "Object" });

    // load the nodes and edges into the graph
    var data = {
	nodes: nodes,
	edges: edges,
    };
                
    // if the network needs to display a configure window, add its
    // <div/>
    options.configure["container"] = document.getElementById("config");

    network = new vis.Network(container, data, options);
                  
    network.on("stabilizationProgress", function(params) {
        document.getElementById('loadingBar').removeAttribute("style");

        var maxWidth = 496;
        var minWidth = 20;
        var widthFactor = params.iterations/params.total;
        var width = Math.max(minWidth,maxWidth * widthFactor);

        document.getElementById('bar').style.width = width + 'px';
        document.getElementById('text').innerHTML = Math.round(widthFactor*100) + '%';
    });

    network.once("stabilizationIterationsDone", function() {
        document.getElementById('text').innerHTML = '100%';
        document.getElementById('bar').style.width = '496px';
        document.getElementById('loadingBar').style.opacity = 0;

        // really clean the DOM element
        setTimeout(
	    function () {
		document.getElementById('loadingBar').style.display = 'none';
	    },
	    500
	);
    });

    return network;
}

drawGraph();
    </script>

  </body>
</html>
