MangaDex Inline Comments

Display chapter comments inside MangaDex's sidebar

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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 = '&nbsp;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);