diff options
Diffstat (limited to '')
-rw-r--r-- | .eslintignore | 1 | ||||
-rw-r--r-- | .eslintrc.yml | 165 | ||||
-rw-r--r-- | .gitlab-ci.yml | 3 | ||||
-rw-r--r-- | Pipfile | 2 | ||||
-rw-r--r-- | docker/ci.Dockerfile | 5 | ||||
-rw-r--r-- | static/js/500.js | 36 | ||||
-rw-r--r-- | static/js/countdown.js | 48 | ||||
-rw-r--r-- | static/js/fouc.js | 28 | ||||
-rw-r--r-- | static/js/jams.js | 77 | ||||
-rw-r--r-- | static/js/revision_diff.js | 123 | ||||
-rw-r--r-- | static/js/wiki.js | 10 |
11 files changed, 338 insertions, 160 deletions
diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..39ba7c90 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +static/js/typewriter.js diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..986ae62e --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,165 @@ +env: + browser: true + es6: true + jquery: true +extends: 'eslint:recommended' +parserOptions: + sourceType: script +globals: + Typewriter: true + UIkit: true +rules: + array-bracket-newline: + - warn + - consistent + block-scoped-var: + - error + brace-style: + - warn + comma-dangle: + - warn + - only-multiline + comma-spacing: + - warn + comma-style: + - warn + curly: + - error + - all + dot-location: + - warn + - property + eol-last: + - warn + eqeqeq: + - error + function-paren-newline: + - warn + - consistent + indent: + - error + - 4 + linebreak-style: + - error + - unix + lines-around-comment: + - warn + - afterBlockComment: true + allowArrayStart: true + allowBlockStart: true + allowClassStart: true + allowObjectStart: true + beforeBlockComment: true + lines-between-class-members: + - warn + max-len: + - warn + - code: 120 + ignoreUrls: true + ignoreRegExpLiterals: true + multiline-comment-style: + - warn + - starred-block + new-parens: + - warn + no-alert: + - warn + no-caller: + - error + no-case-declarations: + - error + no-catch-shadow: + - error + no-confusing-arrow: + - warn + no-else-return: + - warn + no-extra-bind: + - warn + no-extra-label: + - warn + no-floating-decimal: + - warn + no-invalid-this: + - error + no-lone-blocks: + - warn + no-lonely-if: + - warn + no-multi-spaces: + - warn + no-new: + - error + no-param-reassign: + - warn + no-plusplus: + - warn + no-return-assign: + - warn + no-return-await: + - warn + no-tabs: + - warn + no-ternary: + - warn + no-trailing-spaces: + - warn + no-undef-init: + - warn + no-unused-vars: + - warn + no-use-before-define: + - error + no-useless-call: + - warn + no-var: + - error + object-curly-newline: + - warn + operator-assignment: + - warn + - always + prefer-arrow-callback: + - warn + - allowNamedFunctions: false + allowUnboundThis: false + prefer-const: + - warn + prefer-numeric-literals: + - warn + prefer-template: + - warn + quotes: + - warn + - double + quote-props: + - warn + semi: + - error + - always + semi-spacing: + - warn + semi-style: + - warn + sort-imports: + - warn + sort-vars: + - warn + space-infix-ops: + - warn + space-unary-ops: + - warn + - words: true + nonwords: true + spaced-comment: + - warn + - always + strict: + - error + - global + wrap-iife: + - warn + - inside + yoda: + - error + - never diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 770312fb..595cbbdf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,8 +27,11 @@ test: script: - pipenv sync --dev + - pipenv run lint + - pipenv run lintjs - pipenv run lintscss + - pipenv run python gunicorn_config.py - pipenv run test @@ -44,8 +44,10 @@ build = "docker build -t pythondiscord/site:latest -f docker/Dockerfile ." buildbase = "docker build -t pythondiscord/site-base:latest -f docker/Dockerfile.base ." buildscss = "python scss.py scss/pysite:scss/pysite/style.scss:static/css/style.css scss/uikit:scss/uikit/uikit_blurple.scss:static/css/uikit_blurple.css" clean = "rm -rf __pycache__ htmlcov .coverage .pytest_cache" +fixjs = "eslint static/js --fix" start = "gunicorn -w 12 -b 0.0.0.0:10012 -c gunicorn_config.py --log-level info -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker app:app" lint = "python -m flake8" +lintjs = "eslint static/js" lintscss = "scss-lint scss/pysite" push = "docker push pythondiscord/site:latest" pushbase = "docker push pythondiscord/site-base:latest" diff --git a/docker/ci.Dockerfile b/docker/ci.Dockerfile index 69a7e5ba..3771e52b 100644 --- a/docker/ci.Dockerfile +++ b/docker/ci.Dockerfile @@ -13,7 +13,9 @@ RUN apk add --update tini \ ruby-rdoc \ ruby-irb \ docker \ - curl + curl \ + nodejs \ + nodejs-npm ENV PIPENV_VENV_IN_PROJECT=1 ENV PIPENV_IGNORE_VIRTUALENVS=1 @@ -22,3 +24,4 @@ ENV PIPENV_HIDE_EMOJIS=1 RUN pip install pipenv RUN gem install scss_lint +RUN npm install -g eslint --save-dev diff --git a/static/js/500.js b/static/js/500.js index 856c900f..7effe502 100644 --- a/static/js/500.js +++ b/static/js/500.js @@ -1,47 +1,47 @@ "use strict"; window.onload = function () { - let app = document.getElementById('error'); + const app = document.getElementById("error"); - let typewriter = new Typewriter(app, { - loop: false, - deleteSpeed: 40, - typingSpeed: "natural", - devMode: false + const typewriter = new Typewriter(app, { + "loop": false, + "deleteSpeed": 40, + "typingSpeed": "natural", + "devMode": false }); function closeWindow() { - let app = document.getElementById("win"); - let current_class = app.getAttribute("class"); - app.setAttribute("class", current_class + " uk-animation-scale-up uk-animation-reverse"); + 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('>>> ') + 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") + .changeSettings({"typingSpeed": "natural"}) + .typeString(`response = requests.${ window._RequestMethod }('https://pythim`) .deleteChars(2) .typeString("ondiscord.con/") .deleteChars(2) - .typeString("m" + window._Path + "')\n") + .typeString(`m${ window._Path }')\n`) .pauseFor(1000) - .appendText("<Response [" + window._Code + "]>\n>>> ") + .appendText(`<Response [${ window._Code }]>\n>>> `) .typeString("# hmmmm") .pauseFor(1000) .deleteChars(7) .pauseFor(1000) .typeString("response.text\n") - .appendText("" + window._ErrorMsg + "\n>>> ") + .appendText(`${ window._ErrorMsg }\n>>> `) .start(); }; diff --git a/static/js/countdown.js b/static/js/countdown.js index be4a11c9..8ba2e58d 100644 --- a/static/js/countdown.js +++ b/static/js/countdown.js @@ -1,16 +1,16 @@ "use strict"; -(function(){ // Use a closure to avoid polluting global scope +(function(){ // Use a closure to avoid polluting global scope const startjam = new Date(Date.UTC(2018, 2, 23)); const endjam = new Date(Date.UTC(2018, 2, 26)); - let now = Date.now(); + 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 + if (now + 1000 < endjam.getTime()) { // Only do anything if the jam hasn't ended + UIkit.notification( // Spawn the notification { - message: "" + "message": "" + "<div class='uk-text-center'>" + " <span id=\"countdown-title\" class=\"uk-text-center\">" + " <a href=\"/info/jams\">Code Jam</a> Countdown" @@ -18,14 +18,14 @@ + " <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 + "pos": "bottom-right", + "timeout": endjam - now } ); const heading = document.getElementById("countdown-title"); - if (now > startjam.getTime()) { // Jam's already started + if (now > startjam.getTime()) { // Jam's already started heading.innerHTML = "Current <a href=\"/info/jams\">code jam</a> ends in..."; goal = endjam.getTime(); } else { @@ -33,46 +33,46 @@ goal = startjam.getTime(); } - let refreshCountdown = setInterval(function() { // Create a repeating task - let delta = goal - Date.now(); // Time until the goal is met + 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 + 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 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 hours = Math.floor(delta / (60 * 60 * 1000)); + delta -= hours * (60 * 60 * 1000); - let minutes = Math.floor(delta / (60*1000)); - delta -= minutes * (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; + days = `0${days}`; } if (hours < 10) { - hours = "0"+hours; + hours = `0${hours}`; } if (minutes < 10) { - minutes = "0"+minutes; + minutes = `0${minutes}`; } if (seconds < 10) { - seconds = "0"+seconds; + seconds = `0${seconds}`; } try { - document.getElementById('countdown-remaining').innerHTML = `${days}:${hours}:${minutes}:${seconds}`; - } catch (e) { // Notification was probably closed, so we can stop counting + 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/static/js/fouc.js b/static/js/fouc.js index b8f1d07f..01354863 100644 --- a/static/js/fouc.js +++ b/static/js/fouc.js @@ -1,5 +1,7 @@ +"use strict"; + function getScript(url, integrity, crossorigin){ - var script = document.createElement("script") + const script = document.createElement("script"); script.type = "text/javascript"; script.src = url; script.defer = true; @@ -9,28 +11,28 @@ function getScript(url, integrity, crossorigin){ } function setClass(selector, myClass) { - element = document.querySelector(selector); - console.log(element) + const element = document.querySelector(selector); + // console.log(element); element.className = myClass; } function removeClass(selector, myClass) { - element = document.querySelector(selector); - var reg = new RegExp('(^| )'+myClass+'($| )','g'); - element.className = element.className.replace(reg,' '); + const element = document.querySelector(selector); + const reg = new RegExp(`(^| )${myClass}($| )`, "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'); +setClass("html", "prevent-fouc"); // when the DOM has finished loading, unhide the html document.onreadystatechange = function () { - if (document.readyState == "interactive") { - removeClass('html', 'prevent-fouc'); + 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 + "https://pro.fontawesome.com/releases/v5.0.13/js/all.js", // URL + "sha384-d84LGg2pm9KhR4mCAs3N29GQ4OYNy+K+FBHX8WhimHpPm86c839++MDABegrZ3gn", // Integrity + "anonymous" // Cross-origin ); } -}
\ No newline at end of file +}; diff --git a/static/js/jams.js b/static/js/jams.js index b0966550..fdc5c8ea 100644 --- a/static/js/jams.js +++ b/static/js/jams.js @@ -1,20 +1,21 @@ "use strict"; +/* exported refreshLock, Actions */ + function refreshLock() { - console.log("Refreshing lock"); - let oReq = new XMLHttpRequest(); - oReq.addEventListener("load", function() { - let response = JSON.parse(this.responseText); + /* global editor, csrf_token */ // TODO: FIXME + const oReq = new XMLHttpRequest(); + oReq.addEventListener("load", () => { + const response = JSON.parse(oReq.responseText); if (response.error !== undefined) { document.getElementById("submit").disabled = true; if (response.error_lines !== undefined) { editor.session.setAnnotations(response.error_lines); - document.getElementById("preview-div").innerHTML ="<h3>Error - see editor margin</h3>"; + document.getElementById("preview-div").innerHTML = "<h3>Error - see editor margin</h3>"; } else { - console.log("Error: " + response.error); - document.getElementById("preview-div").innerHTML ="<h3>Error</h3><p>" + response.error + "<p>"; + document.getElementById("preview-div").innerHTML = `<h3>Error</h3><p>${ response.error }<p>`; } } else { document.getElementById("submit").disabled = false; @@ -24,9 +25,9 @@ function refreshLock() { } }); - let data = editor.getValue(); + const data = editor.getValue(); - if (data.replace("\s", "").length < 1 || document.getElementById("title").value.length < 1) { + if (data.replace(" ", "").length < 1 || document.getElementById("title").value.length < 1) { document.getElementById("submit").disabled = true; return false; } @@ -46,9 +47,9 @@ class Actions { } send(action, method, data, callback) { - let oReq = new XMLHttpRequest(); + const oReq = new XMLHttpRequest(); - oReq.addEventListener("load", function() { + oReq.addEventListener("load", () => { let result; try { @@ -66,8 +67,8 @@ class Actions { data["action"] = action; - let params = this.get_params(data); - let url = this.url + "?" + params; + const params = this.get_params(data); + const url = `${this.url }?${ params}`; oReq.open(method, url); oReq.setRequestHeader("X-CSRFToken", this.csrf_token); @@ -75,13 +76,13 @@ class Actions { } send_json(action, method, data, callback) { - let oReq = new XMLHttpRequest(); + const oReq = new XMLHttpRequest(); - oReq.addEventListener("load", function() { + oReq.addEventListener("load", () => { let result; try { - result = JSON.parse(this.responseText); + result = JSON.parse(oReq.responseText); } catch (e) { return callback(false); } @@ -93,18 +94,18 @@ class Actions { return callback(true, result); }); - data = JSON.stringify(data); + const obj = JSON.stringify(data); - let params = this.get_params({"action": action}); - let url = this.url + "?" + params; + const params = this.get_params({"action": action}); + const url = `${this.url }?${ params}`; oReq.open(method, url); oReq.setRequestHeader("X-CSRFToken", this.csrf_token); - oReq.send(data); + oReq.send(obj); } - get_params(data) { // https://stackoverflow.com/a/12040639 - return Object.keys(data).map(function(key) { + get_params(data) { // https://stackoverflow.com/a/12040639 + return Object.keys(data).map((key) => { return [key, data[key]].map(encodeURIComponent).join("="); }).join("&"); } @@ -136,18 +137,16 @@ class Actions { "POST", data, callback - ) + ); } delete_question(id, callback) { this.send( "question", "DELETE", - { - "id": id - }, + {"id": id}, callback - ) + ); } associate_question(form, question, callback) { @@ -159,7 +158,7 @@ class Actions { "question": question, }, callback - ) + ); } disassociate_question(form, question, callback) { @@ -171,7 +170,7 @@ class Actions { "question": question, }, callback - ) + ); } create_infraction(id, reason, number, callback) { @@ -184,39 +183,33 @@ class Actions { "number": number }, callback - ) + ); } delete_infraction(id, callback) { this.send( "infraction", "DELETE", - { - "id": id, - }, + {"id": id}, callback - ) + ); } approve_application(id, callback) { this.send( "approve_application", "POST", - { - "id": id, - }, + {"id": id}, callback - ) + ); } unapprove_application(id, callback) { this.send( "unapprove_application", "POST", - { - "id": id, - }, + {"id": id}, callback - ) + ); } } diff --git a/static/js/revision_diff.js b/static/js/revision_diff.js index 9d818c6b..0dd17544 100644 --- a/static/js/revision_diff.js +++ b/static/js/revision_diff.js @@ -1,75 +1,84 @@ "use strict"; (function() { -let buttons = document.querySelectorAll("td input"); // Fetch all radio buttons -let id_reg = /compare-(before|after)-([\w|-]+)/; // Matches compare-after/before-ID + 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){ - let 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) { - let e = revisions.filter((x) => { - return x.id === id; // Filter through all revisions to find the selected one (revisions in declared in the template) - }); - return e[0]; -} - -function radioButtonChecked(element) { - console.log("change detected"); - let id = getRevisionId(element); - let rev = getRevision(id[1]); - if (id[0] === "after"){ - document.querySelector(`#compare-before-${id[1]}`).checked = false; // Deselect the opposite checkbox to the one which has been checked - // because we don't want checking of the same revision + 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 + } - buttons.forEach(function(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(function(e){ - if (getRevisionId(e)[0] === "before" && e.id !== element.id) { - e.checked = false; - } + function getRevision(id) { + /* global revisions */ // TODO: FIXME - if (getRevisionId(e)[0] === "after") { // This makes sure that you do not compare a new revision with an old one - let 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") - } - } + 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]; } - let bef, aft; + function radioButtonChecked(element) { + // console.log("change detected"); + 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 + */ - buttons.forEach((button) => { // Find the selected posts - let id = getRevisionId(button); - if (button.checked && id[0] === "before") { - bef = id[1]; - } + 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; + } - if (button.checked && id[0] === "after") { - aft = id[1]; + // 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"); + } + } + }); } - }) - document.getElementById("compare-submit").href = `/history/compare/${bef}/${aft}` // Switch the buttons HREF to point to the correct compare URL + 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(function(button){ - button.checked = false; // Some browsers remember if a button is checked. - button.onchange = function() { - radioButtonChecked(button); } -}); + + buttons.forEach((button) => { + button.checked = false; // Some browsers remember if a button is checked. + button.onchange = function() { + radioButtonChecked(button); + }; + }); })(); diff --git a/static/js/wiki.js b/static/js/wiki.js index 6ef4efa9..603eebef 100644 --- a/static/js/wiki.js +++ b/static/js/wiki.js @@ -1,15 +1,15 @@ "use strict"; -(function(){ // Use a closure to avoid polluting global scope +(function(){ // Use a closure to avoid polluting global scope 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) + sidebar.classList.remove(visible_class); } else { - sidebar.classList.add(visible_class) + sidebar.classList.add(visible_class); } - } -}()); + }; +})(); |