Add files via upload
diff --git a/README.md b/README.md
index da0248d..27b3fd4 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,74 @@
-# Graf Alternatiu de la FME
-## Independent interface for FME's graph
-
-This project is an alternative read-only user-interface for FME's graph. It is compatible with mobile devices.
-
-## Things done
-Search-bar with autocomplete<br />
-Dialog for node statistics<br />
-Easter-egg<br />
-Basic limit-years<br />
-Circle-mode<br />
-
-
-## Things to do
-Make limit-years prettier.<br />
-Make statistics about the graph: biggest K_n, diameter, etc...<br />
-Make zoom minimum even smaller so that circle-mode can fit in a window.<br />
+# Graf Alternatiu de la FME

+## Independent interface for FME's graph

+

+This project is an alternative read-only user-interface for FME's graph. It is compatible and user-friendly with mobile devices.

+

+## How to contibute

+For the sake of safe and understandable code, we divide our JS scripts and CSS styles into multiples files. But, to make the page load faster, before a pull request, you should concatenate these files in the following manner. <br><br>

+

+In the file script.js, concatenate: (order is important!)

+<ol>

+	<li>circle-mode.js</li>

+	<li>graf.js</li>

+	<li>limit-years.js</li>

+	<li>search-bar.js</li>

+	<li>dialog.js</li>

+	<li>camera.js</li>

+	<li>easter-eg.js</li>

+</ol>

+<br><br>

+

+In the file styles.css, concatenate: (order is important!)

+<ol>

+	<li>general.css</li>

+	<li>graf.css</li>

+	<li>dialog.css</li>

+	<li>option-buttons.css</li>

+	<li>year-list.css</li>

+	<li>search-bar.css</li>

+</ol>

+

+## Things to do

+<ul>

+	<li>

+		Make limit-years prettier and incluse/exclusive with double check-boxes

+	</li>

+	<li>

+		Make statistics about the graph: biggest K_n, diameter, etc...

+	</li>

+	<li>

+		Make circle-mode + year-limit = smaller-circle-mode

+	</li>

+	<li>

+		Modify the autocomplete so that eliminated nodes don't appear

+	</li>

+	<li>

+		Separate non-connex nodes in circle-mode

+	</li>

+</ul>

+

+

+## Things done

+<ul>

+	<li>

+		Search bar with autocomplete

+	</li>

+	<li>

+		Dialog for nodes

+	</li>

+	<li>

+		Easter egg 

+	</li>

+	<li>

+		Limit years to a specific range

+	</li>

+	<li>

+		Circle visualization

+	</li>

+	<li>

+		Hide nodes inside erasing rectangle and very far from origin

+	</li>

+	<li>

+		Hide zoom buttons for tactile devices

+	</li>

+</ul>

diff --git a/api.php b/api.php
index 176fdfe..e6957b6 100644
--- a/api.php
+++ b/api.php
@@ -1,33 +1,33 @@
-<?php
-require_once("config.php");
-
-class write {
-  public static function output($json) {
-    print_r(json_encode($json));
-    exit();
-  }
-
-  public static function error($n, $msg) {
-    self::output(["error" => $n, "msg" => $msg]);
-  }
-}
-
-function get_graph() {
-  global $conf;
-  return json_decode(file_get_contents($conf["apiurl"]), true);
-}
-
-if (!isset($_GET["action"])) {
-  write::error(1, "No action provided");
-}
-
-switch ($_GET["action"]) {
-  case "getgraf":
-  $graf = file_get_contents($conf["apiurl"]);
-  echo $graf;
-  break;
-
-  default:
-  write::error(2, "Unknown action");
-}
-?>
+<?php

+require_once("config.php");

+

+class write {

+  public static function output($json) {

+    print_r(json_encode($json));

+    exit();

+  }

+

+  public static function error($n, $msg) {

+    self::output(["error" => $n, "msg" => $msg]);

+  }

+}

+

+function get_graph() {

+  global $conf;

+  return json_decode(file_get_contents($conf["apiurl"]), true);

+}

+

+if (!isset($_GET["action"])) {

+  write::error(1, "No action provided");

+}

+

+switch ($_GET["action"]) {

+  case "getgraf":

+  $graf = file_get_contents($conf["apiurl"]);

+  echo $graf;

+  break;

+

+  default:

+  write::error(2, "Unknown action");

+}

+?>

diff --git a/assistant/en.json b/assistant/en.json
index cadd504..e7aa8f9 100644
--- a/assistant/en.json
+++ b/assistant/en.json
@@ -1,25 +1,25 @@
-{
-  "new_to_graph": "It seems like {person} is new to the graph, because they don't share any edges with another person.",
-  "edges_1": "Alright, {person} shares an edge with {count}: {edges}.",
-  "edges_2": "Ok, {person} shares an edge with {count}: {edges}.",
-  "edges_3": "{person} shares an edge with {count}: {edges}.",
-  "edges_display": "{person} shares an edge with {count}.",
-  "count_singular": "one person",
-  "count_plural": "{count} people",
-  "not_found": "I'm sorry, but I didn't find anyone called {person} in the graph.",
-  "random_fact": "Here's a random fact: {fact}",
-  "num_vertices": "The number of vertices in the graph is {count}.",
-  "num_edges": "The number of edges in the graph is {count}.",
-  "didyouknow_last_edge": "did you know that the last edge is the one between {person1} and {person2}?",
-  "didyouknow_num_vertices": "Did you know that the number of vertices in the graph is {count}?",
-  "didyouknow_num_edges": "Did you know that the number of edges in the graph is {count}?",
-  "didyouknow_k3": "did you know that the first three vertices of the graph form a K3?",
-  "didyouknow_creator": "Did you know that Dario currently hosts the graph and that he is thought to be its creator?",
-  "didyouknow_groph": "did you know that for a while, the graph wasn't connex and the smallest component was named 'the groph'? Fortunately, right now the Groph is connected to the rest of the graph.",
-  "followup_1": "Is there anything else I can do for you?",
-  "followup_2": "Now, is there anything else you want to know?",
-  "followup_3": "Is there anything else you want to know?",
-  "latest_news": "<speak>These are the latest edges in the graph: {edges}.</speak>",
-  "latest_news_count": "<speak>These are the last {count} edges in the graph: {edges}.</speak>",
-  "and": "and"
-}
+{

+  "new_to_graph": "It seems like {person} is new to the graph, because they don't share any edges with another person.",

+  "edges_1": "Alright, {person} shares an edge with {count}: {edges}.",

+  "edges_2": "Ok, {person} shares an edge with {count}: {edges}.",

+  "edges_3": "{person} shares an edge with {count}: {edges}.",

+  "edges_display": "{person} shares an edge with {count}.",

+  "count_singular": "one person",

+  "count_plural": "{count} people",

+  "not_found": "I'm sorry, but I didn't find anyone called {person} in the graph.",

+  "random_fact": "Here's a random fact: {fact}",

+  "num_vertices": "The number of vertices in the graph is {count}.",

+  "num_edges": "The number of edges in the graph is {count}.",

+  "didyouknow_last_edge": "did you know that the last edge is the one between {person1} and {person2}?",

+  "didyouknow_num_vertices": "Did you know that the number of vertices in the graph is {count}?",

+  "didyouknow_num_edges": "Did you know that the number of edges in the graph is {count}?",

+  "didyouknow_k3": "did you know that the first three vertices of the graph form a K3?",

+  "didyouknow_creator": "Did you know that Dario currently hosts the graph and that he is thought to be its creator?",

+  "didyouknow_groph": "did you know that for a while, the graph wasn't connex and the smallest component was named 'the groph'? Fortunately, right now the Groph is connected to the rest of the graph.",

+  "followup_1": "Is there anything else I can do for you?",

+  "followup_2": "Now, is there anything else you want to know?",

+  "followup_3": "Is there anything else you want to know?",

+  "latest_news": "<speak>These are the latest edges in the graph: {edges}.</speak>",

+  "latest_news_count": "<speak>These are the last {count} edges in the graph: {edges}.</speak>",

+  "and": "and"

+}

diff --git a/assistant/es.json b/assistant/es.json
index e9ba01b..307c2a1 100644
--- a/assistant/es.json
+++ b/assistant/es.json
@@ -1,25 +1,25 @@
-{
-  "new_to_graph": "Parece que {person} se añadió hace poco al grafo, porque no comparte ninguna arista con nadie.",
-  "edges_1": "Veamos, {person} comparte aristas con {count}: {edges}.",
-  "edges_2": "Vale, {person} comparte aristas con {count}: {edges}.",
-  "edges_3": "{person} comparte aristas con {count}: {edges}.",
-  "edges_display": "{person} comparte aristas con {count}.",
-  "count_singular": "una persona",
-  "count_plural": "{count} personas",
-  "not_found": "Lo siento, pero no he encontrado a nadie llamado {person} en el grafo.",
-  "random_fact": "Aquí tienes un dato curioso: {fact}",
-  "num_vertices": "El número de vértices en el grafo es de {count}",
-  "num_edges": "El número de aristas en el grafo es de {count}",
-  "didyouknow_last_edge": "¿sabías que la última arista es la que une {person1} y {person2}?",
-  "didyouknow_num_vertices": "¿Sabías que el número de vértices en el grafo es de {count}?",
-  "didyouknow_num_edges": "¿Sabías que el número de aristas en el grafo es de {count}?",
-  "didyouknow_k3": "¿sabías que los primeros tres vértices del grafo forman un K3?",
-  "didyouknow_creator": "¿Sabías que Dario hospeda actualmente el grafo y que se cree que él es su creador?",
-  "didyouknow_groph": "¿sabías que durante un tiempo, el grafo no era conexo y su componiente más pequeña se le llamó 'groph'? Afortunadamente, ahora el groph está conectado con la resta del grafo.",
-  "followup_1": "¿Hay algo más que pueda hacer por ti?",
-  "followup_2": "¿Tienes alguna otra pregunta?",
-  "followup_3": "¿Hay algo más que quieras saber?",
-  "latest_news": "<speak>Estos son los últimos vértices del grafo: {edges}.</speak>",
-  "latest_news_count": "<speak>Estos son los últimos {count} vértices del grafo: {edges}.</speak>",
-  "and": "y"
-}
+{

+  "new_to_graph": "Parece que {person} se añadió hace poco al grafo, porque no comparte ninguna arista con nadie.",

+  "edges_1": "Veamos, {person} comparte aristas con {count}: {edges}.",

+  "edges_2": "Vale, {person} comparte aristas con {count}: {edges}.",

+  "edges_3": "{person} comparte aristas con {count}: {edges}.",

+  "edges_display": "{person} comparte aristas con {count}.",

+  "count_singular": "una persona",

+  "count_plural": "{count} personas",

+  "not_found": "Lo siento, pero no he encontrado a nadie llamado {person} en el grafo.",

+  "random_fact": "Aquí tienes un dato curioso: {fact}",

+  "num_vertices": "El número de vértices en el grafo es de {count}",

+  "num_edges": "El número de aristas en el grafo es de {count}",

+  "didyouknow_last_edge": "¿sabías que la última arista es la que une {person1} y {person2}?",

+  "didyouknow_num_vertices": "¿Sabías que el número de vértices en el grafo es de {count}?",

+  "didyouknow_num_edges": "¿Sabías que el número de aristas en el grafo es de {count}?",

+  "didyouknow_k3": "¿sabías que los primeros tres vértices del grafo forman un K3?",

+  "didyouknow_creator": "¿Sabías que Dario hospeda actualmente el grafo y que se cree que él es su creador?",

+  "didyouknow_groph": "¿sabías que durante un tiempo, el grafo no era conexo y su componiente más pequeña se le llamó 'groph'? Afortunadamente, ahora el groph está conectado con la resta del grafo.",

+  "followup_1": "¿Hay algo más que pueda hacer por ti?",

+  "followup_2": "¿Tienes alguna otra pregunta?",

+  "followup_3": "¿Hay algo más que quieras saber?",

+  "latest_news": "<speak>Estos son los últimos vértices del grafo: {edges}.</speak>",

+  "latest_news_count": "<speak>Estos son los últimos {count} vértices del grafo: {edges}.</speak>",

+  "and": "y"

+}

diff --git a/assistant_callback.php b/assistant_callback.php
index 15b6f79..aca4d9a 100644
--- a/assistant_callback.php
+++ b/assistant_callback.php
@@ -1,248 +1,248 @@
-<?php
-require_once("config.php");
-
-class write {
-  public static function do($json) {
-    echo json_encode($json)."\n";
-    exit();
-  }
-}
-
-class conv {
-  public static function ask($msg_array) {
-    $items = array();
-
-    foreach ($msg_array as $msg) {
-      $items[] = array("simpleResponse" => array("textToSpeech" => $msg));
-    }
-
-    self::ask_custom($items);
-  }
-
-  public static function ask_followup($msg, &$i18n) {
-    self::ask([$msg, $i18n->msg("followup_".mt_rand(1, 3))]);
-  }
-
-  public static function ask_custom($items) {
-    $json = array();
-    $json["payload"] = array();
-    $json["payload"]["google"] = array();
-    $json["payload"]["google"]["expectUserResponse"] = true;
-    $json["payload"]["google"]["richResponse"] = array();
-    $json["payload"]["google"]["richResponse"]["items"] = $items;
-
-    write::do($json);
-  }
-
-  public static function has($item, &$json) {
-    foreach ($json["originalDetectIntentRequest"]["payload"]["surface"]["capabilities"] as $cap) {
-      if ($cap["name"] == $item) {
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-class i18n {
-  public static $hllist = array("en", "es");
-  public $i18n_strings = null;
-  public $language = null;
-
-  function __construct($lang) {
-    global $_GET;
-    global $conf;
-
-    if (empty($this->i18n_strings)) {
-      $this->i18n_strings = array();
-    }
-
-    $this->language = $lang;
-
-    $this->i18n_strings = json_decode(file_get_contents("assistant/".$this->language.".json"), true);
-
-    return true;
-  }
-
-  function msg($message, $strings = null) {
-    if (!isset($this->i18n_strings[$message])) {
-      return false;
-    }
-
-    $string = $this->i18n_strings[$message];
-
-    if ($strings != null && is_array($strings)) {
-      foreach ($strings as $i => $subst) {
-        $string = str_replace("{".$i."}", $subst, $string);
-      }
-    }
-
-    return $string;
-  }
-}
-
-function get_graph() {
-  global $conf;
-  return json_decode(file_get_contents($conf["apiurl"]), true);
-}
-
-function comma($array, $wait=false) {
-  global $i18n;
-  if (count($array) == 0) {
-    return "";
-  }
-  if (count($array) == 1) {
-    return $array[0];
-  }
-  $break = ($wait ? '<break time="0.5s"/>' : "");
-  return implode($break.", ", array_slice($array, 0, -1)).$break.", ".$i18n->msg("and")." ".$array[count($array)-1];
-}
-
-$json = json_decode(file_get_contents('php://input'), true);
-
-if ($json === NULL || !isset($json["originalDetectIntentRequest"]) || !isset($json["originalDetectIntentRequest"]["source"]) || $json["originalDetectIntentRequest"]["source"] != "google") {
-  exit();
-}
-
-$graph = get_graph();
-$lang = (isset($json["queryResult"]["languageCode"]) && in_array(substr($json["queryResult"]["languageCode"], 0, 2), i18n::$hllist) ? substr($json["queryResult"]["languageCode"], 0, 2) : "en");
-$i18n = new i18n($lang);
-
-switch ($json["queryResult"]["intent"]["displayName"]) {
-  case "showVertex":
-  if (!isset($json["queryResult"]["parameters"]["Vertex"])) {
-    exit();
-  }
-  
-  $shortest = -1;
-  $closest = -1;
-  $closest_id = -1;
-
-  foreach ($graph["nodes"] as $id => $node) {
-    $lev = levenshtein(strtolower($json["queryResult"]["parameters"]["Vertex"]), strtolower($node["name"]));
-
-    if ($lev == 0) {
-      $closest = $node["name"];
-      $shortest = $lev;
-      $closest_id = $id;
-    }
-
-    if (($lev <= $shortest || $shortest < 0) && $lev <= 3) {
-      $closest = $node["name"];
-      $shortest = $lev;
-      $closest_id = $id;
-    }
-  }
-
-  if ($shortest != -1) {
-    $neighbors = array();
-    // We're suposing each vertex has a different name. If not, this would get rid of some of the edges, but if two vertexs had the same name there would be no way to differentiate them anyway, so I think it's ok.
-    foreach ($graph["edges"] as $edge) {
-      if ($edge["a"] == $closest_id) {
-        $neighbors[$graph["nodes"][$edge["b"]]["name"]] = $edge["votes"];
-      } elseif ($edge["b"] == $closest_id) {
-        $neighbors[$graph["nodes"][$edge["a"]]["name"]] = $edge["votes"];
-      }
-    }
-    if (count($neighbors) == 0) {
-      conv::ask_followup($i18n->msg("new_to_graph", array("person" => $closest)), $i18n);
-    } else {
-      arsort($neighbors);
-      $params = array("person" => $closest, "count" => (count($neighbors) == 1 ? $i18n->msg("count_singular") : $i18n->msg("count_plural", array("count" => count($neighbors)))));
-      /*if (conv::has("actions.capability.SCREEN_OUTPUT", $json)) {
-        $items = [
-          array(
-            "simpleResponse" => array(
-              "textToSpeech" => $i18n->msg("edges_display", $params)
-            )
-          ),
-          array(
-            "tableCard" => array(
-              "columnProperties" => [
-                array(
-                  "header" => "Adjacent vertex"
-                ),
-                array("header" => "Votes")
-              ],
-              "rows" => []
-            )
-          ),
-          array(
-            "simpleResponse" => array(
-              "textToSpeech" => $i18n->msg("followup_".mt_rand(1, 3))
-            )
-          )
-        ];
-        foreach ($neighbors as $neighbor => $votes) {
-          $items[1]["tableCard"]["rows"][] = array("cells" => [array("text" => $neighbor), array("text" => (string)$votes)]);
-        }
-        conv::ask_custom($items);
-      } else {*/ // This code shows a table if the user has a screen, but
-                 // unfortunately tables are not public yet.
-                 // @TODO: Uncomment when tables are out of developer preview.
-        $people = array_keys($neighbors);
-        $people_string = comma($people);
-        $params["edges"] = $people_string;
-        $num = mt_rand(1, 3);
-        conv::ask_followup($i18n->msg("edges_".$num, $params), $i18n);
-      /*}*/
-    }
-  } else {
-    conv::ask_followup($i18n->msg("not_found", array("person" => $json["queryResult"]["parameters"]["Vertex"])), $i18n);
-  }
-  break;
-
-  case "randomFact":
-  $rand = mt_rand(0, 5);
-  switch ($rand) {
-    case 0: // Last edge in the graph
-    $last = array_values(array_slice($graph["edges"], -1))[0];
-    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_last_edge", array("person1" => $graph["nodes"][$last["a"]]["name"], "person2" => $graph["nodes"][$last["b"]]["name"])))), $i18n);
-    break;
-
-    case 1: // Num vertices
-    conv::ask_followup($i18n->msg("didyouknow_num_vertices", array("count" => count($graph["nodes"]))), $i18n);
-    break;
-
-    case 2: // Num edges
-    conv::ask_followup($i18n->msg("didyouknow_num_edges", array("count" => count($graph["edges"]))), $i18n);
-    break;
-
-    case 3: // First 3 vertices K3
-    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_k3"))), $i18n);
-    break;
-
-    case 4: // Creator
-    conv::ask_followup($i18n->msg("didyouknow_creator"), $i18n);
-    break;
-
-    case 5: // Groph
-    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_groph"))), $i18n);
-    break;
-  }
-  
-  break;
-
-  case "numVertexs":
-  conv::ask_followup($i18n->msg("num_vertices", array("count" => count($graph["nodes"]))), $i18n);
-  break;
-
-  case "numEdges":
-  conv::ask_followup($i18n->msg("num_edges", array("count" => count($graph["edges"]))), $i18n);
-  break;
-
-  case "latestNews":
-  $param = (isset($json["queryResult"]["parameters"]["numEdges"]) ? $json["queryResult"]["parameters"]["numEdges"] : null);
-  $num = (isset($param) && !empty($param) ? (int)$param : 4);
-  $last = array_values(array_slice($graph["edges"], -$num));
-  $edges = [];
-  foreach ($last as $edge) {
-    $edges[] = $graph["nodes"][$edge["a"]]["name"]." - ".$graph["nodes"][$edge["b"]]["name"];
-  }
-  $edges_string = comma($edges, true);
-  conv::ask_followup($i18n->msg((isset($param) && !empty($param) ? "latest_news_count" : "latest_news"), array("edges" => $edges_string, "count" => $num)), $i18n);
-  break;
-
-  default:
-  exit();
-}
+<?php

+require_once("config.php");

+

+class write {

+  public static function do($json) {

+    echo json_encode($json)."\n";

+    exit();

+  }

+}

+

+class conv {

+  public static function ask($msg_array) {

+    $items = array();

+

+    foreach ($msg_array as $msg) {

+      $items[] = array("simpleResponse" => array("textToSpeech" => $msg));

+    }

+

+    self::ask_custom($items);

+  }

+

+  public static function ask_followup($msg, &$i18n) {

+    self::ask([$msg, $i18n->msg("followup_".mt_rand(1, 3))]);

+  }

+

+  public static function ask_custom($items) {

+    $json = array();

+    $json["payload"] = array();

+    $json["payload"]["google"] = array();

+    $json["payload"]["google"]["expectUserResponse"] = true;

+    $json["payload"]["google"]["richResponse"] = array();

+    $json["payload"]["google"]["richResponse"]["items"] = $items;

+

+    write::do($json);

+  }

+

+  public static function has($item, &$json) {

+    foreach ($json["originalDetectIntentRequest"]["payload"]["surface"]["capabilities"] as $cap) {

+      if ($cap["name"] == $item) {

+        return true;

+      }

+    }

+    return false;

+  }

+}

+

+class i18n {

+  public static $hllist = array("en", "es");

+  public $i18n_strings = null;

+  public $language = null;

+

+  function __construct($lang) {

+    global $_GET;

+    global $conf;

+

+    if (empty($this->i18n_strings)) {

+      $this->i18n_strings = array();

+    }

+

+    $this->language = $lang;

+

+    $this->i18n_strings = json_decode(file_get_contents("assistant/".$this->language.".json"), true);

+

+    return true;

+  }

+

+  function msg($message, $strings = null) {

+    if (!isset($this->i18n_strings[$message])) {

+      return false;

+    }

+

+    $string = $this->i18n_strings[$message];

+

+    if ($strings != null && is_array($strings)) {

+      foreach ($strings as $i => $subst) {

+        $string = str_replace("{".$i."}", $subst, $string);

+      }

+    }

+

+    return $string;

+  }

+}

+

+function get_graph() {

+  global $conf;

+  return json_decode(file_get_contents($conf["apiurl"]), true);

+}

+

+function comma($array, $wait=false) {

+  global $i18n;

+  if (count($array) == 0) {

+    return "";

+  }

+  if (count($array) == 1) {

+    return $array[0];

+  }

+  $break = ($wait ? '<break time="0.5s"/>' : "");

+  return implode($break.", ", array_slice($array, 0, -1)).$break.", ".$i18n->msg("and")." ".$array[count($array)-1];

+}

+

+$json = json_decode(file_get_contents('php://input'), true);

+

+if ($json === NULL || !isset($json["originalDetectIntentRequest"]) || !isset($json["originalDetectIntentRequest"]["source"]) || $json["originalDetectIntentRequest"]["source"] != "google") {

+  exit();

+}

+

+$graph = get_graph();

+$lang = (isset($json["queryResult"]["languageCode"]) && in_array(substr($json["queryResult"]["languageCode"], 0, 2), i18n::$hllist) ? substr($json["queryResult"]["languageCode"], 0, 2) : "en");

+$i18n = new i18n($lang);

+

+switch ($json["queryResult"]["intent"]["displayName"]) {

+  case "showVertex":

+  if (!isset($json["queryResult"]["parameters"]["Vertex"])) {

+    exit();

+  }

+  

+  $shortest = -1;

+  $closest = -1;

+  $closest_id = -1;

+

+  foreach ($graph["nodes"] as $id => $node) {

+    $lev = levenshtein(strtolower($json["queryResult"]["parameters"]["Vertex"]), strtolower($node["name"]));

+

+    if ($lev == 0) {

+      $closest = $node["name"];

+      $shortest = $lev;

+      $closest_id = $id;

+    }

+

+    if (($lev <= $shortest || $shortest < 0) && $lev <= 3) {

+      $closest = $node["name"];

+      $shortest = $lev;

+      $closest_id = $id;

+    }

+  }

+

+  if ($shortest != -1) {

+    $neighbors = array();

+    // We're suposing each vertex has a different name. If not, this would get rid of some of the edges, but if two vertexs had the same name there would be no way to differentiate them anyway, so I think it's ok.

+    foreach ($graph["edges"] as $edge) {

+      if ($edge["a"] == $closest_id) {

+        $neighbors[$graph["nodes"][$edge["b"]]["name"]] = $edge["votes"];

+      } elseif ($edge["b"] == $closest_id) {

+        $neighbors[$graph["nodes"][$edge["a"]]["name"]] = $edge["votes"];

+      }

+    }

+    if (count($neighbors) == 0) {

+      conv::ask_followup($i18n->msg("new_to_graph", array("person" => $closest)), $i18n);

+    } else {

+      arsort($neighbors);

+      $params = array("person" => $closest, "count" => (count($neighbors) == 1 ? $i18n->msg("count_singular") : $i18n->msg("count_plural", array("count" => count($neighbors)))));

+      /*if (conv::has("actions.capability.SCREEN_OUTPUT", $json)) {

+        $items = [

+          array(

+            "simpleResponse" => array(

+              "textToSpeech" => $i18n->msg("edges_display", $params)

+            )

+          ),

+          array(

+            "tableCard" => array(

+              "columnProperties" => [

+                array(

+                  "header" => "Adjacent vertex"

+                ),

+                array("header" => "Votes")

+              ],

+              "rows" => []

+            )

+          ),

+          array(

+            "simpleResponse" => array(

+              "textToSpeech" => $i18n->msg("followup_".mt_rand(1, 3))

+            )

+          )

+        ];

+        foreach ($neighbors as $neighbor => $votes) {

+          $items[1]["tableCard"]["rows"][] = array("cells" => [array("text" => $neighbor), array("text" => (string)$votes)]);

+        }

+        conv::ask_custom($items);

+      } else {*/ // This code shows a table if the user has a screen, but

+                 // unfortunately tables are not public yet.

+                 // @TODO: Uncomment when tables are out of developer preview.

+        $people = array_keys($neighbors);

+        $people_string = comma($people);

+        $params["edges"] = $people_string;

+        $num = mt_rand(1, 3);

+        conv::ask_followup($i18n->msg("edges_".$num, $params), $i18n);

+      /*}*/

+    }

+  } else {

+    conv::ask_followup($i18n->msg("not_found", array("person" => $json["queryResult"]["parameters"]["Vertex"])), $i18n);

+  }

+  break;

+

+  case "randomFact":

+  $rand = mt_rand(0, 5);

+  switch ($rand) {

+    case 0: // Last edge in the graph

+    $last = array_values(array_slice($graph["edges"], -1))[0];

+    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_last_edge", array("person1" => $graph["nodes"][$last["a"]]["name"], "person2" => $graph["nodes"][$last["b"]]["name"])))), $i18n);

+    break;

+

+    case 1: // Num vertices

+    conv::ask_followup($i18n->msg("didyouknow_num_vertices", array("count" => count($graph["nodes"]))), $i18n);

+    break;

+

+    case 2: // Num edges

+    conv::ask_followup($i18n->msg("didyouknow_num_edges", array("count" => count($graph["edges"]))), $i18n);

+    break;

+

+    case 3: // First 3 vertices K3

+    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_k3"))), $i18n);

+    break;

+

+    case 4: // Creator

+    conv::ask_followup($i18n->msg("didyouknow_creator"), $i18n);

+    break;

+

+    case 5: // Groph

+    conv::ask_followup($i18n->msg("random_fact", array("fact" => $i18n->msg("didyouknow_groph"))), $i18n);

+    break;

+  }

+  

+  break;

+

+  case "numVertexs":

+  conv::ask_followup($i18n->msg("num_vertices", array("count" => count($graph["nodes"]))), $i18n);

+  break;

+

+  case "numEdges":

+  conv::ask_followup($i18n->msg("num_edges", array("count" => count($graph["edges"]))), $i18n);

+  break;

+

+  case "latestNews":

+  $param = (isset($json["queryResult"]["parameters"]["numEdges"]) ? $json["queryResult"]["parameters"]["numEdges"] : null);

+  $num = (isset($param) && !empty($param) ? (int)$param : 4);

+  $last = array_values(array_slice($graph["edges"], -$num));

+  $edges = [];

+  foreach ($last as $edge) {

+    $edges[] = $graph["nodes"][$edge["a"]]["name"]." - ".$graph["nodes"][$edge["b"]]["name"];

+  }

+  $edges_string = comma($edges, true);

+  conv::ask_followup($i18n->msg((isset($param) && !empty($param) ? "latest_news_count" : "latest_news"), array("edges" => $edges_string, "count" => $num)), $i18n);

+  break;

+

+  default:

+  exit();

+}

diff --git a/config.default.php b/config.default.php
index 943d1d8..8d304dc 100644
--- a/config.default.php
+++ b/config.default.php
@@ -1,6 +1,6 @@
-<?php
-// Rename this file as config.php and fill in the details below.
-$conf = array();
-$conf["apiurl"] = ""; // L'adreça de l'API del graf original.
-$conf["password"] = ""; // La contrasenya perquè la gent pugui entrar al graf.
-
+<?php

+// Rename this file as config.php and fill in the details below.

+$conf = array();

+$conf["apiurl"] = ""; // L'adreça de l'API del graf original.

+$conf["password"] = ""; // La contrasenya perquè la gent pugui entrar al graf.

+

diff --git a/css/dialog.css b/css/dialog.css
index 723795d..313ec29 100644
--- a/css/dialog.css
+++ b/css/dialog.css
@@ -1,3 +1,5 @@
+/* ********** HERE STARTS dialog.css ****** */

+

 #dialog {

 	position: absolute;

 	top: 0px;

diff --git a/css/general.css b/css/general.css
index 4db942e..eda32ac 100644
--- a/css/general.css
+++ b/css/general.css
@@ -1,11 +1,13 @@
-html, body {
-	margin: 0;
-	width: 100%;
-	height: 100%;
-	background-color: #060606;
-	color: white;
-	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-	user-select: none !important;
-	font-family: 'Roboto';
-}
-
+/* ********** HERE STARTS general.css ****** */

+

+html, body {

+	margin: 0;

+	width: 100%;

+	height: 100%;

+	background-color: #060606;

+	color: white;

+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);

+	user-select: none !important;

+	font-family: 'Roboto';

+}

+

diff --git a/css/graf.css b/css/graf.css
index 62a67af..de6a5a6 100644
--- a/css/graf.css
+++ b/css/graf.css
@@ -1,3 +1,5 @@
+/* ********** HERE STARTS graf.css ****** */

+

 #graf {

 	width: 100%;

 	height: 100%;

diff --git a/css/option-buttons.css b/css/option-buttons.css
index a7c4fa0..f23168b 100644
--- a/css/option-buttons.css
+++ b/css/option-buttons.css
@@ -1,3 +1,4 @@
+/* ********** HERE STARTS search-bar.css ****** */

 #circle-mode {

 	position: absolute;

 	right: 10px;

@@ -31,4 +32,12 @@
 	right: 10px;

 	bottom: 10px;

 	z-index: 100;

+}

+

+#zoomin.touch {

+	display: none;

+}

+

+#zoomout.touch {

+	display: none;

 }
\ No newline at end of file
diff --git a/css/search-bar.css b/css/search-bar.css
index 8c9cc84..46d121e 100644
--- a/css/search-bar.css
+++ b/css/search-bar.css
@@ -1,3 +1,4 @@
+/* ********** HERE STARTS search-bar.css ****** */

 .md-google-search__metacontainer {

 	position: absolute;

 	top: 10px;

diff --git a/css/styles.css b/css/styles.css
new file mode 100644
index 0000000..5ce5330
--- /dev/null
+++ b/css/styles.css
@@ -0,0 +1,302 @@
+/* ********** HERE STARTS general.css ****** */

+

+html, body {

+	margin: 0;

+	width: 100%;

+	height: 100%;

+	background-color: #060606;

+	color: white;

+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);

+	user-select: none !important;

+	font-family: 'Roboto';

+}

+

+

+/* ********** HERE STARTS graf.css ****** */

+

+#graf {

+	width: 100%;

+	height: 100%;

+}

+

+span {

+	position: relative;

+	z-index: -1;

+}

+

+

+/* ********** HERE STARTS dialog.css ****** */

+

+#dialog {

+	position: absolute;

+	top: 0px;

+	left: 0px;

+	width: 300px;

+	height: 100%;

+	background-color: white;

+	color: black;

+	z-index: 120;

+	overflow-y: auto;

+}

+

+#backdrop {

+	display: none;

+	position: absolute;

+	top: 0;

+	left: 0;

+	width: 100%;

+	height: 100%;

+	background-color: rgba(0, 0, 0, .5);

+	z-index: 110;

+}

+

+#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;

+}

+

+/* ********** HERE STARTS search-bar.css ****** */

+#circle-mode {

+	position: absolute;

+	right: 10px;

+	bottom: 210px;

+	z-index: 100;

+}

+

+#settings {

+	position: absolute;

+	right: 10px;

+	bottom: 160px;

+	z-index: 100;

+}

+

+#search {

+	position: absolute;

+	right: 10px;

+	bottom: 110px;

+	z-index: 100;

+}

+

+#zoomin {

+	position: absolute;

+	right: 10px;

+	bottom: 60px;

+	z-index: 100;

+}

+

+#zoomout {

+	position: absolute;

+	right: 10px;

+	bottom: 10px;

+	z-index: 100;

+}

+

+#zoomin.touch {

+	display: none;

+}

+

+#zoomout.touch {

+	display: none;

+}

+

+/* ********** HERE STARTS year-list.css ****** */

+#year-list {

+	padding: 10px;

+	padding-top: 0px;

+	float: right;

+	position:relative;

+	z-index:10;

+	display:none

+}

+

+#year-list-span {

+	position:relative;

+	z-index:10;

+}

+

+

+/* ********** HERE STARTS search-bar.css ****** */

+.md-google-search__metacontainer {

+	position: absolute;

+	top: 10px;

+	height: 48px;

+	width: 100%;

+	z-index: 100;

+}

+

+.md-google-search__container {

+	display: block;

+	margin-left: auto;

+	margin-right: auto;

+	height: 48px;

+	width: Calc(100% - 66px);

+	max-width: 720px;

+	white-space: nowrap;

+}

+

+.md-google-search {

+	height: 48px;

+	background-color: rgba(245, 245, 245, 1);

+	border: 1px solid rgba(0, 0, 0, 0);

+	-webkit-border-radius: 4px;

+	border-radius: 4px;

+	max-width: 720px;

+	position: relative;

+	-webkit-transition: background-color 100ms ease-in, width 100ms ease-out;

+	transition: background-color 100ms ease-in, width 100ms ease-out;

+}

+

+.md-google-search:focus-within {

+	border: 1px solid rgba(0, 0, 0, 0.45);

+	background-color: rgba(255, 255, 255, 1);

+	-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, .5);

+	box-shadow: 0 1px 1px rgba(255, 255, 255, .5);

+}

+

+.md-google-search__search-btn {

+	float: left;

+	background: none;

+	border: none;

+	opacity: .54;

+	outline: none;

+	padding: 0 4px;

+	line-height: 0;

+	color: #212121;

+}

+

+.md-google-search__search-btn svg, .md-google-search__empty-btn svg {

+	padding: 7px;

+	margin: 4px;

+}

+

+.md-google-search__field-container {

+	height: 46px;

+	padding: 0 11px;

+	margin-right: 48px;

+}

+

+.md-google-search__field {

+	border: none;

+	font: normal 16px Roboto, sans-serif;

+	height: 24px;

+	outline: none;

+	padding: 11px 0 11px 16px;

+	width: 100%;

+	background: transparent;

+}

+

+.md-google-search__empty-btn {

+	position: absolute;

+	right: 0;

+	top: 0;

+	background: none;

+	border: none;

+	opacity: .54;

+	outline: none;

+	padding: 0 4px;

+	line-height: 0;

+	color: #212121;

+	cursor: pointer;

+}

+

+/**

+  * Search Box Autocomplete

+  */

+.autocomplete-container {

+	z-index: 110;

+	position: absolute;

+	top: 60px;

+	width: 100%;

+}

+

+.autocomplete-items {

+	display: block;

+	margin-left: auto;

+	margin-right: auto;

+	width: Calc(100% - 66px);

+	max-width: 720px;

+	background-color: white;

+	color: black;

+	box-shadow: 0 2px 5px 0 rgba(255, 255, 255, 0.258824), 0 2px 10px 0

+		rgba(255, 255, 255, 0.156863) !important;

+}

+

+.autocomplete-item {

+	font-size: 16px;

+	padding: 12px 14px;

+	cursor: pointer;

+}

+

+.autocomplete-item:hover, .autocomplete-active {

+	background: #eee;

+}

+

+.autocomplete-year {

+	position: relative;

+	z-index: 120;

+	

+	color: #222;

+}

+

+@media ( max-width : 700px) {

+	#dialog {

+		width: Calc(100% - 32px) !important;

+		height: Calc(100% - 32px) !important;

+		margin: 16px;

+	}

+	#backdrop {

+		display: block;

+	}

+	#min-dialog {

+		display: block !important;

+	}

+}
\ No newline at end of file
diff --git a/css/year-list.css b/css/year-list.css
index 4436d42..45993b2 100644
--- a/css/year-list.css
+++ b/css/year-list.css
@@ -1,6 +1,7 @@
+/* ********** HERE STARTS year-list.css ****** */

 #year-list {

 	padding: 10px;

-	padding-top: 70px;

+	padding-top: 0px;

 	float: right;

 	position:relative;

 	z-index:10;

diff --git a/graf.php b/graf.php
index 9ea6730..6bbcd70 100644
--- a/graf.php
+++ b/graf.php
@@ -26,12 +26,17 @@
 <link rel="manifest" href="manifest.json">

 

 <!-- own css stylesheets -->

+<!-- FOR DEPLOYING -->

+<link rel="stylesheet" href="css/styles.css">

+

+<!-- FOR CODING

 <link rel="stylesheet" href="css/general.css">

 <link rel="stylesheet" href="css/graf.css">

 <link rel="stylesheet" href="css/dialog.css">

 <link rel="stylesheet" href="css/option-buttons.css">

 <link rel="stylesheet" href="css/year-list.css">

 <link rel="stylesheet" href="css/search-bar.css">

+-->

 

 

 <!-- imported css stylesheets -->

@@ -133,9 +138,9 @@
 			<div class="md-google-search">

 				<button class="md-google-search__search-btn">

 					<svg height="24px" viewBox="0 0 24 24" width="24px"

-						xmlns="http://www.w3.org/2000/svg">
+						xmlns="http://www.w3.org/2000/svg">

 						<path

-							d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
+							d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>

 						<path d="M0 0h24v24H0z" fill="none"></path></svg>

 				</button>

 				<div class="md-google-search__field-container">

@@ -145,9 +150,9 @@
 				</div>

 				<button class="md-google-search__empty-btn" style="display: none;">

 					<svg focusable="false" height="24px" viewBox="0 0 24 24"

-						width="24px" xmlns="http://www.w3.org/2000/svg">
+						width="24px" xmlns="http://www.w3.org/2000/svg">

 						<path

-							d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
+							d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>

 						<path d="M0 0h24v24H0z" fill="none"></path></svg>

 				</button>

 			</div>

@@ -166,6 +171,11 @@
 

 	<!-- our scripts -->

 	<script src="./js/autocomplete.js"></script>

+

+	<!-- FOR DEPLOYING -->

+	<script src="./js/script.js"></script>

+

+	<!-- FOR CODING

 	<script src="./js/circle-mode.js"></script>

 	<script src="./js/graf.js"></script>

 	<script src="./js/limit-years.js"></script>

@@ -173,6 +183,7 @@
 	<script src="./js/dialog.js"></script>

 	<script src="./js/camera.js"></script>

 	<script src="./js/easter-egg.js"></script>

+	-->

 	

 	<!--<script src="js/service-worker.js"></script>-->

 

diff --git a/img/graf.svg b/img/graf.svg
index 271d998..eee8b0a 100644
--- a/img/graf.svg
+++ b/img/graf.svg
@@ -1,28 +1,28 @@
-<svg width="194" height="194" xmlns="http://www.w3.org/2000/svg">
- <g>
-  <title>Layer 1</title>
-  <ellipse fill="#060606" stroke-width="0" fill-opacity="null" cx="97" cy="97" id="svg_10" rx="88" ry="88"/>
- </g>
- <g>
-  <title>Layer 2</title>
-  <g id="svg_9">
-   <line fill-opacity="null" x1="48" y1="138" x2="98.5" y2="99" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="98.5" y1="99" x2="105" y2="150" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="53" y1="76" x2="149" y2="84" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="53" y1="76" x2="105" y2="150" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="98.5" y1="99" x2="149" y2="84" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="53" y1="76" x2="98.5" y2="99" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-   <line fill-opacity="null" x1="53" y1="76" x2="121" y2="35" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>
-  </g>
- </g>
- <g>
-  <title>Layer 3</title>
-  <ellipse fill="#d61c08" cx="48" cy="138" id="svg_2" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="98.5" cy="99" id="svg_3" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="105" cy="150" id="svg_4" rx="8" ry="8"/>
-  <ellipse fill="#d61c08" cx="53" cy="76" id="svg_5" rx="8" ry="8"/>
-  <ellipse fill="#d61c08" cx="149" cy="84" id="svg_6" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="121" cy="35" id="svg_7" rx="8" ry="8"/>
-  <ellipse fill="#0ca80a" cx="147" cy="123" id="svg_8" rx="8" ry="8"/>
-  </g>
-</svg>
+<svg width="194" height="194" xmlns="http://www.w3.org/2000/svg">

+ <g>

+  <title>Layer 1</title>

+  <ellipse fill="#060606" stroke-width="0" fill-opacity="null" cx="97" cy="97" id="svg_10" rx="88" ry="88"/>

+ </g>

+ <g>

+  <title>Layer 2</title>

+  <g id="svg_9">

+   <line fill-opacity="null" x1="48" y1="138" x2="98.5" y2="99" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="98.5" y1="99" x2="105" y2="150" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="53" y1="76" x2="149" y2="84" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="53" y1="76" x2="105" y2="150" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="98.5" y1="99" x2="149" y2="84" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="53" y1="76" x2="98.5" y2="99" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+   <line fill-opacity="null" x1="53" y1="76" x2="121" y2="35" id="svg_1" stroke-linejoin="null" stroke-linecap="null" fill="none" stroke="#FFF" stroke-width="2" stroke-opacity="null"/>

+  </g>

+ </g>

+ <g>

+  <title>Layer 3</title>

+  <ellipse fill="#d61c08" cx="48" cy="138" id="svg_2" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="98.5" cy="99" id="svg_3" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="105" cy="150" id="svg_4" rx="8" ry="8"/>

+  <ellipse fill="#d61c08" cx="53" cy="76" id="svg_5" rx="8" ry="8"/>

+  <ellipse fill="#d61c08" cx="149" cy="84" id="svg_6" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="121" cy="35" id="svg_7" rx="8" ry="8"/>

+  <ellipse fill="#0ca80a" cx="147" cy="123" id="svg_8" rx="8" ry="8"/>

+  </g>

+</svg>

diff --git a/img/graf_square.svg b/img/graf_square.svg
index 14f51c7..e7af204 100644
--- a/img/graf_square.svg
+++ b/img/graf_square.svg
@@ -1,28 +1,28 @@
-<svg width="194" height="194" xmlns="http://www.w3.org/2000/svg">
- <g>
-  <title>Layer 1</title>
-  <rect fill="#060606" stroke-width="0" id="svg_10" width="100%" height="100%"/>
- </g>
- <g>
-  <title>Layer 2</title>
-  <g id="svg_9">
-   <line fill-opacity="null" x1="48" y1="138" x2="98.5" y2="99" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="98.5" y1="99" x2="105" y2="150" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="53" y1="76" x2="149" y2="84" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="53" y1="76" x2="105" y2="150" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="98.5" y1="99" x2="149" y2="84" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="53" y1="76" x2="98.5" y2="99" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-   <line fill-opacity="null" x1="53" y1="76" x2="121" y2="35" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />
-  </g>
- </g>
- <g>
-  <title>Layer 3</title>
-  <ellipse fill="#d61c08" cx="48" cy="138" id="svg_2" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="98.5" cy="99" id="svg_3" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="105" cy="150" id="svg_4" rx="8" ry="8"/>
-  <ellipse fill="#d61c08" cx="53" cy="76" id="svg_5" rx="8" ry="8"/>
-  <ellipse fill="#d61c08" cx="149" cy="84" id="svg_6" rx="8" ry="8"/>
-  <ellipse fill="#0159aa" cx="121" cy="35" id="svg_7" rx="8" ry="8"/>
-  <ellipse fill="#0ca80a" cx="147" cy="123" id="svg_8" rx="8" ry="8"/>
-  </g>
-</svg>
+<svg width="194" height="194" xmlns="http://www.w3.org/2000/svg">

+ <g>

+  <title>Layer 1</title>

+  <rect fill="#060606" stroke-width="0" id="svg_10" width="100%" height="100%"/>

+ </g>

+ <g>

+  <title>Layer 2</title>

+  <g id="svg_9">

+   <line fill-opacity="null" x1="48" y1="138" x2="98.5" y2="99" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="98.5" y1="99" x2="105" y2="150" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="53" y1="76" x2="149" y2="84" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="53" y1="76" x2="105" y2="150" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="98.5" y1="99" x2="149" y2="84" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="53" y1="76" x2="98.5" y2="99" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+   <line fill-opacity="null" x1="53" y1="76" x2="121" y2="35" id="svg_1" fill="none" stroke="#FFF" stroke-width="2" />

+  </g>

+ </g>

+ <g>

+  <title>Layer 3</title>

+  <ellipse fill="#d61c08" cx="48" cy="138" id="svg_2" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="98.5" cy="99" id="svg_3" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="105" cy="150" id="svg_4" rx="8" ry="8"/>

+  <ellipse fill="#d61c08" cx="53" cy="76" id="svg_5" rx="8" ry="8"/>

+  <ellipse fill="#d61c08" cx="149" cy="84" id="svg_6" rx="8" ry="8"/>

+  <ellipse fill="#0159aa" cx="121" cy="35" id="svg_7" rx="8" ry="8"/>

+  <ellipse fill="#0ca80a" cx="147" cy="123" id="svg_8" rx="8" ry="8"/>

+  </g>

+</svg>

diff --git a/index.php b/index.php
index 33dc3be..8b8138c 100644
--- a/index.php
+++ b/index.php
@@ -1,69 +1,69 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>Graf alternatiu FME</title>
-
-    <meta name=viewport content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-
-    <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" />
-
-    <style>
-    .mdl-card {
-      height: 320px;
-    }
-    .mdl-card > .mdl-card__title.graf {
-      color: #fff;
-      background: url("img/graf_screenshot.png") center top 15% no-repeat #46B6AC;
-    }
-    .misc {
-      color: #fff;
-      background: #00695C;
-    }
-    </style>
-  </head>
-  <body>
-    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
-      <header class="mdl-layout__header">
-        <div class="mdl-layout__header-row">
-          <!-- Title -->
-          <span class="mdl-layout-title">Graf alternatiu FME</span>
-        </div>
-      </header>
-      <main class="mdl-layout__content">
-        <div class="page-content">
-          <div class="mdl-grid">
-            <div class="mdl-shadow--2dp mdl-cell mdl-cell--4-col mdl-card">
-              <div class="mdl-card__title mdl-card--expand graf">
-                <h2 class="mdl-card__title-text">El graf</h2>
-              </div>
-              <div class="mdl-card__supporting-text">
-                Una versió de només lectura del graf, optimitzada per navegadors móvils i amb una interfície millorada.
-              </div>
-              <div class="mdl-card__actions mdl-card--border">
-                <a href="graf.php" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
-                  Vés-hi
-                </a>
-              </div>
-            </div>
-            <div class="mdl-shadow--2dp mdl-cell mdl-cell--4-col mdl-card">
-              <div class="mdl-card__title mdl-card--expand misc">
-                <h2 class="mdl-card__title-text">App de l'Assistent de Google</h2>
-              </div>
-              <div class="mdl-card__supporting-text">
-                Com a novetat, ara pots navegar el graf amb l'Assistent de Google! Disponible en castellà i en anglés.
-              </div>
-              <div class="mdl-card__actions mdl-card--border">
-                <a href="https://assistant.google.com/services/a/uid/000000249b9f19cb" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
-                  Vés-hi
-                </a>
-              </div>
-            </div>
-          </div>
-        </div>
-      </main>
-    </div>
-    <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
-  </body>
-</html>
+<!DOCTYPE html>

+<html>

+  <head>

+    <meta charset="utf-8">

+    <title>Graf alternatiu FME</title>

+

+    <meta name=viewport content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

+

+    <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" />

+

+    <style>

+    .mdl-card {

+      height: 320px;

+    }

+    .mdl-card > .mdl-card__title.graf {

+      color: #fff;

+      background: url("img/graf_screenshot.png") center top 15% no-repeat #46B6AC;

+    }

+    .misc {

+      color: #fff;

+      background: #00695C;

+    }

+    </style>

+  </head>

+  <body>

+    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">

+      <header class="mdl-layout__header">

+        <div class="mdl-layout__header-row">

+          <!-- Title -->

+          <span class="mdl-layout-title">Graf alternatiu FME</span>

+        </div>

+      </header>

+      <main class="mdl-layout__content">

+        <div class="page-content">

+          <div class="mdl-grid">

+            <div class="mdl-shadow--2dp mdl-cell mdl-cell--4-col mdl-card">

+              <div class="mdl-card__title mdl-card--expand graf">

+                <h2 class="mdl-card__title-text">El graf</h2>

+              </div>

+              <div class="mdl-card__supporting-text">

+                Una versió de només lectura del graf, optimitzada per navegadors móvils i amb una interfície millorada.

+              </div>

+              <div class="mdl-card__actions mdl-card--border">

+                <a href="graf.php" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">

+                  Vés-hi

+                </a>

+              </div>

+            </div>

+            <div class="mdl-shadow--2dp mdl-cell mdl-cell--4-col mdl-card">

+              <div class="mdl-card__title mdl-card--expand misc">

+                <h2 class="mdl-card__title-text">App de l'Assistent de Google</h2>

+              </div>

+              <div class="mdl-card__supporting-text">

+                Com a novetat, ara pots navegar el graf amb l'Assistent de Google! Disponible en castellà i en anglés.

+              </div>

+              <div class="mdl-card__actions mdl-card--border">

+                <a href="https://assistant.google.com/services/a/uid/000000249b9f19cb" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">

+                  Vés-hi

+                </a>

+              </div>

+            </div>

+          </div>

+        </div>

+      </main>

+    </div>

+    <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>

+  </body>

+</html>

diff --git a/js/autocomplete.js b/js/autocomplete.js
index 7751bc1..107b03a 100644
--- a/js/autocomplete.js
+++ b/js/autocomplete.js
@@ -1,3 +1,5 @@
+// *********** HERE STARTS autocomplete.js *************

+

 function autocomplete(inp, obj, act) {

 	/*the autocomplete function takes two arguments,

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

@@ -41,10 +43,17 @@
 					var n = this.dataset.id;

 					inp.value = obj[n].name;

 

+					var node = null;

+					

+					s.graph.nodes().forEach( function(nnode) {

+						if(nnode.id == n) node = nnode;

+					});

+					

+

 					switch (act) {

 						case "search":

 							// Move camera to desired node

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

+							cameraGoto(node.x, node.y);

 							break;

 						case "addEdge":

 							// @TODO: Add an edge between A and B

diff --git a/js/camera.js b/js/camera.js
index ac4894f..44e9f5d 100644
--- a/js/camera.js
+++ b/js/camera.js
@@ -1,3 +1,6 @@
+// *********** HERE STARTS camera.js *************

+

+

 window.addEventListener('load', initCamera);

 

 function cameraGoto(nodeX, nodeY) {

@@ -7,16 +10,44 @@
 	);

 }

 

-function initCamera() {

-	document.querySelector("#zoomin").addEventListener("click", function() {

-		s.camera.goTo({

-			ratio: Math.max(s.camera.settings("zoomMin"), s.camera.ratio / Math.sqrt(2))

-		});

-	});

+function is_touch_device() {

+	  var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');

+	  var mq = function(query) {

+	    return window.matchMedia(query).matches;

+	  }

 

-	document.querySelector("#zoomout").addEventListener("click", function() {

-		s.camera.goTo({

-			ratio: Math.min(s.camera.settings("zoomMax"), s.camera.ratio * Math.sqrt(2))

+	  if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {

+	    return true;

+	  }

+

+	  // include the 'heartz' as a way to have a non matching MQ to help terminate the join

+	  // https://git.io/vznFH

+	  var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');

+	  return mq(query);

+}

+

+function initCamera() {

+	

+	if(!is_touch_device()) {

+		

+		document.querySelector("#zoomin").addEventListener("click", function() {

+			s.camera.goTo({

+				ratio: Math.max(s.camera.settings("zoomMin"), s.camera.ratio / Math.sqrt(2))

+			});

 		});

-	});

+	

+		document.querySelector("#zoomout").addEventListener("click", function() {

+			s.camera.goTo({

+				ratio: Math.min(s.camera.settings("zoomMax"), s.camera.ratio * Math.sqrt(2))

+			});

+		});

+	}

+	else{

+		document.querySelector("#zoomin").style.display = "none";

+		document.querySelector("#zoomout").style.display = "none";

+		

+		document.querySelector("#circle-mode").style.bottom = "110px";

+		document.querySelector("#settings").style.bottom = "60px";

+		document.querySelector("#search").style.bottom = "10px";

+	}

 }

diff --git a/js/circle-mode.js b/js/circle-mode.js
index ea5f423..d5ecff3 100644
--- a/js/circle-mode.js
+++ b/js/circle-mode.js
@@ -1,9 +1,11 @@
+// *********** HERE STARTS circle-mode.js *************

+

 window.addEventListener("load", initCircleMode);

 

 circleMode = false;

 

 function initCircleMode() {

-	document.querySelector("#circle-mode").addEventListener('click', function() {

+	document.querySelector("#circle-mode").addEventListener('click', function() {		

 		if(circleMode) {

 			circleMode = false;

 			document.querySelector("#circle-mode i").innerText = "trip_origin";

@@ -29,4 +31,22 @@
 			s.refresh();

 		}

 	});

+}

+

+function isInRect (x, y, rect) {

+	if (x < -10000 || x > 10000) return true;

+	if (y < -10000 || y > 10000) return true;

+	

+	var ans = true;

+	var c = crossProd (rect[0], rect[1], x, y);

+	

+	for(var i=1; i<4; i++) {	

+		var temp = crossProd (rect[i], rect[(i+1)%4], x, y);

+		if (c*temp < 0) ans = false;

+	}

+	return ans;

+}

+

+function crossProd(r1, r2, x, y) {

+	return r1[0]*r2[1] + r2[0]*y + x*r1[1] - r1[0]*y - r2[0]*r1[1] - x*r2[1]; 

 }
\ No newline at end of file
diff --git a/js/dialog.js b/js/dialog.js
index 275fb0c..ff3268f 100644
--- a/js/dialog.js
+++ b/js/dialog.js
@@ -1,3 +1,5 @@
+// *********** HERE STARTS dialog.js *************

+

 window.addEventListener("load", initDialog);

 

 var dialog = {

diff --git a/js/easter-egg.js b/js/easter-egg.js
index ac73b2a..35dce24 100644
--- a/js/easter-egg.js
+++ b/js/easter-egg.js
@@ -1,3 +1,5 @@
+// *********** HERE STARTS easter-egg.js *************

+

 window.addEventListener("load", initEasterEgg);

 

 var seq = [38, 38, 40, 40, 37, 39, 37, 39, 65, 66, 13];

diff --git a/js/graf.js b/js/graf.js
index feab604..155ce2d 100644
--- a/js/graf.js
+++ b/js/graf.js
@@ -1,3 +1,5 @@
+// *********** HERE STARTS graf.js *************

+

 window.addEventListener("load", initGraf);

 

 // s is the sigma graph

@@ -46,7 +48,7 @@
 			edgeColor: "default",

 			defaultLabelColor: "#fff",

 			autoRescale: false,

-			zoomMax: 10,

+			zoomMax: 30,

 			// enableEdgeHovering: true,

 			font: "Roboto",

 			labelThreshold: 5

@@ -60,11 +62,19 @@
 		graf = JSON.parse(responseText);

 

 		// does graf.nodes have a size attribute?

-		var sizegraf = 0;

+		var rectBorrar = [[0,0], [0,0], [0,0], [0,0]];

 		for (var i in graf.nodes) {

-			sizegraf++;

+			if (graf.nodes[i].name == "Erase")    rectBorrar[0] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Borrar")   rectBorrar[1] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Esborrar") rectBorrar[2] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Delete")   rectBorrar[3] = [ graf.nodes[i].x , graf.nodes[i].y ];

 		}

 		

+		var sizegraf = 0;

+		for (var i in graf.nodes) {

+			if ( isInRect(graf.nodes[i].x, graf.nodes[i].y, rectBorrar) ) continue;	

+			sizegraf++;

+		}		

 		var nnode = 0;

 		for (var i in graf.nodes) {

 			var ncolor = null;

@@ -79,6 +89,8 @@
 			var newX = 5000*Math.cos( 2*Math.PI*nnode/sizegraf );

 			var newY = 5000*Math.sin( 2*Math.PI*nnode/sizegraf );	

 			

+			if (isInRect(graf.nodes[i].x, graf.nodes[i].y, rectBorrar) ) continue;	

+			

 			s.graph.addNode({

 				// we add color, originalColor, size, originalX..Y, circleX..Y atributes

 				id: graf.nodes[i].id,

@@ -96,9 +108,12 @@
 				originalColor: ncolor

 			});

 			nnode++;

+		

 		}

 

 		for (var i in graf.edges) {

+			if (isInRect(graf.nodes[graf.edges[i].a].x, graf.nodes[graf.edges[i].a].y, rectBorrar)) continue;	

+			if (isInRect(graf.nodes[graf.edges[i].b].x, graf.nodes[graf.edges[i].b].y, rectBorrar)) continue;	

 			

 			s.graph.addEdge({

 				id: i,

@@ -113,7 +128,8 @@
 			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;

@@ -131,6 +147,12 @@
 			});

 			

 			if (circleMode) {

+				s.graph.nodes().forEach(function (n) {

+					n.x = n.circleX;

+					n.y = n.circleY;

+					n.size = 10;

+				});

+				

 				e.data.node.x = 0;

 				e.data.node.y = 0;

 				e.data.node.size = 30;

diff --git a/js/limit-years.js b/js/limit-years.js
index 6d99c37..cbbef95 100644
--- a/js/limit-years.js
+++ b/js/limit-years.js
@@ -1,3 +1,5 @@
+// *********** HERE STARTS limit-years.js *************

+

 window.addEventListener("load", addYearList);

 

 var limitYears = false;

@@ -45,10 +47,12 @@
 	

 	if(yearlist.style.display == "none"){

 		yearlist.style.display = "block";

+		document.querySelector("#settings i").innerText = "close";

 		yearLimits = true;

 	}

 	else{

 		yearlist.style.display = "none";

+		document.querySelector("#settings i").innerText = "settings";

 		yearLimits = true;

 	}

 }

diff --git a/js/script.js b/js/script.js
new file mode 100644
index 0000000..1493ae1
--- /dev/null
+++ b/js/script.js
@@ -0,0 +1,551 @@
+// *********** HERE STARTS circle-mode.js *************

+

+window.addEventListener("load", initCircleMode);

+

+circleMode = false;

+

+function initCircleMode() {

+	document.querySelector("#circle-mode").addEventListener('click', function() {		

+		if(circleMode) {

+			circleMode = false;

+			document.querySelector("#circle-mode i").innerText = "trip_origin";

+

+			s.graph.nodes().forEach(function(n) {

+				n.x = n.originalX;

+				n.y = n.originalY;

+				n.size = 10;

+			});

+			

+			s.refresh();

+			

+		}

+		else {	

+			circleMode = true;

+			document.querySelector("#circle-mode i").innerText = "shuffle";

+			

+			s.graph.nodes().forEach(function(n) {

+				n.x = n.circleX;

+				n.y = n.circleY;		

+			});

+			

+			s.refresh();

+		}

+	});

+}

+

+function isInRect (x, y, rect) {

+	if (x < -10000 || x > 10000) return true;

+	if (y < -10000 || y > 10000) return true;

+	

+	var ans = true;

+	var c = crossProd (rect[0], rect[1], x, y);

+	

+	for(var i=1; i<4; i++) {	

+		var temp = crossProd (rect[i], rect[(i+1)%4], x, y);

+		if (c*temp < 0) ans = false;

+	}

+	return ans;

+}

+

+function crossProd(r1, r2, x, y) {

+	return r1[0]*r2[1] + r2[0]*y + x*r1[1] - r1[0]*y - r2[0]*r1[1] - x*r2[1]; 

+}

+

+

+// *********** HERE STARTS graf.js *************

+

+window.addEventListener("load", initGraf);

+

+// s is the sigma graph

+// graf is the JSON graph

+var s, graf;

+

+// query dario JSON for the graph information

+function xhr(method, url, params, callback) {

+	var http = new XMLHttpRequest();

+	if (method == "POST") {

+		http.open(method, url, true);

+	} else {

+		if (params != "") {

+			http.open(method, url+"?"+params, true);

+		} else {

+			http.open(method, url, true);

+		}

+	}

+	http.onload = function() {

+		if(this.status != 200) {

+			console.warn("Attention, status code "+this.status+" when loading via xhr url "+url);

+		}

+		callback(this.responseText, this.status);

+	};

+	if (method == "POST") {

+		http.setRequestHeader("Content-type","application/x-www-form-urlencoded");

+		http.send(params);

+	} else {

+		http.send();

+	}

+}

+

+

+function initGraf() {

+	// create new methods for sigma library

+	updateSigma();

+	

+	// create graf, s is the sigma graf

+	s = new sigma({

+		renderers: [{

+			container: "graf",

+			type: "webgl"

+		}],

+		settings: {

+			defaultEdgeColor: "#fff",

+			edgeColor: "default",

+			defaultLabelColor: "#fff",

+			autoRescale: false,

+			zoomMax: 30,

+			// enableEdgeHovering: true,

+			font: "Roboto",

+			labelThreshold: 5

+		}

+	});

+

+	

+	// query for JSON for graph data

+	xhr("GET", "api.php", "action=getgraf", function(responseText, status) {

+		// graf is the JSON data

+		graf = JSON.parse(responseText);

+

+		// does graf.nodes have a size attribute?

+		var rectBorrar = [[0,0], [0,0], [0,0], [0,0]];

+		for (var i in graf.nodes) {

+			if (graf.nodes[i].name == "Erase")    rectBorrar[0] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Borrar")   rectBorrar[1] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Esborrar") rectBorrar[2] = [ graf.nodes[i].x , graf.nodes[i].y ];

+			if (graf.nodes[i].name == "Delete")   rectBorrar[3] = [ graf.nodes[i].x , graf.nodes[i].y ];

+		}

+		

+		var sizegraf = 0;

+		for (var i in graf.nodes) {

+			if ( isInRect(graf.nodes[i].x, graf.nodes[i].y, rectBorrar) ) continue;	

+			sizegraf++;

+		}		

+		var nnode = 0;

+		for (var i in graf.nodes) {

+			var ncolor = null;

+			

+			if(graf.nodes[i].sex =="F") ncolor = "#d61c08";

+			else if(graf.nodes[i].sex == "M") ncolor = "#0159aa";

+			else ncolor = "#0ca80a";

+			

+			// post-processing for year corrections

+			if(1970 < graf.nodes[i].year && graf.nodes[i].year < 2004) graf.nodes[i].year += 18;

+			

+			var newX = 5000*Math.cos( 2*Math.PI*nnode/sizegraf );

+			var newY = 5000*Math.sin( 2*Math.PI*nnode/sizegraf );	

+			

+			if (isInRect(graf.nodes[i].x, graf.nodes[i].y, rectBorrar) ) continue;	

+			

+			s.graph.addNode({

+				// we add color, originalColor, size, originalX..Y, circleX..Y atributes

+				id: graf.nodes[i].id,

+				year: graf.nodes[i].year,

+				sex: graf.nodes[i].sex,

+				label: graf.nodes[i].name,

+				x: graf.nodes[i].x,

+				y: graf.nodes[i].y, 

+				circleX: newX,

+				circleY: newY,

+				originalX: graf.nodes[i].x,

+				originalY: graf.nodes[i].y, 

+				size: 10,

+				color: ncolor,

+				originalColor: ncolor

+			});

+			nnode++;

+		

+		}

+

+		for (var i in graf.edges) {

+			if (isInRect(graf.nodes[graf.edges[i].a].x, graf.nodes[graf.edges[i].a].y, rectBorrar)) continue;	

+			if (isInRect(graf.nodes[graf.edges[i].b].x, graf.nodes[graf.edges[i].b].y, rectBorrar)) continue;	

+			

+			s.graph.addEdge({

+				id: i,

+				source: graf.edges[i].a,

+				target: graf.edges[i].b,

+				size: Math.min(4, Math.max((7/(2*Math.pow(20, 2)))*Math.pow(graf.edges[i].votes, 2) + 1/2, 0.5))

+			});

+		

+		}

+

+		s.bind('clickNode', function(e) {			

+			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';

+				}

+			});

+			

+			if (circleMode) {

+				s.graph.nodes().forEach(function (n) {

+					n.x = n.circleX;

+					n.y = n.circleY;

+					n.size = 10;

+				});

+				

+				e.data.node.x = 0;

+				e.data.node.y = 0;

+				e.data.node.size = 30;

+			}

+			

+			s.refresh();

+

+			dialog.show(nodeId, toKeep);

+		});

+

+

+		s.refresh();

+		initSearchBar();

+

+		autocomplete(document.querySelector("#search-input"), graf.nodes, "search");

+	});

+}

+

+function updateSigma() {

+	// returns set of neighouts

+	sigma.classes.graph.addMethod("neighbors", function(nodeId) {

+		var k,

+		neighbors = {},

+		index = this.allNeighborsIndex[nodeId] || [];

+

+		for (k in index) {

+			neighbors[k] = this.nodesIndex[k];

+		}

+

+		return neighbors;

+	});

+	

+	// returns number of neighbours from a set of years

+	sigma.classes.graph.addMethod("numNeighborsFromYears", function(nodeId, showYearsCopy) {

+		var k,

+		neighbors = 0,

+		index = this.allNeighborsIndex[nodeId] || [];

+

+		for (k in index) {

+			if(this.nodesIndex){

+				if (showYearsCopy.has("" + this.nodesIndex[k].year)) neighbors++;

+				else if (this.nodesIndex[k].year == 0) neighbors++;

+			}

+		}

+		

+		return neighbors;

+	});

+}

+

+

+// *********** HERE STARTS limit-years.js *************

+

+window.addEventListener("load", addYearList);

+

+var limitYears = false;

+var showYears = new Set();

+

+function repaint() {

+	//targetYear: graf.nodes[e.source].year,

+	if(limitYears) {

+		var added = new Set();

+		

+		s.graph.nodes().forEach(function(n) {

+			var numNeig = s.graph.numNeighborsFromYears(n.id, showYears);

+			

+			if ((n.year == 0 && (n.sex == 'F' || n.sex == 'M') )

+					|| numNeig == 0

+					|| (!showYears.has("" + n.year) && (n.year != 0) )) {

+				n.hidden = true;

+			}

+			else {

+				n.hidden = false;

+				added.add(n.id);

+			}

+		});

+		

+		s.graph.edges().forEach(function(e) {

+			if(!added.has(e.source) && !added.has(e.target)){

+				e.hidden = true;

+			}

+			else e.hidden = false;

+		}); 

+	}

+	else {

+		s.graph.nodes().forEach(function(n) {

+			n.hidden = false;

+		});

+		

+		s.graph.edges().forEach(function(e) {

+			e.hidden = false;

+		});

+	}

+}

+

+function altYearList() {

+	var yearlist = document.querySelector("#year-list");

+	

+	if(yearlist.style.display == "none"){

+		yearlist.style.display = "block";

+		document.querySelector("#settings i").innerText = "close";

+		yearLimits = true;

+	}

+	else{

+		yearlist.style.display = "none";

+		document.querySelector("#settings i").innerText = "settings";

+		yearLimits = true;

+	}

+}

+

+function addYearList() {	

+	var ylistspan = document.querySelector("#year-list-span")

+	for(var year=2006; year<2019; year++) {

+		var yin = document.createElement("input");

+		yin.type = "checkbox";

+		yin.class = "mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored";

+		yin.name = "" + year;

+		yin.addEventListener("change", function(){ 

+			limitYears = true;

+			

+			if(this.checked) {

+				showYears.add(this.name);

+			}

+			else {

+				showYears.delete(this.name);

+			}

+			

+			if(showYears.size == 0) limitYears = false;

+			

+			repaint();

+			

+			s.refresh();

+		});

+		

+		var lab = document.createElement("label");

+		lab.innerHTML = "" + year + "<br>";

+		

+		ylistspan.appendChild(yin);

+		ylistspan.appendChild(lab);

+	}

+	

+	document.querySelector("#settings").addEventListener("click", altYearList);

+}

+

+// *********** HERE STARTS search-bar.js *************

+

+

+function altSearchBar() {

+	if (document.querySelector(".md-google-search__metacontainer").style.display == "none") {

+		document.querySelector(".md-google-search__metacontainer").style.display = "block";

+		document.querySelector("#search i").innerText = "fullscreen";

+	} else {

+		document.querySelector(".md-google-search__metacontainer").style.display = "none";

+		document.querySelector(".autocomplete-container").style.display = "none";

+		document.querySelector("#search i").innerText = "search";

+	}

+}

+

+function initSearchBar() {

+	document.querySelector("#search").addEventListener("click", altSearchBar);

+	if (window.innerWidth > 700) altSearchBar();

+}

+// *********** HERE STARTS dialog.js *************

+

+window.addEventListener("load", initDialog);

+

+var dialog = {

+	fill: function(data, text, html=false) {

+		var el = document.querySelectorAll("*[data-fill=\""+data+"\"]");

+		for (var i in el) {

+			if (html === true) {

+				el[i].innerHTML = text;

+			} else {

+				el[i].innerText = text;

+			}

+		}

+	},

+	show: function(id, neighbors) {

+		var neighbors = Object.values(neighbors);

+

+		this.fill("name", graf.nodes[id].name);

+		this.fill("year", graf.nodes[id].year);

+		this.fill("sex", graf.nodes[id].sex);

+		this.fill("id", "#"+id);

+		this.fill("n-edges", neighbors.length);

+

+		var list = "";

+		neighbors.forEach(function (a) {

+			list += "<li><b>"+graf.nodes[id].name+" - "+a.label+":</b> "+(graf.edges[id+"_"+a.id] ? graf.edges[id+"_"+a.id].votes : graf.edges[a.id+"_"+id].votes)+" vots</li>";

+		});

+		this.fill("edges", list, true);

+

+		if (window.innerWidth > 700) {

+			document.querySelector("#dialog").style.display = "block";

+			document.querySelector("#backdrop-container").style.display = "block";

+		} else {

+			document.querySelector("#summary-dialog").style.display = "block";

+		}

+	},

+	close: function() {

+		document.querySelector("#dialog").style.display = "none";

+		document.querySelector("#summary-dialog").style.display = "none";

+		document.querySelector("#backdrop-container").style.display = "none";

+

+		s.graph.nodes().forEach(function(n) {

+			n.color = n.originalColor;

+		});

+

+		s.graph.edges().forEach(function(e) {

+			e.color = e.originalColor;

+		});

+

+		if(circleMode) {

+			s.graph.nodes().forEach(function (n) {

+				n.x = n.circleX;

+				n.y = n.circleY;

+				n.size = 10;

+			});

+		}

+		else {

+			s.graph.nodes().forEach(function (n) {

+				n.x = n.originalX;

+				n.y = n.originalY;

+				n.size = 10;

+			});

+		}

+		s.refresh();

+

+	},

+	max: function() {

+		document.querySelector("#summary-dialog").style.display = "none";

+		document.querySelector("#dialog").style.display = "block";

+	},

+	min: function() {

+		document.querySelector("#dialog").style.display = "none";

+		document.querySelector("#summary-dialog").style.display = "block";

+	}

+};

+

+

+function initDialog() {

+	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);

+}

+

+// *********** HERE STARTS camera.js *************

+

+

+window.addEventListener('load', initCamera);

+

+function cameraGoto(nodeX, nodeY) {

+	sigma.misc.animation.camera( s.camera,

+		{ x: nodeX, y: nodeY, ratio: 1 },

+		{ duration: s.settings('animationsTime') || 300 }

+	);

+}

+

+function is_touch_device() {

+	  var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');

+	  var mq = function(query) {

+	    return window.matchMedia(query).matches;

+	  }

+

+	  if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {

+	    return true;

+	  }

+

+	  // include the 'heartz' as a way to have a non matching MQ to help terminate the join

+	  // https://git.io/vznFH

+	  var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');

+	  return mq(query);

+}

+

+function initCamera() {

+	

+	if(!is_touch_device()) {

+		

+		document.querySelector("#zoomin").addEventListener("click", function() {

+			s.camera.goTo({

+				ratio: Math.max(s.camera.settings("zoomMin"), s.camera.ratio / Math.sqrt(2))

+			});

+		});

+	

+		document.querySelector("#zoomout").addEventListener("click", function() {

+			s.camera.goTo({

+				ratio: Math.min(s.camera.settings("zoomMax"), s.camera.ratio * Math.sqrt(2))

+			});

+		});

+	}

+	else{

+		document.querySelector("#zoomin").style.display = "none";

+		document.querySelector("#zoomout").style.display = "none";

+		

+		document.querySelector("#circle-mode").style.bottom = "110px";

+		document.querySelector("#settings").style.bottom = "60px";

+		document.querySelector("#search").style.bottom = "10px";

+	}

+}

+

+// *********** HERE STARTS easter-egg.js *************

+

+window.addEventListener("load", initEasterEgg);

+

+var seq = [38, 38, 40, 40, 37, 39, 37, 39, 65, 66, 13];

+var cur = 0;

+

+function justdoit() {

+	s.graph.nodes().forEach(function(n) {

+		switch(n.color) {

+			case "#d61c08":

+			n.color = "#0159aa";

+			break;

+

+			case "#0159aa":

+			n.color = "#0ca80a";

+			break;

+

+			case "#0ca80a":

+			n.color = "#d61c08";

+			break;

+		}

+	});

+

+	s.refresh();

+	setTimeout(justdoit, 333);

+}

+

+

+function initEasterEgg() {

+	document.addEventListener("keydown", function() {

+		if (event.key == "f" && event.target.getAttribute("id") != "search-input") altSearchBar();

+		if (event.which == seq[cur]) {

+			if (cur < seq.length) {

+				++cur;

+				if (cur == seq.length) {

+					justdoit();

+				}

+			}

+		} else cur = 0;

+	});

+}

diff --git a/js/search-bar.js b/js/search-bar.js
index a52ee65..e5c8846 100644
--- a/js/search-bar.js
+++ b/js/search-bar.js
@@ -1,15 +1,18 @@
-function altSearchBar() {
-	if (document.querySelector(".md-google-search__metacontainer").style.display == "none") {
-		document.querySelector(".md-google-search__metacontainer").style.display = "block";
-		document.querySelector("#search i").innerText = "fullscreen";
-	} else {
-		document.querySelector(".md-google-search__metacontainer").style.display = "none";
-		document.querySelector(".autocomplete-container").style.display = "none";
-		document.querySelector("#search i").innerText = "search";
-	}
-}
-
-function initSearchBar() {
-	document.querySelector("#search").addEventListener("click", altSearchBar);
-	if (window.innerWidth > 700) altSearchBar();
-}
+// *********** HERE STARTS search-bar.js *************

+

+

+function altSearchBar() {

+	if (document.querySelector(".md-google-search__metacontainer").style.display == "none") {

+		document.querySelector(".md-google-search__metacontainer").style.display = "block";

+		document.querySelector("#search i").innerText = "fullscreen";

+	} else {

+		document.querySelector(".md-google-search__metacontainer").style.display = "none";

+		document.querySelector(".autocomplete-container").style.display = "none";

+		document.querySelector("#search i").innerText = "search";

+	}

+}

+

+function initSearchBar() {

+	document.querySelector("#search").addEventListener("click", altSearchBar);

+	if (window.innerWidth > 700) altSearchBar();

+}

diff --git a/js/service-worker.js b/js/service-worker.js
index f5ed189..a70346f 100644
--- a/js/service-worker.js
+++ b/js/service-worker.js
@@ -1,102 +1,102 @@
-/*
- Copyright 2015 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-'use strict';
-
-// Incrementing CACHE_VERSION will kick off the install event and force previously cached
-// resources to be cached again.
-const CACHE_VERSION = 1;
-let CURRENT_CACHES = {
-  offline: 'offline-v' + CACHE_VERSION
-};
-const OFFLINE_URL = 'offline.html';
-
-function createCacheBustedRequest(url) {
-  let request = new Request(url, {cache: 'reload'});
-  // See https://fetch.spec.whatwg.org/#concept-request-mode
-  // This is not yet supported in Chrome as of M48, so we need to explicitly check to see
-  // if the cache: 'reload' option had any effect.
-  if ('cache' in request) {
-    return request;
-  }
-
-  // If {cache: 'reload'} didn't have any effect, append a cache-busting URL parameter instead.
-  let bustedUrl = new URL(url, self.location.href);
-  bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();
-  return new Request(bustedUrl);
-}
-
-self.addEventListener('install', event => {
-  event.waitUntil(
-    // We can't use cache.add() here, since we want OFFLINE_URL to be the cache key, but
-    // the actual URL we end up requesting might include a cache-busting parameter.
-    fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {
-      return caches.open(CURRENT_CACHES.offline).then(function(cache) {
-        return cache.put(OFFLINE_URL, response);
-      });
-    })
-  );
-});
-
-self.addEventListener('activate', event => {
-  // Delete all caches that aren't named in CURRENT_CACHES.
-  // While there is only one cache in this example, the same logic will handle the case where
-  // there are multiple versioned caches.
-  let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
-    return CURRENT_CACHES[key];
-  });
-
-  event.waitUntil(
-    caches.keys().then(cacheNames => {
-      return Promise.all(
-        cacheNames.map(cacheName => {
-          if (expectedCacheNames.indexOf(cacheName) === -1) {
-            // If this cache name isn't present in the array of "expected" cache names,
-            // then delete it.
-            console.log('Deleting out of date cache:', cacheName);
-            return caches.delete(cacheName);
-          }
-        })
-      );
-    })
-  );
-});
-
-self.addEventListener('fetch', event => {
-  // We only want to call event.respondWith() if this is a navigation request
-  // for an HTML page.
-  // request.mode of 'navigate' is unfortunately not supported in Chrome
-  // versions older than 49, so we need to include a less precise fallback,
-  // which checks for a GET request with an Accept: text/html header.
-  if (event.request.mode === 'navigate' ||
-      (event.request.method === 'GET' &&
-       event.request.headers.get('accept').includes('text/html'))) {
-    console.log('Handling fetch event for', event.request.url);
-    event.respondWith(
-      fetch(event.request).catch(error => {
-        // The catch is only triggered if fetch() throws an exception, which will most likely
-        // happen due to the server being unreachable.
-        // If fetch() returns a valid HTTP response with an response code in the 4xx or 5xx
-        // range, the catch() will NOT be called. If you need custom handling for 4xx or 5xx
-        // errors, see https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker/fallback-response
-        console.log('Fetch failed; returning offline page instead.', error);
-        return caches.match(OFFLINE_URL);
-      })
-    );
-  }
-
-  // If our if() condition is false, then this fetch handler won't intercept the request.
-  // If there are any other fetch handlers registered, they will get a chance to call
-  // event.respondWith(). If no fetch handlers call event.respondWith(), the request will be
-  // handled by the browser as if there were no service worker involvement.
-});
+/*

+ Copyright 2015 Google Inc. All Rights Reserved.

+ Licensed under the Apache License, Version 2.0 (the "License");

+ you may not use this file except in compliance with the License.

+ You may obtain a copy of the License at

+ http://www.apache.org/licenses/LICENSE-2.0

+ Unless required by applicable law or agreed to in writing, software

+ distributed under the License is distributed on an "AS IS" BASIS,

+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ See the License for the specific language governing permissions and

+ limitations under the License.

+*/

+

+'use strict';

+

+// Incrementing CACHE_VERSION will kick off the install event and force previously cached

+// resources to be cached again.

+const CACHE_VERSION = 1;

+let CURRENT_CACHES = {

+  offline: 'offline-v' + CACHE_VERSION

+};

+const OFFLINE_URL = 'offline.html';

+

+function createCacheBustedRequest(url) {

+  let request = new Request(url, {cache: 'reload'});

+  // See https://fetch.spec.whatwg.org/#concept-request-mode

+  // This is not yet supported in Chrome as of M48, so we need to explicitly check to see

+  // if the cache: 'reload' option had any effect.

+  if ('cache' in request) {

+    return request;

+  }

+

+  // If {cache: 'reload'} didn't have any effect, append a cache-busting URL parameter instead.

+  let bustedUrl = new URL(url, self.location.href);

+  bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();

+  return new Request(bustedUrl);

+}

+

+self.addEventListener('install', event => {

+  event.waitUntil(

+    // We can't use cache.add() here, since we want OFFLINE_URL to be the cache key, but

+    // the actual URL we end up requesting might include a cache-busting parameter.

+    fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {

+      return caches.open(CURRENT_CACHES.offline).then(function(cache) {

+        return cache.put(OFFLINE_URL, response);

+      });

+    })

+  );

+});

+

+self.addEventListener('activate', event => {

+  // Delete all caches that aren't named in CURRENT_CACHES.

+  // While there is only one cache in this example, the same logic will handle the case where

+  // there are multiple versioned caches.

+  let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {

+    return CURRENT_CACHES[key];

+  });

+

+  event.waitUntil(

+    caches.keys().then(cacheNames => {

+      return Promise.all(

+        cacheNames.map(cacheName => {

+          if (expectedCacheNames.indexOf(cacheName) === -1) {

+            // If this cache name isn't present in the array of "expected" cache names,

+            // then delete it.

+            console.log('Deleting out of date cache:', cacheName);

+            return caches.delete(cacheName);

+          }

+        })

+      );

+    })

+  );

+});

+

+self.addEventListener('fetch', event => {

+  // We only want to call event.respondWith() if this is a navigation request

+  // for an HTML page.

+  // request.mode of 'navigate' is unfortunately not supported in Chrome

+  // versions older than 49, so we need to include a less precise fallback,

+  // which checks for a GET request with an Accept: text/html header.

+  if (event.request.mode === 'navigate' ||

+      (event.request.method === 'GET' &&

+       event.request.headers.get('accept').includes('text/html'))) {

+    console.log('Handling fetch event for', event.request.url);

+    event.respondWith(

+      fetch(event.request).catch(error => {

+        // The catch is only triggered if fetch() throws an exception, which will most likely

+        // happen due to the server being unreachable.

+        // If fetch() returns a valid HTTP response with an response code in the 4xx or 5xx

+        // range, the catch() will NOT be called. If you need custom handling for 4xx or 5xx

+        // errors, see https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker/fallback-response

+        console.log('Fetch failed; returning offline page instead.', error);

+        return caches.match(OFFLINE_URL);

+      })

+    );

+  }

+

+  // If our if() condition is false, then this fetch handler won't intercept the request.

+  // If there are any other fetch handlers registered, they will get a chance to call

+  // event.respondWith(). If no fetch handlers call event.respondWith(), the request will be

+  // handled by the browser as if there were no service worker involvement.

+});

diff --git a/login.php b/login.php
index 4c3ccec..eed968f 100644
--- a/login.php
+++ b/login.php
@@ -1,50 +1,50 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>Graf alternatiu FME</title>
-
-    <meta name=viewport content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-
-    <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" />
-
-    <style>
-    .page-content {
-      display: block;
-      max-width: 500px;
-      margin-top: 16px;
-      margin-left: auto;
-      margin-right: auto;
-      padding: 16px;
-    }
-    </style>
-  </head>
-  <body>
-    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
-      <header class="mdl-layout__header">
-        <div class="mdl-layout__header-row">
-          <!-- Title -->
-          <span class="mdl-layout-title">Graf alternatiu FME</span>
-        </div>
-      </header>
-      <main class="mdl-layout__content">
-        <div class="page-content mdl-shadow--4dp">
-          <form action="graf.php" method="POST">
-            <div class="mdl-textfield mdl-textfield--floating-label mdl-js-textfield"><input class="mdl-textfield__input" type="password" name="password" id="password"><label class="mdl-textfield__label" for="password">Contrasenya</label></div>
-            <p><button class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect" type="submit">Entrar</button></p>
-          </form>
-        </div>
-      </main>
-    </div>
-    <div class="mdl-snackbar mdl-js-snackbar"><div class="mdl-snackbar__text"></div><button type="button" class="mdl-snackbar__action"></button></div>
-    
-    <script src="https://code.getmdl.io/1.3.0/material.min.js"></script> 
-
-    <?php
-    if (isset($_GET["msg"]) && $_GET["msg"] == "wrong") {
-      echo "<script>window.addEventListener('load', function() { var notification = document.querySelector('.mdl-js-snackbar'); notification.MaterialSnackbar.showSnackbar({ message: 'Contrasenya incorrecta' }); });</script>";
-    }
-    ?>  
-   </body>
-</html>
+<!DOCTYPE html>

+<html>

+  <head>

+    <meta charset="utf-8">

+    <title>Graf alternatiu FME</title>

+

+    <meta name=viewport content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

+

+    <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" />

+

+    <style>

+    .page-content {

+      display: block;

+      max-width: 500px;

+      margin-top: 16px;

+      margin-left: auto;

+      margin-right: auto;

+      padding: 16px;

+    }

+    </style>

+  </head>

+  <body>

+    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">

+      <header class="mdl-layout__header">

+        <div class="mdl-layout__header-row">

+          <!-- Title -->

+          <span class="mdl-layout-title">Graf alternatiu FME</span>

+        </div>

+      </header>

+      <main class="mdl-layout__content">

+        <div class="page-content mdl-shadow--4dp">

+          <form action="graf.php" method="POST">

+            <div class="mdl-textfield mdl-textfield--floating-label mdl-js-textfield"><input class="mdl-textfield__input" type="password" name="password" id="password"><label class="mdl-textfield__label" for="password">Contrasenya</label></div>

+            <p><button class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect" type="submit">Entrar</button></p>

+          </form>

+        </div>

+      </main>

+    </div>

+    <div class="mdl-snackbar mdl-js-snackbar"><div class="mdl-snackbar__text"></div><button type="button" class="mdl-snackbar__action"></button></div>

+    

+    <script src="https://code.getmdl.io/1.3.0/material.min.js"></script> 

+

+    <?php

+    if (isset($_GET["msg"]) && $_GET["msg"] == "wrong") {

+      echo "<script>window.addEventListener('load', function() { var notification = document.querySelector('.mdl-js-snackbar'); notification.MaterialSnackbar.showSnackbar({ message: 'Contrasenya incorrecta' }); });</script>";

+    }

+    ?>  

+   </body>

+</html>

diff --git a/manifest.json b/manifest.json
index 7560119..85ff23f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,15 +1,15 @@
-{
-  "short_name": "Graf FME",
-  "name": "El graf alternatiu de la FME",
-  "icons": [
-    {
-      "src": "img/graf.png",
-      "type": "image/png",
-      "sizes": "192x192"
-    }
-  ],
-  "background_color": "#060606",
-  "theme_color": "#060606",
-  "display": "fullscreen",
-  "start_url": "graf.php?utm_source=homescreen"
-}
+{

+  "short_name": "Graf FME",

+  "name": "El graf alternatiu de la FME",

+  "icons": [

+    {

+      "src": "img/graf.png",

+      "type": "image/png",

+      "sizes": "192x192"

+    }

+  ],

+  "background_color": "#060606",

+  "theme_color": "#060606",

+  "display": "fullscreen",

+  "start_url": "graf.php?utm_source=homescreen"

+}