740 lines
22 KiB
Plaintext
740 lines
22 KiB
Plaintext
//= require_tree .
|
||
|
||
//= require tom-select.base.js
|
||
//= require TomSelect_remove_button
|
||
//= require TomSelect_caret_position
|
||
//= require TomSelect_input_autogrow
|
||
|
||
"use strict";
|
||
|
||
const csrfToken = () => {
|
||
return document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
}
|
||
|
||
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 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;
|
||
};
|
||
|
||
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) => {
|
||
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 (username) {
|
||
this.curUser = null;
|
||
|
||
this.storyFlagReasons = ({
|
||
<%= Vote::STORY_REASONS.map{|k,v| "'#{k}': '#{v}'" }.join(", ") %>
|
||
});
|
||
|
||
this.commentFlagReasons = ({
|
||
<%= Vote::COMMENT_REASONS.map{|k,v| "'#{k}': '#{v}'" }.join(", ") %>
|
||
});
|
||
}
|
||
|
||
bounceToLogin() {
|
||
document.location = "/login?return=" + encodeURIComponent(document.location);
|
||
}
|
||
|
||
modalFlaggingDropDown(flaggedItemType, voterEl, reasons) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(voterEl, '.story, .comment');
|
||
if (li.classList.contains('flagged')) {
|
||
/* already upvoted, neutralize */
|
||
if (li.classList.contains('story')) {
|
||
Lobster.voteStory(voterEl, -1, null);
|
||
} else {
|
||
Lobster.voteComment(voterEl, -1, null);
|
||
}
|
||
return
|
||
}
|
||
|
||
if (document.querySelector("#flag_dropdown") || document.querySelector('#modal_backdrop')) {
|
||
Lobster.removeFlagModal()
|
||
}
|
||
|
||
const modalDiv = document.createElement("div");
|
||
modalDiv.setAttribute('id', 'modal_backdrop');
|
||
document.body.appendChild(modalDiv);
|
||
|
||
const flaggingDropDown = document.createElement('div');
|
||
flaggingDropDown.setAttribute('id', 'flag_dropdown');
|
||
voterEl.after(flaggingDropDown);
|
||
|
||
Object.keys(reasons).map(function(k, v) {
|
||
let a = document.createElement('a')
|
||
a.textContent = reasons[k]
|
||
a.setAttribute('data', k)
|
||
a.setAttribute('href', '#')
|
||
if (k === '') {
|
||
a.classList.add('cancel-reason')
|
||
}
|
||
flaggingDropDown.append(a);
|
||
});
|
||
}
|
||
|
||
checkStoryDuplicate(form) {
|
||
const formData = new FormData(form);
|
||
const action = '/stories/check_url_dupe';
|
||
fetchWithCSRF(action, {
|
||
method: 'post',
|
||
headers: new Headers({'X-Requested-With': 'XMLHttpRequest'}),
|
||
body: formData,
|
||
}).then (response => {
|
||
response.text().then(text => {
|
||
document.querySelector('.form_errors_header').innerHTML = text;
|
||
});
|
||
});
|
||
}
|
||
|
||
checkStoryTitle() {
|
||
const titleLocation = document.getElementById('story_title');
|
||
if (!titleLocation) return;
|
||
|
||
const title = titleLocation.value;
|
||
if (!title) return;
|
||
|
||
// Check for common prefixes like "ask lobsters:", remove it, and add the appropriate tag.
|
||
const m = title.match(/^(show|ask) lobste\.?rs:? (.+)$/i);
|
||
if (m) {
|
||
const titleEl = document.getElementById('story_title');
|
||
Lobster.tom.addItem(m[1].toLowerCase());
|
||
titleEl.value = 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) {
|
||
const targetUrl = document.getElementById('story_url').value;
|
||
const title_field = document.getElementById('story_title');
|
||
const formData = new FormData();
|
||
const old_text = button.textContent;
|
||
|
||
if (targetUrl == "")
|
||
return;
|
||
|
||
button.setAttribute("disabled", true);
|
||
button.textContent = "Fetching...";
|
||
formData.append('fetch_url', targetUrl);
|
||
|
||
fetchWithCSRF('/stories/fetch_url_attributes', {
|
||
method: 'post',
|
||
headers: new Headers({'X-Requested-With': 'XMLHttpRequest'}),
|
||
body: formData,})
|
||
.then (response => response.json())
|
||
.then (data => {
|
||
title_field.value = data.title
|
||
button.textContent = old_text
|
||
});
|
||
button.removeAttribute("disabled");
|
||
Lobster.checkStoryTitle();
|
||
}
|
||
|
||
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'});
|
||
}
|
||
|
||
removeFlagModal() {
|
||
document.getElementById("flag_dropdown").remove();
|
||
document.getElementById("modal_backdrop").remove();
|
||
}
|
||
|
||
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(formElement) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const formData = new FormData(formElement);
|
||
const previewElement = document.getElementById('inside');
|
||
fetchWithCSRF('/stories/preview', {
|
||
method: 'post',
|
||
headers: new Headers({'X-Requested-With': 'XMLHttpRequest'}),
|
||
body: formData
|
||
}).then(response => {
|
||
response.text().then(text => {
|
||
previewElement.innerHTML = text;
|
||
Lobster.tomSelect();
|
||
});
|
||
});
|
||
}
|
||
|
||
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'});
|
||
}
|
||
|
||
tomSelect(item) {
|
||
if (!document.getElementById('story_tags_a')) {
|
||
return
|
||
}
|
||
|
||
TomSelect.define('caret_position', caret_position);
|
||
TomSelect.define('input_autogrow', input_autogrow);
|
||
TomSelect.define('remove_button', remove_button);
|
||
this.tom = new TomSelect('#story_tags_a', {
|
||
plugins: ['caret_position', 'input_autogrow', 'remove_button'],
|
||
maxOptions: 200,
|
||
maxItems: 10,
|
||
hideSelected: true,
|
||
closeAfterSelect: true,
|
||
selectOnTab: true,
|
||
sortField: {field: "data-value"},
|
||
onInitialize: function() {
|
||
const parent = document.querySelector(".ts-control");
|
||
parent.appendChild(document.querySelector(".ts-dropdown"));
|
||
},
|
||
render: {
|
||
option: function(data) {
|
||
return '<div>' +
|
||
'<span class="dropDownItem">' + data.title + '</span>' +
|
||
'</div>';
|
||
},
|
||
item: function(data) {
|
||
return '<div class="data-ts-item">' + data.value + '</div>';
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
upvoteComment(voterEl) {
|
||
Lobster.voteComment(voterEl, 1);
|
||
}
|
||
|
||
upvoteStory(voterEl) {
|
||
Lobster.voteStory(voterEl, 1);
|
||
}
|
||
|
||
voteStory(voterEl, point, reason) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(voterEl, '.story');
|
||
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";
|
||
}
|
||
|
||
fetchWithCSRF("/stories/" + li.getAttribute("data-shortid") + "/" + action, {
|
||
method: 'post',
|
||
body: formData });
|
||
}
|
||
|
||
voteComment(voterEl, point, reason) {
|
||
if (!Lobster.curUser)
|
||
return Lobster.bounceToLogin();
|
||
|
||
const li = parentSelector(voterEl, ".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");
|
||
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") {
|
||
li.querySelector(".reason").innerHTML = ""
|
||
}
|
||
|
||
if (action == "unvote" && point < 0) {
|
||
li.querySelector(".flagger").textContent = "flag";
|
||
} else if (action == "flag") {
|
||
li.querySelector(".flagger").textContent = "unflag";
|
||
li.querySelector(".reason").innerHTML = "| " + Lobster.commentFlagReasons[reason].toLowerCase();
|
||
}
|
||
|
||
fetchWithCSRF("/comments/" + li.getAttribute("data-shortid") + "/" + action, {
|
||
method: 'post',
|
||
body: formData });
|
||
}
|
||
}
|
||
|
||
const Lobster = new _LobstersFunction();
|
||
|
||
onPageLoad(() => {
|
||
Lobster.curUser = document.body.getAttribute('data-username'); // hack
|
||
autosize(document.querySelectorAll('textarea'));
|
||
|
||
// replace csrf token in forms that may be fragment caches with page token
|
||
for (const i of document.querySelectorAll('form input[name="authenticity_token"]')) {
|
||
i.value = csrfToken();
|
||
}
|
||
|
||
// Global Functions
|
||
|
||
on('click', '.markdown_help_label', (event) => {
|
||
parentSelector(event.target, '.markdown_help_toggler').querySelector('.markdown_help').classList.toggle('display-block');
|
||
});
|
||
|
||
on('click', '#modal_backdrop', () => {
|
||
Lobster.removeFlagModal()
|
||
});
|
||
|
||
on('click', '[data-confirm]', (event) => {
|
||
if (!confirm(event.target.dataset.confirm)) {
|
||
event.preventDefault();
|
||
}
|
||
});
|
||
|
||
// 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
|
||
|
||
Lobster.checkStoryTitle()
|
||
|
||
Lobster.tomSelect();
|
||
|
||
if (document.getElementById('story_url') && document.getElementById('story_preview') && !document.getElementById('story_preview').firstElementChild) {
|
||
document.getElementById('story_url').focus()
|
||
}
|
||
|
||
on('change', '#story_title', Lobster.checkStoryTitle);
|
||
|
||
on('click', '.story #flag_dropdown a', (event) => {
|
||
event.preventDefault();
|
||
if (event.target.getAttribute('data') != '') {
|
||
Lobster.voteStory(parentSelector(event.target, '.story'), -1, event.target.getAttribute('data'));
|
||
}
|
||
Lobster.removeFlagModal();
|
||
});
|
||
|
||
on('click', '#story_fetch_title', (event) => {
|
||
Lobster.fetchURLTitle(event.target);
|
||
});
|
||
|
||
on('click', 'li.story a.upvoter', (event) => {
|
||
event.preventDefault();
|
||
Lobster.upvoteStory(event.target);
|
||
});
|
||
|
||
on('click', 'li.story a.flagger', (event) => {
|
||
event.preventDefault();
|
||
const reasons = Lobster.storyFlagReasons;
|
||
Lobster.modalFlaggingDropDown("story", event.target, reasons);
|
||
});
|
||
|
||
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) => {
|
||
Lobster.previewStory(parentSelector(event.target, 'form'));
|
||
});
|
||
|
||
on('focusout', '#story_url', () => {
|
||
let url_tags = {
|
||
"\.pdf$": "pdf",
|
||
"[\/\.]((youtube|vimeo)\.com|youtu\.be|twitch.tv)\/": "video",
|
||
"[\/\.](slideshare\.net|speakerdeck\.com)\/": "slides",
|
||
"[\/\.](soundcloud\.com)\/": "audio",
|
||
};
|
||
|
||
const storyUrlEl = document.getElementById('story_url');
|
||
for (const [match, tag] of Object.entries(url_tags)) {
|
||
if (storyUrlEl.value.match(new RegExp(match, "i"))) {
|
||
Lobster.tom.addItem(tag.toLowerCase());
|
||
}
|
||
}
|
||
|
||
// check for dupe if there's a URL, but not when editing existing
|
||
if (storyUrlEl.value !== "" &&
|
||
(!document.querySelector('input[name="_method"]') ||
|
||
document.querySelector('input[name="_method"]').getAttribute('value') === 'put')) {
|
||
Lobster.checkStoryDuplicate(parentSelector(storyUrlEl, 'form'));
|
||
}
|
||
});
|
||
|
||
// Comment Related Functions
|
||
|
||
on('click', 'a.comment-disowner', (event) => {
|
||
if (confirm("Are you sure you want to disown this comment?")) {
|
||
let li = parentSelector(event.target, '.comment');
|
||
fetchWithCSRF('/comments/' + li.getAttribute('data-shortid') + '/disown', { method: 'post' })
|
||
.then(response => {
|
||
response.text().then(text => replace(li, text));
|
||
});
|
||
}
|
||
});
|
||
|
||
on("click", "a.comment_replier", (event) => {
|
||
if (!Lobster.curUser) {
|
||
Lobster.bounceToLogin();
|
||
}
|
||
|
||
const comment = parentSelector(event.target, '.comment');
|
||
const commentId = comment.getAttribute('id');
|
||
|
||
// Check to make sure we don't already have a reply box.
|
||
if (document.getElementById('reply_form_' + commentId)) {
|
||
return false;
|
||
}
|
||
|
||
// Inserts "> " on quoted text.
|
||
let sel = document.getSelection().toString();
|
||
if (sel != "") {
|
||
sel = sel.split("\n").map(s => "> " + s + '\n\n').join('');
|
||
sel += "\n";
|
||
}
|
||
|
||
let div = document.createElement('div');
|
||
if (document.querySelector('.comments_subtree')) {
|
||
parentSelector(comment, '.comments_subtree').lastElementChild.prepend(div);
|
||
} else {
|
||
comment.parentElement.lastElementChild.prepend(div);
|
||
}
|
||
|
||
fetch('/comments/' + comment.getAttribute('data-shortid') + '/reply')
|
||
.then(response => {
|
||
response.text().then(text => {
|
||
div.innerHTML = text;
|
||
div.setAttribute('id', 'reply_form_' + commentId );
|
||
var ta = div.querySelector("textarea");
|
||
ta.textContent = sel;
|
||
// This will place the cursor at the end of the quoted string and focus on your reply.
|
||
ta.setSelectionRange(sel.length, sel.length);
|
||
ta.focus();
|
||
autosize(ta);
|
||
})
|
||
});
|
||
});
|
||
|
||
on('click', '.comment a.flagger', (event) => {
|
||
event.preventDefault();
|
||
const reasons = Lobster.commentFlagReasons
|
||
Lobster.modalFlaggingDropDown("comment", event.target, reasons);
|
||
});
|
||
|
||
on('click', '.comment #flag_dropdown a', (event) => {
|
||
event.preventDefault();
|
||
if (event.target.getAttribute('data') != '') {
|
||
Lobster.voteComment(parentSelector(event.target, '.comment'), -1, event.target.getAttribute('data'));
|
||
}
|
||
Lobster.removeFlagModal()
|
||
});
|
||
|
||
on("click", '.comment a.upvoter', (event) => {
|
||
event.preventDefault();
|
||
Lobster.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.parentElement.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));
|
||
});
|
||
});
|
||
|
||
on('click', '.comment_unread', (event) => {
|
||
const nodes = document.getElementsByClassName('comment_unread')
|
||
const foundIndex = Array.from(nodes).findIndex(node => node === event.target)
|
||
const targetIndex = (foundIndex + 1) % nodes.length;
|
||
const targetY = nodes[targetIndex].getBoundingClientRect().top + window.scrollY
|
||
window.scrollTo({ top: targetY, behavior: 'smooth' })
|
||
});
|
||
});
|