diff options
Diffstat (limited to 'pages')
-rw-r--r-- | pages/app.php | 3 | ||||
-rw-r--r-- | pages/bitset.php | 262 | ||||
-rw-r--r-- | pages/edit.php | 5 | ||||
-rw-r--r-- | pages/emergency.php | 197 | ||||
-rw-r--r-- | pages/fronting.php | 875 | ||||
-rw-r--r-- | pages/home.php | 45 | ||||
-rw-r--r-- | pages/page.php | 31 | ||||
-rw-r--r-- | pages/parser.php | 4 | ||||
-rw-r--r-- | pages/score.php | 16 | ||||
-rw-r--r-- | pages/together-dev.php | 4 | ||||
-rw-r--r-- | pages/together.php | 941 | ||||
-rw-r--r-- | pages/travelling.php | 101 |
12 files changed, 1390 insertions, 1094 deletions
diff --git a/pages/app.php b/pages/app.php new file mode 100644 index 0000000..ceed71e --- /dev/null +++ b/pages/app.php @@ -0,0 +1,3 @@ +<?php + +header("Location: /app/") and die();
\ No newline at end of file diff --git a/pages/bitset.php b/pages/bitset.php new file mode 100644 index 0000000..c3cdf0a --- /dev/null +++ b/pages/bitset.php @@ -0,0 +1,262 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; +if (!$isLoggedIn) header("Location: /login") and die(); + +$title = "Bitset Calculator"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +?> + +<br> +<div class="container"> + <div id="page-content"> + <h2>Bitset Calculator</h2> + <!--<div class="alert alert-dark"> + <details> + <summary>About the 24bit integer-based metadata system</summary> + </details> + </div>--> + </div> + <div style="display:grid; grid-template-columns: repeat(24, 1fr);"> + <div data-bs-toggle="tooltip" title="Shared memory access" id="binary-bit-1" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fa77ef;">0</div> + <div data-bs-toggle="tooltip" title="Shared memory access" id="binary-bit-2" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fa77ef;">0</div> + <div data-bs-toggle="tooltip" title="Median system" id="binary-bit-3" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #a14700;">0</div> + <div data-bs-toggle="tooltip" title="Little/age regressor" id="binary-bit-4" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#b277fa;">0</div> + <div data-bs-toggle="tooltip" title="Little/age regressor" id="binary-bit-5" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#b277fa;">0</div> + <div data-bs-toggle="tooltip" title="Protector" id="binary-bit-6" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#77faab;">0</div> + <div data-bs-toggle="tooltip" title="Fictive" id="binary-bit-7" class="font-monospace tooltip-nohelp text-info" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Not talking" id="binary-bit-8" class="font-monospace tooltip-nohelp text-danger" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Host" id="binary-bit-9" class="font-monospace tooltip-nohelp text-primary" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-10" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> + <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-11" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> + <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-12" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> + <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-13" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">1</div> + <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-14" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> + <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-15" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> + <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-16" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> + <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-17" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-18" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-19" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-20" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-21" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Robot" id="binary-bit-22" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-23" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-24" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> + </div> + <br> + <p> + <b>Input:</b> <input onchange="calculateInput();" onkeydown="calculateInput()" onkeyup="calculateInput()" value="2048" id="input" type="number" class="form-control" style="width:256px;display: inline-block;color:white;background:#111;border-color:#222;" maxlength="8" max="16777215" min="16777215"><br> + <b>Output:</b> <span id="output-color" style="border-radius:5px;display:inline-block;width:16px;height:16px;background-color:black;vertical-align:middle;"></span> <span style="vertical-align:middle;"><code id="output-bin">0b000000000000100000000000</code>, <code id="output-hex">0x000800</code>, <code id="output-dec">2048</code></span> + </p> + <script> + for (let i = 1; i <= 24; i++) { + document.getElementById("binary-bit-" + i).onclick = (event) => { + let el = event.target; + if (el.innerText === "0") { + el.innerText = "1"; + } else { + el.innerText = "0"; + } + + calculateOutput(); + + let binString = ("0".repeat(24 - parseInt(document.getElementById("input").value).toString(2).length)) + parseInt(document.getElementById("input").value).toString(2); + let bin = binString.split("").map((i) => parseInt(i)); + + for (let i = 1; i <= 24; i++) { + if (bin[i - 1]) { + document.getElementById("binary-bit-" + i).innerText = bin[i - 1].toString(); + } else { + document.getElementById("binary-bit-" + i).innerText = "0"; + } + } + + let sharedMemory = parseInt(binString.substring(0, 2), 2); + let median = binString.substring(2, 3) !== "0"; + let little = parseInt(binString.substring(3, 5), 2); + let protector = binString.substring(5, 6) !== "0"; + let fictive = binString.substring(6, 7) !== "0"; + let notTalking = binString.substring(7, 8) !== "0"; + let host = binString.substring(8, 9) !== "0"; + let robot = binString.substring(21, 22) !== "0"; + let species1 = binString.substring(9, 13); + let species2 = binString.substring(13, 17); + + document.getElementById("value-0").value = sharedMemory; + document.getElementById("value-1").value = little; + document.getElementById("value-2").value = species1; + document.getElementById("value-3").value = species2; + document.getElementById("value-4").checked = median; + document.getElementById("value-5").checked = protector; + document.getElementById("value-6").checked = fictive; + document.getElementById("value-7").checked = notTalking; + document.getElementById("value-8").checked = host; + document.getElementById("value-9").checked = robot; + } + } + + function calculateOutput() { + let bin = [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null].map((_, i) => { + let e = i + 1; + return document.getElementById("binary-bit-" + e).innerText; + }).join(""); + + document.getElementById("output-bin").innerText = "0b" + bin; + + let hex = parseInt(bin, 2).toString(16); + + document.getElementById("output-hex").innerText = "0x" + "0".repeat(6 - hex.length) + hex; + document.getElementById("output-color").style.backgroundColor = "#" + "0".repeat(6 - hex.length) + hex; + document.getElementById("output-dec").innerText = parseInt(bin, 2).toString(); + document.getElementById("input").value = parseInt(bin, 2).toString(); + } + + function calculateInput() { + if (parseInt(document.getElementById("input").value).toString().length > 8) return; + + let binString = ("0".repeat(24 - parseInt(document.getElementById("input").value).toString(2).length)) + parseInt(document.getElementById("input").value).toString(2); + let bin = binString.split("").map((i) => parseInt(i)); + + for (let i = 1; i <= 24; i++) { + if (bin[i - 1]) { + document.getElementById("binary-bit-" + i).innerText = bin[i - 1].toString(); + } else { + document.getElementById("binary-bit-" + i).innerText = "0"; + } + } + + let sharedMemory = parseInt(binString.substring(0, 2), 2); + let median = binString.substring(2, 3) !== "0"; + let little = parseInt(binString.substring(3, 5), 2); + let protector = binString.substring(5, 6) !== "0"; + let fictive = binString.substring(6, 7) !== "0"; + let notTalking = binString.substring(7, 8) !== "0"; + let host = binString.substring(8, 9) !== "0"; + let robot = binString.substring(21, 22) !== "0"; + let species1 = binString.substring(9, 13); + let species2 = binString.substring(13, 17); + + document.getElementById("value-0").value = sharedMemory; + document.getElementById("value-1").value = little; + document.getElementById("value-2").value = species1; + document.getElementById("value-3").value = species2; + document.getElementById("value-4").checked = median; + document.getElementById("value-5").checked = protector; + document.getElementById("value-6").checked = fictive; + document.getElementById("value-7").checked = notTalking; + document.getElementById("value-8").checked = host; + document.getElementById("value-9").checked = robot; + + calculateOutput(); + + return bin; + } + + function updateFromSelection() { + let val0 = document.getElementById("value-0").value; + let val1 = document.getElementById("value-1").value; + let val2 = document.getElementById("value-2").value; + let val3 = document.getElementById("value-3").value; + let val4 = document.getElementById("value-4").checked; + let val5 = document.getElementById("value-5").checked; + let val6 = document.getElementById("value-6").checked; + let val7 = document.getElementById("value-7").checked; + let val8 = document.getElementById("value-8").checked; + let val9 = document.getElementById("value-9").checked; + + let val0bin = parseInt(val0).toString(2); + val0bin = val0bin.length === 1 ? "0" + val0bin : val0bin; + + let val1bin = parseInt(val1).toString(2); + val1bin = val1bin.length === 1 ? "0" + val1bin : val1bin; + + let val2bin = val2; + let val3bin = val3; + + let val4bin = val4 ? "1" : "0"; + let val5bin = val5 ? "1" : "0"; + let val6bin = val6 ? "1" : "0"; + let val7bin = val7 ? "1" : "0"; + let val8bin = val8 ? "1" : "0"; + let val9bin = val9 ? "1" : "0"; + + let bin = val0bin + val4bin + val1bin + val5bin + val6bin + val7bin + val8bin + val2bin + val3bin + "0000" + val9bin + "00"; + + console.log(bin, parseInt(bin, 2)); + + let parts = bin.split("").map((i) => parseInt(i)); + + for (let i = 1; i <= 24; i++) { + if (parts[i - 1]) { + document.getElementById("binary-bit-" + i).innerText = parts[i - 1].toString(); + } else { + document.getElementById("binary-bit-" + i).innerText = "0"; + } + } + + calculateOutput(); + } + </script> + + <p> + <b>Shared memory access: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-0" onchange="updateFromSelection();"> + <option value="2">Full direct access</option> + <option value="1">Partial direct access</option> + <option value="0" selected>No direct access</option> + </select> + <br> + <b>Little/age regressor: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-1" onchange="updateFromSelection();"> + <option value="0" selected>None</option> + <option value="2">Little</option> + <option value="3">Younger</option> + <option value="1">Age regressor</option> + </select> + <br> + <b>Species: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-2" onchange="updateFromSelection();"> + <option value="0000" disabled>None</option> + <option value="0001">Earth pony</option> + <option value="0010">Unicorn</option> + <option value="0011">Pegasus</option> + <option value="0100">Alicorn</option> + <option value="0101">Bat pony</option> + <option value="0110">Crystal pony</option> + </select> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-3" onchange="updateFromSelection();"> + <option value="0000">None</option> + <option value="0001">Earth pony</option> + <option value="0010">Unicorn</option> + <option value="0011">Pegasus</option> + <option value="0100">Alicorn</option> + <option value="0101">Bat pony</option> + <option value="0110">Crystal pony</option> + </select> + </p> + <p> + <label> + <input type="checkbox" id="value-4" onchange="updateFromSelection();"> + Part of a median system + </label><br> + <label> + <input type="checkbox" id="value-5" onchange="updateFromSelection();"> + Protector + </label><br> + <label> + <input type="checkbox" id="value-6" onchange="updateFromSelection();"> + Fictive + </label><br> + <label> + <input type="checkbox" id="value-7" onchange="updateFromSelection();"> + Not talking + </label><br> + <label> + <input type="checkbox" id="value-8" onchange="updateFromSelection();"> + Host + </label><br> + <label> + <input type="checkbox" id="value-9" onchange="updateFromSelection();"> + Robot + </label> + </p> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?> diff --git a/pages/edit.php b/pages/edit.php index c031c22..74105e2 100644 --- a/pages/edit.php +++ b/pages/edit.php @@ -20,10 +20,11 @@ if (!isset($_GET['_']) || trim($_GET['_']) === "") header("Location: /?error=Inv $parts = explode("/", $_GET['_']); array_shift($parts); +array_shift($parts); $system = $parts[0]; $member = ($parts[1] ?? null) === "" ? null : $parts[1]; -if ($system !== "cloudburst" && $system !== "raindrops") header("Location: /?error=Invalid system name") and die(); +if ($system !== "cloudburst" && $system !== "raindrops") header("Location: /?error=Invalid system name: " . $system) and die(); $systemCommonName = $system === "cloudburst" ? "Cloudburst System" : "Raindrops System"; $systemID = $system === "cloudburst" ? "ynmuc" : "gdapd"; @@ -58,7 +59,7 @@ if ($member === null) { require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/subsysedit.php'; } else { - header("Location: /?error=System member or subsystem not found") and die(); + header("Location: /?error=System member or subsystem not found: " . $subsystemID) and die(); } } diff --git a/pages/emergency.php b/pages/emergency.php index 02e483d..2e38a6f 100644 --- a/pages/emergency.php +++ b/pages/emergency.php @@ -11,203 +11,8 @@ $title = "Emergency Alert"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/ <br> <div class="container"> <div id="page-content"> - <h2>Emergency Alert - <details style="display: inline-block;font-size:12px;"> - <summary class="text-muted" style="opacity:.5;"></summary> - <label><input id="test-mode" type="checkbox"> Test Mode</label> · <label><input id="fake-requests" type="checkbox"> Fake Requests</label> - </details> - </h2> - <span data-bs-toggle="modal" data-bs-target="#turn-on" id="btn-on" style="background: #7f0000;font-size: 48px;padding: 10px 50px;border-radius: 10px;width: max-content;display: block;margin-left: auto;margin-right: auto;cursor: pointer;">Turn <b>ON</b></span> - <span data-bs-toggle="modal" data-bs-target="#turn-off" id="btn-off" style="display:none;background: #007f0b;font-size: 48px;padding: 10px 50px;border-radius: 10px;width: max-content;margin-left: auto;margin-right: auto;cursor: pointer;">Turn <b>OFF</b></span> - <p style="text-align:center;margin-top:10px;">Sending next notification <b><span id="next-notification">never</span></b></p> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/emergency.php"; ?> </div> </div> -<style> - .modal-header { - border-bottom: 1px solid #353738; - } - - .modal-content { - border: 1px solid rgba(255, 255, 255, .2); - background-color: #111; - } - - .btn-close { - filter: invert(1); - } -</style> - -<div class="modal fade" id="turn-on"> - <div class="modal-dialog"> - <div class="modal-content"> - - <div class="modal-header"> - <h4 class="modal-title">This is to be treated seriously.</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <div class="alert alert-danger"> - <b>WARNING:</b> This alert should be used IF AND ONLY IF you are in a situation that requires immediate help from a loved one. If your life is at immediate risk, please do not use this emergency system and call your local emergency services. - </div> - <div class="alert alert-danger"> - Keep this page open until you receive help. If you close the page, alert notifications will not be sent. - </div> - - <button onclick="enableAlert();" data-bs-dismiss="modal" class="btn btn-danger" style="font-size:20px;font-weight:bold;display:block;width:100%;">I understand the risk, enable the alert now.</button> - - <hr> - - <div class="alert alert-danger"> - Once enabled, disable the alert system ONLY once you are safe. <b>Your mental health is more important than sleep.</b> - </div> - - <div class="alert alert-warning"> - This emergency alert system is designed to make sure a loved one can get in touch with you as soon as possible. Therefore, it will emit sudden alerts, and may surprise somecreature if e.g. they are sleeping. Keep that in mind. - </div> - - <p> - <b>Disclaimer:</b> This emergency alert system MUST NOT be used in life-threatening situations. Although it has been extensively tested in multiple conditions, it may stop working correctly or stop working at all at any time and without warning. If your life is at immediate risk, call your local emergency services. - </p> - <p> - This service makes use of the ntfy platform, provided by a third party. Do note that their privacy policy applies when delivering the notifications; although they should not contain personal information in any way. - </p> - <p> - © <?= date('Y') ?> Equestria.dev - </p> - </div> - </div> - </div> -</div> - -<div class="modal fade" id="turn-off"> - <div class="modal-dialog"> - <div class="modal-content"> - - <div class="modal-header"> - <h4 class="modal-title">Make sure you are safe.</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <div class="alert alert-danger"> - Make sure you are safe, and you got in touch with a loved one before disabling this alert system. It can be re-enabled later at any time. Remember, <b>your mental health is more important than sleep.</b> - </div> - - <p class="text-muted"> - Scroll until the bottom of the page to disable the alert. - </p> - - <p> - <b>Read this if you want to disable the alert before receiving help:</b> Remember you are loved, cared for. Ponies rely on you, need you, love you. We never want to lose any of you, so please, keep the alert enabled until you are safe. If you are unsure, always keep the alert enabled. - </p> - <p> - <b>Disclaimer:</b> This emergency alert system MUST NOT be used in life-threatening situations. Although it has been extensively tested in multiple conditions, it may stop working correctly or stop working at all at any time and without warning. If your life is at immediate risk, call your local emergency services. - </p> - <p> - This service makes use of the ntfy platform, provided by a third party. Do note that their privacy policy applies when delivering the notifications; although they should not contain personal information in any way. - </p> - <p> - © <?= date('Y') ?> Equestria.dev - </p> - - <button id="disable-button" onclick="disableAlert();" data-bs-dismiss="modal" class="btn btn-success disabled" style="display:block;width:100%;">I am safe, disable the alert now. <span id="disable-timer">(X)</span></button> - </div> - </div> - </div> -</div> - -<script> - window.alertInterval = null; - window.alertIntervalCounter = 15; - window.alertDisablerCounter = 9; - window.alertDisablerEnabled = false; - window.alertDisablerCounterInterval = null; - - setInterval(() => { - if (document.getElementById("turn-off").offsetWidth > 0 || document.getElementById("turn-off").offsetHeight > 0) { - if (!window.alertDisablerEnabled) { - window.alertDisablerEnabled = true; - window.alertDisablerCounter = 9; - - document.getElementById("disable-timer").innerText = "(" + window.alertDisablerCounter + ")"; - document.getElementById("disable-button").classList.add("disabled"); - - window.alertDisablerCounterInterval = setInterval(() => { - window.alertDisablerCounter--; - - if (window.alertDisablerCounter > 0) { - document.getElementById("disable-timer").innerText = "(" + window.alertDisablerCounter + ")"; - document.getElementById("disable-button").classList.add("disabled"); - } else { - document.getElementById("disable-timer").innerText = ""; - document.getElementById("disable-button").classList.remove("disabled"); - } - }, 1000); - } - } else { - window.alertDisablerEnabled = false; - try { clearInterval(window.alertDisablerCounterInterval) } catch (e) {} - window.alertDisablerCounter = 9; - } - }); - - function sendNotification() { - window.alertIntervalCounter = -1; - - if (document.getElementById("test-mode").checked) { - document.getElementById("next-notification").innerText = "now"; - if (document.getElementById("fake-requests").checked) { - window.alertIntervalCounter = 15; - document.getElementById("next-notification").innerText = "15 seconds"; - } else { - window.fetch("/api/emergency").then(() => { - window.alertIntervalCounter = 15; - document.getElementById("next-notification").innerText = "15 seconds"; - }) - } - } else { - document.getElementById("next-notification").innerText = "now"; - if (document.getElementById("fake-requests").checked) { - window.alertIntervalCounter = 15; - document.getElementById("next-notification").innerText = "15 seconds"; - } else { - window.fetch("/api/emergency-real").then(() => { - window.alertIntervalCounter = 15; - document.getElementById("next-notification").innerText = "15 seconds"; - }) - } - } - } - - function enableAlert() { - sendNotification(); - document.getElementById("btn-on").style.display = "none"; - document.getElementById("btn-off").style.display = "block"; - document.getElementById("test-mode").disabled = true; - document.getElementById("fake-requests").disabled = true; - - window.alertInterval = setInterval(() => { - window.alertIntervalCounter--; - - if (window.alertIntervalCounter === 0) { - sendNotification(); - } else if (window.alertIntervalCounter > -1) { - document.getElementById("next-notification").innerText = window.alertIntervalCounter + " second" + (window.alertIntervalCounter > 1 ? "s" : ""); - } - }, 1000); - } - - function disableAlert() { - clearInterval(window.alertInterval); - window.alertIntervalCounter = 15; - document.getElementById("next-notification").innerText = "never"; - document.getElementById("btn-on").style.display = "block"; - document.getElementById("btn-off").style.display = "none"; - document.getElementById("test-mode").disabled = false; - document.getElementById("fake-requests").disabled = false; - } -</script> - <?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?> diff --git a/pages/fronting.php b/pages/fronting.php index 359deb7..21d6691 100644 --- a/pages/fronting.php +++ b/pages/fronting.php @@ -1,879 +1,10 @@ -<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; if (!$isLoggedIn) header("Location: /login") and die(); $title = "Front Planner"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; - -$cloudburst = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-planner.json"), true); -$raindrops = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-planner.json"), true); - -?> +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; if (!$isLoggedIn) header("Location: /login") and die(); $title = "Front Planner"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; ?> <br> <div class="container"> <div> - <h2>Front Planner</h2> - <table id="planner"> - <tbody> - <tr class="planner-day"> - <td colspan="4" id="planner-header-0">Today</td> - <?php - - if (!$cloudburst[date('Y-m-d')]) $cloudburst[date('Y-m-d')] = []; - $dayCloudburst = $cloudburst[date('Y-m-d')]; - if (!$raindrops[date('Y-m-d')]) $raindrops[date('Y-m-d')] = []; - $dayRaindrops = $raindrops[date('Y-m-d')]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d') ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 0);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d') ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 0);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day" id="planner-header-1"> - <td colspan="4">Tomorrow</td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + 86400)]) $cloudburst[date('Y-m-d', time() + 86400)] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + 86400)]; - if (!$raindrops[date('Y-m-d', time() + 86400)]) $raindrops[date('Y-m-d', time() + 86400)] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + 86400)]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + 86400) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 1);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + 86400) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 1);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day" id="planner-header-2"> - <td colspan="4"><?= date('l', time() + (86400 * 2)) ?></td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + (86400 * 2))]) $cloudburst[date('Y-m-d', time() + (86400 * 2))] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + (86400 * 2))]; - if (!$raindrops[date('Y-m-d', time() + (86400 * 2))]) $raindrops[date('Y-m-d', time() + (86400 * 2))] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + (86400 * 2))]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 2)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 2);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 2)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 2);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day"> - <td colspan="4" id="planner-header-3"><?= date('l', time() + (86400 * 3)) ?></td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + (86400 * 3))]) $cloudburst[date('Y-m-d', time() + (86400 * 3))] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + (86400 * 3))]; - if (!$raindrops[date('Y-m-d', time() + (86400 * 3))]) $raindrops[date('Y-m-d', time() + (86400 * 3))] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + (86400 * 3))]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 3)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 3);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 3)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 3);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day"> - <td colspan="4" id="planner-header-4"><?= date('l', time() + (86400 * 4)) ?></td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + (86400 * 4))]) $cloudburst[date('Y-m-d', time() + (86400 * 4))] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + (86400 * 4))]; - if (!$raindrops[date('Y-m-d', time() + (86400 * 4))]) $raindrops[date('Y-m-d', time() + (86400 * 4))] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + (86400 * 4))]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 4)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 4);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 4)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 4);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day"> - <td colspan="4" id="planner-header-5"><?= date('l', time() + (86400 * 5)) ?></td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + (86400 * 5))]) $cloudburst[date('Y-m-d', time() + (86400 * 5))] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + (86400 * 5))]; - if (!$raindrops[date('Y-m-d', time() + (86400 * 5))]) $raindrops[date('Y-m-d', time() + (86400 * 5))] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + (86400 * 5))]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 5)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 5);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 5)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 5);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - - <tr class="planner-day"> - <td colspan="4" id="planner-header-6"><?= date('l', time() + (86400 * 6)) ?></td> - <?php - - if (!$cloudburst[date('Y-m-d', time() + (86400 * 6))]) $cloudburst[date('Y-m-d', time() + (86400 * 6))] = []; - $dayCloudburst = $cloudburst[date('Y-m-d', time() + (86400 * 6))]; - if (!$raindrops[date('Y-m-d', time() + (86400 * 6))]) $raindrops[date('Y-m-d', time() + (86400 * 6))] = []; - $dayRaindrops = $raindrops[date('Y-m-d', time() + (86400 * 6))]; - - $index = 0; - $lengthCloudburst = count($dayCloudburst); - $lengthRaindrops = count($dayRaindrops); - $biggest = max($lengthCloudburst, $lengthRaindrops); - - ?> - </tr> - <tr class="planner-header"> - <td colspan="2">Cloudburst System</td> - <td colspan="2">Raindrops System</td> - </tr> - <?php for ($i = 0; $i <= $biggest; $i++): ?> - <tr class="planner-member"> - <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <td class="planner-link"> - <?php $member = getSystemMember("ynmuc", $dayCloudburst[$index]); ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 6)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('cloudburst', 6);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> - <?= $index + 1 ?> - </td> - <?php $member = getSystemMember("gdapd", $dayRaindrops[$index]); ?> - <td class="planner-link"> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * 6)) ?>')"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - </td> - <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="2"> - <a href="#" onclick="addFronter('raindrops', 6);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> - </a> - </td> - <?php else: ?> - <td colspan="2" class="planner-empty"></td> - <?php endif; ?> - </tr> - <?php $index++; endfor; ?> - <tr class="planner-day planner-end-of-day"> - <td colspan="4"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["display_name"] ?? getSystemMember("ynmuc", $dayCloudburst[count($dayCloudburst) - 1])["name"]) ?> will sleep with <?= getMiniName(getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["display_name"] ?? getSystemMember("gdapd", $dayRaindrops[count($dayRaindrops) - 1])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> - </td> - </tr> - <tr class="planner-separator"></tr> - </tbody> - </table> - </div> - - <style> - #planner { - margin-top: 10px; - border-collapse: collapse; - width: 100%; - } - - .planner-header { - font-weight: bold; - text-align: center; - } - - .planner-header td { - width: 50%; - } - - td { - border: 1px solid rgba(255, 255, 255, .25); - padding: 5px 10px; - } - - .planner-day { - text-align: center; - color: rgba(255, 255, 255, .5); - font-weight: bold; - } - - .planner-end-of-day { - font-weight: normal; - } - - .planner-end-of-day td { - border-bottom-left-radius: 10px; - } - - .planner-separator { - height: 20px; - } - - .planner-member-id { - width: 10%; - text-align: right; - } - - .planner-link { - padding: 0; - } - - .planner-link a { - padding: 5px 10px; - display: block; - } - - .planner-add-link { - color: rgba(255, 255, 255, .75); - text-decoration: none; - } - - .planner-add-link:hover { - color: rgba(255, 255, 255, .75); - } - - .planner-link:hover { - background-color: rgba(255, 255, 255, .125); - } - - .planner-link:active { - background-color: rgba(255, 255, 255, .25); - } - - .planner-add-icon { - filter: invert(1); - width: 24px; - vertical-align: middle; - opacity: .75; - } - - .planner-add-text { - vertical-align: middle; - } - - .modal-header { - border-bottom: 1px solid #353738; - } - - .modal-content { - border: 1px solid rgba(255, 255, 255, .2); - background-color: #111; - } - - .btn-close { - filter: invert(1); - } - - .list-group-item { - color: #fff; - background-color: #222; - border: 1px solid rgba(255, 255, 255, .125); - } - - .list-group-item.disabled { - color: #fff; - background-color: #222; - border-color: rgba(255, 255, 255, .125); - opacity: .75; - } - - .list-group-item:hover { - background-color: #252525; - color: #ddd; - } - - .list-group-item:active, .list-group-item:focus { - background-color: #272727; - color: #bbb; - } - - .member-link, .list-group-item-action { - cursor: pointer !important; - } - - </style> -</div> - -<!--suppress JSUnresolvedVariable, JSUnresolvedFunction --> -<script> - window.currentWorkingDate; - window.fronting = JSON.parse(window.atob(`<?= base64_encode(json_encode([ - "raindrops" => $raindrops, - "cloudburst" => $cloudburst - ])) ?>`)); - window.names = JSON.parse(window.atob(`<?php - - $names = []; - foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true) as $member) { - $names[$member['id']] = $member['display_name'] ?? $member['name']; - } - foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true) as $member) { - $names[$member['id']] = $member['display_name'] ?? $member['name']; - } - - echo(base64_encode(json_encode($names))) ?>`)); - - function ordinal(n) { - let s = ["th", "st", "nd", "rd"]; - let v = n % 100; - return n + (s[(v - 20) % 10] || s[v] || s[0]); - } - - function openEditFronter(system, id, date) { - let display; - - switch (date) { - case "<?= date('Y-m-d') ?>": - display = "today"; - break; - - case "<?= date('Y-m-d', time() + 86400) ?>": - display = "tomorrow"; - break; - - case "<?= date('Y-m-d', time() + (86400 * 2)) ?>": - display = "on <?= date('l', time() + (86400 * 2)) ?>"; - break; - - case "<?= date('Y-m-d', time() + (86400 * 3)) ?>": - display = "on <?= date('l', time() + (86400 * 3)) ?>"; - break; - - case "<?= date('Y-m-d', time() + (86400 * 4)) ?>": - display = "on <?= date('l', time() + (86400 * 4)) ?>"; - break; - - case "<?= date('Y-m-d', time() + (86400 * 5)) ?>": - display = "on <?= date('l', time() + (86400 * 5)) ?>"; - break; - - case "<?= date('Y-m-d', time() + (86400 * 6)) ?>": - display = "on <?= date('l', time() + (86400 * 6)) ?>"; - break; - } - - window.selectedFronting = { - system: system === "cloudburst" ? "ynmuc" : "gdapd", - date: date, - index: id - } - - document.getElementById("edit-fronter-name").innerText = names[fronting[system][date][id]] ?? fronting[system][date][id]; - document.getElementById("edit-fronter-date").innerText = display; - document.getElementById("edit-fronter-pos").innerText = ordinal(id + 1); - document.getElementById("edit-fronter-system").innerText = system === "cloudburst" ? "Cloudburst System" : "Raindrops System"; - - let modal = new bootstrap.Modal(document.getElementById('edit-fronter')); - modal.show(); - } - - function addFronter(system, offset) { - let date; - let display; - - switch (offset) { - case 0: - date = "<?= date('Y-m-d') ?>"; - display = "today"; - break; - - case 1: - date = "<?= date('Y-m-d', time() + 86400) ?>"; - display = "tomorrow"; - break; - - case 2: - date = "<?= date('Y-m-d', time() + (86400 * 2)) ?>"; - display = "on <?= date('l', time() + (86400 * 2)) ?>"; - break; - - case 3: - date = "<?= date('Y-m-d', time() + (86400 * 3)) ?>"; - display = "on <?= date('l', time() + (86400 * 3)) ?>"; - break; - - case 4: - date = "<?= date('Y-m-d', time() + (86400 * 4)) ?>"; - display = "on <?= date('l', time() + (86400 * 4)) ?>"; - break; - - case 5: - date = "<?= date('Y-m-d', time() + (86400 * 5)) ?>"; - display = "on <?= date('l', time() + (86400 * 5)) ?>"; - break; - - case 6: - date = "<?= date('Y-m-d', time() + (86400 * 6)) ?>"; - display = "on <?= date('l', time() + (86400 * 6)) ?>"; - break; - } - - window.currentWorkingDate = date; - document.getElementById("new-fronter-date").innerText = display; - document.getElementById("new-fronter-system").innerText = system === "cloudburst" ? "Cloudburst System" : "Raindrops System"; - document.getElementById("list-" + system).style.display = ""; - document.getElementById("list-" + (system === "cloudburst" ? "raindrops" : "cloudburst")).style.display = "none"; - - let modal = new bootstrap.Modal(document.getElementById('new-fronter')); - modal.show(); - } - - function confirmFronterAdd(system, id) { - Array.from(document.getElementsByClassName("new-fronter-link")).forEach((i) => { - i.classList.add("disabled"); - }); - - document.getElementById("new-fronter-close").classList.add("disabled"); - - window.fetch("/api/fronter?t=add&d=" + window.currentWorkingDate + "&m=" + id + "&s=" + system).then(() => { - location.reload(); - }); - } - - function deleteFronter() { - Array.from(document.getElementsByClassName("edit-fronter-link")).forEach((i) => { - i.classList.add("disabled"); - }); - - document.getElementById("edit-fronter-close").classList.add("disabled"); - - window.fetch("/api/fronter?t=delete&d=" + window.selectedFronting["date"] + "&i=" + window.selectedFronting["index"] + "&s=" + window.selectedFronting["system"]).then(() => { - location.reload(); - }); - } - - function moveFronterDown() { - Array.from(document.getElementsByClassName("edit-fronter-link")).forEach((i) => { - i.classList.add("disabled"); - }); - - document.getElementById("edit-fronter-close").classList.add("disabled"); - - window.fetch("/api/fronter?t=down&d=" + window.selectedFronting["date"] + "&i=" + window.selectedFronting["index"] + "&s=" + window.selectedFronting["system"]).then(() => { - location.reload(); - }); - } - - function moveFronterUp() { - Array.from(document.getElementsByClassName("edit-fronter-link")).forEach((i) => { - i.classList.add("disabled"); - }); - - document.getElementById("edit-fronter-close").classList.add("disabled"); - - window.fetch("/api/fronter?t=up&d=" + window.selectedFronting["date"] + "&i=" + window.selectedFronting["index"] + "&s=" + window.selectedFronting["system"]).then(() => { - location.reload(); - }); - } -</script> - -<div class="modal fade" id="new-fronter" data-bs-backdrop="static" data-bs-keyboard="false"> - <div class="modal-dialog"> - <div class="modal-content"> - - <div class="modal-header"> - <h4 class="modal-title">Add new fronter <span id="new-fronter-date">n/a</span></h4> - <button id="new-fronter-close" type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <p class="text-muted">Adding for the <span id="new-fronter-system">n/a</span></p> - - <div class="list-group" id="list-raindrops"> - <?php foreach (scoreOrder(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true), "gdapd") as $member): ?> - <a onclick="confirmFronterAdd('gdapd', '<?= $member['id'] ?>');" class="new-fronter-link member-link list-group-item list-group-item-action" href="#"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - <?php endforeach; ?> - </div> - - <div class="list-group" id="list-cloudburst"> - <?php foreach (scoreOrder(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true), "ynmuc") as $member): ?> - <a onclick="confirmFronterAdd('ynmuc', '<?= $member['id'] ?>');" class="new-fronter-link member-link list-group-item list-group-item-action" href="#"><img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?></a> - <?php endforeach; ?> - </div> - </div> - - </div> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/planner.php"; ?> </div> </div> -<div class="modal fade" id="edit-fronter" data-bs-backdrop="static" data-bs-keyboard="false"> - <div class="modal-dialog"> - <div class="modal-content"> - - <div class="modal-header"> - <h4 class="modal-title">Edit <span id="edit-fronter-name">n/a</span> fronting in <span id="edit-fronter-pos">n/a</span> <span id="edit-fronter-date">n/a</span></h4> - <button id="edit-fronter-close" type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <p class="text-muted">Editing for the <span id="edit-fronter-system">n/a</span></p> - - <div class="list-group" id="list-cloudburst"> - <a class="list-group-item list-group-item-action edit-fronter-link" onclick="deleteFronter();"> - <img src="/assets/icons/delete.svg" style="width:24px;filter:invert(1);vertical-align: middle;"> - <span style="vertical-align: middle;">Delete</span> - </a> - <a class="list-group-item list-group-item-action edit-fronter-link" onclick="moveFronterUp();"> - <img src="/assets/icons/up.svg" style="width:24px;filter:invert(1);vertical-align: middle;"> - <span style="vertical-align: middle;">Move up</span> - </a> - <a class="list-group-item list-group-item-action edit-fronter-link" onclick="moveFronterDown();"> - <img src="/assets/icons/down.svg" style="width:24px;filter:invert(1);vertical-align: middle;"> - <span style="vertical-align: middle;">Move down</span> - </a> - </div> - </div> - - </div> - </div> -</div> - -<?php - -file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-planner.json", json_encode($cloudburst)); -file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-planner.json", json_encode($raindrops)); - -require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; - -?> +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?> diff --git a/pages/home.php b/pages/home.php index 4e853a6..2127712 100644 --- a/pages/home.php +++ b/pages/home.php @@ -9,27 +9,46 @@ </div> <?php endif; ?> - <div style="background:rgba(255, 255, 255, .1);max-width:100%;width:max-content;display:grid;grid-template-columns:128px 1fr;border-radius:10px;margin-left:auto;margin-right:auto;color:white;"> + <?php global $isLoggedIn; global $_PROFILE; global $travelling; if ($isLoggedIn): $byColor = getMembersByColor(); ?> + <div style="text-align: center;"> + <img alt="" src="/assets/logo/logo.png" style="width:128px;"> + <p style="z-index:999;position:relative;background:black;margin: 20px -10px 0 -20px;padding-right:30px;height:32px;text-align: center;display:grid;grid-template-columns: repeat(<?= count($byColor) ?>, 1fr);"> + <?php foreach ($byColor as $member): ?><a data-bs-html="true" class="rainbow-item tooltip-nohelp" title="<b><?= $member["display_name"] ?? $member["name"] ?></b><br><?= ($travelling[$member['id']]["travelling"] ? $member["_system"] === "ynmuc" : $member["_system"] === "gdapd") ? "Raindrops System" : "Cloudburst System" ?>" data-bs-toggle="tooltip" style="overflow: hidden;" href="/<?= $member["_system"] === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"><img src="/assets/uploads/pt-<?= $member["name"] ?>.png" style="width:32px;height:32px;position:absolute;z-index:99;"></a><?php endforeach; ?> + </p> + <div style="margin-top:-33px;margin-bottom:0;margin-left:-20px;margin-right:20px;height:32px;text-align: center;display:grid;grid-template-columns: repeat(<?= count($byColor) ?>, 1fr);"> + <?php foreach ($byColor as $member): ?><div> + <span style="display: inline-block;background: transparent;position:absolute;width: 20px;height: 15px;margin-top: 17px;box-shadow: 0 6px 20px 7px #<?= $member["color"] ?>;z-index: 9;margin-left: 8px;opacity: .75;"></span> + </div><?php endforeach; ?> + </div> + <div style="padding:5px 10px;background:#222;border-bottom-left-radius: 10px;border-bottom-right-radius: 10px;"> + <h2 style="margin-top: 20px;">Cuties and Plurality</h2> + <p>A safe place for <?= count($byColor) ?> ponies in 2 plural systems</p> + </div> + </div> + <hr style="border-color:rgba(255, 255, 255, .25);"> + <?php else: ?> + <div style="background:rgba(255, 255, 255, .1);max-width:100%;width:max-content;display:grid;grid-template-columns:128px 1fr;border-radius:10px;margin-left:auto;margin-right:auto;color:white;"> - <!-- Logo --> - <img src="/assets/uploads/logo.jpg" alt="" style="width:128px;border-top-left-radius:10px;border-bottom-left-radius:10px;"> + <!-- Logo --> + <img src="/assets/uploads/logo.jpg" alt="" style="width:128px;border-top-left-radius:10px;border-bottom-left-radius:10px;"> - <!-- Banner text --> - <div style="padding:20px;display:flex;align-items:center;justify-content:center;"> - <div> + <!-- Banner text --> + <div style="padding:20px;display:flex;align-items:center;justify-content:center;"> + <div> - <!-- Main title --> - <span style="font-weight:bold;font-size:24px;">Cuties and Plurality</span><br> + <!-- Main title --> + <span style="font-weight:bold;font-size:24px;">Cuties and Plurality</span><br> - <!-- Tagline --> - <span style="font-weight:normal;font-size:16px;">Just a small safe place for two plural systems</span> + <!-- Tagline --> + <span style="font-weight:normal;font-size:16px;">Just a small safe place for two plural systems</span> + </div> </div> - </div> - </div> + </div> + <?php endif; ?> - <?php global $isLoggedIn; global $_PROFILE; if ($isLoggedIn && ((int)date('H') >= 20 || (int)date('H') < 6)): ?> + <?php if ($isLoggedIn && ((int)date('H') >= 20 || (int)date('H') < 6)): ?> <a href="/emergency" style="text-decoration: none;margin-top:15px;display:block;font-size:24px;"> <div class="alert alert-danger"> <b>Are you in need of help?</b> If you need immediate help from a loved one, you may want to enable the emergency alert by clicking here, even if that will wake up the <?= $_PROFILE['name'] === "Raindrops System" ? "Cloudburst System" : "Raindrops System" ?>. Use it as you need. diff --git a/pages/page.php b/pages/page.php index 58f40eb..00622b3 100644 --- a/pages/page.php +++ b/pages/page.php @@ -1,5 +1,6 @@ <?php +$travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling.json"), true); if (!isset($_GET['_']) || trim($_GET['_']) === "") header("Location: /?error=Invalid request") and die(); $parts = explode("/", $_GET['_']); @@ -19,7 +20,11 @@ if ($member === null) { header("Location: /?error=Page not found") and die(); } } else { + $memberFoundInAnotherSystem = false; + $traveller = false; + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $systemID . "-members.json"), true); + $members2 = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === "gdapd" ? "ynmuc" : "gdapd") . "-members.json"), true); $memberData = null; $memberCommonName = null; $memberID = null; @@ -32,7 +37,31 @@ if ($member === null) { } } - if ($memberData === null) header("Location: /?error=System member not found") and die(); + foreach ($members2 as $m) { + if ($m['name'] === $member && $travelling[$m['id']]['travelling']) { + $traveller = true; + $memberData = $m; + $memberCommonName = $m['display_name'] ?? $m['name']; + $memberID = $m['id']; + $system = $systemID === "gdapd" ? "cloudburst" : "raindrops"; + $systemCommonName = $system === "cloudburst" ? "Cloudburst System" : "Raindrops System"; + $systemID = $system === "cloudburst" ? "ynmuc" : "gdapd"; + } else if ($m['name'] === $member) { + $memberFoundInAnotherSystem = true; + } + } + + if ($memberData === null) { + if ($memberFoundInAnotherSystem) { + header("Location: /" . ($systemID === "gdapd" ? "cloudburst" : "raindrops") . "/" . $member) and die(); + } else { + header("Location: /?error=System member not found") and die(); + } + } + + if ($travelling[$memberID]['travelling'] && !$traveller) { + header("Location: /" . ($systemID === "gdapd" ? "cloudburst" : "raindrops") . "/" . $member) and die(); + } require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/member.php'; } diff --git a/pages/parser.php b/pages/parser.php index a35f594..e3d70b8 100644 --- a/pages/parser.php +++ b/pages/parser.php @@ -90,7 +90,7 @@ $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true); foreach ($members as $member) { - $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $data = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true)); if ($data["host"]) { echo $member['id']; } @@ -101,7 +101,7 @@ $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true); foreach ($members as $member) { - $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $data = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true)); if ($data["host"]) { echo $member['id']; } diff --git a/pages/score.php b/pages/score.php index 841decc..0074016 100644 --- a/pages/score.php +++ b/pages/score.php @@ -18,7 +18,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true) as $member) { if ($member["name"] !== "unknown") { require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = $score["total"]; @@ -40,7 +40,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl if ($member["name"] !== "unknown") { $name = $member["display_name"] ?? $member["name"]; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = [ @@ -75,7 +75,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true) as $member) { if ($member["name"] !== "unknown") { require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = $score["total"]; @@ -97,7 +97,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl if ($member["name"] !== "unknown") { $name = $member["display_name"] ?? $member["name"]; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = [ @@ -131,7 +131,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-members.json"), true) as $member) { if ($member["name"] !== "unknown") { require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = $score["total"]; @@ -140,7 +140,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-members.json"), true) as $member) { if ($member["name"] !== "unknown") { require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = $score["total"]; @@ -162,7 +162,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl if ($member["name"] !== "unknown") { $name = $member["display_name"] ?? $member["name"]; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = [ @@ -177,7 +177,7 @@ $title = "Score System Testing"; require_once $_SERVER['DOCUMENT_ROOT'] . '/incl if ($member["name"] !== "unknown") { $name = $member["display_name"] ?? $member["name"]; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.php"; - $metadata = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true); + $metadata = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc-$member[id]-metadata.json"), true)); $score = calculateScore($metadata, $member["display_name"] ?? $member["name"]); $scores[] = [ diff --git a/pages/together-dev.php b/pages/together-dev.php new file mode 100644 index 0000000..d85cfe5 --- /dev/null +++ b/pages/together-dev.php @@ -0,0 +1,4 @@ +<?php + +global $WebSocketAddress; $WebSocketAddress = "ws://localhost:22666"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/together.php";
\ No newline at end of file diff --git a/pages/together.php b/pages/together.php new file mode 100644 index 0000000..644a6c0 --- /dev/null +++ b/pages/together.php @@ -0,0 +1,941 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; +if (!$isLoggedIn) header("Location: /login") and die(); + +$title = "Watch Together"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; + +global $WebSocketAddress; + +if (!isset($WebSocketAddress)) { + $WebSocketAddress = "wss://ponies.equestria.horse/_WatchTogether-WebSocket-EntryPoint/socket"; +} + +?> + +<style> + .list-group-item { + color: #fff; + background-color: #222; + border: 1px solid rgba(255, 255, 255, .125); + } + + .list-group-item.disabled { + color: #fff; + background-color: #222; + border-color: rgba(255, 255, 255, .125); + opacity: .75; + } + + .list-group-item-action:hover { + background-color: #252525; + color: #ddd; + } + + .list-group-item-action:active, .list-group-item-action:focus { + background-color: #272727; + color: #bbb; + } + + .video-queue-item { + display: grid; + grid-template-columns: 1fr max-content; + grid-gap: 10px; + cursor: pointer; + } + + .video-queue-item-status { + filter: invert(1); + display: block; + height: 32px; + width: 32px; + } + + .video-queue-item-part { + display: flex; + align-items: center; + } + + .video-queue-item-metadata { + width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + .video-queue-item-title-outer, .video-queue-item-author-outer { + display: flex; + align-items: center; + } + + .video-queue-item-title, .video-queue-item-title-outer, .video-queue-item-author, .video-queue-item-author-outer { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 100%; + } + + .modal-header { + border-bottom: 1px solid #353738; + } + + .modal-content { + border: 1px solid rgba(255, 255, 255, .2); + background-color: #111; + } + + .btn-close { + filter: invert(1); + } + + .user-ping { + float: right; + display: inline-block; + width: 16px; + height: 16px; + border-radius: 999px; + position: relative; + top: 5px; + } + + .control-item { + display: inline-block; + cursor: pointer; + border-radius: 999px; + background-color: rgba(0, 0, 0, 0); + border: 1px solid rgba(255, 255, 255, 0); + transition: background-color 200ms, border-color 200ms; + } + + .control-item:hover { + background-color: rgba(0, 0, 0, .25); + border-color: rgba(255, 255, 255, .1); + } + + .control-item:active { + background-color: rgba(0, 0, 0, .5); + border-color: rgba(255, 255, 255, .25); + } + + .control-icon { + filter: invert(1); + width: 24px; + height: 24px; + margin: 12px; + pointer-events: none; + } + + body.hide-controls * { + cursor: none; + } + + #controls-outer { + opacity: 1; + transition: opacity 500ms; + } + + .navbar, #sidebar { + opacity: 1; + transition: opacity 500ms; + } + + body.hide-controls #controls-outer { + opacity: 0; + } + + body.hide-controls .navbar, body.hide-controls #sidebar { + opacity: .25; + } + + body.fullscreen #app-container { + grid-template-columns: 1fr !important; + } + + body.fullscreen .navbar, body.fullscreen #sidebar { + display: none; + } + + body.fullscreen #app-container div, body.fullscreen #app-container video { + height: 100vh !important; + } + + body.fullscreen #controls-outer, body.fullscreen #notification { + inset: 0 !important; + } + + #notification { + opacity: 0; + pointer-events: none; + transition: opacity 200ms; + } + + #video-title { + opacity: 1; + transition: opacity 200ms; + } + + body.notification #notification { + opacity: 1; + } + + body.notification #video-title { + opacity: 0; + } + + body.skipper #skipper { + opacity: 1 !important; + pointer-events: initial !important; + } + + #skipper { + opacity: 0; + pointer-events: none; + transition: opacity 200ms; + position: fixed; + bottom: 79px; + z-index: 999; + background: rgba(0, 0, 0, .5); + border-radius: 10px; + padding: 10px 20px; + left: 20px; + border: 1px solid rgba(255, 255, 255, .25); + cursor: pointer; + } + + #skipper:hover { + background: rgba(0, 0, 0, .75) !important; + } + + #skipper:active { + background: rgba(0, 0, 0, 1) !important; + } +</style> + +<script src="/assets/editor/thing.js"></script> + +<div class="modal fade" id="error" data-bs-backdrop="static" data-bs-keyboard="false" style="z-index: 99999;"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">An error occurred</h4> + </div> + + <div class="modal-body"> + <div class="alert alert-danger" id="error-message"></div> + </div> + </div> + </div> +</div> + +<div class="modal fade" id="add" data-bs-backdrop="static" data-bs-keyboard="false"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Add new video</h4> + </div> + + <div class="modal-body"> + <p>Enter a YouTube video ID to add to the queue:</p> + <input type="text" class="form-control" id="add-id" placeholder="e.g. K9tUKbOFots" style="color:white;background:#111;border-color:#222;"> + <p style="margin-top:10px;margin-bottom: 0;"> + <div class="btn-group"> + <span onclick="doAdd();" id="add-btn-yes" class="btn btn-primary">Add</span> + <span onclick="closeAdd();" id="add-btn-no" class="btn btn-secondary">Cancel</span> + </div> + <a href="#" onclick="document.getElementById('add-id').value = 'K9tUKbOFots';">test</a> + </p> + </div> + </div> + </div> +</div> + +<div class="modal fade" id="welcome" data-bs-backdrop="static" data-bs-keyboard="false"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Welcome to Watch Together!</h4> + </div> + + <div class="modal-body"> + <h5>Host a new session</h5> + <p>You will be given an invite code to share with your partner·s.</p> + <span class="btn btn-primary" id="host-start" onclick="hostSession();">Start</span> + + <hr> + + <h5>Join an existing session</h5> + <p>Enter the invite code your partner has given you:</p> + <div style="display:grid;grid-template-columns: repeat(8, 1fr);grid-gap:10px;"> + <input type="text" class="form-control" id="invite-input-1" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-2" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-3" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-4" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-5" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-6" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-7" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + <input type="text" class="form-control" id="invite-input-8" style="text-align: center;color:white;background:#111;border-color:#222;" onchange="processInviteCode(event);" onkeydown="processInviteCode(event);" onkeyup="processInviteCode(event);" maxlength="1"> + </div> + </div> + </div> + </div> +</div> +<script> + document.getElementById("invite-input-1").disabled = true; + document.getElementById("invite-input-2").disabled = true; + document.getElementById("invite-input-3").disabled = true; + document.getElementById("invite-input-4").disabled = true; + document.getElementById("invite-input-5").disabled = true; + document.getElementById("invite-input-6").disabled = true; + document.getElementById("invite-input-7").disabled = true; + document.getElementById("invite-input-8").disabled = true; + document.getElementById("host-start").classList.add("disabled"); + + let modal = new bootstrap.Modal(document.getElementById('welcome')); + modal.show(); + document.getElementById("invite-input-1").focus(); + + window.modalAdd = new bootstrap.Modal(document.getElementById('add')); + + function openAdd() { + modalAdd.show(); + } + + function closeAdd() { + document.getElementById("add-id").value = ""; + modalAdd.hide(); + } + + function doAdd() { + document.getElementById("add-id").disabled = true; + document.getElementById("add-btn-yes").classList.add("disabled"); + document.getElementById("add-btn-no").classList.add("disabled"); + + socket.send(JSON.stringify({ + task: "UPDATE_QUEUE", + payload: { + operation: "+", + video: document.getElementById("add-id").value + } + })) + } + + function toPrettyTime(seconds) { + let parts = new Date(seconds * 1000).toUTCString().split(" ")[4].split(":"); + parts[0] = parseInt(parts[0]).toString(); + + if (parts[0] === "0") { + parts[1] = parseInt(parts[1]).toString(); + parts.shift(); + } + + return parts.join(":"); + } + + function processInviteCode(event) { + let i1 = document.getElementById("invite-input-1").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i2 = document.getElementById("invite-input-2").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i3 = document.getElementById("invite-input-3").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i4 = document.getElementById("invite-input-4").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i5 = document.getElementById("invite-input-5").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i6 = document.getElementById("invite-input-6").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i7 = document.getElementById("invite-input-7").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + let i8 = document.getElementById("invite-input-8").value.trim().toLowerCase().replace(/[^\da-z]+/gm, ""); + + document.getElementById("invite-input-1").value = i1; + document.getElementById("invite-input-2").value = i2; + document.getElementById("invite-input-3").value = i3; + document.getElementById("invite-input-4").value = i4; + document.getElementById("invite-input-5").value = i5; + document.getElementById("invite-input-6").value = i6; + document.getElementById("invite-input-7").value = i7; + document.getElementById("invite-input-8").value = i8; + + if (i8 === "") { + document.getElementById("invite-input-8").focus(); + } + if (i7 === "") { + document.getElementById("invite-input-7").focus(); + } + if (i6 === "") { + document.getElementById("invite-input-6").focus(); + } + if (i5 === "") { + document.getElementById("invite-input-5").focus(); + } + if (i4 === "") { + document.getElementById("invite-input-4").focus(); + } + if (i3 === "") { + document.getElementById("invite-input-3").focus(); + } + if (i2 === "") { + document.getElementById("invite-input-2").focus(); + } + if (i1 === "") { + document.getElementById("invite-input-1").focus(); + } + + if (event instanceof KeyboardEvent) { + if (event.code === "Backspace" && event.type === "keydown") { + let el = event.target; + let ep = document.getElementById("invite-input-" + (el['id'].split("-")[2] - 1)); + + if (ep !== null) { + if (el.value.trim() === "") { + ep.value = ""; + ep.focus(); + } else { + el.value = ""; + ep.focus(); + } + } + } + } + + if (i1.length === 1 && i2.length === 1 && i3.length === 1 && i4.length === 1 && i5.length === 1 && i6.length === 1 && i7.length === 1 && i8.length === 1 && ((event.type && event.type === "keyup") || !event.type)) { + joinSession(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8); + } + } + + async function joinSession(code) { + document.getElementById("invite-input-1").disabled = true; + document.getElementById("invite-input-2").disabled = true; + document.getElementById("invite-input-3").disabled = true; + document.getElementById("invite-input-4").disabled = true; + document.getElementById("invite-input-5").disabled = true; + document.getElementById("invite-input-6").disabled = true; + document.getElementById("invite-input-7").disabled = true; + document.getElementById("invite-input-8").disabled = true; + document.getElementById("host-start").classList.add("disabled"); + + window.inviteCode = code; + + startSession(); + } + + async function hostSession() { + document.getElementById("invite-input-1").disabled = true; + document.getElementById("invite-input-2").disabled = true; + document.getElementById("invite-input-3").disabled = true; + document.getElementById("invite-input-4").disabled = true; + document.getElementById("invite-input-5").disabled = true; + document.getElementById("invite-input-6").disabled = true; + document.getElementById("invite-input-7").disabled = true; + document.getElementById("invite-input-8").disabled = true; + document.getElementById("host-start").classList.add("disabled"); + + window.inviteCode = null; + + startSession(); + } + + window.terminated = false; + window.users = null; + window.queue = null; + window.segments = []; + window.originalTitle = document.title; + + async function connect() { + window.identity = JSON.parse(await (await window.fetch("/api/me")).text()); + window.sessionToken = await (await window.fetch("/api/token")).text(); + + window.socket = new WebSocket("<?= $WebSocketAddress ?>"); + + socket.onclose = (event) => { + console.log("[ws:close] ",event); + + if (window.terminated) { + modal.hide(); + return; + } + + if (event.wasClean) { + document.getElementById('error-message').innerText = "Connection closed"; + } else { + if (event.reason) { + document.getElementById('error-message').innerText = "Connection closed unexpectedly with code " + event.code + ": " + event.reason; + } else { + document.getElementById('error-message').innerText = "Connection closed unexpectedly with code " + event.code; + } + } + + let errorModal = new bootstrap.Modal(document.getElementById('error')); + errorModal.show(); + modal.hide(); + } + + socket.onmessage = async (event) => { + let data = JSON.parse(event.data); + if (data.task !== "HEARTBEAT_ACK") console.log("[ws:message]", data); + + if (data.task === "CONFIG") { + document.getElementById("invite-input-1").disabled = false; + document.getElementById("invite-input-2").disabled = false; + document.getElementById("invite-input-3").disabled = false; + document.getElementById("invite-input-4").disabled = false; + document.getElementById("invite-input-5").disabled = false; + document.getElementById("invite-input-6").disabled = false; + document.getElementById("invite-input-7").disabled = false; + document.getElementById("invite-input-8").disabled = false; + document.getElementById("host-start").classList.remove("disabled"); + + setInterval(() => { + socket.send(JSON.stringify({ + task: "HEARTBEAT", + payload: { + videoPositon: document.getElementById("video").currentTime + } + })); + }, data.payload.heartbeatInterval); + + return; + } + + if (data.task === "SESSION") { + window.users = data.payload.users; + window.queue = data.payload.queue; + + updateQueue(); + + document.getElementById("participants").innerHTML = ""; + for (let user of data.payload.users) { + document.getElementById("participants").innerHTML += "<li class='list-group-item'>" + user.id + "<span id='user-" + user.id + "-ping' class='bg-primary user-ping'></span></li>"; + } + + document.getElementById("invite-code").innerText = data.payload.code; + } + + if (data.task === "VIDEO_UPDATE") { + if (data.payload.id !== null) { + try { + window.segments = JSON.parse(await (await window.fetch("https://sponsor.ajay.app/api/skipSegments/" + data.payload.sha.substring(0, 32) + '?categories=["sponsor","intro","outro","music_offtopic"]')).text()).filter(i => i['videoID'] === data.payload.id)[0].segments; + notification("This video is enhanced by smart playback", "success"); + } catch (e) { + notification("This video is not compatible with smart playback", "warning"); + window.segments = []; + } + } + + if (data.payload.url) document.getElementById("video").src = data.payload.url; + + if (data.payload.state === 1) { + document.getElementById("video").play(); + } else if (!!data.payload.state) { + document.getElementById("video").pause(); + } + + if (data.payload.title && data.payload.title.trim() !== "") { + document.getElementById("video-title").innerText = data.payload.title; + document.title = data.payload.title + " · " + window.originalTitle; + } else { + if (data.payload.title) { + document.getElementById("video-title").innerText = ""; + document.title = window.originalTitle; + } + } + + document.getElementById("video").currentTime = data.payload.position; + } + + if (data.task === "UPDATE_USERS") { + window.users = data.payload.users; + + document.getElementById("participants").innerHTML = ""; + for (let user of data.payload.users) { + document.getElementById("participants").innerHTML += "<li class='list-group-item'>" + user.id + "<span id='user-" + user.id + "-ping' class='bg-primary user-ping'></span></li>"; + } + } + + if (data.task === "UPDATE_QUEUE") { + window.queue = data.payload.queue; + + if (data.payload.poster === identity.id) { + document.getElementById("add-id").disabled = false; + document.getElementById("add-id").value = ""; + document.getElementById("add-btn-yes").classList.remove("disabled"); + document.getElementById("add-btn-no").classList.remove("disabled"); + + modalAdd.hide(); + } + + updateQueue(); + } + + if (data.task === "HEARTBEAT_ACK") { + for (let user of Object.keys(data.payload.delays)) { + let abs = Math.abs(data.payload.delays[user]); + let color = "primary"; + + if (abs >= 500) color = "warning"; + if (abs >= 1000) color = "danger"; + if (abs < 500) color = "success"; + + let sign = "±"; + if (data.payload.delays[user] < 0) sign = "-"; + if (data.payload.delays[user] > 0) sign = "+"; + + document.getElementById("user-" + user + "-ping").title = sign + abs + " ms"; + document.getElementById("user-" + user + "-ping").classList.remove("bg-primary"); + document.getElementById("user-" + user + "-ping").classList.remove("bg-danger"); + document.getElementById("user-" + user + "-ping").classList.remove("bg-warning"); + document.getElementById("user-" + user + "-ping").classList.remove("bg-green"); + document.getElementById("user-" + user + "-ping").classList.add("bg-" + color); + } + } + + if (data.task === "TERMINATE" || data.task === "FAILURE") { + window.terminated = true; + + if (data.payload.code) { + if (data.payload.reason) { + document.getElementById('error-message').innerText = data.payload.code + ": " + data.payload.reason; + let errorModal = new bootstrap.Modal(document.getElementById('error')); + errorModal.show(); + } else { + document.getElementById('error-message').innerText = data.payload.code; + let errorModal = new bootstrap.Modal(document.getElementById('error')); + errorModal.show(); + } + } else { + document.getElementById('error-message').innerText = "Error"; + let errorModal = new bootstrap.Modal(document.getElementById('error')); + errorModal.show(); + } + + if (data.task === "FAILURE") socket.close(); + } + } + + socket.onopen = (event) => { + console.log("[ws:open] ", event); + + socket.send(JSON.stringify({ + task: "IDENTIFY", + payload: { + token: window.sessionToken + } + })) + } + } + + function updateQueue() { + document.getElementById("queue").innerHTML = ""; + + for (let video of window.queue) { + document.getElementById("queue").innerHTML += ` +<li class="list-group-item list-group-item-action video-queue-item"> + <div class="video-queue-item-part video-queue-item-metadata"> + <div> + <div class="video-queue-item-title-outer"> + <span class="video-queue-item-title">${video.title}</span> + </div> + <div class="video-queue-item-author-outer"> + <span class="video-queue-item-author text-muted">${video.author}</span> + </div> + </div> + </div> + <div class="video-queue-item-part text-muted">${video['duration_pretty'] ?? toPrettyTime(video.duration)}</div> +</li> +`; + } + } + + function startSession() { + socket.send(JSON.stringify({ + task: "SESSION", + payload: { + id: window.inviteCode + } + })) + + modal.hide(); + } + + connect(); +</script> + +<div style="height:calc(100vh - 60px);display:grid;grid-template-columns: 3fr 1.5fr;" id="app-container"> + <div style="height:calc(100vh - 60px);"> + <video id="video" style="width:100%;height:calc(100vh - 60px);"></video> + </div> + <script> + + document.getElementById("video").onplay = () => { + console.log("play"); + + if (socket) socket.send(JSON.stringify({ + task: "VIDEO_UPDATE", + payload: { + state: 1, + position: document.getElementById("video").currentTime + } + })); + } + + document.getElementById("video").onpause = () => { + console.log("pause"); + + if (socket) socket.send(JSON.stringify({ + task: "VIDEO_UPDATE", + payload: { + state: 0, + position: document.getElementById("video").currentTime + } + })); + } + + let controlsFadeInterval; + document.body.classList.remove("hide-controls"); + + document.body.onmousemove = document.body.onmouseup = () => { + document.body.classList.remove("hide-controls"); + try { clearTimeout(controlsFadeInterval) } catch (e) {} + controlsFadeInterval = setTimeout(() => { + if (!document.getElementById("video").paused) document.body.classList.add("hide-controls"); + }, 5000); + } + + </script> + <div class="container" id="sidebar" style="border-left:1px solid rgba(255, 255, 255, .25);"> + <br> + <h4>Participants</h4> + <ul class="list-group" id="participants"></ul> + <p style="margin-top:10px;margin-bottom:0;">Invite new participants with this invite code: <code id="invite-code">--------</code></p> + + <br> + <h4>Queue</h4> + <ul class="list-group" id="queue"></ul> + <p style="margin-top:10px;margin-bottom:0;"> + <span class="btn btn-primary" onclick="openAdd()">Add video</span> + </p> + </div> +</div> + +<div id="notification" style="z-index:999;padding: 15px 20px;font-size: 20px;position: fixed;top: 60px;left:0;text-shadow: 0 0 10px black;"> + Notification. +</div> +<a onclick="controls_skipIntroOutro();" id="skipper">Skip</a> +<div id="controls-outer" style="position:fixed;background: linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 35%, rgba(0,0,0,0) 65%, rgba(0,0,0,0.5) 100%);top: 60px;left: 0;right: 0;bottom: 0;"> + <div id="video-title" style="padding: 15px 20px;font-size: 20px;"></div> + <div id="controls" style="position: fixed;bottom: 20px;height: 50px;left: 15px;display:grid;grid-template-columns: max-content 1fr max-content;"> + <div> + <a id="control-play" onclick="controls_playPause();" class="control-item"> + <img alt="" src="/assets/icons/together/play.svg" id="control-play-icon" class="control-icon"> + </a> + <a id="control-next" onclick="controls_next();" class="control-item"> + <img alt="" src="/assets/icons/together/next.svg" id="control-next-icon" class="control-icon"> + </a> + </div> + <div style="display: flex;align-items: center;padding-left:15px;padding-right:15px;"> + <div style="padding: 22px 0;width:100%;" id="seek-bar"> + <div style="height:6px;width:100%;background:rgba(255, 255, 255, .1);border-radius:999px;pointer-events: none;"> + <div style="height:6px;width:0;background:rgba(255, 255, 255, .75);border-radius:999px;" id="control-progress"></div> + </div> + </div> + </div> + <div> + <div style="display: inline-flex;align-items: center;"> + <span id="time-remaining" style="font-family:monospace;">-0:00</span> + </div> + <a id="control-full" onclick="controls_fullscreen();" class="control-item"> + <img alt="" src="/assets/icons/together/full-on.svg" id="control-full-icon" class="control-icon"> + </a> + </div> + </div> + <div id="seek-bar-dot" style="position: fixed;bottom: 38px;width: 12px;height: 12px;background: white;border-radius: 999px;pointer-events: none;display:none;"></div> +</div> +<script> + document.getElementById("controls").style.width = (document.getElementById("video").clientWidth - 30) + "px"; + document.getElementById("controls-outer").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px"; + document.getElementById("notification").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px"; + + let notificationDecayTimeout; + + function notification(text, color) { + document.getElementById("notification").innerText = text; + document.getElementById("notification").classList.add("text-" + color); + document.body.classList.add("notification"); + + try { clearTimeout(notificationDecayTimeout) } catch (e) {} + + notificationDecayTimeout = setTimeout(() => { + document.body.classList.remove("notification"); + setTimeout(() => { + document.getElementById("notification").innerText = "Notification."; + document.getElementById("notification").classList.remove("text-" + color); + }, 200); + }, 5000) + } + + window.onresize = () => { + document.getElementById("controls").style.width = (document.getElementById("video").clientWidth - 30) + "px"; + document.getElementById("controls-outer").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px"; + document.getElementById("notification").style.right = (window.innerWidth - document.getElementById("video").clientWidth) + "px"; + } + + function controls_playPause() { + if (document.getElementById("video").src.trim() === "") return; + + if (document.getElementById("video").paused) { + document.getElementById("video").play(); + } else { + document.getElementById("video").pause(); + } + } + + function controls_fullscreen() { + if (document.fullscreen) { + document.exitFullscreen(); + } else { + document.documentElement.requestFullscreen(); + } + } + + function controls_next() { + document.getElementById("video").play(); + document.getElementById("video").currentTime = document.getElementById("video").duration; + } + + window.seeking = false; + window.seekPosition = 0; + + document.getElementById("controls-outer").onclick = (event) => { + if (event.target === document.getElementById("controls-outer")) controls_playPause(); + } + + document.getElementById("controls-outer").ondblclick = (event) => { + if (event.target === document.getElementById("controls-outer")) controls_fullscreen(); + } + + document.getElementById("seek-bar").onmouseenter = () => { + document.getElementById("seek-bar-dot").style.display = ""; + console.log("> ENTER <"); + window.seeking = true; + } + + document.getElementById("seek-bar").onclick = (event) => { + let percentage = (event.offsetX / document.getElementById("seek-bar").clientWidth) * 100; + let multiplier = event.offsetX / document.getElementById("seek-bar").clientWidth; + + document.getElementById("seek-bar-dot").style.left = event.clientX + "px"; + + console.log(" CLICK: ", event.offsetX, "(" + percentage.toFixed(3) + "%, " + toPrettyTime(document.getElementById("video").duration * multiplier) + ")"); + window.seekPosition = document.getElementById("video").duration * multiplier; + + document.getElementById("video").pause(); + document.getElementById("video").currentTime = window.seekPosition; + document.getElementById("video").play(); + } + + document.getElementById("seek-bar").onmouseleave = () => { + document.getElementById("seek-bar-dot").style.display = "none"; + console.log("< LEAVE >"); + window.seeking = false; + } + + function controls_skipIntroOutro() { + let skipper = null; + + for (let segment of window.segments.filter(i => i['category'] === "intro")) { + if (segment.segment) { + if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) { + skipper = segment.segment[1]; + } + } + } + for (let segment of window.segments.filter(i => i['category'] === "outro")) { + if (segment.segment) { + if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) { + skipper = segment.segment[1]; + } + } + } + + if (skipper) { + document.getElementById("video").pause(); + document.getElementById("video").currentTime = skipper; + document.getElementById("video").play(); + } + } + + document.getElementById("seek-bar").onmousemove = (event) => { + let percentage = (event.offsetX / document.getElementById("seek-bar").clientWidth) * 100; + let multiplier = event.offsetX / document.getElementById("seek-bar").clientWidth; + + document.getElementById("seek-bar-dot").style.left = event.clientX + "px"; + + console.log(" POS: ", event.offsetX, "(" + percentage.toFixed(3) + "%, " + toPrettyTime(document.getElementById("video").duration * multiplier) + ")"); + window.seekPosition = document.getElementById("video").duration * multiplier; + } + + setInterval(() => { + if (document.getElementById("video").src.trim() === "") { + document.getElementById("controls").style.display = "none"; + } else { + document.getElementById("controls").style.display = "grid"; + } + + document.getElementById("control-play-icon").src = document.getElementById("video").paused ? "/assets/icons/together/play.svg" : "/assets/icons/together/pause.svg"; + document.getElementById("control-full-icon").src = document.fullscreen ? "/assets/icons/together/full-off.svg" : "/assets/icons/together/full-on.svg"; + document.getElementById("control-progress").style.width = ((document.getElementById("video").currentTime / document.getElementById("video").duration) * 100) + "%"; + + if (document.fullscreen) { + document.body.classList.add("fullscreen"); + } else { + document.body.classList.remove("fullscreen"); + } + + if (window.seeking) { + document.getElementById("time-remaining").classList.add("text-warning"); + document.getElementById("time-remaining").innerText = toPrettyTime(window.seekPosition); + } else { + document.getElementById("time-remaining").classList.remove("text-warning"); + + if (!isNaN(document.getElementById("video").duration)) { + document.getElementById("time-remaining").innerText = "-" + toPrettyTime(Math.round(document.getElementById("video").duration) - Math.round(document.getElementById("video").currentTime)); + } else { + document.getElementById("time-remaining").innerText = "-0:00"; + } + } + }) + + setInterval(() => { + if (!document.getElementById("video").paused) { + for (let segment of window.segments.filter(i => i['category'] === "sponsor")) { + if (segment.segment) { + if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) { + document.getElementById("video").pause(); + document.getElementById("video").currentTime = segment.segment[1]; + document.getElementById("video").play(); + notification("Advert skipped by smart playback", "primary"); + } + } + } + + let skipper = false; + + for (let segment of window.segments.filter(i => i['category'] === "intro")) { + if (segment.segment) { + if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) { + skipper = true; + document.getElementById("skipper").innerText = "Skip intro"; + document.body.classList.add("skipper"); + } + } + } + for (let segment of window.segments.filter(i => i['category'] === "outro")) { + if (segment.segment) { + if (document.getElementById("video").currentTime >= segment.segment[0] && document.getElementById("video").currentTime < segment.segment[1]) { + skipper = true; + document.getElementById("skipper").innerText = "Skip to the end"; + document.body.classList.add("skipper"); + } + } + } + + if (!skipper) { + document.body.classList.remove("skipper"); + } + } + }) +</script>
\ No newline at end of file diff --git a/pages/travelling.php b/pages/travelling.php new file mode 100644 index 0000000..260ea15 --- /dev/null +++ b/pages/travelling.php @@ -0,0 +1,101 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn; +if (!$isLoggedIn) header("Location: /login") and die(); +$travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling.json"), true); + +if (isset($_GET['toggleTravel'])) { + if (isset($travelling[$_GET['member']])) { + if ($travelling[$_GET['member']]["travelling"]) { + $travelling[$_GET['member']]["travelling"] = false; + $travelling[$_GET['member']]["history"][count($travelling[$_GET['member']]["history"]) - 1]["end"] = date("c"); + } else { + $travelling[$_GET['member']]["travelling"] = true; + $travelling[$_GET['member']]["history"][] = [ + "start" => date("c"), + "end" => null + ]; + } + } + + header("Location: /-/travelling"); + file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling.json", json_encode($travelling, JSON_PRETTY_PRINT)); + die(); +} + +$title = "System Travelling"; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php'; +global $travelling; + +?> + +<style> + .member-link:hover, .relation-intro:hover { + opacity: .75; + } + + .member-link:active, .relation-intro:active { + opacity: .5; + } +</style> + +<br> +<div class="container"> + <div> + <h2>System Travelling</h2> + <?php foreach (scoreOrderGlobal() as $member): ?> + <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1fr 2fr max-content;"> + <a class="relation-intro" style="background-color:rgba(255, 255, 255, .05);border-right:1px solid rgba(255, 255, 255, .1);margin:-10px;padding:10px;border-top-left-radius:10px;border-bottom-left-radius:10px;color: white;display:flex;align-items:center;text-decoration: none;" href="/<?= $member["_system"] === "gdapd" ? "raindrops" : "cloudburst" ?>/<?= $member["name"] ?>"> + <img src="/assets/uploads/pt<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member['name'] . ".png") ? "-" . $member['name'] : "" ?>.png" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> + </a> + + <div class="relation-item" style="display:flex;align-items:center;margin-left:10px;padding:0 20px;"> + <div> + <b style="padding-right:5px;">Current System:</b><span class="list-separator-mobile"><br></span> + <?php $system = $member['_system'] === "gdapd" ? ($travelling[$member['id']]["travelling"] ? "ynmuc" : "gdapd") : ($travelling[$member['id']]["travelling"] ? "gdapd" : "ynmuc"); ?> + <a class="member-link" href="/<?= $system === "gdapd" ? "raindrops" : "cloudburst" ?>"><img style="width:24px;border-radius:5px;" src="/assets/uploads/<?= $system === "gdapd" ? "raindrops" : "cloudburst" ?>.png"> <?= $system === "gdapd" ? "Raindrops" : "Cloudburst" ?> System</a> + <?php if ($travelling[$member['id']]["travelling"]): ?> + <span class="text-muted">(<?= timeAgo($travelling[$member['id']]["history"][count($travelling[$member['id']]["history"]) - 1]["start"]) ?>)</span> + <?php endif; ?> + </div> + </div> + + <div> + <?php if ($travelling[$member['id']]["travelling"]): ?> + <a href="?toggleTravel&member=<?= $member['id'] ?>" class="btn btn-outline-danger">Stop travelling</a> + <?php else: ?> + <a href="?toggleTravel&member=<?= $member['id'] ?>" class="btn btn-outline-success">Start travelling</a> + <?php endif; ?> + </div> + </div> + <?php endforeach; ?> + </div> + + <style> + @media (max-width: 991px) { + .relation { + grid-template-columns: 1fr !important; + } + + .relation-intro { + text-align: center; + border-bottom-left-radius: 0 !important; + border-top-right-radius: 10px; + border-right: none !important; + border-bottom: 1px solid rgba(255, 255, 255, .1); + } + + .relation-item-marefriends { + margin-top: 20px !important; + } + + .relation-item { + margin-top: 10px; + margin-left: 0 !important; + padding: 10px 0 !important; + text-align: center; + } + } + </style> +</div> + +<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?> |