Display chapter comments inside MangaDex's sidebar
// ==UserScript==
// @name MangaDex Inline Comments
// @namespace metapone
// @version 0.2.1
// @description Display chapter comments inside MangaDex's sidebar
// @author metapone
// @license GPL-2.0-only; https://opensource.org/licenses/GPL-2.0
// @homepage https://github.com/metapone/userscript-collection
// @supportURL https://github.com/metapone/userscript-collection/issues
// @noframes
// @match *://mangadex.org/chapter/*
// @grant GM_addStyle
// ==/UserScript==
GM_addStyle('\
/* Fix unable to click on scrollbar because of invisible swiping divs */\
#right_swipe_area {\
display: none;\
}\
\
/* Fix overlapping flex items */\
.reader-controls-mode, .reader-controls-footer {\
flex-shrink: 0 !important;\
}\
\
/* Fix sidebar going off-screen because of the collapser */\
.reader-controls {\
max-width: calc(100% - 34px);\
}\
');
function updateSidebar() {
let modeNode = document.querySelector('.reader-controls-mode');
let footerNode = document.querySelector('.reader-controls-footer');
let pageNode = document.querySelector('.reader-controls-pages');
let oldWrapperNodes = document.querySelectorAll('.inline-comments'); // Multiple divs can show up if users switch chapter too fast
// Clear old comments
if (oldWrapperNodes) {
for (let i = 0; i < oldWrapperNodes.length; i++) {
oldWrapperNodes[i].parentNode.removeChild(oldWrapperNodes[i]);
}
}
// Hide sidebar components. Comment out anything you want to keep
modeNode.style.setProperty('display', 'none', 'important'); // Common keyboard shortcuts
footerNode.style.setProperty('display', 'none', 'important'); // Footer credit
// Push pagination to the bottom of the sidebar
if (footerNode.style.display === 'none') pageNode.classList.add('mt-auto');
addToggleCommentButton();
}
function addToggleCommentButton() {
let oldToggleCommentNode = document.querySelector('.toggle-comments');
/* Reset node state if already exists, else create a new node */
if (oldToggleCommentNode) {
oldToggleCommentNode.querySelector('#kbd_comment_usage').textContent = 'Show comments';
} else {
let toggleCommentNode = document.createElement('div');
toggleCommentNode.className = 'toggle-comments cursor-pointer pt-2 pb-2 pl-2';
/* Inner content */
let kbNode = document.createElement('kbd');
kbNode.innerHTML = ' k/numpad 5';
let whitespaceNode = document.createTextNode('\u00A0');
let iconNode = document.createElement('span');
iconNode.className = 'fas fa-comments fa-fw';
iconNode.setAttribute('aria-hidden', 'true');
iconNode.title = 'Display comments';
let usageNode = document.createElement('span');
usageNode.id = 'kbd_comment_usage';
usageNode.textContent = 'Show comments';
toggleCommentNode.appendChild(kbNode);
toggleCommentNode.appendChild(whitespaceNode);
toggleCommentNode.appendChild(iconNode);
toggleCommentNode.appendChild(whitespaceNode.cloneNode(true));
toggleCommentNode.appendChild(usageNode);
/* END inner content */
/* Event listener */
toggleCommentNode.addEventListener('click', handleToggleComments);
document.addEventListener('keydown', function (e) {
// 12 is 5 in numpad with NumLock off
// 101 is 5 in numpad with NumLock on
// 75 is k
if (e.keyCode === 12 || e.keyCode === 101 || e.keyCode === 75) handleToggleComments();
});
/* END event listener */
let footerNode = document.querySelector('.reader-controls-footer');
footerNode.parentNode.insertBefore(toggleCommentNode, footerNode);
}
}
function handleToggleComments() {
let textNode = document.querySelector('#kbd_comment_usage');
toggleComments(textNode.textContent.indexOf('Hide') === -1);
if (textNode.textContent.indexOf('Hide') === -1) {
textNode.textContent = 'Hide comments';
let oldWrapperNodes = document.querySelector('.inline-comments');
if (!oldWrapperNodes) insertComments();
} else {
textNode.textContent = 'Show comments';
}
}
function toggleComments(isDisplay) {
let oldWrapperNode = document.querySelector('.inline-comments');
if (oldWrapperNode) oldWrapperNode.style.display = !!isDisplay ? '' : 'none';
}
function insertComments() {
let oldChapterId = lastChapterId;
let xhr = new XMLHttpRequest();
xhr.open('GET', '/chapter/' + lastChapterId + '/comments');
xhr.responseType = 'document';
xhr.addEventListener('load', function () {
// Only display comments if user hasn't switched chapter before the request finished
if (xhr.status !== 200 || oldChapterId !== lastChapterId) return;
let htmlDocument = xhr.responseXML.documentElement;
let posts = htmlDocument.querySelectorAll('table .post:not(.post-moderated)');
let wrapperNode = document.createElement('div');
wrapperNode.className = 'inline-comments';
wrapperNode.style.overflow = 'auto';
wrapperNode.style.overflowWrap = 'break-word';
wrapperNode.style.wordWrap = 'break-word';
for (let i = 0; i < posts.length; i++) {
let poster = posts[i].querySelector('td:first-child span');
let post = posts[i].querySelector('.postbody');
let commentNode = document.createElement('div');
if (!(i % 2)) commentNode.style.backgroundColor = 'rgba(0,0,0,.05)';
commentNode.innerHTML = '<div style="color: #06f">' + poster.innerHTML + '</div>' + post.innerHTML;
wrapperNode.appendChild(commentNode);
}
// Button to redirect to chapter thread
let toThread = htmlDocument.querySelector('table+div');
if (toThread) wrapperNode.appendChild(toThread);
let footerNode = document.querySelector('.reader-controls-footer');
footerNode.parentNode.insertBefore(wrapperNode, footerNode);
fixSpoilerButton(wrapperNode);
});
xhr.send();
}
function fixSpoilerButton(post) {
let spoilerBtns = post.querySelectorAll('.btn-spoiler');
for (let i = 0; i < spoilerBtns.length; i++) {
spoilerBtns[i].addEventListener('click', function () {
this.nextElementSibling.classList.toggle('display-none');
});
}
}
let lastPath = '';
let lastChapterId = '';
setInterval(function () {
if (lastPath !== location.pathname) {
lastPath = location.pathname;
let path = location.pathname.split('/');
if (path.length === 4 && !isNaN(path[3]) && lastChapterId !== path[2]) {
lastChapterId = path[2];
updateSidebar();
}
}
}, 500);