diff options
Diffstat (limited to 'js/src')
-rw-r--r-- | js/src/countdown.js | 79 | ||||
-rw-r--r-- | js/src/errors.js | 49 | ||||
-rw-r--r-- | js/src/fouc.js | 51 | ||||
-rw-r--r-- | js/src/jams.js | 147 | ||||
-rw-r--r-- | js/src/revision_diff.js | 83 | ||||
-rw-r--r-- | js/src/wiki.js | 17 |
6 files changed, 426 insertions, 0 deletions
diff --git a/js/src/countdown.js b/js/src/countdown.js new file mode 100644 index 00000000..7eaa650c --- /dev/null +++ b/js/src/countdown.js @@ -0,0 +1,79 @@ +"use strict"; + +(function(){ // Use a closure to avoid polluting global scope + // TODO: This needs to be built into the jams system + const startjam = new Date(Date.UTC(2018, 2, 23)); + const endjam = new Date(Date.UTC(2018, 2, 26)); + + const now = Date.now(); + let goal; + + if (now + 1000 < endjam.getTime()) { // Only do anything if the jam hasn't ended + UIkit.notification( // Spawn the notification + { + "message": "" + + "<div class='uk-text-center'>" + + " <span id=\"countdown-title\" class=\"uk-text-center\">" + + " <a href=\"/info/jams\">Code Jam</a> Countdown" + + " </span>" + + " <p class='uk-text-large' id=\"countdown-remaining\">...</p>" + + "<small style='font-size: 0.6em;'>(Tap/click to dismiss)</small>" + + "</div>", + "pos": "bottom-right", + "timeout": endjam - now + } + ); + + const heading = document.getElementById("countdown-title"); + + if (now > startjam.getTime()) { // Jam's already started + heading.innerHTML = "Current <a href=\"/info/jams\">code jam</a> ends in..."; + goal = endjam.getTime(); + } else { + heading.innerHTML = "Next <a href=\"/info/jams\">code jam</a> starts in..."; + goal = startjam.getTime(); + } + + const refreshCountdown = setInterval(() => { // Create a repeating task + let delta = goal - Date.now(); // Time until the goal is met + + if (delta <= 1000) { // Goal has been met, best reload + clearInterval(refreshCountdown); + return location.reload(); + } + + let days = Math.floor(delta / (24 * 60 * 60 * 1000)); + delta -= days * (24 * 60 * 60 * 1000); + + let hours = Math.floor(delta / (60 * 60 * 1000)); + delta -= hours * (60 * 60 * 1000); + + let minutes = Math.floor(delta / (60 * 1000)); + delta -= minutes * (60 * 1000); + + let seconds = Math.floor(delta / 1000); + + if (days < 10) { + days = `0${days}`; + } + + if (hours < 10) { + hours = `0${hours}`; + } + + if (minutes < 10) { + minutes = `0${minutes}`; + } + + if (seconds < 10) { + seconds = `0${seconds}`; + } + + try { + document.getElementById("countdown-remaining").innerHTML = `${days}:${hours}:${minutes}:${seconds}`; + } catch (e) { // Notification was probably closed, so we can stop counting + return clearInterval(refreshCountdown); + } + }, 500); + } +})(); diff --git a/js/src/errors.js b/js/src/errors.js new file mode 100644 index 00000000..492285d4 --- /dev/null +++ b/js/src/errors.js @@ -0,0 +1,49 @@ +"use strict"; + +/* exported error_typewriter */ + +function error_typewriter() { + const app = document.getElementById("error"); + + const typewriter = new Typewriter(app, { + "loop": false, + "deleteSpeed": 40, + "typingSpeed": "natural", + "devMode": false + }); + + function closeWindow() { + const app = document.getElementById("win"); + const current_class = app.getAttribute("class"); + app.setAttribute("class", `${current_class } uk-animation-scale-up uk-animation-reverse`); + typewriter.stop(); + } + + document.getElementById("terminal-close").onclick = closeWindow; + + typewriter.appendText("Python 3.6.4 (default, Jan 5 2018, 02:35:40)\n") + .appendText("[GCC 7.2.1 20171224] on darwin\n") + .appendText("Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n") + .appendText(">>> ") + .pauseFor(1000) + .typeString("impor requests") + .deleteChars(9) + .typeString("t requests\n") + .appendText(">>> ") + .pauseFor(750) + .changeSettings({"typingSpeed": "natural"}) + .typeString(`response = requests.${ window._RequestMethod }('https://pythim`) + .deleteChars(2) + .typeString("ondiscord.con/") + .deleteChars(2) + .typeString(`m${ window._Path }')\n`) + .pauseFor(1000) + .appendText(`<Response [${ window._Code }]>\n>>> `) + .typeString("# hmmmm") + .pauseFor(1000) + .deleteChars(7) + .pauseFor(1000) + .typeString("response.text\n") + .appendText(`${ window._ErrorMsg }\n>>> `) + .start(); +} diff --git a/js/src/fouc.js b/js/src/fouc.js new file mode 100644 index 00000000..3611ec27 --- /dev/null +++ b/js/src/fouc.js @@ -0,0 +1,51 @@ +"use strict"; + +function getScript(url, integrity, cross_origin) { + const script = document.createElement("script"); + script.type = "text/javascript"; + script.src = url; + script.defer = true; + + if (integrity !== undefined) { + script.integrity = integrity; + } + + if (cross_origin !== undefined) { + script.crossOrigin = cross_origin; + } + + document.getElementsByTagName("head")[0].appendChild(script); +} + +function setClass(selector, my_class) { + const element = document.querySelector(selector); + // console.log(element); + element.className = my_class; +} + +function removeClass(selector, my_class) { + const element = document.querySelector(selector); + const reg = new RegExp(`(^| )${my_class}($| )`, "g"); + element.className = element.className.replace(reg, " "); +} + +// hide the html when the page loads, but only if js is turned on. +setClass("html", "prevent-fouc"); + +// when the DOM has finished loading, unhide the html +document.onreadystatechange = function () { + if (document.readyState === "interactive") { + removeClass("html", "prevent-fouc"); + getScript( + "https://pro.fontawesome.com/releases/v5.0.13/js/all.js", // URL + "sha384-d84LGg2pm9KhR4mCAs3N29GQ4OYNy+K+FBHX8WhimHpPm86c839++MDABegrZ3gn", // Integrity + "anonymous" // Cross-origin + ); + getScript( + "https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ace.js" + ); + getScript( + "https://cdn.jsdelivr.net/npm/flatpickr" + ); + } +}; diff --git a/js/src/jams.js b/js/src/jams.js new file mode 100644 index 00000000..ee2ee2ea --- /dev/null +++ b/js/src/jams.js @@ -0,0 +1,147 @@ +"use strict"; + +/* exported JamActions */ + +class JamActions { + constructor(url, csrf_token) { + this.url = url; + this.csrf_token = csrf_token; + } + + send(action, method, data, callback) { + data["action"] = action; + + $.ajax(this.url, { + "data": data, + "dataType": "json", + "headers": {"X-CSRFToken": this.csrf_token}, + "method": method, + }).done(data => { + if ("error_code" in data) { + return callback(false, data); + } + + return callback(true, data); + }).fail(() => callback(false)); + } + + send_json(action, method, data, callback) { + data["action"] = action; + + $.ajax(this.url, { + "data": JSON.stringify(data), + "dataType": "json", + "headers": {"X-CSRFToken": this.csrf_token}, + "method": method + }).done(data => { + if ("error_code" in data) { + return callback(false, data); + } + + return callback(true, data); + }).fail(() => callback(false)); + } + + set_state(jam, state, callback) { + this.send( + "state", + "POST", + { + "jam": jam, + "state": state + }, + callback + ); + } + + get_questions(callback) { + this.send( + "questions", + "GET", + {}, + callback + ); + } + + create_question(data, callback) { + this.send_json( + "questions", + "POST", + data, + callback + ); + } + + delete_question(id, callback) { + this.send( + "question", + "DELETE", + {"id": id}, + callback + ); + } + + associate_question(form, question, callback) { + this.send( + "associate_question", + "POST", + { + "form": form, + "question": question, + }, + callback + ); + } + + disassociate_question(form, question, callback) { + this.send( + "disassociate_question", + "POST", + { + "form": form, + "question": question, + }, + callback + ); + } + + create_infraction(id, reason, number, callback) { + this.send( + "infraction", + "POST", + { + "participant": id, + "reason": reason, + "number": number + }, + callback + ); + } + + delete_infraction(id, callback) { + this.send( + "infraction", + "DELETE", + {"id": id}, + callback + ); + } + + approve_application(id, callback) { + this.send( + "approve_application", + "POST", + {"id": id}, + callback + ); + } + + unapprove_application(id, callback) { + this.send( + "unapprove_application", + "POST", + {"id": id}, + callback + ); + } +} diff --git a/js/src/revision_diff.js b/js/src/revision_diff.js new file mode 100644 index 00000000..f124fbec --- /dev/null +++ b/js/src/revision_diff.js @@ -0,0 +1,83 @@ +"use strict"; + +/* exported revision_diff */ + +function revision_diff(revisions) { + const buttons = document.querySelectorAll("td input"); // Fetch all radio buttons + const id_reg = /compare-(before|after)-([\w|-]+)/; // Matches compare-after/before-ID + + + function getRevisionId(element){ + const e = element.id.match(id_reg); // Match ID with RegExp + return [e[1], e[2]]; // e is in format of [full id, after/before, ID] we only want ID & mode + } + + function getRevision(id) { + const e = revisions.filter((x) => { + // Filter through all revisions to find the selected one (revisions in declared in the template) + return x.id === id; + }); + return e[0]; + } + + function radioButtonChecked(element) { + const id = getRevisionId(element); + const rev = getRevision(id[1]); + if (id[0] === "after"){ + /* + * Deselect the opposite checkbox to the one which has been checked + * because we don't want checking of the same revision + */ + + document.querySelector(`#compare-before-${id[1]}`).checked = false; + + buttons.forEach((e) => { + if (getRevisionId(e)[0] === "after" && e.id !== element.id) { // Deselect all checkboxes in the same row + e.checked = false; + } + }); + } else { // This else does the same as above but for the before column + document.querySelector(`#compare-after-${id[1]}`).checked = false; + buttons.forEach((e) => { + if (getRevisionId(e)[0] === "before" && e.id !== element.id) { + e.checked = false; + } + + // This makes sure that you do not compare a new revision with an old one + if (getRevisionId(e)[0] === "after") { + const tmprev = getRevision(getRevisionId(e)[1]); + // console.log(tmprev); + if (tmprev.date <= rev.date) { + document.querySelector(`#${e.id}`).setAttribute("disabled", ""); + } else { + document.querySelector(`#${e.id}`).removeAttribute("disabled"); + } + } + }); + } + + let aft, bef; + + buttons.forEach((button) => { // Find the selected posts + const id = getRevisionId(button); + if (button.checked && id[0] === "before") { + bef = id[1]; + } + + if (button.checked && id[0] === "after") { + aft = id[1]; + } + }); + + // Switch the buttons HREF to point to the correct compare URL + document.getElementById("compare-submit").href = `/history/compare/${bef}/${aft}`; + + } + + buttons.forEach((button) => { + button.checked = false; // Some browsers remember if a button is checked. + button.onchange = function() { + radioButtonChecked(button); + }; + }); +} diff --git a/js/src/wiki.js b/js/src/wiki.js new file mode 100644 index 00000000..f4bc18e8 --- /dev/null +++ b/js/src/wiki.js @@ -0,0 +1,17 @@ +"use strict"; + +/* exported wiki_sidebar */ + +function wiki_sidebar(){ + const visible_class = "uk-visible@s"; + const sidebar = document.getElementById("wiki-sidebar"); + const display_button = document.getElementById("wiki-sidebar-button"); + + display_button.onclick = function() { + if (sidebar.classList.contains(visible_class)) { + sidebar.classList.remove(visible_class); + } else { + sidebar.classList.add(visible_class); + } + }; +} |