693 lines
19 KiB
Plaintext
693 lines
19 KiB
Plaintext
//= require jquery
|
||
//= require jquery_ujs
|
||
//= require_tree .
|
||
|
||
"use strict";
|
||
|
||
var _Lobsters = Class.extend({
|
||
curUser: null,
|
||
|
||
storyFlagReasons: { <%= Vote::STORY_REASONS.map{|k,v|
|
||
"#{k.inspect}: #{v.inspect}" }.join(", ") %> },
|
||
commentFlagReasons: { <%= Vote::COMMENT_REASONS.map{|k,v|
|
||
"#{k.inspect}: #{v.inspect}" }.join(", ") %> },
|
||
|
||
upvoteStory: function(voterEl) {
|
||
Lobster.vote("story", voterEl, 1);
|
||
},
|
||
flagStory: function(voterEl) {
|
||
Lobsters._showFlagWhyAt("story", voterEl, function(k) {
|
||
Lobster.vote("story", voterEl, -1, k); });
|
||
},
|
||
|
||
upvoteComment: function(voterEl) {
|
||
Lobster.vote("comment", voterEl, 1);
|
||
},
|
||
flagComment: function(voterEl) {
|
||
Lobsters._showFlagWhyAt("comment", voterEl, function(k) {
|
||
Lobster.vote("comment", voterEl, -1, k); });
|
||
},
|
||
_showFlagWhyAt: function(thingType, voterEl, onChooseWhy) {
|
||
if (!Lobsters.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
var li = $(voterEl).closest(".story, .comment");
|
||
if (li.hasClass("flagged")) {
|
||
/* already upvoted, neutralize */
|
||
Lobster.vote(thingType, voterEl, -1, null);
|
||
return;
|
||
}
|
||
|
||
if ($("#flag_why"))
|
||
$("#flag_why").remove();
|
||
if ($("#flag_why_shadow"))
|
||
$("#flag_why_shadow").remove();
|
||
|
||
var sh = $("<div id=\"flag_why_shadow\"></div>");
|
||
$(voterEl).after(sh);
|
||
sh.click(function() {
|
||
$("#flag_why_shadow").remove();
|
||
$("#flag_why").remove();
|
||
});
|
||
|
||
var d = $("<div id=\"flag_why\"></div>");
|
||
|
||
var reasons;
|
||
if (thingType == "comment")
|
||
reasons = Lobsters.commentFlagReasons;
|
||
else
|
||
reasons = Lobsters.storyFlagReasons;
|
||
|
||
$.each(reasons, function(k, v) {
|
||
var a = $("<a href=\"#\"" + (k == "" ? " class=\"cancelreason\"" : "") +
|
||
">" + v + "</a>");
|
||
|
||
a.click(function() {
|
||
$("#flag_why").remove();
|
||
$("#flag_why_shadow").remove();
|
||
|
||
if (k != "")
|
||
onChooseWhy(k);
|
||
|
||
return false;
|
||
});
|
||
|
||
d.append(a);
|
||
});
|
||
|
||
if (thingType == "story") {
|
||
$(voterEl).closest("li").after(d);
|
||
d.position({
|
||
my: "left top",
|
||
at: "left bottom",
|
||
offset: "-2 -2",
|
||
of: $(voterEl),
|
||
collision: "none",
|
||
});
|
||
d.css("left", $(voterEl).position().left);
|
||
} else {
|
||
// place flag menu outside of the comment to avoid inheriting opacity
|
||
var voterPos = $(voterEl).position();
|
||
d.appendTo($(voterEl).parent());
|
||
d.css({
|
||
left: voterPos.left,
|
||
top: voterPos.top + $(voterEl).outerHeight()
|
||
});
|
||
}
|
||
},
|
||
|
||
previewStory: function(form) {
|
||
$("#inside").load("/stories/preview", $(form).serializeArray(),
|
||
function() {
|
||
Lobsters.runSelect2();
|
||
});
|
||
},
|
||
|
||
runSelect2: function() {
|
||
$("#story_tags_a").select2({
|
||
formatSelection: function(what) {
|
||
return what.id;
|
||
},
|
||
matcher: function(term, text) {
|
||
return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
|
||
},
|
||
sortResults: function(results, container, query) {
|
||
if (query.term) {
|
||
var tmatches = [];
|
||
var dmatches = [];
|
||
|
||
/* prefer tag matches first, then description matches */
|
||
for (var x in results) {
|
||
var r = results[x];
|
||
|
||
if (r.id.toUpperCase().indexOf(query.term.toUpperCase()) == 0)
|
||
tmatches.push(r);
|
||
else
|
||
dmatches.push(r);
|
||
}
|
||
|
||
tmatches = tmatches.sort(function(a, b) {
|
||
return a.text.localeCompare(b.text);
|
||
});
|
||
dmatches = dmatches.sort(function(a, b) {
|
||
return a.text.localeCompare(b.text);
|
||
});
|
||
|
||
return tmatches.concat(dmatches);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
});
|
||
},
|
||
|
||
fetchURLTitle: function(button, url_field, title_field) {
|
||
if (url_field.val() == "")
|
||
return;
|
||
|
||
var old_value = button.val();
|
||
button.prop("disabled", true);
|
||
button.val("Fetching...");
|
||
|
||
$.post("/stories/fetch_url_attributes", {
|
||
fetch_url: url_field.val(),
|
||
})
|
||
.success(function(data) {
|
||
if (data) {
|
||
if (data.title)
|
||
title_field.val(data.title.substr(0, title_field.maxLength));
|
||
if (data.url)
|
||
url_field.val(data.url);
|
||
}
|
||
|
||
button.val(old_value);
|
||
button.prop("disabled", false);
|
||
})
|
||
.error(function() {
|
||
button.val(old_value);
|
||
button.prop("disabled", false);
|
||
});
|
||
Lobster.checkStoryTitle();
|
||
},
|
||
});
|
||
|
||
var Lobsters = new _Lobsters();
|
||
|
||
$(document).ready(function() {
|
||
|
||
$(document).on("click", "a.comment_replier", function() {
|
||
if (!Lobsters.curUser) {
|
||
Lobster.bounceToLogin();
|
||
return false;
|
||
}
|
||
|
||
var comment = $(this).closest(".comment");
|
||
if ($("#reply_form_" + comment.attr("id")).length > 0)
|
||
return false;
|
||
|
||
var sel = "";
|
||
if (window.getSelection)
|
||
sel = window.getSelection().toString();
|
||
else if (document.selection && document.selection.type != "Control")
|
||
sel = document.selection.createRange().text;
|
||
|
||
if (sel != "") {
|
||
var t = "";
|
||
$.each(sel.split("\n"), function(k, v) {
|
||
t += (t == "" ? "" : "\n") + "> " + v;
|
||
});
|
||
sel = t;
|
||
|
||
if (sel != "")
|
||
sel += "\n\n";
|
||
}
|
||
|
||
var replies = comment.nextAll(".comments").first();
|
||
$.get("/comments/" + comment.attr("data-shortid") + "/reply",
|
||
function(data) {
|
||
var reply = $($.parseHTML(data));
|
||
reply.attr("id", "reply_form_" + comment.attr("id"));
|
||
replies.prepend(reply);
|
||
var ta = reply.find("textarea");
|
||
ta.focus().text(sel);
|
||
autosize(ta);
|
||
});
|
||
|
||
return false;
|
||
});
|
||
|
||
$(document).on("click", "a.comment_disownor", function() {
|
||
if (confirm("Are you sure you want to disown this comment?")) {
|
||
var li = $(this).closest(".comment");
|
||
$.post("/comments/" + $(li).attr("data-shortid") + "/disown",
|
||
function(d) {
|
||
$(li).replaceWith(d);
|
||
});
|
||
}
|
||
});
|
||
|
||
Lobsters.runSelect2();
|
||
|
||
$(document).on("blur", "#story_url", function() {
|
||
var url_tags = {
|
||
"\.pdf$": "pdf",
|
||
"[\/\.]((youtube|vimeo)\.com|youtu\.be|twitch.tv)\/": "video",
|
||
"[\/\.](slideshare\.net|speakerdeck\.com)\/": "slides",
|
||
"[\/\.](soundcloud\.com)\/": "audio",
|
||
};
|
||
|
||
for (var u in url_tags) {
|
||
var tag = url_tags[u];
|
||
|
||
if ($("#story_url").val().match(new RegExp(u, "i"))) {
|
||
var ta = $("#story_tags_a").data("select2");
|
||
if (ta.getVal().indexOf(tag) < 0)
|
||
ta.addSelectedChoice({ id: tag });
|
||
}
|
||
}
|
||
|
||
// check for dupe if there's a URL, but not when editing existing
|
||
if ($("#story_url").val().length > 0 &&
|
||
$("#edit_story input[name=_method]").val() !== "put") {
|
||
Lobster.checkStoryDuplicate(parentSelector(document.getElementById('story_url'), 'form'));
|
||
}
|
||
});
|
||
});
|
||
|
||
const parentSelector = (target, selector) => {
|
||
let parent = target;
|
||
while (!parent.matches(selector)) {
|
||
parent = parent.parentElement;
|
||
if (parent === null) {
|
||
throw new Error(`Did not match a parent of ${target} with the selector ${selector}`);
|
||
}
|
||
}
|
||
return parent;
|
||
};
|
||
|
||
function on(eventTypes, selector, callback) {
|
||
eventTypes.split(/ /).forEach( (eventType) => {
|
||
document.addEventListener(eventType, event => {
|
||
if (event.target.matches(selector)) {
|
||
callback(event);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
const onPageLoad = (callback) => {
|
||
document.addEventListener('DOMContentLoaded', callback);
|
||
};
|
||
|
||
const replace = (oldElement, newHTMLString) => {
|
||
const placeHolder = document.createElement('div');
|
||
placeHolder.insertAdjacentHTML('afterBegin', newHTMLString);
|
||
const newElements = placeHolder.childNodes.values();
|
||
oldElement.replaceWith(...newElements);
|
||
removeExtraInputs();
|
||
}
|
||
|
||
const slideDownJS = (element) => {
|
||
if (element.classList.contains('slide-down'))
|
||
return;
|
||
|
||
element.classList.add('slide-down');
|
||
const cs = getComputedStyle(element);
|
||
const paddingHeight = parseInt(cs.paddingTop) + parseInt(cs.paddingBottom);
|
||
const height = (element.clientHeight - paddingHeight) + 'px';
|
||
element.style.height = '0px';
|
||
setTimeout(() => { element.style.height = height; }, 0);
|
||
};
|
||
|
||
const fetchWithCSRF = (url, params) => {
|
||
let csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
params = params || {};
|
||
params['headers'] = params['headers'] || new Headers;
|
||
params['headers'].append('X-CSRF-Token', csrfToken);
|
||
return fetch(url, params);
|
||
}
|
||
|
||
const removeExtraInputs = () => {
|
||
// This deletion will resovle a bug that creates an extra hidden input when rendering the comment elements.
|
||
const extraInputs = document.querySelectorAll('.comment_folder_button + .comment_folder_button');
|
||
for (const i of extraInputs) {
|
||
i.remove();
|
||
}
|
||
}
|
||
|
||
class _LobstersFunction {
|
||
constructor () {
|
||
this.curUser = document.body.getAttribute('data-username');
|
||
|
||
this.storyFlagReasons = ({<%= Vote::STORY_REASONS.map{|k,v|
|
||
"#{k.inspect}: #{v.inspect}" }.join(", ") %>});
|
||
|
||
this.commentFlagReasons = ({<%= Vote::COMMENT_REASONS.map{|k,v|
|
||
"#{k.inspect}: #{v.inspect}" }.join(", ") %>});
|
||
}
|
||
|
||
bounceToLogin() {
|
||
document.location = "/login?return=" + encodeURIComponent(document.location);
|
||
}
|
||
|
||
checkStoryDuplicate(form) {
|
||
const formData = new FormData(form);
|
||
const action = '/stories/check_url_dupe';
|
||
fetch(action, {
|
||
method: 'POST',
|
||
body: formData,
|
||
}).then (response => {
|
||
response.text().then(text => {
|
||
document.querySelector('.form_errors_header').innerHTML = text;
|
||
});
|
||
});
|
||
}
|
||
|
||
checkStoryTitle() { //partial removal (due to select2)
|
||
const titleLocation = document.getElementById('story_title');
|
||
if (!titleLocation) return;
|
||
|
||
const title = titleLocation.value;
|
||
if (!title) return;
|
||
|
||
// Will check title for common phrases to be removed like "ask/show" lobsters :"
|
||
// Then it will add the keyword "ask/show" to the tags.
|
||
const m = title.match(/^(show|ask) lobste\.?rs:? (.+)$/i);
|
||
if (m) {
|
||
const ta = $("#story_tags_a").data("select2");
|
||
if (ta.getVal().indexOf(m[1].toLowerCase()) < 0) {
|
||
ta.addSelectedChoice({ id: m[1].toLowerCase() });
|
||
$("#story_title").val(m[2]);
|
||
}
|
||
}
|
||
// common separators or (parens) that don't enclose a 4-digit year
|
||
if (title.match(/: | - | – | — | \| | · | • | by /) ||
|
||
(title.match(/\([^\)]*\)/g) || []).some(function (p) { return !p.match(/\(\d{4}\)/) })) {
|
||
slideDownJS(document.querySelector('.title-reminder'));
|
||
}
|
||
}
|
||
|
||
fetchURLTitle(button, urlField, titleField) {
|
||
|
||
}
|
||
|
||
flagComment(voterEl) { //requires [_showFlagWhyAt]
|
||
|
||
}
|
||
|
||
flagStory(voterEl) { //requires [_showFlagWhyAt]
|
||
|
||
}
|
||
|
||
hideStory(hiderEl) {
|
||
if (!Lobster.curUser) return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(hiderEl, ".story, .comment");
|
||
let act;
|
||
if (li.classList.contains("hidden")) {
|
||
act = "unhide";
|
||
li.classList.remove("hidden");
|
||
hiderEl.innerHTML = "hide";
|
||
} else {
|
||
act = "hide";
|
||
li.classList.add("hidden");
|
||
hiderEl.innerHTML = "unhide";
|
||
}
|
||
fetchWithCSRF("/stories/" + li.getAttribute("data-shortid") + "/" + act, {method: 'post'});
|
||
}
|
||
|
||
postComment(form) {
|
||
const formData = new FormData(form);
|
||
const action = form.getAttribute('action');
|
||
formData.append('show_tree_lines', true);
|
||
fetchWithCSRF (action, {
|
||
method: 'POST',
|
||
headers: new Headers({'X-Requested-With': 'XMLHttpRequest'}),
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
response.text().then(text => replace(form.parentElement, text));
|
||
})
|
||
}
|
||
|
||
previewComment(form) {
|
||
const formData = new FormData(form);
|
||
const action = form.getAttribute('action');
|
||
formData.append('preview', 'true');
|
||
formData.append('show_tree_lines', 'true');
|
||
fetchWithCSRF(action, {
|
||
method: 'POST',
|
||
headers: new Headers({'X-Requested-With': 'XMLHttpRequest'}),
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
response.text().then(text => {
|
||
replace(form.parentElement, text);
|
||
autosize(document.querySelectorAll('textarea'));
|
||
});
|
||
});
|
||
}
|
||
|
||
previewStory(form) { //requires [runSelect2]
|
||
|
||
}
|
||
|
||
runSelect2() { //requires [] (will actully replace select2)
|
||
|
||
}
|
||
|
||
saveStory(saverEl) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(saverEl, ".story, .comment");
|
||
let act;
|
||
|
||
if (li.classList.contains("saved")) {
|
||
act = "unsave";
|
||
li.classList.remove("saved");
|
||
saverEl.innerHTML = "save";
|
||
} else {
|
||
act = "save";
|
||
li.classList.add("saved");
|
||
saverEl.innerHTML = "unsave";
|
||
}
|
||
fetchWithCSRF("/stories/" + li.getAttribute("data-shortid") + "/" + act, {method: 'post'});
|
||
}
|
||
|
||
_showFlagWhyAt(thingType, voterEl, onChooseWhy) {
|
||
|
||
}
|
||
|
||
upvoteComment(voterEl) {
|
||
|
||
}
|
||
|
||
upvoteStory(voterEl) {
|
||
}
|
||
|
||
vote(thingType, voterEl, point, reason) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(voterEl, ".story, .comment");
|
||
const scoreDiv = li.querySelector("div.score");
|
||
const formData = new FormData();
|
||
formData.append('reason', reason || '');
|
||
let showScore = true;
|
||
let score = parseInt(scoreDiv.innerHTML);
|
||
let action = "";
|
||
|
||
if (isNaN(score)) {
|
||
showScore = false;
|
||
score = 0;
|
||
}
|
||
|
||
if (li.classList.contains("upvoted") && point > 0) {
|
||
/* already upvoted, neutralize */
|
||
li.classList.remove("upvoted");
|
||
score--;
|
||
action = "unvote";
|
||
} else if (li.classList.contains("flagged") && point < 0) {
|
||
/* already flagged, neutralize */
|
||
li.classList.remove("flagged");
|
||
score++;
|
||
action = "unvote";
|
||
} else if (point > 0) {
|
||
if (li.classList.contains("flagged")) {
|
||
/* Give back the lost flagged point */
|
||
score++;
|
||
}
|
||
li.classList.remove("flagged");
|
||
li.classList.add("upvoted");
|
||
score++;
|
||
action = "upvote";
|
||
} else if (point < 0) {
|
||
if (li.classList.contains("upvoted")) {
|
||
/* Removes the upvote point this user already gave the story*/
|
||
score--;
|
||
}
|
||
li.classList.remove("upvoted");
|
||
li.classList.add("flagged");
|
||
if (li.parentElement.querySelector('.comment_folder_button')) {
|
||
li.parentElement.querySelector('.comment_folder_button').setAttribute("checked", true);
|
||
};
|
||
showScore = false;
|
||
score--;
|
||
action = "flag";
|
||
}
|
||
if (showScore) {
|
||
scoreDiv.innerHTML = score;
|
||
} else {
|
||
scoreDiv.innerHTML = '~';
|
||
}
|
||
if (action == "upvote" || action == "unvote") {
|
||
if (li.querySelector(".reason")) {
|
||
li.querySelector(".reason").innerHTML = ""
|
||
};
|
||
|
||
if (action == "unvote" && point < 0)
|
||
li.querySelector(".flagger").textContent = "flag";
|
||
} else if (action == "flag") {
|
||
li.querySelector(".flagger").textContent = "unflag";
|
||
if (thingType == "comment") {
|
||
li.querySelector(".reason").innerHTML = "| " + Lobster.commentFlagReasons[reason].toLowerCase();
|
||
}
|
||
}
|
||
|
||
fetchWithCSRF("/" + (thingType == "story" ? "stories" : thingType + "s") + "/" +
|
||
li.getAttribute("data-shortid") + "/" + action, {
|
||
method: 'post',
|
||
body: formData });
|
||
}
|
||
}
|
||
|
||
onPageLoad(() => {
|
||
|
||
// Global Functions
|
||
|
||
const Lobster = new _LobstersFunction();
|
||
|
||
on('click', '.markdown_help_label', (event) => {
|
||
parentSelector(event.target, '.markdown_help_toggler').querySelector('.markdown_help').classList.toggle('display-block');
|
||
});
|
||
|
||
// Account Settings Functions
|
||
|
||
on('focusout', '#user_homepage', (event) => {
|
||
const homePage = event.target
|
||
if (homePage.value.trim() !== '' && !homePage.value.match('^[a-z]+:\/\/'))
|
||
homePage.value = 'https://' + homePage.value
|
||
})
|
||
|
||
// Inbox Related Funtions
|
||
|
||
on('change', '#message_hat_id', (event) => {
|
||
let selectedOption = event.target.selectedOptions[0];
|
||
document.getElementById('message_mod_note').checked = (selectedOption.getAttribute('data-modnote') === 'true');
|
||
});
|
||
|
||
// Story Related Functions
|
||
|
||
on('change', '#story_title', Lobster.checkStoryTitle);
|
||
|
||
Lobster.checkStoryTitle()
|
||
|
||
on('click', 'li.story a.upvoter', (event) => {
|
||
event.preventDefault();
|
||
Lobsters.upvoteStory(event.target);
|
||
});
|
||
|
||
on('click', 'li.story a.flagger', (event) => {
|
||
event.preventDefault();
|
||
Lobsters.flagStory(event.target);
|
||
});
|
||
|
||
on('click', 'li.story a.hider', (event) => {
|
||
event.preventDefault();
|
||
Lobster.hideStory(event.target);
|
||
})
|
||
|
||
on('click', 'li.story a.saver', (event) => {
|
||
event.preventDefault();
|
||
Lobster.saveStory(event.target);
|
||
});
|
||
|
||
on('click', 'button.story-preview', (event) => {
|
||
Lobsters.previewStory(parentSelector(event.target, 'form'));
|
||
});
|
||
|
||
// Comment Related Functions
|
||
|
||
on('click', '.comment a.flagger', (event) => {
|
||
event.preventDefault();
|
||
Lobsters.flagComment(event.target);
|
||
});
|
||
|
||
on("click", '.comment a.upvoter', (event) => {
|
||
event.preventDefault();
|
||
Lobsters.upvoteComment(event.target);
|
||
});
|
||
|
||
on('click', 'button.comment-preview', (event) => {
|
||
Lobster.previewComment(parentSelector(event.target, 'form'));
|
||
});
|
||
|
||
on('submit', '.comment_form_container form', (event) => {
|
||
event.preventDefault();
|
||
Lobster.postComment(event.target);
|
||
});
|
||
|
||
on('keydown', 'textarea#comment', (event) => {
|
||
if ((event.metaKey || event.ctrlKey) && event.keyCode == 13) {
|
||
Lobster.postComment(parentSelector(event.target, 'form'));
|
||
}
|
||
});
|
||
|
||
on('click', 'button.comment-cancel', (event) => {
|
||
const comment = (parentSelector(event.target, '.comment'));
|
||
const commentId = comment.getAttribute('data-shortid');
|
||
if (commentId !== null && commentId !== '') {
|
||
fetch('/comments/' + commentId + '?show_tree_lines=true')
|
||
.then(response => {
|
||
response.text().then(text => replace(comment, text));
|
||
});
|
||
} else {
|
||
comment.remove();
|
||
}
|
||
});
|
||
|
||
on('click', 'a.comment_editor', (event) => {
|
||
let comment = parentSelector(event.target, '.comment');
|
||
const commentId = comment.getAttribute('data-shortid')
|
||
fetch('/comments/' + commentId + '/edit')
|
||
.then(response => {
|
||
response.text().then(text => {
|
||
replace(comment, text);
|
||
autosize(document.querySelectorAll('textarea'));
|
||
});
|
||
});
|
||
autosize(document.querySelectorAll('textarea'));
|
||
});
|
||
|
||
on("click", "a.comment_deletor", (event) => {
|
||
event.preventDefault();
|
||
if (confirm("Are you sure you want to delete this comment?")) {
|
||
const comment = parentSelector(event.target, '.comment');
|
||
const commentId = comment.getAttribute('data-shortid');
|
||
fetchWithCSRF('/comments/' + commentId + '/delete',{method: 'post'})
|
||
.then(response => {
|
||
response.text().then(text => replace(comment, text));
|
||
});
|
||
}
|
||
});
|
||
|
||
on('click', 'a.comment_undeletor', (event) => {
|
||
event.preventDefault();
|
||
if (confirm("Are uou sure you want to undelete this comment?")) {
|
||
const comment = parentSelector(event.target, '.comment');
|
||
const commentId = comment.getAttribute('data-shortid');
|
||
fetchWithCSRF('/comments/' + commentId + '/undelete', {method: 'post'})
|
||
.then(response => {
|
||
response.text().then(text => replace(comment, text));
|
||
});
|
||
}
|
||
});
|
||
|
||
on('click', 'a.comment_moderator', (event) => {
|
||
const reason = prompt("Moderation reason:");
|
||
if (reason == null || reason == '')
|
||
return false;
|
||
|
||
const formData = new FormData();
|
||
formData.append('reason', reason);
|
||
const comment = parentSelector(event.target, '.comment');
|
||
const commentId = comment.getAttribute('data-shortid');
|
||
fetchWithCSRF('/comments/' + commentId + '/delete', { method: 'post', body: formData })
|
||
.then(response => {
|
||
response.text().then(text => replace(comment, text));
|
||
});
|
||
});
|
||
});
|
||
|