Merge pull request #2 from Huguet57/master

Implementació de la barra de cerca
diff --git a/api.php b/api.php
index 5bdbb00..26b655a 100644
--- a/api.php
+++ b/api.php
@@ -2,13 +2,13 @@
 require_once("config.php");
 
 class write {
-  public static function do($json) {
+  public static function funct($json) {
     print_r(json_encode($json));
     exit();
   }
 
   public static function error($n, $msg) {
-    self::do(["error" => $n, "msg" => $msg]);
+    self::funct(["error" => $n, "msg" => $msg]);
   }
 }
 
diff --git a/css/autocomplete.css b/css/autocomplete.css
new file mode 100644
index 0000000..148b882
--- /dev/null
+++ b/css/autocomplete.css
@@ -0,0 +1,54 @@
+* {

+  box-sizing: border-box;

+}

+

+.autocomplete {

+  /*the container must be positioned relative:*/

+  position: relative;

+  display: inline-block;

+}

+input {

+  border: 1px solid transparent;

+  background-color: #f1f1f1;

+  padding: 10px;

+  font-size: 16px;

+}

+input[type=text] {

+  background-color: #f1f1f1;

+  width: 100%;

+}

+input[type=submit] {

+  background-color: DodgerBlue;

+  color: #fff;

+  cursor: pointer;

+}

+.autocomplete-items {

+  position: absolute;

+  border: 1px solid #d4d4d4;

+  border-bottom: none;

+  border-top: none;

+  z-index: 99;

+  /*position the autocomplete items to be the same width as the container:*/

+  top: auto;

+  left: 0;

+  right: 0;

+  color: black;

+}

+.autocomplete-items div {

+  padding: 10px;

+  cursor: pointer;

+  background-color: #fff; 

+  border-bottom: 1px solid #d4d4d4; 

+}

+.autocomplete-items div:hover {

+  /*when hovering an item:*/

+  background-color: #e9e9e9; 

+}

+.autocomplete-active {

+  /*when navigating through the items using the arrow keys:*/

+  background-color: DodgerBlue !important; 

+  color: #ffffff; 

+}

+.autocomplete-year {

+	color: #ccc;

+}
\ No newline at end of file
diff --git a/css/dialog.css b/css/dialog.css
new file mode 100644
index 0000000..3f2b73b
--- /dev/null
+++ b/css/dialog.css
@@ -0,0 +1,53 @@
+#dialog-vertex, #dialog-edge{

+  padding: 8px;

+  user-select: auto;

+}

+

+#dialog h2 {

+  font-weight: bold;

+  font-size: 20px;

+}

+

+#dialog h3 {

+  font-weight: bold;

+  font-size: 16px;

+  margin-bottom: 0;

+}

+

+#quit-dialog, #quit2-dialog {

+  position: absolute;

+  top: 8px;

+  right: 8px;

+}

+

+#min-dialog, #max-dialog {

+  position: absolute;

+  top: 8px;

+  right: 48px;

+}

+

+#min-dialog {

+  display: none;

+}

+

+#summary-dialog {

+  position: absolute;

+  top: 0px;

+  left: 0px;

+  width: 100%;

+  height: 100px;

+  background-color: white;

+  color: black;

+  z-index: 120;

+}

+

+#summary-vertex {

+  padding: 8px;

+  user-select: auto;

+}

+

+#summary-dialog h2 {

+  font-weight: bold;

+  font-size: 20px;

+  margin: 0;

+}
\ No newline at end of file
diff --git a/css/modal.css b/css/modal.css
new file mode 100644
index 0000000..cdcba8c
--- /dev/null
+++ b/css/modal.css
@@ -0,0 +1,38 @@
+/* The Modal (background) */

+.modal {

+    display: none; /* Hidden by default */

+    position: fixed; /* Stay in place */

+    z-index: 1; /* Sit on top */

+    padding-top: 100px; /* Location of the box */

+    left: 0;

+    top: 0;

+    width: 100%; /* Full width */

+    height: 100%; /* Full height */

+    overflow: auto; /* Enable scroll if needed */

+    background-color: rgb(0,0,0); /* Fallback color */

+    background-color: rgba(0,0,0,0.4); /* Black w/ opacity */

+}

+

+/* Modal Content */

+.modal-content {

+    background-color: #fefefe;

+    margin: auto;

+    padding: 20px;

+    border: 1px solid #888;

+    width: 80%;

+}

+

+/* The Close Button */

+.closeBox {

+    color: #aaaaaa;

+    float: right;

+    font-size: 28px;

+    font-weight: bold;

+}

+

+.closeBox:hover,

+.closeBox:focus {

+    color: #000;

+    text-decoration: none;

+    cursor: pointer;

+}
\ No newline at end of file
diff --git a/css/styles.css b/css/styles.css
index 5c6b812..0343114 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -37,58 +37,26 @@
   z-index: 110;
 }
 
-#dialog-vertex, #dialog-edge{
-  padding: 8px;
-  user-select: auto;
+.addegde-button {
+	line-height: 20px;
+	font-size: 20px;
+	width: 100%;
+	background-color: rgba(200,0,0,0.4);
 }
 
-#dialog h2 {
-  font-weight: bold;
-  font-size: 20px;
+.addegde-msg {
+	display: none;
+	line-height: 20px;
+	font-size: 20px;
+	width: 100%;
+	background-color: rgba(0,200,0,0.4);
 }
 
-#dialog h3 {
-  font-weight: bold;
-  font-size: 16px;
-  margin-bottom: 0;
-}
-
-#quit-dialog, #quit2-dialog {
+#searchButton {
   position: absolute;
-  top: 8px;
-  right: 8px;
-}
-
-#min-dialog, #max-dialog {
-  position: absolute;
-  top: 8px;
-  right: 48px;
-}
-
-#min-dialog {
-  display: none;
-}
-
-#summary-dialog {
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  width: 100%;
-  height: 100px;
-  background-color: white;
-  color: black;
-  z-index: 120;
-}
-
-#summary-vertex {
-  padding: 8px;
-  user-select: auto;
-}
-
-#summary-dialog h2 {
-  font-weight: bold;
-  font-size: 20px;
-  margin: 0;
+  right: 10px;
+  bottom: 110px;
+  z-index: 100;
 }
 
 #zoomin {
diff --git a/graf.php b/graf.php
index c0837c0..486afde 100644
--- a/graf.php
+++ b/graf.php
@@ -22,7 +22,13 @@
     <meta name=viewport content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <link rel="manifest" href="manifest.json">
 
-    <link rel="stylesheet" href="css/styles.css">
+    <link rel="stylesheet" href="css/dialog.css">
+	<link rel="stylesheet" href="css/styles.css">
+      
+    <!-- Seach Bar CSS files -->  
+    <link rel="stylesheet" href="./css/modal.css"></link>
+    <link rel="stylesheet" href="./css/autocomplete.css"></link>
+
     <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
     <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.blue_grey-blue.min.css" />
 
@@ -33,9 +39,10 @@
     <meta name="apple-mobile-web-app-status-bar-style" content="black">
   </head>
   <body>
-    <button id="zoomin" class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored"><i class="material-icons">zoom_in</i></button>
+	<button id="searchButton"class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored"><i class="material-icons">search</i></button>
+	<button id="zoomin" class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored"><i class="material-icons">zoom_in</i></button>
     <button id="zoomout" class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored"><i class="material-icons">zoom_out</i></button>
-
+	
     <div id="backdrop-container" style="display: none;">
       <div id="backdrop"></div>
     </div>
@@ -47,11 +54,18 @@
         <ul>
           <li><b>Any:</b> <span data-fill="year"></span></li>
           <li><b>Sexe:</b> <span data-fill="sex"></span></li>
-          <li><b>ID:</b> <span data-fill="id"></span></li>
+          <li><b>ID:</b> <span data-fill="id" id="node-id"></span></li>
         </ul>
-        <h3>Arestes (<span data-fill="n-edges"></span>):</h3>
-        <ul data-fill="edges">
-        </ul>
+		<div id="addedge-box">
+			<div id="addedge-button" class="addegde-button"><h2>Afegir una nova aresta +</h2></div>
+			<div id="addedge-msg" class="addegde-msg"><h2>Aresta afegida!</h2></div>
+			<input id="addedge-input" type="text" style="display:none;">
+		</div>	
+		<div id="edge-list">
+			<h3>Arestes (<span data-fill="n-edges"></span>):</h3>
+			<ul data-fill="edges">
+			</ul>
+		</div>
       </div>
       <div id="dialog-edge" style="display: none;">
       </div>
@@ -64,11 +78,38 @@
         <p><span data-fill="year"></span>, <span data-fill="sex"></span>, <span data-fill="id"></span></p>
       </div>
     </div>
+    <!-- Search Box -->
+    <div id="searchBox" class="modal">
+      <!-- Search Box Content -->
+      <div class="modal-content">
+        <span class="closeBox">&times;</span>
 
+        <div class="in-box-content">
+            <center>
+                <form autocomplete="off">
+                  <div class="autocomplete">
+                    <input id="searchInput" type="text" placeholder="Busca una persona al graf">
+                  </div>
+                </form>
+            </center>
+        </div>
+      </div>
+    </div>
+       
     <div id="graf"></div>
-
+    
     <script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.2.0/sigma.min.js"></script>
-    <script src="js/script.js"></script>
+    <script>
+		// Initialize the graph and the sigma
+		var s, graf;
+	</script>
+	   
+    <!-- Search Bar JS files -->
+    <script src="./js/modal.js"></script>
+    <script src="./js/autocomplete.js" ></script>
+	
+	<script src="js/script.js"></script>
+	
     <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
     <!--<script src="js/service-worker.js"></script>-->
   </body>
diff --git a/js/autocomplete.js b/js/autocomplete.js
new file mode 100644
index 0000000..4c32827
--- /dev/null
+++ b/js/autocomplete.js
@@ -0,0 +1,199 @@
+function autocomplete(inp, obj, act) {

+  /*the autocomplete function takes two arguments,

+  the text field element and an objay of possible autocompleted values:*/

+  var currentFocus;

+  /*execute a function when someone writes in the text field:*/

+  inp.addEventListener("input", function(e) {

+      var a, b, i, val = this.value;

+      /*close any already open lists of autocompleted values*/

+      closeAllLists();

+      if (!val || val.length < 3) return false;

+      currentFocus = -1;

+	  

+      /*create a DIV element that will contain the items (values):*/

+      a = document.createElement("DIV");

+      a.setAttribute("id", this.id + "autocomplete-list");

+      a.setAttribute("class", "autocomplete-items");

+      /*append the DIV element as a child of the autocomplete container:*/

+      this.parentNode.appendChild(a);

+	  

+      /*for each item in the object...*/

+      for (node in obj) {

+		var nomNode = obj[node].name;

+				

+		if (nomNode.toUpperCase().includes(val.toUpperCase())) {

+			var parts = nomNode.toUpperCase().split(val.toUpperCase());

+			

+			  /*create a DIV element for each matching element:*/

+			  b = document.createElement("DIV");

+			  

+			  /*make the matching letters bold:*/

+			  if (parts[0].length == 0) b.innerHTML = "";

+			  else b.innerHTML = nomNode.substr(0, parts[0].length);

+			  b.innerHTML += "<strong>" + nomNode.substr(parts[0].length, val.length) + "</strong>";

+			  b.innerHTML += nomNode.substr(parts[0].length + val.length);

+			  b.innerHTML += " - <span class='autocomplete-year'>" + obj[node].year + "</span>";

+			  /*insert a input field that will hold the current object item's value:*/

+			  b.innerHTML += "<input type='hidden' value='" + node + "'>";

+			 

+			 /*execute a function when someone clicks on the item value (DIV element):*/

+			  b.addEventListener("click", function(e) {

+				  /*insert the value for the autocomplete text field:*/

+				  var n = this.getElementsByTagName("input")[0].value;

+				  inp.value = obj[n].name;

+				  

+				  switch (act) {

+					case "search":

+					  // Move camera to desired node

+						cameraGoto(obj[n].x, obj[n].y);

+					  // Close the search box

+					  var searchBox = document.getElementById('searchBox');

+					  searchBox.style.display = "none";

+					  

+					  // Empty the input bar

+					  var searchInput = document.getElementById('searchInput');

+					  searchInput.value = "";

+						break;

+					case "addEdge":					

+					  // Add an edge between A and B

+					  var sourceID = document.getElementById("node-id").innerText.substr(1);

+					  var edgeName = Math.min(sourceID, n) + "_" + Math.max(sourceID, n);

+					  if (!graf.edges[edgeName]) {

+						  graf.edges[edgeName] = {

+							  a: Math.min(sourceID, n),

+							  b: Math.max(sourceID, n),

+							  votes: 1

+						  };

+						  

+						  // Temporary fix, just for testing

+						  s.graph.addEdge({

+							  id: edgeName,

+							  source: sourceID,

+							  target: n,

+							  size: 0.5

+							});

+							

+						  clickNode(s, sourceID, s.graph.neighbors(sourceID));

+						  dialog.max();

+						  

+						  // Give a message of validation

+						  var edgeMSG = document.querySelector("#addedge-msg");

+						  addedEdgeMSG(edgeMSG);

+

+					  } else {

+						  alert("Edge already exists");

+					  }

+									  

+					  // Empty the input bar

+					  var addEdgeInput = document.getElementById('addedge-input');

+					  addEdgeInput.value = "";

+					  

+					  // Return to default view

+					  document.querySelector("#addedge-input").style.display = "none";  

+					  document.querySelector("#edge-list").style.display = "block";

+					  break;

+				  }

+				  

+				  /*close the list of autocompleted values,

+				  (or any other open lists of autocompleted values:*/

+				  closeAllLists();

+			  });

+			  

+			  // Set "data-edges" attribute and compare with others

+			  var nEdges = Object.keys(s.graph.neighbors(node)).length;

+			  b.dataset.edges = nEdges;

+			  var inserted = false;

+			  

+			  // Sort nodes by degree

+			  for (i in a.childNodes) {

+				  var child = a.childNodes[i];

+				  if (!child.dataset) break;

+				  if (nEdges > child.dataset.edges) {

+					  a.insertBefore(b, child);

+					  inserted = true;

+					  break;

+				  }

+			  }

+			  

+			  if (!inserted) {

+				  a.appendChild(b);

+				  console.log("inserted");

+			  }

+		  }

+        }

+  });

+  /*execute a function presses a key on the keyboard:*/

+  inp.addEventListener("keydown", function(e) {

+      var x = document.getElementById(this.id + "autocomplete-list");

+      if (x) x = x.getElementsByTagName("div");

+      if (e.keyCode == 40) {

+        /*If the objow DOWN key is pressed,

+        increase the currentFocus variable:*/

+        currentFocus++;

+        /*and and make the current item more visible:*/

+        addActive(x);

+      } else if (e.keyCode == 38) { //up

+        /*If the objow UP key is pressed,

+        decrease the currentFocus variable:*/

+        currentFocus--;

+        /*and and make the current item more visible:*/

+        addActive(x);

+      } else if (e.keyCode == 13) {

+        /*If the ENTER key is pressed, prevent the form from being submitted,*/

+        e.preventDefault();

+        if (currentFocus > -1) {

+          /*and simulate a click on the "active" item:*/

+          if (x) x[currentFocus].click();

+        }

+      }

+  });

+  function addActive(x) {

+    /*a function to classify an item as "active":*/

+    if (!x) return false;

+    /*start by removing the "active" class on all items:*/

+    removeActive(x);

+    if (currentFocus >= x.length) currentFocus = 0;

+    if (currentFocus < 0) currentFocus = (x.length - 1);

+    /*add class "autocomplete-active":*/

+    x[currentFocus].classList.add("autocomplete-active");

+  }

+  function removeActive(x) {

+    /*a function to remove the "active" class from all autocomplete items:*/

+    for (var i = 0; i < x.length; i++) {

+      x[i].classList.remove("autocomplete-active");

+    }

+  }

+  function closeAllLists(elmnt) {

+    /*close all autocomplete lists in the document,

+    except the one passed as an argument:*/

+    var x = document.getElementsByClassName("autocomplete-items");

+    for (var i = 0; i < x.length; i++) {

+      if (elmnt != x[i] && elmnt != inp) {

+        x[i].parentNode.removeChild(x[i]);

+      }

+    }

+  }

+  /*execute a function when someone clicks in the document:*/

+  document.addEventListener("click", function (e) {	  

+      closeAllLists(e.target);

+  });

+}

+

+function addedEdgeMSG(edgeMSG) {

+  var opacity = 8;

+  edgeMSG.style.display = "block";

+  

+  var opacityChange = window.setInterval(function() {

+	  edgeMSG.style.color = "rgba(0,0,0, "+ opacity/10 +")";

+	  var edgeList = document.querySelector("#edge-list ul");

+	  edgeList.style.backgroundColor = "rgba(0,200,0, "+ opacity/10 +")";

+	  --opacity;

+	  console.log(opacity);

+	}, 100);

+	

+  var backToDef = window.setTimeout(function() {

+	  edgeMSG.style.display = "none";

+	  edgeMSG.style.color = "black";

+	  window.clearInterval(opacityChange);

+	}, 1000);

+}
\ No newline at end of file
diff --git a/js/modal.js b/js/modal.js
new file mode 100644
index 0000000..179e936
--- /dev/null
+++ b/js/modal.js
@@ -0,0 +1,34 @@
+// Get the box

+var searchBox = document.getElementById('searchBox');

+

+// Get the button that opens the box

+var searchButton = document.getElementById("searchButton");

+

+// Get the <span> element that closes the box

+var closeBox = document.getElementsByClassName("closeBox")[0];

+

+// When the user clicks the button, open the box 

+searchButton.onclick = function() {

+	dialog.close();

+    searchBox.style.display = "block";

+	// Focus on the search input bar

+	document.getElementById("searchInput").focus();

+}

+

+// When the user clicks on <span> (x), close the box

+closeBox.onclick = function() {

+    searchBox.style.display = "none";

+	// Empty the input bar

+	var searchInput = document.getElementById('searchInput');

+	searchInput.value = "";

+}

+

+// When the user clicks anywhere outside of the box, close it

+window.onclick = function(event) {

+    if (event.target == searchBox) {

+        searchBox.style.display = "none";

+		// Empty the input bar

+		var searchInput = document.getElementById('searchInput');

+		searchInput.value = "";

+    }

+}
\ No newline at end of file
diff --git a/js/script.js b/js/script.js
index 4e74231..6fcf868 100644
--- a/js/script.js
+++ b/js/script.js
@@ -1,5 +1,3 @@
-var s, graf;
-
 function xhr(method, url, params, callback) {
   var http = new XMLHttpRequest();
   if (method == "POST") {
@@ -65,6 +63,7 @@
 
     s.graph.nodes().forEach(function(n) {
       n.color = n.originalColor;
+	  n.label = n.originalLabel;
     });
 
     s.graph.edges().forEach(function(e) {
@@ -80,6 +79,13 @@
   min: function() {
     document.querySelector("#dialog").style.display = "none";
     document.querySelector("#summary-dialog").style.display = "block";
+  },
+  addEdge: function() {
+	document.querySelector("#addedge-input").style.display = "block";  
+	document.querySelector("#edge-list").style.display = "none";
+	autocomplete(document.getElementById("addedge-input"), graf.nodes, "addEdge");
+	// Focus on the addEdge input bar
+	document.getElementById("addedge-input").focus();
   }
 };
 
@@ -124,6 +130,7 @@
         s.graph.addNode({
           id: graf.nodes[i].id,
           label: graf.nodes[i].name,
+		  originalLabel: graf.nodes[i].name,
           x: graf.nodes[i].x,
           y: graf.nodes[i].y,
           size: 10,
@@ -145,32 +152,14 @@
         var nodeId = e.data.node.id,
             toKeep = s.graph.neighbors(nodeId);
         //toKeep[nodeId] = e.data.node;
-
-        s.graph.nodes().forEach(function(n) {
-          if (toKeep[n.id] || n.id == nodeId) {
-            n.color = n.originalColor;
-          } else {
-            n.color = '#333';
-          }
-        });
-
-        s.graph.edges().forEach(function(e) {
-          if ((e.source == nodeId || e.target == nodeId) && (toKeep[e.source] || toKeep[e.target])) {
-            e.color = '#fff';
-          } else {
-            e.color = '#333';
-          }
-        });
-
-        s.refresh();
-
-        dialog.show(nodeId, toKeep);
+		clickNode(s, nodeId, toKeep);
       });
 
       document.querySelector("#quit-dialog").addEventListener("click", dialog.close);
       document.querySelector("#quit2-dialog").addEventListener("click", dialog.close);
       document.querySelector("#max-dialog").addEventListener("click", dialog.max);
       document.querySelector("#min-dialog").addEventListener("click", dialog.min);
+	  document.querySelector("#addedge-button").addEventListener("click", dialog.addEdge);
 
       document.querySelector("#zoomin").addEventListener("click", function() {
         s.camera.goTo({
@@ -185,7 +174,40 @@
       });
 
       s.refresh();
+	  autocomplete(document.getElementById("searchInput"), graf.nodes, "search");
     });
 }
 
+function cameraGoto(nodeX, nodeY) {
+	sigma.misc.animation.camera( s.camera,
+	  { x: nodeX, y: nodeY, ratio: 1 },
+	  { duration: s.settings('animationsTime') || 300 }
+	);
+}
+
+function clickNode(s, nodeId, toKeep) {
+	s.graph.nodes().forEach(function(n) {
+          if (toKeep[n.id] || n.id == nodeId) {
+            n.color = n.originalColor;
+			n.label = n.originalLabel;
+			console.log("Hola");
+          } else {
+            n.color = '#333';
+			n.label = '';
+          }
+        });
+
+        s.graph.edges().forEach(function(e) {
+          if ((e.source == nodeId || e.target == nodeId) && (toKeep[e.source] || toKeep[e.target])) {
+            e.color = '#fff';
+          } else {
+            e.color = '#333';
+          }
+        });
+
+        s.refresh();
+
+        dialog.show(nodeId, toKeep);
+}
+
 window.addEventListener("load", init);