/** * 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 => 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 };