philomena/assets/js/comment.js

203 lines
5.9 KiB
JavaScript

/**
* Comments.
*/
import { $ } from './utils/dom';
import { showOwnedComments } from './communications/comment';
import { filterNode } from './imagesclientside';
import { fetchHtml } from './utils/requests';
function handleError(response) {
const errorMessage = '<div>Comment failed to load!</div>';
if (!response.ok) {
return errorMessage;
}
return response.text();
}
function commentPosted(response) {
const commentEditTab = $('#js-comment-form a[data-click-tab="write"]'),
commentEditForm = $('#js-comment-form'),
container = document.getElementById('comments'),
requestOk = response.ok;
commentEditTab.click();
commentEditForm.reset();
if (requestOk) {
response.text().then(text => {
if (text.includes('<div class="flash flash--warning">')) {
window.location.reload();
}
else {
displayComments(container, text);
}
});
}
else {
window.location.reload();
window.scrollTo(0, 0); // Error message is displayed at the top of the page (flash)
}
}
function loadParentPost(event) {
const clickedLink = event.target,
// Find the comment containing the link that was clicked
fullComment = clickedLink.closest('article.block'),
// Look for a potential image and comment ID
commentMatches = /(\w+)#comment_(\w+)$/.exec(clickedLink.getAttribute('href'));
// If the clicked link is already active, just clear the parent comments
if (clickedLink.classList.contains('active_reply_link')) {
clearParentPost(clickedLink, fullComment);
return true;
}
if (commentMatches) {
// If the regex matched, get the image and comment ID
const [ , imageId, commentId ] = commentMatches;
fetchHtml(`/images/${imageId}/comments/${commentId}`)
.then(handleError)
.then(data => {
clearParentPost(clickedLink, fullComment);
insertParentPost(data, clickedLink, fullComment);
});
return true;
}
}
function insertParentPost(data, clickedLink, fullComment) {
// Add the 'subthread' class to the comment with the clicked link
fullComment.classList.add('subthread');
// Insert parent comment
fullComment.insertAdjacentHTML('beforebegin', data);
// Add class subthread and fetched-comment - use separate add()-methods to support IE11
fullComment.previousSibling.classList.add('subthread');
fullComment.previousSibling.classList.add('fetched-comment');
// Execute timeago on the new comment - it was not present when first run
window.booru.timeAgo(fullComment.previousSibling.getElementsByTagName('time'));
// Add class active_reply_link to the clicked link
clickedLink.classList.add('active_reply_link');
// Filter images (if any) in the loaded comment
filterNode(fullComment.previousSibling);
}
function clearParentPost(clickedLink, fullComment) {
// Remove any previous siblings with the class fetched-comment
while (fullComment.previousSibling && fullComment.previousSibling.classList.contains('fetched-comment')) {
fullComment.previousSibling.parentNode.removeChild(fullComment.previousSibling);
}
// Remove class active_reply_link from all links in the comment
[].slice.call(fullComment.getElementsByClassName('active_reply_link')).forEach(link => {
link.classList.remove('active_reply_link');
});
// If this full comment isn't a fetched comment, remove the subthread class.
if (!fullComment.classList.contains('fetched-comment')) {
fullComment.classList.remove('subthread');
}
}
function displayComments(container, commentsHtml) {
container.innerHTML = commentsHtml;
// Execute timeago on comments
window.booru.timeAgo(document.getElementsByTagName('time'));
// Filter images in the comments
filterNode(container);
// Show options on own comments
showOwnedComments();
}
function loadComments(event) {
const container = document.getElementById('comments'),
hasHref = event.target && event.target.getAttribute('href'),
hasHash = window.location.hash && window.location.hash.match(/#comment_([a-f0-9]+)/),
getURL = hasHref || (hasHash ? `${container.dataset.currentUrl}?comment_id=${window.location.hash.substring(9, window.location.hash.length)}`
: container.dataset.currentUrl);
fetchHtml(getURL)
.then(handleError)
.then(data => {
displayComments(container, data);
// Make sure the :target CSS selector applies to the inserted content
// https://bugs.chromium.org/p/chromium/issues/detail?id=98561
if (hasHash) {
// eslint-disable-next-line
window.location = window.location;
}
});
return true;
}
function setupComments() {
const comments = document.getElementById('comments'),
hasHash = window.location.hash && window.location.hash.match(/^#comment_([a-f0-9]+)$/),
targetOnPage = hasHash ? Boolean($(window.location.hash)) : true;
// Load comments over AJAX if we are on a page with element #comments
if (comments) {
if (!comments.dataset.loaded || !targetOnPage) {
// There is no event associated with the initial load, so use false
loadComments(false);
}
else {
filterNode(comments);
showOwnedComments();
}
}
// Define clickable elements and the function to execute on click
const targets = {
'article[id*="comment"] .communication__body__text a[href]': loadParentPost,
'#comments .pagination a[href]': loadComments,
'#js-refresh-comments': loadComments,
};
document.addEventListener('click', event => {
if (event.button === 0) { // Left-click only
for (const target in targets) {
if (event.target && event.target.closest(target)) {
targets[target](event) && event.preventDefault();
}
}
}
});
document.addEventListener('fetchcomplete', event => {
if (event.target.id === 'js-comment-form') commentPosted(event.detail);
});
}
export { setupComments };