frontend: use gulp, eslint, code refactor

Apologies in advance for the insanely huge commit. This commit is
primarily based on Anton Linevych's amazing work found here [1]. While
he had gone through the pains of making small, atomic changes to each
file, I had put off reviewing the PR for a long time. By the time I
finally got around to it, the project had changed so much that it didn't
make sense to keep the commits the same way. So I've cherry-picked most
of his commits, with some changes here and there, and I've squashed them
into one commit.

[1] https://gitlab.com/linevych/commento-ce/tree/feature/frontend_building_improvements
This commit is contained in:
Anton Linevych 2018-06-24 04:01:21 +02:00 committed by Adhityaa Chandrasekar
parent e4f71fe402
commit 9e3935b3b2
30 changed files with 5059 additions and 395 deletions

33
frontend/.eslintrc Normal file
View File

@ -0,0 +1,33 @@
{
"env": {
"browser": true
},
"globals": {
"$": true
},
"rules": {
"no-bitwise": 2,
"camelcase": 2,
"brace-style": ["error", "1tbs"],
"curly": ["error", "all"],
"eqeqeq": ["error", "smart"],
"indent": ["error", 2],
"no-use-before-define": [
2,
{
"functions": false
}
],
"new-cap": 2,
"no-caller": 2,
"quotes": [
2,
"double"
],
"no-unused-vars": 2,
"strict": [
2,
"function"
]
}
}

1
frontend/.gitignore vendored
View File

@ -1 +1,2 @@
.sass-cache .sass-cache
node_modules/

View File

@ -1,117 +1,13 @@
SHELL = bash
# list of JS files to be built
JS_BUILD = jquery.js vue.js highlight.js chartist.js login.js forgot.js reset.js signup.js dashboard.js logout.js commento.js
jquery.js = jquery.js
vue.js = vue.js
highlight.js = highlight.js
chartist.js = chartist.js
login.js = utils.js http.js auth-common.js login.js
forgot.js = utils.js http.js forgot.js
reset.js = utils.js http.js reset.js
signup.js = utils.js http.js auth-common.js signup.js
dashboard.js = utils.js http.js errors.js self.js dashboard.js dashboard-setting.js dashboard-domain.js dashboard-installation.js dashboard-general.js dashboard-moderation.js dashboard-statistics.js dashboard-import.js dashboard-danger.js
logout.js = utils.js logout.js
commento.js = commento.js
# for each file in $(JS_BUILD), list its composition
BUILD_DIR = build BUILD_DIR = build
DEVEL_BUILD_DIR = $(BUILD_DIR)/devel GULP = node_modules/.bin/gulp
PROD_BUILD_DIR = $(BUILD_DIR)/prod
HTML_SRC_DIR = . devel:
HTML_SRC_FILES = $(wildcard $(HTML_SRC_DIR)/*.html) yarn install
HTML_DEVEL_BUILD_DIR = $(DEVEL_BUILD_DIR) $(GULP) devel
HTML_DEVEL_BUILD_FILES = $(patsubst $(HTML_SRC_DIR)/%, $(HTML_DEVEL_BUILD_DIR)/%, $(HTML_SRC_FILES))
HTML_PROD_BUILD_DIR = $(PROD_BUILD_DIR)
HTML_PROD_BUILD_FILES = $(patsubst $(HTML_SRC_DIR)/%, $(HTML_PROD_BUILD_DIR)/%, $(HTML_SRC_FILES))
HTML_MINIFIER = html-minifier prod:
HTML_MINIFIER_FLAGS = --collapse-whitespace --remove-comments yarn install
$(GULP) prod
JS_SRC_DIR = js
JS_SRC_FILES = $(wildcard $(JS_SRC_DIR)/*.js)
JS_DEVEL_BUILD_DIR = $(DEVEL_BUILD_DIR)/js
JS_DEVEL_BUILD_FILES = $(addprefix $(JS_DEVEL_BUILD_DIR)/, $(JS_BUILD))
JS_PROD_BUILD_DIR = $(PROD_BUILD_DIR)/js
JS_PROD_BUILD_FILES = $(addprefix $(JS_PROD_BUILD_DIR)/, $(JS_BUILD))
JS_MINIFIER = uglifyjs
JS_MINIFIER_FLAGS = --compress --mangle
SASS_SRC_DIR = sass
SASS_SRC_FILES = $(wildcard $(SASS_SRC_DIR)/*.scss)
CSS_DEVEL_BUILD_DIR = $(DEVEL_BUILD_DIR)/css
CSS_DEVEL_BUILD_FILES = $(patsubst $(SASS_SRC_DIR)/%.scss, $(CSS_DEVEL_BUILD_DIR)/%.css, $(SASS_SRC_FILES))
CSS_PROD_BUILD_DIR = $(PROD_BUILD_DIR)/css
CSS_PROD_BUILD_FILES = $(patsubst $(SASS_SRC_DIR)/%.scss, $(CSS_PROD_BUILD_DIR)/%.css, $(SASS_SRC_FILES))
CSS = sass
CSS_DEVEL_FLAGS =
CSS_PROD_FLAGS = $(CSS_DEVEL_FLAGS) --style compressed
IMGS_SRC_DIR = images
IMGS_SRC_FILES = $(wildcard $(IMGS_SRC_DIR)/*)
IMGS_DEVEL_BUILD_DIR = $(DEVEL_BUILD_DIR)/images
IMGS_DEVEL_BUILD_FILES = $(patsubst $(IMGS_SRC_DIR)/%, $(IMGS_DEVEL_BUILD_DIR)/%, $(IMGS_SRC_FILES))
IMGS_PROD_BUILD_DIR = $(PROD_BUILD_DIR)/images
IMGS_PROD_BUILD_FILES = $(patsubst $(IMGS_SRC_DIR)/%, $(IMGS_PROD_BUILD_DIR)/%, $(IMGS_SRC_FILES))
devel: devel-html devel-js devel-css devel-imgs
prod: devel prod-html prod-js prod-css prod-imgs
clean: clean:
-rm -rf $(BUILD_DIR); -rm -rf $(BUILD_DIR);
devel-html: $(HTML_DEVEL_BUILD_FILES)
$(HTML_DEVEL_BUILD_FILES): $(HTML_DEVEL_BUILD_DIR)/%.html: $(HTML_SRC_DIR)/%.html
cp $^ $@;
prod-html: $(HTML_PROD_BUILD_FILES)
$(HTML_PROD_BUILD_FILES): $(HTML_PROD_BUILD_DIR)/%.html: $(HTML_DEVEL_BUILD_DIR)/%.html
$(HTML_MINIFIER) $(HTML_MINIFIER_FLAGS) -o $@ $^;
devel-js: $(JS_DEVEL_BUILD_FILES)
.SECONDEXPANSION:
$(JS_DEVEL_BUILD_FILES): $(JS_DEVEL_BUILD_DIR)/%.js: $$(addprefix $$(JS_SRC_DIR)/, $$(%.js))
>$@; \
for f in $^; do \
printf "// %s\n" "$$f" >>$@; \
cat $$f >>$@; \
printf "\n" >>$@; \
done;
prod-js: $(JS_PROD_BUILD_FILES)
$(JS_PROD_BUILD_FILES): $(JS_PROD_BUILD_DIR)/%.js: $(JS_DEVEL_BUILD_DIR)/%.js
$(JS_MINIFIER) $(JS_MINIFIER_FLAGS) -o $@ $^;
devel-css: $(CSS_DEVEL_BUILD_FILES)
$(CSS_DEVEL_BUILD_FILES): $(CSS_DEVEL_BUILD_DIR)/%.css: $(SASS_SRC_DIR)/%.scss $(SASS_SRC_FILES)
$(CSS) $(CSS_DEVEL_FLAGS) $< $@;
prod-css: $(CSS_PROD_BUILD_FILES)
$(CSS_PROD_BUILD_FILES): $(CSS_PROD_BUILD_DIR)/%.css: $(SASS_SRC_DIR)/%.scss
$(CSS) $(CSS_PROD_FLAGS) $^ $@;
$(shell mkdir -p $(HTML_DEVEL_BUILD_DIR) $(JS_DEVEL_BUILD_DIR) $(CSS_DEVEL_BUILD_DIR) $(HTML_PROD_BUILD_DIR) $(JS_PROD_BUILD_DIR) $(CSS_PROD_BUILD_DIR))
devel-imgs: $(IMGS_DEVEL_BUILD_FILES)
$(IMGS_DEVEL_BUILD_FILES): $(IMGS_DEVEL_BUILD_DIR)/%: $(IMGS_SRC_DIR)/%
cp $^ $@;
prod-imgs: $(IMGS_PROD_BUILD_FILES)
$(IMGS_PROD_BUILD_FILES): $(IMGS_PROD_BUILD_DIR)/%: $(IMGS_SRC_DIR)/%
cp $^ $@
$(shell mkdir -p $(HTML_DEVEL_BUILD_DIR) $(JS_DEVEL_BUILD_DIR) $(CSS_DEVEL_BUILD_DIR) $(IMGS_DEVEL_BUILD_DIR) $(HTML_PROD_BUILD_DIR) $(JS_PROD_BUILD_DIR) $(CSS_PROD_BUILD_DIR) $(IMGS_PROD_BUILD_DIR))

134
frontend/gulpfile.js Normal file
View File

@ -0,0 +1,134 @@
"use strict";
const gulp = require("gulp");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const cleanCss = require("gulp-clean-css");
const htmlMinifier = require("gulp-html-minifier");
const uglify = require("gulp-uglify");
const concat = require("gulp-concat");
const rename = require("gulp-rename");
const eslint = require("gulp-eslint");
const develPath = "build/devel/";
const prodPath = "build/prod/";
const scssSrc = "./sass/*.scss";
const cssDir = "css/";
const imagesDir = "images/";
const imagesGlob = imagesDir + "**/*";
const jsDir = "js/";
const jsGlob = jsDir + "*.js";
const htmlGlob = "./*.html";
const jsCompileMap = {
"js/jquery.js": ["node_modules/jquery/dist/jquery.min.js"],
"js/vue.js": ["node_modules/vue/dist/vue.min.js"],
"js/highlight.js": ["node_modules/highlightjs/highlight.pack.min.js"],
"js/chartist.js": ["node_modules/chartist/dist/chartist.min.js"],
"js/login.js": [
"js/utils.js",
"js/http.js",
"js/auth-common.js",
"js/login.js"
],
"js/forgot.js": [
"js/utils.js",
"js/http.js",
"js/forgot.js"
],
"js/reset.js": [
"js/js/utils.js",
"js/http.js",
"js/reset.js"
],
"js/signup.js": [
"js/utils.js",
"js/http.js",
"js/auth-common.js",
"js/signup.js"
],
"js/dashboard.js": [
"js/utils.js",
"js/http.js",
"js/errors.js",
"js/self.js",
"js/dashboard.js",
"js/dashboard-setting.js",
"js/dashboard-domain.js",
"js/dashboard-installation.js",
"js/dashboard-general.js",
"js/dashboard-moderation.js",
"js/dashboard-statistics.js",
"js/dashboard-import.js",
"js/dashboard-danger.js",
],
"js/logout.js": [
"js/utils.js",
"js/logout.js"
],
"js/commento.js": ["js/commento.js"],
};
gulp.task("scss-devel", function () {
return gulp.src(scssSrc)
.pipe(sourcemaps.init())
.pipe(sass({outputStyle: "expanded"}).on("error", sass.logError))
.pipe(sourcemaps.write())
.pipe(gulp.dest(develPath + cssDir));
});
gulp.task("scss-prod", function () {
return gulp.src(scssSrc)
.pipe(sass({outputStyle: "compressed"}).on("error", sass.logError))
.pipe(cleanCss({compatibility: "ie8", level: 2}))
.pipe(gulp.dest(prodPath + cssDir));
});
gulp.task("html-devel", function () {
gulp.src([htmlGlob]).pipe(gulp.dest(develPath));
});
gulp.task("html-prod", function () {
gulp.src(htmlGlob)
.pipe(htmlMinifier({collapseWhitespace: true, removeComments: true}))
.pipe(gulp.dest(prodPath))
});
gulp.task("images-devel", function () {
gulp.src([imagesGlob]).pipe(gulp.dest(develPath + imagesDir));
});
gulp.task("images-prod", function () {
gulp.src([imagesGlob]).pipe(gulp.dest(prodPath + imagesDir));
});
gulp.task("js-devel", function () {
for (let outputFile in jsCompileMap) {
gulp.src(jsCompileMap[outputFile])
.pipe(sourcemaps.init())
.pipe(concat(outputFile))
.pipe(rename(outputFile))
.pipe(uglify())
.pipe(sourcemaps.write())
.pipe(gulp.dest(develPath))
}
});
gulp.task("js-prod", function () {
for (let outputFile in jsCompileMap) {
gulp.src(jsCompileMap[outputFile])
.pipe(concat(outputFile))
.pipe(rename(outputFile))
.pipe(uglify())
.pipe(gulp.dest(prodPath))
}
});
gulp.task("lint", function () {
return gulp.src(jsGlob)
.pipe(eslint())
.pipe(eslint.failAfterError())
});
gulp.task("devel", ["scss-devel", "html-devel", "images-devel", "lint", "js-devel"]);
gulp.task("prod", ["scss-prod", "html-prod", "images-prod", "lint", "js-prod"]);

View File

@ -1,17 +1,19 @@
(function (global, document) { (function (global, document) {
"use strict"
// Redirect the user to the dashboard if there's a cookie. If the cookie is // Redirect the user to the dashboard if there's a cookie. If the cookie is
// invalid, they would be redirected back to the login page *after* the // invalid, they would be redirected back to the login page *after* the
// cookie is deleted. // cookie is deleted.
global.loggedInRedirect = function() { global.loggedInRedirect = function() {
if (global.cookieGet("commentoOwnerToken") !== undefined) if (global.cookieGet("commentoOwnerToken") !== undefined) {
document.location = global.origin + "/dashboard"; document.location = global.origin + "/dashboard";
}
} }
// Prefills the email field from the URL parameter. // Prefills the email field from the URL parameter.
global.prefillEmail = function() { global.prefillEmail = function() {
if (paramGet("email") != undefined) { if (paramGet("email") !== undefined) {
$("#email").val(paramGet("email")); $("#email").val(paramGet("email"));
$("#password").click(); $("#password").click();
} }

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,20 @@
(function(global, document) { (function(global, document) {
'use strict'; "use strict";
if (global.commento === undefined) { if (global.commento === undefined) {
console.log("[commento] error: window.commento namespace not defined; maybe there's a mismatch in version between the backend and the frontend?"); console.log("[commento] error: window.commento namespace not defined; maybe there's a mismatch in version between the backend and the frontend?");
return; return;
} else {
global = global.commento;
} }
global = global.commento;
// Do not use other files like utils.js and http.js in the Makefile to build // Do not use other files like utils.js and http.js in the gulpfile to build
// commento.js for the following reasons: // commento.js for the following reasons:
// - We don't use jQuery in the actual JavaScript payload because we need // - We don't use jQuery in the actual JavaScript payload because we need
// to be lightweight. // to be lightweight.
// - They pollute the global/window namespace (with global.post, etc.). // - They pollute the global/window namespace (with global.post, etc.).
// That's NOT fine when we expect them source our JavaScript. For example, // That's NOT fine when we expect them to source our JavaScript. For example,
// the user may have their own window.post defined. We don't want to // the user may have their own window.post defined. We don't want to
// override that. // override that.
@ -67,13 +68,12 @@
var origin = global.origin; var origin = global.origin;
var cdn = global.cdn; var cdn = global.cdn;
var root = null; var root = null;
var cssOverride = undefined; var cssOverride;
var autoInit = undefined; var autoInit;
var isAuthenticated = false; var isAuthenticated = false;
var comments = []; var comments = [];
var commenters = []; var commenters = [];
var requireIdentification = true; var requireIdentification = true;
var requireModeration = true;
var isModerator = false; var isModerator = false;
var isFrozen = false; var isFrozen = false;
var shownSubmitButton = {"root": false}; var shownSubmitButton = {"root": false};
@ -96,16 +96,6 @@
} }
function dataGet(el, key) {
return el.dataset[key];
}
function dataSet(el, key, data) {
el.dataset[key] = data;
}
function append(root, el) { function append(root, el) {
root.appendChild(el); root.appendChild(el);
} }
@ -122,8 +112,9 @@
function classRemove(el, cls) { function classRemove(el, cls) {
if (el !== null) if (el !== null) {
el.classList.remove("commento-" + cls); el.classList.remove("commento-" + cls);
}
} }
@ -133,24 +124,26 @@
function remove(el) { function remove(el) {
if (el !== null) if (el !== null) {
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}
} }
function attrGet(node, a) { function attrGet(node, a) {
var attr = node.attributes[a]; var attr = node.attributes[a];
if (attr === undefined) if (attr === undefined) {
return undefined; return undefined;
}
return attr.value; return attr.value;
} }
function onclick(node, f, ...args) { function onclick(node, f, arg) {
node.addEventListener("click", function() { node.addEventListener("click", function() {
f(...args); f(arg);
}, false); }, false);
} }
@ -176,7 +169,7 @@
function get(url, callback) { function get(url, callback) {
var xmlDoc = new XMLHttpRequest(); var xmlDoc = new XMLHttpRequest();
xmlDoc.open('GET', url, true); xmlDoc.open("GET", url, true);
xmlDoc.onload = function() { xmlDoc.onload = function() {
callback(JSON.parse(xmlDoc.response)); callback(JSON.parse(xmlDoc.response));
}; };
@ -186,32 +179,25 @@
function call(callback) { function call(callback) {
if (typeof(callback) == "function") if (typeof(callback) === "function") {
callback(); callback();
} }
function rootSpinnerShow() {
var spinner = create("div");
classAdd(spinner, "loading");
append(root, spinner);
} }
function cookieGet(name) { function cookieGet(name) {
var c = "; " + document.cookie; var c = "; " + document.cookie;
var x = c.split("; " + name + "="); var x = c.split("; " + name + "=");
if (x.length == 2) if (x.length === 2) {
return x.pop().split(";").shift(); return x.pop().split(";").shift();
}
} }
function cookieSet(name, value) { function cookieSet(name, value) {
var expires = ""; var expires = "";
var date = new Date(); var date = new Date();
date.setTime(date.getTime() + (365*24*60*60*1000)); date.setTime(date.getTime() + (365 * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString(); expires = "; expires=" + date.toUTCString();
document.cookie = name + "=" + value + expires + "; path=/"; document.cookie = name + "=" + value + expires + "; path=/";
@ -220,8 +206,9 @@
function commenterTokenGet() { function commenterTokenGet() {
var commenterToken = cookieGet("commentoCommenterToken"); var commenterToken = cookieGet("commentoCommenterToken");
if (commenterToken === undefined) if (commenterToken === undefined) {
return "anonymous"; return "anonymous";
}
return commenterToken; return commenterToken;
} }
@ -235,7 +222,7 @@
function selfGet(callback) { function selfGet(callback) {
var commenterToken = commenterTokenGet(); var commenterToken = commenterTokenGet();
if (commenterToken == "anonymous") { if (commenterToken === "anonymous") {
isAuthenticated = false; isAuthenticated = false;
call(callback); call(callback);
return; return;
@ -273,14 +260,14 @@
attrSet(loggedContainer, "style", "display: none"); attrSet(loggedContainer, "style", "display: none");
attrSet(name, "href", resp.commenter.link); attrSet(name, "href", resp.commenter.link);
if (resp.commenter.photo == "undefined") { if (resp.commenter.photo === "undefined") {
avatar = create("div"); avatar = create("div");
avatar.style["background"] = color; avatar.style["background"] = color;
avatar.innerHTML = resp.commenter.name[0].toUpperCase(); avatar.innerHTML = resp.commenter.name[0].toUpperCase();
classAdd(avatar, "avatar"); classAdd(avatar, "avatar");
} else { } else {
avatar = create("img"); avatar = create("img");
if (resp.commenter.provider == "google") { if (resp.commenter.provider === "google") {
attrSet(avatar, "src", resp.commenter.photo + "?sz=50"); attrSet(avatar, "src", resp.commenter.photo + "?sz=50");
} else { } else {
attrSet(avatar, "src", resp.commenter.photo); attrSet(avatar, "src", resp.commenter.photo);
@ -303,7 +290,7 @@
function cssLoad(file, onload) { function cssLoad(file, onload) {
var link = create("link"); var link = create("link");
var head = document.getElementsByTagName('head')[0]; var head = document.getElementsByTagName("head")[0];
link.type = "text/css"; link.type = "text/css";
attrSet(link, "href", file); attrSet(link, "href", file);
@ -357,7 +344,6 @@
return; return;
} }
requireModeration = resp.requireModeration;
requireIdentification = resp.requireIdentification; requireIdentification = resp.requireIdentification;
isModerator = resp.isModerator; isModerator = resp.isModerator;
isFrozen = resp.isFrozen; isFrozen = resp.isFrozen;
@ -448,12 +434,12 @@
append(buttons, createButton); append(buttons, createButton);
append(buttons, loginButton); append(buttons, loginButton);
append(buttonsContainer, buttons); append(buttonsContainer, buttons);
if (!requireIdentification) if (!requireIdentification) {
append(buttonsContainer, anonymousButton); append(buttonsContainer, anonymousButton);
}
append(textareaContainer, question); append(textareaContainer, question);
append(textareaContainer, buttonsContainer); append(textareaContainer, buttonsContainer);
} } else {
else {
onclick(textarea, global.showSubmitButton, id); onclick(textarea, global.showSubmitButton, id);
attrSet(textarea, "placeholder", "Join the discussion!"); attrSet(textarea, "placeholder", "Join the discussion!");
@ -478,14 +464,15 @@
commentsArea.innerHTML = ""; commentsArea.innerHTML = "";
if (isLocked) { if (isLocked || isFrozen) {
if (isAuthenticated) if (isAuthenticated) {
append(mainArea, messageCreate("This thread is locked. You cannot add new comments.")); append(mainArea, messageCreate("This thread is locked. You cannot add new comments."));
else } else {
append(mainArea, textareaCreate("root")); append(mainArea, textareaCreate("root"));
} }
else } else {
append(mainArea, textareaCreate("root")); append(mainArea, textareaCreate("root"));
}
append(mainArea, commentsArea); append(mainArea, commentsArea);
append(root, mainArea); append(root, mainArea);
@ -510,11 +497,10 @@
var comment = textarea.value; var comment = textarea.value;
if (comment == "") { if (comment === "") {
classAdd(textarea, "red-border"); classAdd(textarea, "red-border");
return; return;
} } else {
else {
classRemove(textarea, "red-border"); classRemove(textarea, "red-border");
} }
@ -539,16 +525,18 @@
commentsRender(); commentsRender();
var message = ""; var message = "";
if (resp.state == "unapproved") if (resp.state === "unapproved") {
message = "Your comment is under moderation."; message = "Your comment is under moderation.";
else if (resp.state == "flagged") } else if (resp.state === "flagged") {
message = "Your comment was flagged as spam and is under moderation."; message = "Your comment was flagged as spam and is under moderation.";
}
if (message != "") { if (message !== "") {
if (id == "root") if (id === "root") {
prepend($(ID_SUPER_CONTAINER + id), messageCreate(message)); prepend($(ID_SUPER_CONTAINER + id), messageCreate(message));
else } else {
append($(ID_BODY + id), messageCreate(message)); append($(ID_BODY + id), messageCreate(message));
}
} }
}); });
}); });
@ -567,8 +555,9 @@
]; ];
var total = 0; var total = 0;
for (var i = 0; i < name.length; i++) for (var i = 0; i < name.length; i++) {
total += name.charCodeAt(i); total += name.charCodeAt(i);
}
var color = colors[total % colors.length]; var color = colors[total % colors.length];
return color; return color;
@ -586,34 +575,29 @@
var elapsed = current - previous; var elapsed = current - previous;
if (elapsed < msJustNow) { if (elapsed < msJustNow) {
return 'just now'; return "just now";
} } else if (elapsed < msPerMinute) {
else if (elapsed < msPerMinute) { return Math.round(elapsed / 1000) + " seconds ago";
return Math.round(elapsed/1000) + ' seconds ago'; } else if (elapsed < msPerHour) {
} return Math.round(elapsed / msPerMinute) + " minutes ago";
else if (elapsed < msPerHour) { } else if (elapsed < msPerDay ) {
return Math.round(elapsed/msPerMinute) + ' minutes ago'; return Math.round(elapsed / msPerHour ) + " hours ago";
} } else if (elapsed < msPerMonth) {
else if (elapsed < msPerDay ) { return Math.round(elapsed / msPerDay) + " days ago";
return Math.round(elapsed/msPerHour ) + ' hours ago'; } else if (elapsed < msPerYear) {
} return Math.round(elapsed / msPerMonth) + " months ago";
else if (elapsed < msPerMonth) { } else {
return Math.round(elapsed/msPerDay) + ' days ago'; return Math.round(elapsed / msPerYear ) + " years ago";
}
else if (elapsed < msPerYear) {
return Math.round(elapsed/msPerMonth) + ' months ago';
}
else {
return Math.round(elapsed/msPerYear ) + ' years ago';
} }
} }
function scorify(score) { function scorify(score) {
if (score != 1) if (score !== 1) {
return score + " points"; return score + " points";
else } else {
return score + " point"; return score + " point";
}
} }
@ -624,10 +608,12 @@
} }
cur.sort(function(a, b) { cur.sort(function(a, b) {
if (a.commentHex == stickyCommentHex) if (a.commentHex === stickyCommentHex) {
return -Infinity; return -Infinity;
if (b.commentHex == stickyCommentHex) }
if (b.commentHex === stickyCommentHex) {
return Infinity; return Infinity;
}
return b.score - a.score; return b.score - a.score;
}); });
@ -655,10 +641,11 @@
var contents = create("div"); var contents = create("div");
var color = colorGet(comment.commenterHex + "-" + commenter.name); var color = colorGet(comment.commenterHex + "-" + commenter.name);
var name; var name;
if (commenter.link != "undefined" && commenter.link != "https://undefined" && commenter.link != "") if (commenter.link !== "undefined" && commenter.link !== "https://undefined" && commenter.link !== "") {
name = create("a"); name = create("a");
else } else {
name = create("div"); name = create("div");
}
card.id = ID_CARD + comment.commentHex; card.id = ID_CARD + comment.commentHex;
body.id = ID_BODY + comment.commentHex; body.id = ID_BODY + comment.commentHex;
@ -674,8 +661,9 @@
approve.id = ID_APPROVE + comment.commentHex; approve.id = ID_APPROVE + comment.commentHex;
remove.id = ID_REMOVE + comment.commentHex; remove.id = ID_REMOVE + comment.commentHex;
sticky.id = ID_STICKY + comment.commentHex; sticky.id = ID_STICKY + comment.commentHex;
if (children) if (children) {
children.id = ID_CHILDREN + comment.commentHex; children.id = ID_CHILDREN + comment.commentHex;
}
contents.id = ID_CONTENTS + comment.commentHex; contents.id = ID_CONTENTS + comment.commentHex;
name.id = ID_NAME + comment.commentHex; name.id = ID_NAME + comment.commentHex;
@ -686,14 +674,15 @@
reply.title = "Reply"; reply.title = "Reply";
approve.title = "Approve"; approve.title = "Approve";
remove.title = "Remove"; remove.title = "Remove";
if (stickyCommentHex == comment.commentHex) { if (stickyCommentHex === comment.commentHex) {
if (isModerator) if (isModerator) {
sticky.title = "Unsticky"; sticky.title = "Unsticky";
else } else {
sticky.title = "This comment has been stickied"; sticky.title = "This comment has been stickied";
} }
else } else {
sticky.title = "Sticky"; sticky.title = "Sticky";
}
card.style["borderLeft"] = "2px solid " + color; card.style["borderLeft"] = "2px solid " + color;
name.innerText = commenter.name; name.innerText = commenter.name;
@ -701,14 +690,14 @@
timeago.innerHTML = timeDifference((new Date()).getTime(), Date.parse(comment.creationDate)); timeago.innerHTML = timeDifference((new Date()).getTime(), Date.parse(comment.creationDate));
score.innerText = scorify(comment.score); score.innerText = scorify(comment.score);
if (commenter.photo == "undefined") { if (commenter.photo === "undefined") {
avatar = create("div"); avatar = create("div");
avatar.style["background"] = color; avatar.style["background"] = color;
avatar.innerHTML = commenter.name[0].toUpperCase(); avatar.innerHTML = commenter.name[0].toUpperCase();
classAdd(avatar, "avatar"); classAdd(avatar, "avatar");
} else { } else {
avatar = create("img"); avatar = create("img");
if (commenter.provider == "google") { if (commenter.provider === "google") {
attrSet(avatar, "src", commenter.photo + "?sz=50"); attrSet(avatar, "src", commenter.photo + "?sz=50");
} else { } else {
attrSet(avatar, "src", commenter.photo); attrSet(avatar, "src", commenter.photo);
@ -717,10 +706,12 @@
} }
classAdd(card, "card"); classAdd(card, "card");
if (isModerator && comment.state != "approved") if (isModerator && comment.state !== "approved") {
classAdd(card, "dark-card"); classAdd(card, "dark-card");
if (comment.state == "flagged") }
if (comment.state === "flagged") {
classAdd(name, "flagged"); classAdd(name, "flagged");
}
classAdd(header, "header"); classAdd(header, "header");
classAdd(name, "name"); classAdd(name, "name");
classAdd(subtitle, "subtitle"); classAdd(subtitle, "subtitle");
@ -728,8 +719,9 @@
classAdd(score, "score"); classAdd(score, "score");
classAdd(body, "body"); classAdd(body, "body");
classAdd(options, "options"); classAdd(options, "options");
if (mobileView) if (mobileView) {
classAdd(options, "options-mobile"); classAdd(options, "options-mobile");
}
classAdd(edit, "option-button"); classAdd(edit, "option-button");
classAdd(edit, "option-edit"); classAdd(edit, "option-edit");
classAdd(reply, "option-button"); classAdd(reply, "option-button");
@ -745,16 +737,18 @@
classAdd(remove, "option-button"); classAdd(remove, "option-button");
classAdd(remove, "option-remove"); classAdd(remove, "option-remove");
classAdd(sticky, "option-button"); classAdd(sticky, "option-button");
if (stickyCommentHex == comment.commentHex) if (stickyCommentHex === comment.commentHex) {
classAdd(sticky, "option-unsticky"); classAdd(sticky, "option-unsticky");
else } else {
classAdd(sticky, "option-sticky"); classAdd(sticky, "option-sticky");
}
if (isAuthenticated) { if (isAuthenticated) {
if (comment.direction > 0) if (comment.direction > 0) {
classAdd(upvote, "upvoted"); classAdd(upvote, "upvoted");
else if (comment.direction < 0) } else if (comment.direction < 0) {
classAdd(downvote, "downvoted"); classAdd(downvote, "downvoted");
}
} }
onclick(collapse, global.commentCollapse, comment.commentHex); onclick(collapse, global.commentCollapse, comment.commentHex);
@ -762,20 +756,22 @@
onclick(remove, global.commentDelete, comment.commentHex); onclick(remove, global.commentDelete, comment.commentHex);
onclick(sticky, global.commentSticky, comment.commentHex); onclick(sticky, global.commentSticky, comment.commentHex);
if (isAuthenticated) if (isAuthenticated) {
upDownOnclickSet(upvote, downvote, comment.commentHex, comment.direction); upDownOnclickSet(upvote, downvote, comment.commentHex, comment.direction);
else { } else {
onclick(upvote, global.loginBoxShow); onclick(upvote, global.loginBoxShow);
onclick(downvote, global.loginBoxShow); onclick(downvote, global.loginBoxShow);
} }
if (isAuthenticated || chosenAnonymous) if (isAuthenticated || chosenAnonymous) {
onclick(reply, global.replyShow, comment.commentHex); onclick(reply, global.replyShow, comment.commentHex);
else } else {
onclick(reply, global.loginBoxShow); onclick(reply, global.loginBoxShow);
}
if (commenter.link != "undefined" && commenter.link != "https://undefined" && commenter.link != "") if (commenter.link !== "undefined" && commenter.link !== "https://undefined" && commenter.link !== "") {
attrSet(name, "href", commenter.link); attrSet(name, "href", commenter.link);
}
append(options, collapse); append(options, collapse);
@ -786,26 +782,30 @@
append(options, reply); append(options, reply);
if (isModerator) { if (isModerator) {
if (parentHex == "root") if (parentHex === "root") {
append(options, sticky); append(options, sticky);
}
append(options, remove); append(options, remove);
if (comment.state != "approved") if (comment.state !== "approved") {
append(options, approve); append(options, approve);
} }
else { } else {
if (stickyCommentHex == comment.commentHex) if (stickyCommentHex === comment.commentHex) {
append(options, sticky); append(options, sticky);
}
} }
attrSet(options, "style", "width: " + ((options.childNodes.length+1)*32) + "px;"); attrSet(options, "style", "width: " + ((options.childNodes.length+1)*32) + "px;");
for (var i = 0; i < options.childNodes.length; i++) for (var i = 0; i < options.childNodes.length; i++) {
attrSet(options.childNodes[i], "style", "right: " + (i*32) + "px;"); attrSet(options.childNodes[i], "style", "right: " + (i*32) + "px;");
}
append(subtitle, score); append(subtitle, score);
append(subtitle, timeago); append(subtitle, timeago);
if (!mobileView) if (!mobileView) {
append(header, options); append(header, options);
}
append(header, avatar); append(header, avatar);
append(header, name); append(header, name);
append(header, subtitle); append(header, subtitle);
@ -847,7 +847,6 @@
var card = $(ID_CARD + commentHex); var card = $(ID_CARD + commentHex);
var name = $(ID_NAME + commentHex); var name = $(ID_NAME + commentHex);
var options = $(ID_OPTIONS + commentHex);
var tick = $(ID_APPROVE + commentHex); var tick = $(ID_APPROVE + commentHex);
classRemove(card, "dark-card"); classRemove(card, "dark-card");
@ -878,28 +877,29 @@
function nameWidthFix() { function nameWidthFix() {
var els = document.getElementsByClassName("commento-name"); var els = document.getElementsByClassName("commento-name");
for (var i = 0; i < els.length; i++) for (var i = 0; i < els.length; i++) {
attrSet(els[i], "style", "max-width: " + (els[i].getBoundingClientRect()["width"] + 20) + "px;") attrSet(els[i], "style", "max-width: " + (els[i].getBoundingClientRect()["width"] + 20) + "px;")
}
} }
function upDownOnclickSet(upvote, downvote, commentHex, direction) { function upDownOnclickSet(upvote, downvote, commentHex, direction) {
if (direction > 0) { if (direction > 0) {
onclick(upvote, global.vote, commentHex, 1, 0); onclick(upvote, global.vote, commentHex, [1, 0]);
onclick(downvote, global.vote, commentHex, 1, -1); onclick(downvote, global.vote, commentHex, [1, -1]);
} } else if (direction < 0) {
else if (direction < 0) { onclick(upvote, global.vote, commentHex, [-1, 1]);
onclick(upvote, global.vote, commentHex, -1, 1); onclick(downvote, global.vote, commentHex, [-1, 0]);
onclick(downvote, global.vote, commentHex, -1, 0); } else {
} onclick(upvote, global.vote, commentHex, [0, 1]);
else { onclick(downvote, global.vote, commentHex, [0, -1]);
onclick(upvote, global.vote, commentHex, 0, 1);
onclick(downvote, global.vote, commentHex, 0, -1);
} }
} }
global.vote = function(commentHex, oldVote, direction) { global.vote = function(commentHex, dirs) {
var oldVote = dirs[0];
var direction = dirs[1];
var upvote = $(ID_UPVOTE + commentHex); var upvote = $(ID_UPVOTE + commentHex);
var downvote = $(ID_DOWNVOTE + commentHex); var downvote = $(ID_DOWNVOTE + commentHex);
var score = $(ID_SCORE + commentHex); var score = $(ID_SCORE + commentHex);
@ -914,20 +914,27 @@
classRemove(upvote, "upvoted"); classRemove(upvote, "upvoted");
classRemove(downvote, "downvoted"); classRemove(downvote, "downvoted");
if (direction > 0) if (direction > 0) {
classAdd(upvote, "upvoted"); classAdd(upvote, "upvoted");
else if (direction < 0) } else if (direction < 0) {
classAdd(downvote, "downvoted"); classAdd(downvote, "downvoted");
}
score.innerText = scorify(parseInt(score.innerText.replace(/[^\d-.]/g, "")) + direction - oldVote); score.innerText = scorify(parseInt(score.innerText.replace(/[^\d-.]/g, "")) + direction - oldVote);
post(origin + "/api/comment/vote", json, function(resp) {}); post(origin + "/api/comment/vote", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return;
}
});
} }
global.replyShow = function(id) { global.replyShow = function(id) {
if (id in shownReply && shownReply[id]) if (id in shownReply && shownReply[id]) {
return; return;
}
var body = $(ID_BODY + id); var body = $(ID_BODY + id);
append(body, textareaCreate(id)); append(body, textareaCreate(id));
@ -965,8 +972,9 @@
var children = $(ID_CHILDREN + id); var children = $(ID_CHILDREN + id);
var button = $(ID_COLLAPSE + id); var button = $(ID_COLLAPSE + id);
if (children) if (children) {
classAdd(children, "hidden"); classAdd(children, "hidden");
}
classRemove(button, "option-collapse"); classRemove(button, "option-collapse");
classAdd(button, "option-uncollapse"); classAdd(button, "option-uncollapse");
@ -981,8 +989,9 @@
var children = $(ID_CHILDREN + id); var children = $(ID_CHILDREN + id);
var button = $(ID_COLLAPSE + id); var button = $(ID_COLLAPSE + id);
if (children) if (children) {
classRemove(children, "hidden"); classRemove(children, "hidden");
}
classRemove(button, "option-uncollapse"); classRemove(button, "option-uncollapse");
classAdd(button, "option-collapse"); classAdd(button, "option-collapse");
@ -1015,8 +1024,9 @@
global.showSubmitButton = function(id) { global.showSubmitButton = function(id) {
if (id in shownSubmitButton && shownSubmitButton[id]) if (id in shownSubmitButton && shownSubmitButton[id]) {
return; return;
}
shownSubmitButton[id] = true; shownSubmitButton[id] = true;
@ -1037,7 +1047,8 @@
append(el, submit); append(el, submit);
} }
global.anonymousChoose = function(provider) {
global.anonymousChoose = function() {
cookieSet("commentoCommenterToken", "anonymous"); cookieSet("commentoCommenterToken", "anonymous");
chosenAnonymous = true; chosenAnonymous = true;
refreshAll(); refreshAll();
@ -1178,12 +1189,13 @@
append(loginBox, oauthPretext); append(loginBox, oauthPretext);
append(oauthButtonsContainer, oauthButtons); append(oauthButtonsContainer, oauthButtons);
append(loginBox, oauthButtonsContainer); append(loginBox, oauthButtonsContainer);
if (!requireIdentification) if (!requireIdentification) {
append(loginBox, anonymousButton); append(loginBox, anonymousButton);
}
oauthButtonsShown = true; oauthButtonsShown = true;
} } else {
else
oauthButtonsShown = false; oauthButtonsShown = false;
}
append(loginBox, close); append(loginBox, close);
@ -1279,15 +1291,16 @@
global.passwordAsk = function() { global.passwordAsk = function() {
var loginBox = $(ID_LOGIN_BOX); var loginBox = $(ID_LOGIN_BOX);
var subtitle = $(ID_LOGIN_BOX_SUBTITLE); var subtitle = $(ID_LOGIN_BOX_SUBTITLE);
var emailInput = $(ID_LOGIN_BOX_EMAIL_INPUT);
var emailButton = $(ID_LOGIN_BOX_EMAIL_BUTTON); var emailButton = $(ID_LOGIN_BOX_EMAIL_BUTTON);
var loginLinkContainer = $(ID_LOGIN_BOX_LOGIN_LINK_CONTAINER); var loginLinkContainer = $(ID_LOGIN_BOX_LOGIN_LINK_CONTAINER);
var hr = $(ID_LOGIN_BOX_HR); var hr = $(ID_LOGIN_BOX_HR);
var oauthButtonsContainer = $(ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER); var oauthButtonsContainer = $(ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER);
var oauthPretext = $(ID_LOGIN_BOX_OAUTH_PRETEXT); var oauthPretext = $(ID_LOGIN_BOX_OAUTH_PRETEXT);
var anonymousButton = null; var anonymousButton = null;
if (!requireIdentification); if (!requireIdentification){
anonymousButton = $(ID_LOGIN_BOX_ANONYMOUS_BUTTON); ;
}
anonymousButton = $(ID_LOGIN_BOX_ANONYMOUS_BUTTON);
remove(emailButton); remove(emailButton);
remove(loginLinkContainer); remove(loginLinkContainer);
@ -1302,23 +1315,23 @@
var order, id, type, placeholder; var order, id, type, placeholder;
if (loginBoxType == "signup") { if (loginBoxType === "signup") {
var order = ["name", "website", "password"]; var order = ["name", "website", "password"];
var id = [ID_LOGIN_BOX_NAME_INPUT, ID_LOGIN_BOX_WEBSITE_INPUT, ID_LOGIN_BOX_PASSWORD_INPUT]; var id = [ID_LOGIN_BOX_NAME_INPUT, ID_LOGIN_BOX_WEBSITE_INPUT, ID_LOGIN_BOX_PASSWORD_INPUT];
var type = ["text", "text", "password"]; var type = ["text", "text", "password"];
var placeholder = ["Real Name", "Website (Optional)", "Password"]; var placeholder = ["Real Name", "Website (Optional)", "Password"];
} } else {
else {
var order = ["password"]; var order = ["password"];
var id = [ID_LOGIN_BOX_PASSWORD_INPUT]; var id = [ID_LOGIN_BOX_PASSWORD_INPUT];
var type = ["password"]; var type = ["password"];
var placeholder = ["Password"]; var placeholder = ["Password"];
} }
if (loginBoxType == "signup") if (loginBoxType === "signup") {
subtitle.innerText = "Finish the rest of your profile to complete." subtitle.innerText = "Finish the rest of your profile to complete."
else } else {
subtitle.innerText = "Enter your password to log in." subtitle.innerText = "Enter your password to log in."
}
for (var i = 0; i < order.length; i++) { for (var i = 0; i < order.length; i++) {
var fieldContainer = create("div"); var fieldContainer = create("div");
@ -1338,15 +1351,16 @@
append(field, fieldInput); append(field, fieldInput);
append(fieldContainer, field); append(fieldContainer, field);
if (order[i] == "password") { if (order[i] === "password") {
var fieldButton = create("button"); var fieldButton = create("button");
classAdd(fieldButton, "email-button"); classAdd(fieldButton, "email-button");
fieldButton.innerText = loginBoxType; fieldButton.innerText = loginBoxType;
if (loginBoxType == "signup") if (loginBoxType === "signup") {
onclick(fieldButton, global.signup); onclick(fieldButton, global.signup);
else } else {
onclick(fieldButton, global.login); onclick(fieldButton, global.login);
}
append(field, fieldButton); append(field, fieldButton);
} }
@ -1354,10 +1368,11 @@
append(loginBox, fieldContainer); append(loginBox, fieldContainer);
} }
if (loginBoxType == "signup") if (loginBoxType === "signup") {
$(ID_LOGIN_BOX_NAME_INPUT).focus(); $(ID_LOGIN_BOX_NAME_INPUT).focus();
else } else {
$(ID_LOGIN_BOX_PASSWORD_INPUT).focus(); $(ID_LOGIN_BOX_PASSWORD_INPUT).focus();
}
} }
@ -1392,36 +1407,41 @@
lock.disabled = true; lock.disabled = true;
pageUpdate(function(success) { pageUpdate(function(success) {
lock.disabled = false; if (success) {
if (isLocked) lock.disabled = false;
lock.innerHTML = "Unlock Thread"; if (isLocked) {
else lock.innerHTML = "Unlock Thread";
lock.innerHTML = "Lock Thread"; } else {
lock.innerHTML = "Lock Thread";
}
}
}); });
} }
global.commentSticky = function(commentHex) { global.commentSticky = function(commentHex) {
if (stickyCommentHex != "none") { if (stickyCommentHex !== "none") {
var sticky = $(ID_STICKY + stickyCommentHex); var sticky = $(ID_STICKY + stickyCommentHex);
classRemove(sticky, "option-unsticky"); classRemove(sticky, "option-unsticky");
classAdd(sticky, "option-sticky"); classAdd(sticky, "option-sticky");
} }
if (stickyCommentHex == commentHex) if (stickyCommentHex === commentHex) {
stickyCommentHex = "none"; stickyCommentHex = "none";
else } else {
stickyCommentHex = commentHex; stickyCommentHex = commentHex;
}
pageUpdate(function(success) { pageUpdate(function(success) {
var sticky = $(ID_STICKY + commentHex); if (success) {
if (stickyCommentHex == commentHex) { var sticky = $(ID_STICKY + commentHex);
classRemove(sticky, "option-sticky"); if (stickyCommentHex === commentHex) {
classAdd(sticky, "option-unsticky"); classRemove(sticky, "option-sticky");
} classAdd(sticky, "option-unsticky");
else { } else {
classRemove(sticky, "option-unsticky"); classRemove(sticky, "option-unsticky");
classAdd(sticky, "option-sticky"); classAdd(sticky, "option-sticky");
}
} }
}); });
} }
@ -1449,10 +1469,11 @@
classAdd(modTools, "mod-tools"); classAdd(modTools, "mod-tools");
if (isLocked) if (isLocked) {
lock.innerHTML = "Unlock Thread"; lock.innerHTML = "Unlock Thread";
else } else {
lock.innerHTML = "Lock Thread"; lock.innerHTML = "Lock Thread";
}
onclick(lock, global.threadLockToggle); onclick(lock, global.threadLockToggle);
@ -1464,10 +1485,11 @@
global.loadCssOverride = function() { global.loadCssOverride = function() {
if (cssOverride === undefined) if (cssOverride === undefined) {
global.allShow(); global.allShow();
else } else {
cssLoad(cssOverride, "window.allShow()"); cssLoad(cssOverride, "window.allShow()");
}
} }
@ -1479,11 +1501,13 @@
attrSet(mainArea, "style", ""); attrSet(mainArea, "style", "");
if (isModerator) if (isModerator) {
attrSet(modTools, "style", ""); attrSet(modTools, "style", "");
}
if (loggedContainer) if (loggedContainer) {
attrSet(loggedContainer, "style", ""); attrSet(loggedContainer, "style", "");
}
attrSet(footer, "style", ""); attrSet(footer, "style", "");
@ -1502,7 +1526,7 @@
} }
global.loginBoxShow = function(signup) { global.loginBoxShow = function() {
var mainArea = $(ID_MAIN_AREA); var mainArea = $(ID_MAIN_AREA);
var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER); var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
@ -1527,8 +1551,9 @@
autoInit = attrGet(scripts[i], "data-auto-init"); autoInit = attrGet(scripts[i], "data-auto-init");
ID_ROOT = attrGet(scripts[i], "data-id-root"); ID_ROOT = attrGet(scripts[i], "data-id-root");
if (ID_ROOT === undefined) if (ID_ROOT === undefined) {
ID_ROOT = "commento"; ID_ROOT = "commento";
}
} }
} }
} }
@ -1559,42 +1584,46 @@
var initted = false; var initted = false;
function init() { function init() {
if (initted) if (initted) {
return; return;
}
initted = true; initted = true;
dataTagsLoad(); dataTagsLoad();
if (autoInit == "true" || autoInit === undefined) if (autoInit === "true" || autoInit === undefined) {
global.main(undefined); global.main(undefined);
else if (autoInit != "false") } else if (autoInit !== "false") {
console.log("[commento] error: invalid value for data-auto-init; allowed values: true, false"); console.log("[commento] error: invalid value for data-auto-init; allowed values: true, false");
}
} }
var readyLoad = function() { var readyLoad = function() {
var readyState = document.readyState; var readyState = document.readyState;
if (readyState == "loading") { if (readyState === "loading") {
// The document is still loading. The div we need to fill might not have // The document is still loading. The div we need to fill might not have
// been parsed yet, so let's wait and retry when the readyState changes. // been parsed yet, so let's wait and retry when the readyState changes.
// If there is more than one state change, we aren't affected because we // If there is more than one state change, we aren't affected because we
// have a double-call protection in init(). // have a double-call protection in init().
document.addEventListener("readystatechange", readyLoad); document.addEventListener("readystatechange", readyLoad);
} } else if (readyState === "interactive") {
else if (readyState == "interactive") {
// The document has been parsed and DOM objects are now accessible. While // The document has been parsed and DOM objects are now accessible. While
// JS, CSS, and images are still loading, we don't need to wait. // JS, CSS, and images are still loading, we don't need to wait.
init(); init();
} } else if (readyState === "complete") {
else if (readyState == "complete") {
// The page has fully loaded (including JS, CSS, and images). From our // The page has fully loaded (including JS, CSS, and images). From our
// point of view, this is practically no different from interactive. // point of view, this is practically no different from interactive.
init(); init();
} }
}; };
readyLoad(); readyLoad();
}(window, document)); }(window, document));

View File

@ -1,4 +1,5 @@
(function (global, document) { (function (global, document) {
"use strict";
// Opens the danger zone. // Opens the danger zone.
global.dangerOpen = function() { global.dangerOpen = function() {
@ -12,8 +13,9 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
global.domainDelete(data.domains[data.cd].domain, function(success) { global.domainDelete(data.domains[data.cd].domain, function(success) {
if (success) if (success) {
document.location = global.origin + '/dashboard'; document.location = global.origin + "/dashboard";
}
}); });
} }

View File

@ -1,4 +1,5 @@
(function (global, document) { (function (global, document) {
"use strict";
// Selects a domain. // Selects a domain.
global.domainSelect = function(domain) { global.domainSelect = function(domain) {
@ -6,14 +7,14 @@
var domains = data.domains; var domains = data.domains;
for (var i = 0; i < domains.length; i++) { for (var i = 0; i < domains.length; i++) {
if (domains[i].domain == domain) { if (domains[i].domain === domain) {
global.vs("frozen", domains[i].state == "frozen"); global.vs("frozen", domains[i].state === "frozen");
domains[i].selected = true; domains[i].selected = true;
data.cd = i; data.cd = i;
data.importedComments = domains[i].importedComments; data.importedComments = domains[i].importedComments;
} } else {
else
domains[i].selected = false; domains[i].selected = false;
}
} }
data.showSettings = true; data.showSettings = true;
@ -28,8 +29,9 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
var domains = data.domains; var domains = data.domains;
for (var i = 0; i < domains.length; i++) for (var i = 0; i < domains.length; i++) {
domains[i].selected = false; domains[i].selected = false;
}
} }
@ -76,8 +78,8 @@
} }
resp.domains = resp.domains.sort(function(a, b) { resp.domains = resp.domains.sort(function(a, b) {
var x = a.creationDate; var y = b.creationDate; var x = a.creationDate; var y = b.creationDate;
return ((x < y) ? -1 : ((x > y) ? 1 : 0)); return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}); });
for (var i = 0; i < resp.domains.length; i++) { for (var i = 0; i < resp.domains.length; i++) {
@ -98,8 +100,9 @@
global.vs("domains", resp.domains); global.vs("domains", resp.domains);
if (callback !== undefined) if (callback !== undefined) {
callback(); callback();
}
}); });
}; };
@ -112,8 +115,9 @@
}; };
global.post(global.origin + "/api/domain/update", json, function(resp) { global.post(global.origin + "/api/domain/update", json, function(resp) {
if (callback !== undefined) if (callback !== undefined) {
callback(resp.success); callback(resp.success);
}
if (!resp.success) { if (!resp.success) {
global.globalErrorShow(resp.message); global.globalErrorShow(resp.message);
@ -136,8 +140,9 @@
return; return;
} }
if (callback !== undefined) if (callback !== undefined) {
callback(resp.success); callback(resp.success);
}
}); });
} }

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Opens the general settings window. // Opens the general settings window.
global.generalOpen = function() { global.generalOpen = function() {

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Opens the import window. // Opens the import window.
global.importOpen = function() { global.importOpen = function() {

View File

@ -1,17 +1,18 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Opens the installation view. // Opens the installation view.
global.installationOpen = function() { global.installationOpen = function() {
var data = global.dashboard.$data; var html = "" +
"<div id=\"commento\"></div>\n" +
var html = '' + "<script src=\"" + window.commentoCdn + "/js/commento.js\"><\/script>\n" +
'<div id="commento"></div>\n' + "";
'<script src="' + window.commentoCdn + '/js/commento.js"><\/script>\n' +
'';
$("#code-div").text(html); $("#code-div").text(html);
$('pre code').each(function(i, block) { $("pre code").each(function(i, block) {
hljs.highlightBlock(block); hljs.highlightBlock(block);
}); });

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Opens the moderatiosn settings window. // Opens the moderatiosn settings window.
global.moderationOpen = function() { global.moderationOpen = function() {
@ -20,13 +23,13 @@
var idx = -1; var idx = -1;
for (var i = 0; i < data.domains[data.cd].moderators.length; i++) { for (var i = 0; i < data.domains[data.cd].moderators.length; i++) {
if (data.domains[data.cd].moderators[i].email == email) { if (data.domains[data.cd].moderators[i].email === email) {
idx = i; idx = i;
break; break;
} }
} }
if (idx == -1) { if (idx === -1) {
data.domains[data.cd].moderators.push({"email": email, "timeAgo": "just now"}); data.domains[data.cd].moderators.push({"email": email, "timeAgo": "just now"});
global.buttonDisable("#new-mod-button"); global.buttonDisable("#new-mod-button");
global.post(global.origin + "/api/domain/moderator/new", json, function(resp) { global.post(global.origin + "/api/domain/moderator/new", json, function(resp) {
@ -41,8 +44,7 @@
$("#new-mod").val(""); $("#new-mod").val("");
$("#new-mod").focus(); $("#new-mod").focus();
}); });
} } else {
else {
global.globalErrorShow("Already a moderator."); global.globalErrorShow("Already a moderator.");
} }
} }
@ -60,13 +62,13 @@
var idx = -1; var idx = -1;
for (var i = 0; i < data.domains[data.cd].moderators.length; i++) { for (var i = 0; i < data.domains[data.cd].moderators.length; i++) {
if (data.domains[data.cd].moderators[i].email == email) { if (data.domains[data.cd].moderators[i].email === email) {
idx = i; idx = i;
break; break;
} }
} }
if (idx != -1) { if (idx !== -1) {
data.domains[data.cd].moderators.splice(idx, 1); data.domains[data.cd].moderators.splice(idx, 1);
global.post(global.origin + "/api/domain/moderator/delete", json, function(resp) { global.post(global.origin + "/api/domain/moderator/delete", json, function(resp) {
if (!resp.success) { if (!resp.success) {

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Sets the vue.js toggle to select and deselect panes visually. // Sets the vue.js toggle to select and deselect panes visually.
function settingSelectCSS(id) { function settingSelectCSS(id) {
@ -6,10 +9,9 @@
var settings = data.settings; var settings = data.settings;
for (var i = 0; i < settings.length; i++) { for (var i = 0; i < settings.length; i++) {
if (settings[i].id == id) { if (settings[i].id === id) {
settings[i].selected = true; settings[i].selected = true;
} } else {
else {
settings[i].selected = false; settings[i].selected = false;
} }
} }
@ -28,8 +30,9 @@
$(".original").addClass("current"); $(".original").addClass("current");
for (var i = 0; i < settings.length; i++) { for (var i = 0; i < settings.length; i++) {
if (id == settings[i].id) if (id === settings[i].id) {
settings[i].open(); settings[i].open();
}
} }
}; };
@ -39,8 +42,9 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
var settings = data.settings; var settings = data.settings;
for (var i = 0; i < settings.length; i++) for (var i = 0; i < settings.length; i++) {
settings[i].selected = false; settings[i].selected = false;
}
} }
} (window.commento, document)); } (window.commento, document));

View File

@ -1,35 +1,41 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
global.numberify = function(x) { global.numberify = function(x) {
if (x == 0) if (x === 0) {
return {"zeros": "000", "num": "", "units": ""} return {"zeros": "000", "num": "", "units": ""}
}
if (x < 10) if (x < 10) {
return {"zeros": "00", "num": x, "units": ""} return {"zeros": "00", "num": x, "units": ""}
}
if (x < 100) if (x < 100) {
return {"zeros": "0", "num": x, "units": ""} return {"zeros": "0", "num": x, "units": ""}
}
if (x < 1000) if (x < 1000) {
return {"zeros": "", "num": x, "units": ""} return {"zeros": "", "num": x, "units": ""}
}
var res; var res;
if (x < 1000000) { if (x < 1000000) {
res = global.numberify((x/1000).toFixed(0)) res = global.numberify((x/1000).toFixed(0))
res.units = "K" res.units = "K"
} } else if (x < 1000000000) {
else if (x < 1000000000) {
res = global.numberify((x/1000000).toFixed(0)) res = global.numberify((x/1000000).toFixed(0))
res.units = "M" res.units = "M"
} } else if (x < 1000000000000) {
else if (x < 1000000000000) {
res = global.numberify((x/1000000000).toFixed(0)) res = global.numberify((x/1000000000).toFixed(0))
res.units = "B" res.units = "B"
} }
if (res.num*10 % 10 == 0) if (res.num*10 % 10 === 0) {
res.num = Math.ceil(res.num); res.num = Math.ceil(res.num);
}
return res; return res;
} }
@ -74,12 +80,12 @@
var labels = new Array(); var labels = new Array();
for (var i = 0; i < views.length; i++) { for (var i = 0; i < views.length; i++) {
if ((views.length-i) % 7 == 0) { if ((views.length-i) % 7 === 0) {
var x = (views.length-i)/7; var x = (views.length-i)/7;
labels.push(x + " week" + (x > 1 ? "s" : "") + " ago"); labels.push(x + " week" + (x > 1 ? "s" : "") + " ago");
} } else {
else
labels.push(""); labels.push("");
}
} }
new Chartist.Line("#views-graph", { new Chartist.Line("#views-graph", {
@ -92,8 +98,12 @@
series: [comments], series: [comments],
}, options); }, options);
data.domains[data.cd].viewsLast30Days = numberify(views.reduce(function(a, b) { return a + b; }, 0)); data.domains[data.cd].viewsLast30Days = numberify(views.reduce(function(a, b) {
data.domains[data.cd].commentsLast30Days = numberify(comments.reduce(function(a, b) { return a + b; }, 0)); return a + b;
}, 0));
data.domains[data.cd].commentsLast30Days = numberify(comments.reduce(function(a, b) {
return a + b;
}, 0));
}); });
} }

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Sets a vue.js field. Short for "vue set". // Sets a vue.js field. Short for "vue set".
function vs(field, value) { function vs(field, value) {
@ -82,8 +85,9 @@
data: reactiveData, data: reactiveData,
}); });
if (callback !== undefined) if (callback !== undefined) {
callback(); callback();
}
}; };
} (window.commento, document)); } (window.commento, document));

View File

@ -1,9 +1,10 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Registers a given ID for a fade out after 5 seconds. // Registers a given ID for a fade out after 5 seconds.
global.registerHide = function(id) { global.registerHide = function(id) {
var el = $(id);
setTimeout(function() { setTimeout(function() {
$(id).fadeOut("fast"); $(id).fadeOut("fast");
}, 5000); }, 5000);

View File

@ -1,12 +1,15 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Talks to the API and sends an reset email. // Talks to the API and sends an reset email.
global.sendResetHex = function() { global.sendResetHex = function() {
var all_ok = global.unfilledMark(["#email"], function(el) { var allOk = global.unfilledMark(["#email"], function(el) {
el.css("border-bottom", "1px solid red"); el.css("border-bottom", "1px solid red");
}); });
if (!all_ok) { if (!allOk) {
global.textSet("#err", "Please make sure all fields are filled."); global.textSet("#err", "Please make sure all fields are filled.");
return; return;
} }

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,7 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Performs a JSON POST request to the given url with the given data and // Performs a JSON POST request to the given url with the given data and
// calls the callback function with the JSON response. // calls the callback function with the JSON response.

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,15 @@
(function (global, document) { (function (global, document) {
"use strict";
(document);
// Shows messages produced from email confirmation attempts. // Shows messages produced from email confirmation attempts.
function displayConfirmedEmail() { function displayConfirmedEmail() {
var confirmed = global.paramGet("confirmed"); var confirmed = global.paramGet("confirmed");
if (confirmed == "true") { if (confirmed === "true") {
$("#msg").html("Successfully confirmed! Login to continue.") $("#msg").html("Successfully confirmed! Login to continue.")
} } else if (confirmed === "false") {
else if (confirmed == "false") {
$("#err").html("That link has expired.") $("#err").html("That link has expired.")
} }
} }
@ -17,7 +19,7 @@
function displayChangedPassword() { function displayChangedPassword() {
var changed = global.paramGet("changed"); var changed = global.paramGet("changed");
if (changed == "true") { if (changed === "true") {
$("#msg").html("Password changed successfully! Login to continue.") $("#msg").html("Password changed successfully! Login to continue.")
} }
} }
@ -26,7 +28,7 @@
function displaySignedUp() { function displaySignedUp() {
var signedUp = global.paramGet("signedUp"); var signedUp = global.paramGet("signedUp");
if (signedUp == "true") { if (signedUp === "true") {
$("#msg").html("Registration successful! Login to continue.") $("#msg").html("Registration successful! Login to continue.")
} }
} }
@ -42,11 +44,11 @@
// Logs the user in and redirects to the dashboard. // Logs the user in and redirects to the dashboard.
global.login = function() { global.login = function() {
var all_ok = global.unfilledMark(["#email", "#password"], function(el) { var allOk = global.unfilledMark(["#email", "#password"], function(el) {
el.css("border-bottom", "1px solid red"); el.css("border-bottom", "1px solid red");
}); });
if (!all_ok) { if (!allOk) {
global.textSet("#err", "Please make sure all fields are filled"); global.textSet("#err", "Please make sure all fields are filled");
return; return;
} }

View File

@ -1,4 +1,5 @@
(function (global, document) { (function (global, document) {
"use strict";
global.logout = function() { global.logout = function() {
global.cookieDelete("commentoOwnerToken"); global.cookieDelete("commentoOwnerToken");

View File

@ -1,16 +1,17 @@
(function (global, document) { (function (global, document) {
"use strict";
global.resetPassword = function() { global.resetPassword = function() {
var all_ok = global.unfilledMark(["#password", "#password2"], function(el) { var allOk = global.unfilledMark(["#password", "#password2"], function(el) {
el.css("border-bottom", "1px solid red"); el.css("border-bottom", "1px solid red");
}); });
if (!all_ok) { if (!allOk) {
global.textSet("#err", "Please make sure all fields are filled."); global.textSet("#err", "Please make sure all fields are filled.");
return; return;
} }
if ($("#password").val() != $("#password2").val()) { if ($("#password").val() !== $("#password2").val()) {
global.textSet("#err", "The two passwords do not match."); global.textSet("#err", "The two passwords do not match.");
return; return;
} }

View File

@ -1,4 +1,5 @@
(function (global, document) { (function (global, document) {
"use strict";
// Get self details. // Get self details.
global.selfGet = function(callback) { global.selfGet = function(callback) {

View File

@ -1,19 +1,20 @@
(function (global, document) { (function (global, document) {
"use strict"
// Signs up the user and redirects to either the login page or the email // Signs up the user and redirects to either the login page or the email
// confirmation, depending on whether or not SMTP is configured in the // confirmation, depending on whether or not SMTP is configured in the
// backend. // backend.
global.signup = function() { global.signup = function() {
if ($("#password").val() != $("#password2").val()) { if ($("#password").val() !== $("#password2").val()) {
global.textSet("#err", "The two passwords don't match"); global.textSet("#err", "The two passwords don't match");
return; return;
} }
var all_ok = global.unfilledMark(["#email", "#name", "#password", "#password2"], function(el) { var allOk = global.unfilledMark(["#email", "#name", "#password", "#password2"], function(el) {
el.css("border-bottom", "1px solid red"); el.css("border-bottom", "1px solid red");
}); });
if (!all_ok) { if (!allOk) {
global.textSet("#err", "Please make sure all fields are filled"); global.textSet("#err", "Please make sure all fields are filled");
return; return;
} }
@ -33,10 +34,11 @@
return; return;
} }
if (resp.confirmEmail) if (resp.confirmEmail) {
document.locatidocumenton = global.origin + "/confirm-email"; document.locatidocumenton = global.origin + "/confirm-email";
else } else {
document.location = global.origin + "/login?signedUp=true"; document.location = global.origin + "/login?signedUp=true";
}
}); });
}; };

View File

@ -1,14 +1,16 @@
(function (global, document) { (function (global, document) {
"use strict";
// Gets a GET parameter in the current URL. // Gets a GET parameter in the current URL.
global.paramGet = function(param) { global.paramGet = function(param) {
var pageURL = decodeURIComponent(window.location.search.substring(1)); var pageURL = decodeURIComponent(window.location.search.substring(1));
var urlVariables = pageURL.split('&'); var urlVariables = pageURL.split("&");
for (var i = 0; i < urlVariables.length; i++) { for (var i = 0; i < urlVariables.length; i++) {
var paramURL = urlVariables[i].split('='); var paramURL = urlVariables[i].split("=");
if (paramURL[0] === param) if (paramURL[0] === param) {
return paramURL[1] === undefined ? true : paramURL[1]; return paramURL[1] === undefined ? true : paramURL[1];
}
} }
return null; return null;
@ -42,16 +44,16 @@
// Given an array of input IDs, this function calls a callback function with // Given an array of input IDs, this function calls a callback function with
// the first unfilled ID. // the first unfilled ID.
global.unfilledMark = function(fields, callback) { global.unfilledMark = function(fields, callback) {
var all_ok = true; var allOk = true;
for (var i = 0; i < fields.length; i++) { for (var i = 0; i < fields.length; i++) {
var el = $(fields[i]); var el = $(fields[i]);
if (el.val() == "") { if (el.val() === "") {
callback(el); callback(el);
} }
} }
return all_ok; return allOk;
} }
@ -59,8 +61,9 @@
global.cookieGet = function(name) { global.cookieGet = function(name) {
var c = "; " + document.cookie; var c = "; " + document.cookie;
var x = c.split("; " + name + "="); var x = c.split("; " + name + "=");
if (x.length == 2) if (x.length === 2) {
return x.pop().split(";").shift(); return x.pop().split(";").shift();
}
}; };
@ -72,8 +75,9 @@
expires = "; expires=" + date.toUTCString(); expires = "; expires=" + date.toUTCString();
var cookieString = name + "=" + value + expires + "; path=/"; var cookieString = name + "=" + value + expires + "; path=/";
if (/^https:\/\//i.test(origin)) if (/^https:\/\//i.test(origin)) {
cookieString += "; secure"; cookieString += "; secure";
}
document.cookie = cookieString; document.cookie = cookieString;
} }
@ -81,7 +85,7 @@
// Deletes a cookie. // Deletes a cookie.
global.cookieDelete = function(name) { global.cookieDelete = function(name) {
document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;'; document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;";
} }
@ -90,29 +94,35 @@
var seconds = Math.floor((new Date() - date) / 1000); var seconds = Math.floor((new Date() - date) / 1000);
var interval = Math.floor(seconds / 31536000); var interval = Math.floor(seconds / 31536000);
if (interval > 1) if (interval > 1) {
return interval + " years ago"; return interval + " years ago";
}
interval = Math.floor(seconds / 2592000); interval = Math.floor(seconds / 2592000);
if (interval > 1) if (interval > 1) {
return interval + " months ago"; return interval + " months ago";
}
interval = Math.floor(seconds / 86400); interval = Math.floor(seconds / 86400);
if (interval > 1) if (interval > 1) {
return interval + " days ago"; return interval + " days ago";
}
interval = Math.floor(seconds / 3600); interval = Math.floor(seconds / 3600);
if (interval > 1) if (interval > 1) {
return interval + " hours ago"; return interval + " hours ago";
}
interval = Math.floor(seconds / 60); interval = Math.floor(seconds / 60);
if (interval > 1) if (interval > 1) {
return interval + " minutes ago"; return interval + " minutes ago";
}
if (seconds > 5) if (seconds > 5) {
return Math.floor(seconds) + " seconds ago"; return Math.floor(seconds) + " seconds ago";
else } else {
return "just now"; return "just now";
}
} }
} (window.commento, document)); } (window.commento, document));

File diff suppressed because one or more lines are too long

33
frontend/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "commento-ce",
"version": "1.0.0",
"main": "index.js",
"repository": "git@gitlab.com:commento/commento-ce.git",
"author": "Adhityaa <c.adhityaa@gmail.com> Anton Linevych anton@linevich.net",
"license": "MIT",
"private": true,
"devDependencies": {
"chartist": "0.11.0",
"fixmyjs": "2.0.0",
"gulp": "3.9.1",
"gulp-clean-css": "3.9.4",
"gulp-concat": "2.6.1",
"gulp-eslint": "5.0.0",
"gulp-html-minifier": "0.1.8",
"gulp-rename": "1.3.0",
"gulp-sass": "4.0.1",
"gulp-sourcemaps": "2.6.4",
"gulp-uglify": "3.0.0",
"highlightjs": "9.10.0",
"html-minifier": "3.5.7",
"jquery": "3.2.1",
"natives": "^1.1.6",
"normalize-scss": "7.0.1",
"sass": "1.5.1",
"uglify-js": "3.4.1",
"vue": "2.5.16"
},
"dependencies": {
"eslint": "^5.10.0"
}
}

4501
frontend/yarn.lock Normal file

File diff suppressed because it is too large Load Diff