frontend: add commento embed js and scss

This commit is contained in:
Adhityaa 2018-06-07 13:30:53 +05:30
parent 8f2d7e58a5
commit a6c6598c4a
8 changed files with 1492 additions and 1 deletions

View File

@ -1,7 +1,7 @@
SHELL = bash
# list of JS files to be built
JS_BUILD = jquery.js vue.js highlight.js chartist.js login.js signup.js dashboard.js logout.js
JS_BUILD = jquery.js vue.js highlight.js chartist.js login.js signup.js dashboard.js logout.js commento.js
jquery.js = jquery.js
vue.js = vue.js
@ -11,6 +11,7 @@ login.js = utils.js http.js auth-common.js login.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

975
frontend/js/commento.js Normal file
View File

@ -0,0 +1,975 @@
(function(global, document) {
'use strict';
// Do not use other files like utils.js and http.js in the Makefile to build
// commento.js for the following reasons:
// - We don't use jQuery in the actual JavaScript payload because we need
// to be lightweight.
// - They pollute the global/window namespace (with global.post, etc.).
// That's NOT fine when we expect them source our JavaScript. For example,
// the user may have their own window.post defined. We don't want to
// override that.
var origin = global.commento_origin;
var cdn = global.commento_cdn;
var root = null;
var isAuthenticated = false;
var comments = [];
var commenters = [];
var requireIdentification = true;
var requireModeration = true;
var isModerator = false;
var isFrozen = false;
var chosenAnonymous = false;
var shownSubmitButton = {"root": false};
var shownReply = {};
function $(id) {
return document.getElementById(id);
}
function dataGet(el, key) {
return el.dataset[key];
}
function dataSet(el, key, data) {
el.dataset[key] = data;
}
function append(root, el) {
root.appendChild(el);
}
function prepend(root, el) {
root.prepend(el);
}
function classAdd(el, cls) {
el.classList.add("commento-" + cls);
}
function classRemove(el, cls) {
el.classList.remove("commento-" + cls);
}
function create(el) {
return document.createElement(el);
}
function remove(el) {
el.parentNode.removeChild(el);
}
function attr(node, a, value) {
node.setAttribute(a, value);
}
function post(url, data, callback) {
var xmlDoc = new XMLHttpRequest();
xmlDoc.open("POST", url, true);
xmlDoc.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlDoc.onload = function() {
callback(JSON.parse(xmlDoc.response));
};
xmlDoc.send(JSON.stringify(data));
}
function get(url, callback) {
var xmlDoc = new XMLHttpRequest();
xmlDoc.open('GET', url, true);
xmlDoc.onload = function() {
callback(JSON.parse(xmlDoc.response));
};
xmlDoc.send(null);
}
function call(callback) {
if (typeof(callback) == "function")
callback();
}
function rootSpinnerShow() {
var spinner = create("div");
classAdd(spinner, "loading");
append(root, spinner);
}
function cookieGet(name) {
var c = "; " + document.cookie;
var x = c.split("; " + name + "=");
if (x.length == 2)
return x.pop().split(";").shift();
}
function cookieSet(name, value) {
var expires = "";
var date = new Date();
date.setTime(date.getTime() + (365*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
document.cookie = name + "=" + value + expires + "; path=/";
}
function sessionGet() {
var session = cookieGet("session");
if (session === undefined)
return "anonymous";
return session;
}
global.logout = function() {
cookieSet("session", "anonymous");
refreshAll();
}
function selfGet(callback) {
var session = sessionGet();
if (session == "anonymous") {
isAuthenticated = false;
call(callback);
return;
}
var json = {
session: sessionGet(),
};
post(origin + "/api/commenter/self", json, function(resp) {
console.log(resp);
if (!resp.success) {
cookieSet("session", "anonymous");
call(callback);
return;
}
var loggedContainer = create("div");
var loggedInAs = create("div");
var name = create("a");
var photo = create("img");
var logout = create("div");
classAdd(loggedContainer, "logged-container");
classAdd(loggedInAs, "logged-in-as");
classAdd(name, "name");
classAdd(photo, "photo");
classAdd(logout, "logout");
name.innerText = resp.commenter.name;
logout.innerText = "Logout";
attr(name, "href", resp.commenter.link);
if (resp.commenter.provider == "google") {
attr(photo, "src", resp.commenter.photo + "?sz=50");
} else {
attr(photo, "src", resp.commenter.photo);
}
attr(logout, "onclick", "logout()");
append(loggedInAs, photo);
append(loggedInAs, name);
append(loggedContainer, loggedInAs);
append(loggedContainer, logout);
append(root, loggedContainer);
isAuthenticated = true;
call(callback);
});
}
function cssLoad(file) {
var link = create("link");
var head = document.getElementsByTagName('head')[0];
link.type = "text/css";
attr(link, "href", file);
attr(link, "rel", "stylesheet");
append(head, link);
}
function jsLoad(file, ready) {
var script = document.createElement("script");
var loaded = false;
script.type = "application/javascript";
script.src = file;
script.async = true;
script.onreadysessionchange = script.onload = function() {
if (!loaded &&
(!this.readySession ||
this.readySession === "loaded" ||
this.readySession === "complete"))
{
ready();
}
loaded = true;
script.onload = script.onreadysessionchange = null;
};
append(document.body, script);
}
function footerLoad() {
var footer = create("div");
var aContainer = create("div");
var a = create("a");
var img = create("img");
var text = create("span");
classAdd(footer, "footer");
classAdd(aContainer, "logo-container");
classAdd(a, "logo");
classAdd(img, "logo-svg");
classAdd(text, "logo-text");
attr(a, "href", "https://commento.io");
attr(a, "target", "_blank");
attr(img, "src", cdn + "/images/logo.svg");
text.innerText = "Powered by Commento";
append(a, img);
append(a, text);
append(aContainer, a);
append(footer, aContainer);
append(root, footer);
}
function commentsGet(callback) {
var json = {
session: sessionGet(),
domain: location.host,
path: location.pathname,
};
post(origin + "/api/comment/list", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return;
}
requireModeration = resp.requireModeration;
requireIdentification = resp.requireIdentification;
isModerator = resp.isModerator;
isFrozen = resp.isFrozen;
comments = resp.comments;
commenters = resp.commenters;
cssLoad(cdn + "/css/commento.css");
call(callback);
});
}
function errorShow(text) {
var el = $(ID_ERROR);
el.innerText = text;
attr(el, "style", "display: block;");
}
function createErrorElement() {
var el = create("div");
el.id = ID_ERROR;
classAdd(el, "error-box");
attr(el, "style", "display: none;");
append(root, el);
}
function autoExpander(el) {
return function() {
el.style.height = "";
el.style.height = Math.min(Math.max(el.scrollHeight, 75), 400) + "px";
}
};
function isMobile() {
var mobile = false;
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4)))
mobile = true;
return mobile;
}
function textareaCreate(id) {
var textareaSuperContainer = create("div");
var textareaContainer = create("div");
var textarea = create("textarea");
textareaSuperContainer.id = ID_SUPER_CONTAINER + id;
textareaContainer.id = ID_TEXTAREA_CONTAINER + id;
textarea.id = ID_TEXTAREA + id;
classAdd(textareaContainer, "textarea-container");
if (!isAuthenticated && !chosenAnonymous) {
var buttonsContainer = create("div");
if (!isMobile()) {
classAdd(buttonsContainer, "buttons-container");
classAdd(textarea, "blurred-textarea");
} else {
classAdd(textarea, "hidden");
classAdd(buttonsContainer, "mobile-buttons-container");
classAdd(buttonsContainer, "opaque");
}
var oauths = ["google"];
if (!requireIdentification)
oauths.push("anonymous");
for (var i = 0; i < oauths.length; i++) {
var oauthButton = create("button");
classAdd(oauthButton, "button");
classAdd(oauthButton, oauths[i] + "-button");
if (isMobile())
classAdd(oauthButton, "opaque");
attr(oauthButton, "onclick", "commentoAuth('" + oauths[i] + "', '" + id + "')");
oauthButton.innerText = oauths[i][0].toUpperCase() + oauths[i].slice(1);
append(buttonsContainer, oauthButton);
}
attr(textarea, "disabled", true);
append(textareaContainer, buttonsContainer);
}
attr(textarea, "placeholder", "Join the discussion!");
attr(textarea, "onclick", "showSubmitButton('" + id + "')");
textarea.oninput = autoExpander(textarea);
append(textareaContainer, textarea);
append(textareaSuperContainer, textareaContainer);
return textareaSuperContainer;
}
function rootCreate(callback) {
var commentsArea = create("div");
commentsArea.id = ID_COMMENTS_AREA;
classAdd(commentsArea, "comments");
commentsArea.innerHTML = "";
append(root, textareaCreate("root"));
append(root, commentsArea);
call(callback);
}
function messageCreate(text) {
var msg = create("div");
classAdd(msg, "moderation-notice");
msg.innerText = text;
return msg;
}
global.postComment = function(id) {
var textarea = $(ID_TEXTAREA + id);
var comment = textarea.value;
if (comment == "") {
classAdd(textarea, "red-border");
return;
}
else {
classRemove(textarea, "red-border");
}
var json = {
"session": sessionGet(),
"domain": location.host,
"path": location.pathname,
"parentHex": id,
"markdown": comment,
};
post(origin + "/api/comment/new", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return;
}
$(ID_TEXTAREA + id).value = "";
commentsGet(function() {
$(ID_COMMENTS_AREA).innerHTML = "";
commentsRender();
if (requireModeration && !isModerator) {
if (id == "root") {
var body = $(ID_SUPER_CONTAINER + id);
prepend(body, messageCreate("Your comment is under moderation."));
} else {
var body = $(ID_BODY + id);
append(body, messageCreate("Your comment is under moderation."));
}
}
});
});
}
function colorGet(name) {
var colors = [
// some visually distincy
"#35b2de", // some kind of teal/cyan
"#62cd0a", // fresh lemon green
"#383838", // shade of gray
"#e4a90f", // comfy yellow
"#f80707", // sharp red
"#f0479c", // bright pink
];
var total = 0;
for (var i = 0; i < name.length; i++)
total += name.charCodeAt(i);
var color = colors[total % colors.length];
return color;
}
function timeDifference(current, previous) { // thanks stackoverflow
var msJustNow = 5000;
var msPerMinute = 60000;
var msPerHour = 3600000;
var msPerDay = 86400000;
var msPerMonth = 2592000000;
var msPerYear = 946080000000;
var elapsed = current - previous;
if (elapsed < msJustNow) {
return 'just now';
}
else if (elapsed < msPerMinute) {
return Math.round(elapsed/1000) + ' seconds ago';
}
else if (elapsed < msPerHour) {
return Math.round(elapsed/msPerMinute) + ' minutes ago';
}
else if (elapsed < msPerDay ) {
return Math.round(elapsed/msPerHour ) + ' hours ago';
}
else if (elapsed < msPerMonth) {
return Math.round(elapsed/msPerDay) + ' days ago';
}
else if (elapsed < msPerYear) {
return Math.round(elapsed/msPerMonth) + ' months ago';
}
else {
return Math.round(elapsed/msPerYear ) + ' years ago';
}
}
function scorify(score) {
if (score != 1)
return score + " points";
else
return score + " point";
}
var ID_ERROR = "commento-error";
var ID_COMMENTS_AREA = "commento-comments-area";
var ID_SUPER_CONTAINER = "commento-textarea-super-container-";
var ID_TEXTAREA_CONTAINER = "commento-textarea-container-";
var ID_TEXTAREA = "commento-textarea-";
var ID_CARD = "commento-comment-card-";
var ID_BODY = "commento-comment-body-";
var ID_SUBTITLE = "commento-comment-subtitle-";
var ID_SCORE = "commento-comment-score-";
var ID_OPTIONS = "commento-comment-options-";
var ID_EDIT = "commento-comment-edit-";
var ID_REPLY = "commento-comment-reply-";
var ID_COLLAPSE = "commento-comment-collapse-";
var ID_UPVOTE = "commento-comment-upvote-";
var ID_DOWNVOTE = "commento-comment-downvote-";
var ID_APPROVE = "commento-comment-approve-";
var ID_REMOVE = "commento-comment-remove-";
var ID_CONTENTS = "commento-comment-contents-";
var ID_SUBMIT_BUTTON = "commento-submit-button-";
function commentsRecurse(parentMap, parentHex) {
var cur = parentMap[parentHex];
if (!cur || !cur.length) {
return null;
}
var cards = create("div");
cur.forEach(function(comment) {
var commenter = commenters[comment.commenterHex];
var avatar;
var card = create("div");
var header = create("div");
var subtitle = create("div");
var score = create("div");
var body = create("div");
var options = create("div");
var edit = create("button");
var reply = create("button");
var collapse = create("button");
var upvote = create("div");
var downvote = create("div");
var approve = create("button");
var remove = create("button");
var children = commentsRecurse(parentMap, comment.commentHex);
var contents = create("div");
var color = colorGet(commenter.name);
var name;
if (commenter.link != "undefined")
name = create("a");
else
name = create("div");
card.id = ID_CARD + comment.commentHex;
body.id = ID_BODY + comment.commentHex;
subtitle.id = ID_SUBTITLE + comment.commentHex;
score.id = ID_SCORE + comment.commentHex;
options.id = ID_OPTIONS + comment.commentHex;
edit.id = ID_EDIT + comment.commentHex;
reply.id = ID_REPLY + comment.commentHex;
collapse.id = ID_COLLAPSE + comment.commentHex;
upvote.id = ID_UPVOTE + comment.commentHex;
downvote.id = ID_DOWNVOTE + comment.commentHex;
approve.id = ID_APPROVE + comment.commentHex;
remove.id = ID_REMOVE + comment.commentHex;
contents.id = ID_CONTENTS + comment.commentHex;
collapse.title = "Collapse";
upvote.title = "Upvote";
downvote.title = "Downvote";
edit.title = "Edit";
reply.title = "Reply";
approve.title = "Approve";
remove.title = "Remove";
card.style["borderLeft"] = "2px solid " + color;
name.innerText = commenter.name;
body.innerHTML = comment.html;
subtitle.innerHTML = timeDifference((new Date()).getTime(), Date.parse(comment.creationDate));
score.innerText = scorify(comment.score);
if (commenter.photo == "undefined") {
avatar = create("div");
avatar.style["background"] = color;
avatar.style["boxShadow"] = "0px 0px 0px 2px " + color;
avatar.innerHTML = commenter.name[0].toUpperCase();
classAdd(avatar, "avatar");
} else {
avatar = create("img");
if (commenter.provider == "google") {
attr(avatar, "src", commenter.photo + "?sz=50");
} else {
attr(avatar, "src", commenter.photo);
}
classAdd(avatar, "avatar-img");
}
classAdd(card, "card");
if (isModerator && comment.state == "unapproved")
classAdd(card, "dark-card");
classAdd(header, "header");
classAdd(name, "name");
classAdd(subtitle, "subtitle");
classAdd(score, "score");
classAdd(body, "body");
classAdd(options, "options");
classAdd(edit, "option-button");
classAdd(edit, "option-edit");
classAdd(reply, "option-button");
classAdd(reply, "option-reply");
classAdd(collapse, "option-button");
classAdd(collapse, "option-collapse");
classAdd(upvote, "option-button");
classAdd(upvote, "option-upvote");
classAdd(downvote, "option-button");
classAdd(downvote, "option-downvote");
classAdd(approve, "option-button");
classAdd(approve, "option-approve");
classAdd(remove, "option-button");
classAdd(remove, "option-remove");
if (isAuthenticated) {
if (comment.direction > 0)
classAdd(upvote, "upvoted");
else if (comment.direction < 0)
classAdd(downvote, "downvoted");
}
attr(edit, "onclick", "startEdit('" + comment.commentHex + "')");
attr(reply, "onclick", "replyShow('" + comment.commentHex + "')");
attr(collapse, "onclick", "commentCollapse('" + comment.commentHex + "')");
attr(approve, "onclick", "commentApprove('" + comment.commentHex + "')");
attr(remove, "onclick", "commentDelete('" + comment.commentHex + "')");
if (isAuthenticated) {
if (comment.direction > 0) {
attr(upvote, "onclick", "vote('" + comment.commentHex + "', 1, 0)");
attr(downvote, "onclick", "vote('" + comment.commentHex + "', 1, -1)");
}
else if (comment.direction < 0) {
attr(upvote, "onclick", "vote('" + comment.commentHex + "', -1, 1)");
attr(downvote, "onclick", "vote('" + comment.commentHex + "', -1, 0)");
}
else {
attr(upvote, "onclick", "vote('" + comment.commentHex + "', 0, 1)");
attr(downvote, "onclick", "vote('" + comment.commentHex + "', 0, -1)");
}
} else if (!chosenAnonymous) {
attr(upvote, "onclick", "replyShow('" + comment.commentHex + "')");
}
if (isAuthenticated) {
if (isModerator) {
if (comment.state == "unapproved")
attr(options, "style", "width: 192px;");
else
attr(options, "style", "width: 160px;");
}
else
attr(options, "style", "width: 128px;");
}
else
attr(options, "style", "width: 32px;");
if (commenter.link != "undefined")
attr(name, "href", commenter.link);
if (false) // replace when edit is implemented
append(options, edit);
if (isAuthenticated) {
append(options, upvote);
append(options, downvote);
append(options, reply);
}
if (isModerator) {
if (comment.state == "unapproved")
append(options, approve);
append(options, remove);
}
append(options, collapse);
append(subtitle, score);
append(header, options);
append(header, avatar);
append(header, name);
append(header, subtitle);
append(contents, body);
if (children) {
classAdd(children, "body");
append(contents, children);
}
append(card, header);
append(card, contents);
append(cards, card);
shownSubmitButton[comment.commentHex] = false;
});
return cards;
}
global.commentApprove = function(commentHex) {
var json = {
"session": sessionGet(),
"commentHex": commentHex,
}
post(origin + "/api/comment/approve", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return
}
var card = $(ID_CARD + commentHex);
var options = $(ID_OPTIONS + commentHex);
var tick = $(ID_APPROVE + commentHex);
classRemove(card, "dark-card");
attr(options, "style", "width: 160px;");
remove(tick);
});
}
global.commentDelete = function(commentHex) {
var json = {
"session": sessionGet(),
"commentHex": commentHex,
}
post(origin + "/api/comment/delete", json, function(resp) {
if (!resp.success) {
errorShow(resp.message);
return
}
var card = $(ID_CARD + commentHex);
remove(card);
});
}
function nameWidthFix() {
var els = document.getElementsByClassName("commento-name");
for (var i = 0; i < els.length; i++)
attr(els[i], "style", "max-width: " + (els[i].getBoundingClientRect()["width"] + 20) + "px;")
}
global.vote = function(commentHex, oldVote, direction) {
var upvote = $(ID_UPVOTE + commentHex);
var downvote = $(ID_DOWNVOTE + commentHex);
var score = $(ID_SCORE + commentHex);
var json = {
"session": sessionGet(),
"commentHex": commentHex,
"direction": direction,
};
if (direction > 0) {
attr(upvote, "onclick", "vote('" + commentHex + "', 1, 0)");
attr(downvote, "onclick", "vote('" + commentHex + "', 1, -1)");
}
else if (direction < 0) {
attr(upvote, "onclick", "vote('" + commentHex + "', -1, 1)");
attr(downvote, "onclick", "vote('" + commentHex + "', -1, 0)");
}
else {
attr(upvote, "onclick", "vote('" + commentHex + "', 0, 1)");
attr(downvote, "onclick", "vote('" + commentHex + "', 0, -1)");
}
classRemove(upvote, "upvoted");
classRemove(downvote, "downvoted");
if (direction > 0)
classAdd(upvote, "upvoted");
else if (direction < 0)
classAdd(downvote, "downvoted");
score.innerText = scorify(parseInt(score.innerText.replace(/[^\d-.]/g, "")) + direction - oldVote);
post(origin + "/api/comment/vote", json, function(resp) {});
}
global.replyShow = function(id) {
if (id in shownReply && shownReply[id])
return;
var body = $(ID_BODY + id);
append(body, textareaCreate(id));
shownReply[id] = true;
var replyButton = $(ID_REPLY + id);
classRemove(replyButton, "option-reply");
classAdd(replyButton, "option-cancel");
replyButton.title = "Cancel reply";
attr(replyButton, "onclick", "replyCollapse('" + id + "')")
};
global.replyCollapse = function(id) {
var replyButton = $(ID_REPLY + id);
var el = $(ID_SUPER_CONTAINER + id);
el.remove();
shownReply[id] = false;
shownSubmitButton[id] = false;
classAdd(replyButton, "option-reply");
classRemove(replyButton, "option-cancel");
replyButton.title = "Reply to this comment";
attr(replyButton, "onclick", "replyShow('" + id + "')")
}
global.commentCollapse = function(id) {
var contents = $(ID_CONTENTS + id);
var button = $(ID_COLLAPSE + id);
classAdd(contents, "hidden");
classRemove(button, "option-collapse");
classAdd(button, "option-uncollapse");
button.title = "Expand";
attr(button, "onclick", "commentUncollapse('" + id + "')");
}
global.commentUncollapse = function(id) {
var contents = $(ID_CONTENTS + id);
var button = $(ID_COLLAPSE + id);
classRemove(contents, "hidden");
classRemove(button, "option-uncollapse");
classAdd(button, "option-collapse");
button.title = "Collapse";
attr(button, "onclick", "commentCollapse('" + id + "')");
}
function commentsRender() {
var parentMap = {};
var parentHex;
var commentsArea = $(ID_COMMENTS_AREA);
comments.forEach(function(comment) {
parentHex = comment.parentHex;
if (!(parentHex in parentMap)) {
parentMap[parentHex] = [];
}
parentMap[parentHex].push(comment);
});
var cards = commentsRecurse(parentMap, "root");
if (cards) {
append(commentsArea, cards);
}
}
global.showSubmitButton = function(id) {
if (id in shownSubmitButton && shownSubmitButton[id])
return;
shownSubmitButton[id] = true;
var el = $(ID_SUPER_CONTAINER + id);
var submit = create("button");
submit.id = ID_SUBMIT_BUTTON + id;
submit.innerText = "Add Comment";
classAdd(submit, "button");
classAdd(submit, "submit-button");
classAdd(el, "button-margin");
attr(submit, "onclick", "postComment('" + id + "')");
append(el, submit);
}
global.commentoAuth = function(provider, id) {
if (provider == "anonymous") {
cookieSet("session", "anonymous");
chosenAnonymous = true;
refreshAll(function() {
if (id != "root")
global.replyShow(id);
$(ID_TEXTAREA + id).click();
$(ID_TEXTAREA + id).focus();
});
return;
}
var popup = window.open("", "_blank");
get(origin + "/api/commenter/session/new", function(resp) {
if (!resp.success) {
errorShow(resp.message);
return;
}
cookieSet("session", resp.session);
popup.location = origin + "/api/oauth/" + provider + "/redirect?session=" + resp.session;
var interval = setInterval(function() {
if (popup.closed) {
refreshAll(function() {
if (id != "root")
global.replyShow(id);
$(ID_TEXTAREA + id).click();
$(ID_TEXTAREA + id).focus();
});
clearInterval(interval);
}
}, 250);
});
}
function refreshAll(callback) {
$("commento").innerHTML = "";
shownSubmitButton = {"root": false};
shownReply = {};
main(callback);
}
function main(callback) {
root = $("commento");
createErrorElement();
selfGet(function() {
commentsGet(function() {
rootCreate(function() {
commentsRender();
nameWidthFix();
footerLoad();
attr(root, "style", "");
call(callback);
});
});
});
}
document.addEventListener("DOMContentLoaded", main);
}(window, document));

View File

@ -0,0 +1,99 @@
@import "colors-main.scss";
@mixin mask-image($image) {
-webkit-mask-image: url($image);
mask-image: url($image);
}
.commento-option-button {
border: none;
cursor: pointer;
outline: none;
padding: 0px;
position: absolute;
top: 0px;
z-index: 3;
background: $gray-4;
}
.commento-option-reply {
height: 20px;
width: 20px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="matrix(1.7358 0 0 1.7335 4.2642 15.217)"><path d="m10 2.5 1e-6 -4.4421-9 7.4421 9 6.5759-1e-6 -4.6759c6.4194-1.4839 11.739 2.1762 11.739 2.1762s-2.0774-6.5475-11.739-7.0762z" fill="#abbac4"/></g></svg>');
margin: 9px 3px 9px 3px;
right: 32px;
}
.commento-option-cancel {
height: 13px;
width: 13px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 129 129" version="1.1" viewBox="0 0 129 129" xmlns="http://www.w3.org/2000/svg"><path d="m7.6 121.4c0.8 0.8 1.8 1.2 2.9 1.2s2.1-0.4 2.9-1.2l51.1-51.1 51.1 51.1c0.8 0.8 1.8 1.2 2.9 1.2 1 0 2.1-0.4 2.9-1.2 1.6-1.6 1.6-4.2 0-5.8l-51.1-51.1 51.1-51.1c1.6-1.6 1.6-4.2 0-5.8s-4.2-1.6-5.8 0l-51.1 51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8 0s-1.6 4.2 0 5.8l51.1 51.1-51.1 51.1c-1.6 1.6-1.6 4.2 0 5.8z" fill="#abbac4"/></svg>');
margin: 12px 6px 12px 6px;
right: 32px;
background: $gray-5;
}
.commento-option-collapse {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 42 42" version="1.1" viewBox="0 0 42 42" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><polygon points="42 20 0 20 0 22 20 22 22 22 42 22" fill="#1e2127"/></svg>');
margin: 12px 6px 12px 6px;
right: 0px;
background: $gray-7;
}
.commento-option-uncollapse {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 42 42" version="1.1" viewBox="0 0 42 42" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><polygon points="42 20 22 20 22 0 20 0 20 20 0 20 0 22 20 22 20 42 22 42 22 22 42 22" fill="#1e2127"/></svg>');
margin: 12px 6px 12px 6px;
right: 0px;
background: $gray-7;
}
.commento-option-upvote,
.commento-option-downvote {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 284.929 284.929" version="1.1" viewBox="0 0 284.93 284.93" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"> <path d="m282.08 195.28-133.05-133.04c-1.901-1.903-4.088-2.856-6.562-2.856s-4.665 0.953-6.567 2.856l-133.04 133.04c-1.906 1.906-2.856 4.093-2.856 6.568 0 2.474 0.953 4.664 2.856 6.566l14.272 14.271c1.903 1.903 4.093 2.854 6.567 2.854s4.664-0.951 6.567-2.854l112.2-112.2 112.21 112.21c1.902 1.903 4.093 2.848 6.563 2.848 2.478 0 4.668-0.951 6.57-2.848l14.274-14.277c1.902-1.902 2.847-4.093 2.847-6.566 1e-3 -2.476-0.944-4.666-2.846-6.569z" fill="#abbac4"/></svg>');
margin: 12px 6px 12px 6px;
}
.commento-option-upvote {
right: 96px;
}
.commento-option-downvote {
transform: rotate(180deg);
right: 64px;
}
.commento-upvoted {
background: $orange-7;
}
.commento-downvoted {
background: $indigo-6;
}
.commento-option-remove {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 59 59" version="1.1" viewBox="0 0 59 59" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g fill="#1e2127"> <path d="m29.5 51c0.552 0 1-0.447 1-1v-33c0-0.553-0.448-1-1-1s-1 0.447-1 1v33c0 0.553 0.448 1 1 1z"/> <path d="m19.5 51c0.552 0 1-0.447 1-1v-33c0-0.553-0.448-1-1-1s-1 0.447-1 1v33c0 0.553 0.448 1 1 1z"/> <path d="m39.5 51c0.552 0 1-0.447 1-1v-33c0-0.553-0.448-1-1-1s-1 0.447-1 1v33c0 0.553 0.448 1 1 1z"/> <path d="M52.5,6H38.456c-0.11-1.25-0.495-3.358-1.813-4.711C35.809,0.434,34.751,0,33.499,0H23.5c-1.252,0-2.31,0.434-3.144,1.289 C19.038,2.642,18.653,4.75,18.543,6H6.5c-0.552,0-1,0.447-1,1s0.448,1,1,1h2.041l1.915,46.021C10.493,55.743,11.565,59,15.364,59 h28.272c3.799,0,4.871-3.257,4.907-4.958L50.459,8H52.5c0.552,0,1-0.447,1-1S53.052,6,52.5,6z M21.792,2.681 C22.24,2.223,22.799,2,23.5,2h9.999c0.701,0,1.26,0.223,1.708,0.681c0.805,0.823,1.128,2.271,1.24,3.319H20.553 C20.665,4.952,20.988,3.504,21.792,2.681z M46.544,53.979C46.538,54.288,46.4,57,43.636,57H15.364 c-2.734,0-2.898-2.717-2.909-3.042L10.542,8h37.915L46.544,53.979z"/></g></svg>');
margin: 12px 6px 12px 6px;
right: 128px;
background: $red-8;
}
.commento-option-approve {
height: 14px;
width: 14px;
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 26 26" version="1.1" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="m0.3 14c-0.2-0.2-0.3-0.5-0.3-0.7s0.1-0.5 0.3-0.7l1.4-1.4c0.4-0.4 1-0.4 1.4 0l0.1 0.1 5.5 5.9c0.2 0.2 0.5 0.2 0.7 0l13.4-13.9h0.1v-8.8818e-16c0.4-0.4 1-0.4 1.4 0l1.4 1.4c0.4 0.4 0.4 1 0 1.4l-16 16.6c-0.2 0.2-0.4 0.3-0.7 0.3s-0.5-0.1-0.7-0.3l-7.8-8.4-0.2-0.3z" fill="#1e2127"/></svg>');
margin: 12px 6px 12px 6px;
right: 160px;
background: $green-7;
}
.commento-option-button:focus {
outline: none;
}

View File

@ -0,0 +1,187 @@
@import "colors-main.scss";
textarea,
input[type=text] {
background: #ffffff;
border: 1px solid rgba(50, 50, 93, .1);
border-radius: 3px;
color: #525f7f;
}
input[type=text]::placeholder {
color: #cacaca;
}
textarea::placeholder {
color: #aaa;
font-size: 22px;
padding-top: 13px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
textarea {
display: inline-block;
font-family: "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
padding: 8px;
outline: none;
overflow: auto;
min-height: 75px;
width: 100%;
}
.commento-red-border {
border: 1px solid $red-7;
}
.commento-textarea-container {
display: flex;
justify-content: center;
align-items: center;
&:hover {
.commento-button, .commento-buttons-container::before {
opacity: 1;
transform: translate(0px,-3px);
}
.commento-submit-button {
transform: none;
}
.commento-blurred-textarea {
opacity: .7;
transform: scale(.95);
filter: blur(4px);
}
@media only screen and (max-width: 550px) {
.commento-buttons-container::before {
display: none;
}
}
}
}
.commento-buttons-container {
display: inline-block;
position: absolute;
z-index: 1;
opacity: 1;
}
.commento-mobile-buttons-container::before,
.commento-buttons-container::before {
content: "Authenticate with";
display: inline-flex;
justify-content: center;
align-items: center;
font-weight: bold;
line-height: 24px;
font-size: 14px;
padding: 6px;
color: $gray-8;
transition: all 0.3s;
opacity: 0;
outline: none;
}
.commento-mobile-buttons-container::before {
content: "To join the discussion, authenticate with";;
display: block;
text-align: center;
}
@media only screen and (max-width: 550px) {
.commento-buttons-container::before {
display: none;
}
}
.commento-button {
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
cursor: pointer;
font-weight: bold;
line-height: 24px;
font-size: 14px;
padding: 6px;
padding-left: 8px;
padding-right: 8px;
box-shadow: 0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08);
border: 1px solid transparent;
border-radius: 3px;
color: #fff;
width: 100px;
margin-left: 5px;
margin-left: 5px;
opacity: 0;
}
.commento-opaque {
opacity: 1;
}
.commento-opaque::before {
opacity: 1;
}
.commento-google-button {
transition: all 0.3s;
background: #dd4b39;
}
.commento-github-button {
transition: all 0.3s;
background: #000000;
}
.commento-anonymous-button {
transition: all 0.3s;
background: #096fa6;
}
.commento-blurred-textarea {
list-style: none;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
justify-content: space-between;
transition: 0.3s all;
will-change: transform;
}
.commento-approve-button,
.commento-delete-button,
.commento-submit-button {
margin-top: 10px;
opacity: 1;
font-size: 14px;
width: -moz-fit-content;
width: -webkit-fit-content;
width: -ms-fit-content;
width: -o-fit-content;
width: fit-content;
}
.commento-submit-button {
float: right;
background: $indigo-7;
}
.commento-approve-button {
background: $green-7;
}
.commento-delete-button {
background: $red-7;
}
.commento-button-margin {
padding-bottom: 60px;
}

View File

@ -0,0 +1,37 @@
@import "colors-main.scss";
.commento-logged-container {
width: 100%;
text-align: left;
margin-bottom: 16px;
position: relative;
.commento-logout {
cursor: pointer;
position: absolute;
top: 6px;
right: 16px;
color: $gray-5;
}
.commento-logged-in-as {
position: relative;
.commento-name {
color: $gray-8;
border: none;
font-weight: bold;
position: absolute;
top: 6px;
left: 64px;
}
.commento-photo {
width: 32px;
height: 32px;
border-radius: 50%;
margin-left: 16px;
margin-right: 16px;
}
}
}

View File

@ -0,0 +1,38 @@
@import "colors-main.scss";
.commento-footer {
margin: 36px 0px 12px 0px;
border-top: 1px solid $gray-1;
padding-right: 12px;
.commento-logo-container {
float: right;
.commento-logo {
border: none;
width: auto;
height: 32px;
display: flex;
align-items: center;
padding: 5px;
border-radius: 3px;
.commento-logo-svg {
display: inline;
width: 18px;
height: 18px;
margin-right: 8px;
outline: none;
}
}
.commento-logo-text {
font-size: 13px;
color: $gray-6;
display: inline;
line-height: 24px;
position: relative;
font-weight: bold;
}
}
}

View File

@ -0,0 +1,20 @@
@import "colors-main.scss";
code {
background: $red-3;
font-family: monospace;
line-height: 1.5;
color: $red-6;
padding: 4px;
}
a {
color: #1f89ff;
border-bottom: 1px solid #1f89ff;
outline: none;
text-decoration: none;
}
a:focus {
box-shadow: 0 0 0 1px rgba(87, 85, 217, .2);
}

134
frontend/sass/commento.scss Normal file
View File

@ -0,0 +1,134 @@
#commento {
font-family: "Source Sans Pro", "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
font-size: 14px;
line-height: 1.5;
color: #50596c;
overflow-x: hidden;
text-rendering: optimizeLegibility;
padding: 8px;
@import "colors-main.scss";
@import "common-main.scss";
@import "commento-tags.scss";
@import "commento-logo.scss";
@import "commento-input.scss";
@import "commento-logged.scss";
@import "commento-buttons.scss";
.commento-hidden {
display: none;
}
.commento-error-box {
width: 100%;
border-radius: 4px;
height: 32px;
text-align: center;
color: $red-7;
font-weight: bold;
}
.commento-moderation-notice {
width: 100%;
border-radius: 4px;
height: 32px;
text-align: center;
color: $orange-7;
font-weight: bold;
margin-top: 16px;
}
.commento-dark-card {
background: $blue-1;
}
.commento-card {
padding: 12px 0px 0px 12px;
margin-top: 16px;
border-top: 1px solid #f0f0f0;
.commento-header {
padding-bottom: 12px;
}
.commento-avatar {
width: 34px;
height: 34px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 22px;
float: left;
margin-right: 10px;
border: 1px solid #fff;
box-shadow: 0px 0px 0px 2px #f00;
}
.commento-avatar-img {
width: 38px;
height: 38px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
float: left;
margin-right: 10px;
}
.commento-avatar::after {
content:"";
display:block;
}
.commento-name {
font-weight: bold;
font-size: 14px;
color: #555;
border: none;
display: block;
z-index: 1;
margin-left: 48px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.commento-subtitle {
display: block;
color: #999;
font-size: 12px;
margin-left: 48px;
}
.commento-score {
display: inline;
color: #999;
font-size: 12px;
}
.commento-score::before {
content: "\00a0 \00a0 \00b7 \00a0 \00a0";
}
.commento-body {
p {
margin-top: 6px;
margin-bottom: 6px;
}
}
.commento-options {
float: right;
position: relative;
height: 38px;
z-index: 2;
}
.commento-moderation {
height: 48px;
}
}
}