Эх сурвалжийг харах

Refs #5 show quizz results
Refs #4 remove unused mysql dependency
Refs #1 show server list and channel list
Refs #2 Cron api to ping periodically the servers

isundil 6 жил өмнө
parent
commit
7950a5a7bd
15 өөрчлөгдсөн 413 нэмэгдсэн , 106 устгасан
  1. 5 0
      .htaccess
  2. 7 5
      .htconfig.php.sample
  3. 73 0
      api.php
  4. 0 65
      api/index.php
  5. 21 0
      inc/channels.php
  6. 76 0
      inc/quizz.php
  7. 37 0
      inc/servers.php
  8. 9 9
      inc/stats.php
  9. 44 21
      index.php
  10. 40 0
      quizz.js
  11. 58 0
      quizz.php
  12. 8 3
      script.js
  13. 25 2
      style.css
  14. 1 0
      template/footer.php
  15. 9 1
      template/header.php

+ 5 - 0
.htaccess

@@ -0,0 +1,5 @@
+RewriteEngine on
+RewriteBase /
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ $1.php

+ 7 - 5
.htconfig.php.sample

@@ -5,17 +5,19 @@ define("DB_PORT", 3306);
 define("DB_NAME", "irc_anope");
 define("DB_USER", "root");
 define("DB_PASS", "");
+define("API_KEY", "hackme");
+define("PING_FILE", "/tmp/irc_ping_state");
 
 // DO NOT EDIT BELOW
 
 $dblink = null;
 
-try {
+function getLink() {
     global $dblink;
+    if ($dblink)
+        return $dblink;
     $dblink = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";port=".DB_PORT, DB_USER, DB_PASS);
-}
-catch (Exception $e) {
-    echo "Error: (" .$e->getCode() .") " .$e->getMessage();
-    exit();
+    $dblink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+    return $dblink;
 }
 ?>

+ 73 - 0
api.php

@@ -0,0 +1,73 @@
+<?php
+
+function pingServer($hostname, $port) {
+    $sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname("tcp"));
+    if (!$sock)
+        return false;
+    if (!@socket_connect($sock, $hostname, $port)) {
+        error_log("Failed to connect to ${hostname}:${port}");
+        return false;
+    }
+    socket_close($sock);
+    return true;
+}
+
+if (isset($_GET["command"])) {
+    switch ($_GET["command"]) {
+    case "version":
+        $fic = substr(file_get_contents("./.git/HEAD"), 5);
+        if ($fic === FALSE) {
+            header("HTTP/1.1 500 Server error");
+            break;
+        }
+        $fic = file_get_contents("./.git/".trim($fic));
+        if ($fic === FALSE) {
+            header("HTTP/1.1 500 Server error");
+            break;
+        }
+        echo json_encode(trim($fic));
+        break;
+
+    case "servers":
+        require_once("./inc/servers.php");
+        echo json_encode(getServers());
+        break;
+
+    case "channels":
+        require_once("./inc/channels.php");
+        echo json_encode(getChannels());
+        break;
+
+    case "ping":
+        require_once("./.htconfig.php");
+        require_once("inc/servers.php");
+        if (!isset($_GET["key"]) || $_GET["key"] !== API_KEY) {
+            $state = getServersState();
+            if (!$state) {
+                header("HTTP/0.0 500 Internal Server Error");
+                die;
+            }
+            echo json_encode($state);
+            die;
+        }
+        require_once("./inc/servers.php");
+        $result = [];
+        foreach (getServers() as $i => $attrs) {
+            $success = false;
+            foreach ($attrs["ports"] as $port => $unused) {
+                if (pingServer($i, $port)) {
+                    $success = true;
+                    break;
+                }
+            }
+            $result[$i] = $success;
+        }
+        $result = array(
+            "result" => $result,
+            "date" => time()
+        );
+        writeServersState($result);
+    }
+}
+
+?>

+ 0 - 65
api/index.php

@@ -1,65 +0,0 @@
-<?php
-
-if (isset($_GET["command"])) {
-    switch ($_GET["command"]) {
-    case "official_channels.list":
-        require("../.htconfig.php");
-        global $dblink;
-        $chanList = $dblink->query("SELECT channel, default_join FROM knacki_official;");
-        $chanList->setFetchMode(PDO::FETCH_OBJ);
-        $result = array();
-        while ($i = $chanList->fetch())
-            array_push($result, $i);
-        echo json_encode($result);
-        break;
-
-    case "version":
-        $fic = substr(file_get_contents("../.git/HEAD"), 5);
-        if ($fic === FALSE) {
-            header("HTTP/1.1 500 Server error");
-            break;
-        }
-        $fic = file_get_contents("../.git/".trim($fic));
-        if ($fic === FALSE) {
-            header("HTTP/1.1 500 Server error");
-            break;
-        }
-        echo json_encode(trim($fic));
-        break;
-
-    case "servers":
-        echo json_encode(array(
-            "irc.knacki.info" => array(
-                "ports" => array(
-                    "6667" => false,
-                    "6697" => true
-                )
-            ),
-            "mirror.knacki.info" => array(
-                "ports" => array(
-                    "6667" => false,
-                    "6697" => true
-                )
-            )
-        ));
-
-    case "channels":
-        echo json_encode(array(
-            "generaux" => array(
-                "#Accueil" => "#Accueil",
-                "#15-20ans" => "#15-20ans",
-                "#20+" => "#20+",
-                "#40+" => "#40+"
-            ),
-            "jeux" => array(
-                "#Quizz" => "#Quizz"
-            ),
-            "techniques" => array(
-                "#Aide" => "#Aide",
-                "#Dev" => "#Dev"
-            )
-        ));
-    }
-}
-
-?>

+ 21 - 0
inc/channels.php

@@ -0,0 +1,21 @@
+<?php
+
+function getChannels() {
+    return array(
+        "generaux" => array(
+            "#Accueil" => "#Accueil",
+            "#15-20ans" => "#15-20ans",
+            "#20+" => "#20+",
+            "#40+" => "#40+"
+        ),
+        "jeux" => array(
+            "#Quizz" => "#Quizz"
+        ),
+        "techniques" => array(
+            "#Aide" => "#Aide",
+            "#Dev" => "#Dev"
+        )
+    );
+}
+
+?>

+ 76 - 0
inc/quizz.php

@@ -0,0 +1,76 @@
+<?php
+
+class Period {
+    public function Period($data) {
+        $this->id = $data["id"];
+        $this->start = strtotime($data["start"]);
+        $this->end = strtotime($data["end"]);
+        $this->host = $data["host"];
+    }
+}
+
+function getLastQuizzPeriod() {
+    require_once("./.htconfig.php");
+    $dblink = getlink();
+    $result = array();
+    $userCountPerChan = $dblink->prepare("SELECT * FROM `knackizz_period` ORDER BY `id` DESC LIMIT 1");
+    $userCountPerChan->execute();
+    if ($row = $userCountPerChan->fetch(PDO::FETCH_ASSOC))
+        return new Period($row);
+    return null;
+}
+
+function getCurrentOrLastQuizzPeriod($periodId)
+{
+    if ($periodId == null)
+        return getLastQuizzPeriod();
+    require_once("./.htconfig.php");
+    $dblink = getlink();
+    $result = array();
+    $userCountPerChan = $dblink->prepare("SELECT * FROM `knackizz_period` WHERE `id`=:id");
+    $userCountPerChan->execute([ "id" => $periodId ]);
+    if ($row = $userCountPerChan->fetch(PDO::FETCH_ASSOC))
+        return new Period($row);
+    return getLastQuizzPeriod();
+}
+
+function getPreviousQuizzPeriod($periodId) {
+    require_once("./.htconfig.php");
+    $dblink = getlink();
+    $result = array();
+    $userCountPerChan = $dblink->prepare("SELECT * FROM `knackizz_period` WHERE `id` < :id ORDER BY `id` DESC LIMIT 1");
+    $userCountPerChan->execute([ "id" => $periodId ]);
+    if ($row = $userCountPerChan->fetch(PDO::FETCH_ASSOC))
+        return new Period($row);
+    return null;
+}
+
+function getNextQuizzPeriod($periodId) {
+    require_once("./.htconfig.php");
+    $dblink = getlink();
+    $result = array();
+    $userCountPerChan = $dblink->prepare("SELECT * FROM `knackizz_period` WHERE `id` > :id ORDER BY `id` DESC LIMIT 1");
+    $userCountPerChan->execute([ "id" => $periodId ]);
+    if ($row = $userCountPerChan->fetch(PDO::FETCH_ASSOC))
+        return new Period($row);
+    return null;
+}
+
+function getQuizzScores($periodId) {
+    $result = [];
+    require_once("./.htconfig.php");
+    $dblink = getlink();
+    $result = array();
+    $userCountPerChan = $dblink->prepare("SELECT * FROM `knackizz_scores` WHERE `period_id`=:pid ORDER BY `score` DESC");
+    $userCountPerChan->execute([ "pid" => $periodId ]);
+    $i = 0;
+    while ($row = $userCountPerChan->fetch(PDO::FETCH_ASSOC))
+        $result[] = array(
+            "rank" => ++$i,
+            "pseudo" => $row["pseudo"],
+            "score" => $row["score"]
+        );
+    return $result;
+}
+
+?>

+ 37 - 0
inc/servers.php

@@ -0,0 +1,37 @@
+<?php
+
+function getServers() {
+    return array(
+        "irc.knacki.info" => array(
+            "ports" => array(
+                "6667" => false,
+                "6697" => true
+            )
+        ),
+        "mirror.knacki.info" => array(
+            "ports" => array(
+                "6667" => false,
+                "6697" => true
+            )
+        )
+    );
+}
+
+function getServersState() {
+    require_once("./.htconfig.php");
+
+    $state = @file_get_contents(PING_FILE);
+    if (!$state) {
+        error_log("Cannot read state file /tmp/irc_ping_state");
+        return null;
+    }
+    return json_decode($state);
+}
+
+function writeServersState($states) {
+    require_once("./.htconfig.php");
+
+    file_put_contents(PING_FILE, json_encode($states));
+}
+
+?>

+ 9 - 9
inc/stats.php

@@ -1,30 +1,30 @@
 <?php
 
-require("./.htconfig.php");
-
 function readNbUserPerChannel()
 {
-    global $dblink;
+    require_once("./.htconfig.php");
+    $dblink = getlink();
     $result = array();
     $userCountPerChan = $dblink->query("SELECT anope_userstats_chan.channel, count(nickid) as nbuser FROM `anope_userstats_ison` inner join anope_userstats_chan on anope_userstats_chan. chanid=anope_userstats_ison.chanid inner join knacki_official ON knacki_official.channel = anope_userstats_chan.channel group by anope_userstats_chan.chanid order by channel");
     $userCountPerChan->setFetchMode(PDO::FETCH_OBJ);
     while ($i = $userCountPerChan->fetch())
-	$result[$i->channel] = $i->nbuser;
+    $result[$i->channel] = $i->nbuser;
     return $result;
 }
 
 function printNbUserPerChannelAsTable()
 {
+    // FIXME catch error
   ?><table>
       <tr>
-	<th>Channel</th>
-	<th>Connected user</th>
+    <th>Channel</th>
+    <th>Connected user</th>
       </tr>
       <?php foreach(readNbUserPerChannel() as $channel => $usercount){ ?>
         <tr>
-	  <td><?php echo $channel; ?></td>
-	  <td><?php echo $usercount; ?></td>
-	</tr>
+      <td><?php echo $channel; ?></td>
+      <td><?php echo $usercount; ?></td>
+    </tr>
       <?php } ?>
       </table><?php
 }

+ 44 - 21
index.php

@@ -1,13 +1,9 @@
-<?php require("./inc/stats.php"); ?>
+<?php
+require_once("./inc/stats.php");
+require_once("./inc/channels.php");
+require_once("./inc/servers.php");
+?>
 <?php require("./template/header.php"); generateHeader("IRC Knacki - Tchat en ligne", "Tchat en ligne gratuit et sans inscription. Chat français, discussions et rencontres", "IRC, irl,online,tchat,chat,tchate,rencontre,amitie,discussion,rencontres,amitiés,messagerie,discussions,tchatche,gamer,gaming,discussion en ligne,salon de tchat"); ?>
-    <header>
-      <ul>
-        <li><a href="#chanlist">Salons</a></li>
-        <li><a href="#register">S'enregistrer</a></li>
-        <li><a href="#server">Serveur IRC</a></li>
-        <li><a href="#contact">Contacts</a></li>
-      </ul>
-    </header>
     <div class="body body-centered" style="background-image: url('img/gears-4188632.jpg');">
       <div class="container">
         <div class="joinform">
@@ -34,12 +30,17 @@ Des modérateurs sont présents sur le serveur, en cas de soucis avec un user, a
 <a name="chanlist" class="link-target"></a>
 <h3>Les salons officiels sont les suivants :</h3>
 <p>
-<ul class="chanlist"><li>#accueil</li>
-<li>#15-20ans</li>
-<li>#20+</li>
-<li>#40+</li>
-<li>#Quizz</li>
-<li>#Aide</li></ul>
+<ul class="chancategory">
+<?php
+foreach (getChannels() as $category => $i) {
+    echo "<li>${category} :</li><ul class=\"chanlist\">";
+    foreach ($i as $chanId => $chanName) {
+        echo "<li>${chanName}</li>";
+    }
+    echo "</ul>";
+}
+?>
+</ul>
 </p><p>
 Il est possible de créer vos propres salons en tapant la commande IRC : <code>/join #nomdevotresalon</code>
 </p>
@@ -66,12 +67,35 @@ Exemple :
 C’est un protocole de communication informatique, « Internet Relay Chat » ou en français « discussions relayées par internet ». qui permet une communication instantanée par salons ou messages privés.
 N’hésitez pas a vous renseigner sur toutes les commandes IRC disponibles aux users.
 </p><p>
-Pour les habitués, vous pouvez vous connecter via votre client habituel en utilisant les paramètres suivants :
-<ul class="serverconfig"><li><span class="legend">Adresse/serveur</span> : <span class="value">irc.knacki.info</span></li>
-<li><span class="legend">Port</span> : <span class="value">6667</span></li>
-<li><span class="legend">Port SSL</span> : <span class="value">6697</span></li>
+Pour les habitués, vous pouvez vous connecter via votre client habituel en utilisant un des serveur suivant :
+<?php
+foreach (getServers() as $addr => $config) {
+    echo '<ul class="serverconfig">';
+    echo "<li><span class=\"legend\">Adresse / serveur</span> : <span class=\"value\">${addr}</span></li>";
+    $portWithSSL = array();
+    $portStd = array();
+    foreach ($config["ports"] as $port => $useSSL) {
+        if ($useSSL)
+            array_push($portWithSSL, $port);
+        else
+            array_push($portStd, $port);
+    }
+    if (count($portWithSSL)) {
+        $portList = implode(", ", $portWithSSL);
+        echo "<li><span class=\"legend\">Port SSL</span> : <span class=\"value\">${portList}</span></li>";
+    }
+    if (count($portStd)) {
+        $portList = implode(", ", $portStd);
+        echo "<li><span class=\"legend\">Port</span> : <span class=\"value\">${portList}</span></li>";
+    }
+    echo '</ul>';
+}
+?>
+</ul>
+<ul class="serverconfig">
 <li><span class="legend">Salon principal</span> : <span class="value">#accueil</span></li>
-<li><span class="legend">Salon d'aide</span> : <span class="value">#aide</span></li></ul>
+<li><span class="legend">Salon d'aide</span> : <span class="value">#aide</span></li>
+</ul>
 </p>
 <a name="contact" class="link-target"></a>
 <h3>Contactez-nous :</h3>
@@ -81,5 +105,4 @@ Pour les habitués, vous pouvez vous connecter via votre client habituel en util
         </div>
       </div>
     </div>
-    <script src="script.js"></script>
 <?php require("./template/footer.php"); ?>

+ 40 - 0
quizz.js

@@ -0,0 +1,40 @@
+
+(function() {
+var table = {},
+    input = document.getElementById("querypseudo"),
+    lastFilter = "";
+
+function isFiltering(pseudo) {
+    return pseudo.indexOf(lastFilter) < 0;
+}
+
+function filterResults() {
+    var inputLower = input.value.toLowerCase().trim();
+    if (inputLower === lastFilter)
+        return;
+    lastFilter = inputLower;
+    for (var i in table) {
+        table[i].style.display = isFiltering(i) ? "none" : "";
+    }
+}
+
+function buildTable() {
+    var result = {},
+        table = document.querySelectorAll("#scoreDataTable tr.score-data");
+    table.forEach(i => {
+        var pseudo = i.children[1].innerHTML.toLowerCase().trim();
+        result[pseudo] = i;
+        i.addEventListener("click", function() { document.location.hash = '#' +pseudo; });
+    });
+    return result;
+}
+
+table = buildTable();
+
+input.addEventListener("input", filterResults);
+input.addEventListener("blur", filterResults);
+
+if (document.location.hash.length && !input.value.length)
+    input.value = document.location.hash.substr(1);
+filterResults();
+})();

+ 58 - 0
quizz.php

@@ -0,0 +1,58 @@
+<?php require("./template/header.php"); generateHeader("IRC Knacki - Tchat en ligne", "Tchat en ligne gratuit et sans inscription. Chat français, discussions et rencontres", "IRC, irl,online,tchat,chat,tchate,rencontre,amitie,discussion,rencontres,amitiés,messagerie,discussions,tchatche,gamer,gaming,discussion en ligne,salon de tchat"); ?>
+    <div class="body body-fixed quizz" style="background-image: url('img/gears-4188632.jpg');"><div class="container">
+        <div class="quizz-header">
+          <h1><img src="img/logo_irc2019.png" alt="IRC Knacki" class="logo" />
+          R&eacute;sultats du quizz</h1>
+        </div>
+<?php
+require_once("./inc/quizz.php");
+setlocale(LC_TIME, [ "fr_FR.UTF-8", "en_US.UTF-8" ]);
+try {
+    $currentPeriod = getCurrentOrLastQuizzPeriod(isset($_GET["period"]) ? $_GET["period"] : null);
+    $prevPeriod = getPreviousQuizzPeriod($currentPeriod->id);
+    $nextPeriod = getNextQuizzPeriod($currentPeriod->id);
+    $quizzScores = getQuizzScores($currentPeriod->id);
+} catch (Exception $e) {
+    header("HTTP/1.0 500 Internal Server Error");
+    error_log($e->getMessage());
+    echo "<div>Erreur lors de la r&eacute;cup&eacute;ration des donn&eacute;es</div>";
+    die;
+}
+
+function stringifyDate($dateObj) {
+    return strftime("%d %B %Y", $dateObj);
+}
+?>
+        <div class="period">
+            <?php if ($prevPeriod != null) { ?>
+            <a class="prev" href="?period=<?php echo $prevPeriod->id; ?>">&lt; <?php echo stringifyDate($prevPeriod->start); ?></a>
+            <?php } else { ?>
+            <a class="prev"></a>
+            <?php } ?>
+            <span class="current"><?php echo stringifyDate($currentPeriod->start) ." - " .stringifyDate($currentPeriod->end); ?></span>
+            <?php if ($nextPeriod != null) { ?>
+            <a class="next" href="?period=<?php echo $nextPeriod->id; ?>"><?php echo stringifyDate($nextPeriod->end); ?> &gt;</a>
+            <?php } else { ?>
+            <a class="next"></a>
+            <?php } ?>
+        </div>
+        <div class="score-table">
+            <table id="scoreDataTable" class="block-1f2 block-1s1">
+                <tr>
+                    <th>#</th>
+                    <th>Pseudo</th>
+                    <th>Points</th>
+                </tr>
+                <tr><td></td><td><input type="text" placeholder="Recherche" id="querypseudo"/></td><td></td></tr>
+                <?php foreach($quizzScores as $i) { ?>
+                <tr class="score-data rank-<?php echo $i["rank"];?>">
+                    <td class="rank"><?php echo $i["rank"]; ?></td>
+                    <td class="pseudo"><?php echo $i["pseudo"]; ?></td>
+                    <td class="score"><?php echo $i["score"]; ?></td>
+                </tr>
+                <?php } ?>
+            </table>
+        </div>
+    </div></div>
+    <script src="quizz.js"></script>
+<?php require("./template/footer.php"); ?>

+ 8 - 3
script.js

@@ -1,8 +1,8 @@
 
 var headerBackground = true;
 
-window.addEventListener("scroll", function() {
-    if (window.scrollY > window.innerHeight * 0.27) {
+function updateScroll(checkHeight) {
+    if (checkHeight) {
         if (headerBackground)
             document.querySelector("header").style.background = "black";
         headerBackground = false;
@@ -11,5 +11,10 @@ window.addEventListener("scroll", function() {
             document.querySelector("header").style.background = "";
         headerBackground = true;
     }
-});
+}
+
+window.addEventListener("scroll", function() { updateScroll(window.scrollY > this.innerHeight * 0.27); });
+var fixedContent = document.querySelector(".body.body-fixed");
+if (fixedContent)
+    fixedContent.addEventListener("scroll", function() { updateScroll(this.scrollTop > this.clientHeight * 0.27); });
 

+ 25 - 2
style.css

@@ -1,7 +1,7 @@
 body { margin: 0; padding: 0; overflow-x: hidden; font-family: "Roboto", sans-serif; }
 h1,h2,h3 { font-family: "Rubik Mono One", sans-serif; color: rgb(132, 132, 132); }
 h3 { margin-top: 2em; }
-header { position: fixed; text-align: center; top: 0; left: 0; right: 0; transition: background .5s; }
+header { position: fixed; text-align: center; top: 0; left: 0; right: 0; transition: background .5s; z-index: 50; }
 header ul { display: flex; margin: 0 auto; padding: .75em 0; list-style-type: none; max-width: 600px; }
 header li { flex: 1; display: inline-block; }
 header li a { color: white; text-decoration: none; }
@@ -10,6 +10,9 @@ header li a { color: white; text-decoration: none; }
 .link-target { position: absolute; margin-top:-2.5em; padding-top: 2.5em; }
 .body.body-centered > .container { display: flex; text-align: center; align-items: center; width: 100%; }
 .body.body-centered > .container > * { width: 100%; max-width: 300px; margin: auto; text-align: center; }
+.body.body-fixed { overflow: auto; max-height: 1vh; }
+.body.body-fixed > .container { padding-top: 1.5rem; }
+.body.body-fixed > .container > div{ padding-top: 1.5rem; background: rgba(255, 255, 255, .3); display: block; }
 .joinform { background: rgba(0, 0, 0, 0.5); padding: 1em; border-radius: 20px; color: rgb(132, 132, 132); }
 .joinform .logo { margin: 0 auto; }
 .joinform h1 { margin-top: 0.5em; }
@@ -19,10 +22,30 @@ header li a { color: white; text-decoration: none; }
 .container { width: 100%; font-size: 0; }
 .container > div { display: inline-block; vertical-align: top; font-size: 1rem; padding: 1em; box-sizing: border-box; text-align: left; }
 a { text-decoration: none; color: rgb(132, 132, 132); }
-ul.chanlist { list-style: none; }
+ul.chancategory { padding: 0; list-style: none; }
+ul.chancategory > li { text-decoration: underline; }
+ul.chanlist { list-style: none; margin-bottom: 1em; }
 ul.serverconfig { list-style: none; }
 code { margin: 0.75em 0; padding: 0 0.75em; display: block; font-family: "Roboto Mono", monospace; }
 
+.body.body-fixed > .container > div.quizz-header { background: none; text-align: center; }
+.quizz-header .logo { height: 75px; vertical-align: -50%; }
+
+.body.quizz .container .period { display: flex; }
+.body.quizz .container .period .prev { flex: 1; text-align: left; }
+.body.quizz .container .period .current { flex: 1; text-align: center; }
+.body.quizz .container .period .next { flex: 1; text-align: right; }
+.body.quizz .score-table table { margin: auto; }
+.body.quizz .score-table table input { width: 100%; }
+.body.quizz .score-table .rank { text-align: right; padding-right: 15px; }
+.body.quizz .score-table .pseudo::before { content: ' '; display: inline-block; width: 0.8em; height: 0.8em; border-radius: 0.4em; vertical-align: -0.15em; margin-right: .5em; z-index: 2; position: relative; }
+.body.quizz .score-table .score { text-align: left; padding-left: 15px; }
+.body.quizz .score-table .pseudo { border-bottom: solid #d3d3d345 1px; font-family: "Roboto Mono", monospace; position: relative; }
+.body.quizz .score-table .rank-1 .pseudo::before { background-color: yellow; }
+.body.quizz .score-table .rank-2 .pseudo::before { background-color: grey; }
+.body.quizz .score-table .rank-3 .pseudo::before { background-color: brown; }
+.body.quizz .score-table .rank-1 .pseudo::after, .body.quizz .score-table .rank-2 .pseudo::after, .body.quizz .score-table .rank-3 .pseudo::after { position: absolute; left: 3px; top: 2px; width: 0.4em; height: 0.4em; transform: rotate(45deg); content: ' '; border: 3px solid blue; border-top: none; border-left: none; z-index: 1; }
+
 .block-1f1{ width: 100%; }
 .block-1f2 { width: 50%; }
 .block-1f3 { width: 33.333%; }

+ 1 - 0
template/footer.php

@@ -1,4 +1,5 @@
     <footer>
     </footer>
+    <script src="script.js"></script>
   </body>
 </html>

+ 9 - 1
template/header.php

@@ -11,4 +11,12 @@
     <meta name="keywords" content="<?php echo $keywords;?>" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
   </head>
-  <body><?php } ?>
+  <body>
+    <header>
+      <ul>
+        <li><a href=".#chanlist">Salons</a></li>
+        <li><a href=".#register">S'enregistrer</a></li>
+        <li><a href=".#server">Serveur IRC</a></li>
+        <li><a href=".#contact">Contacts</a></li>
+      </ul>
+    </header><?php } ?>