// ==UserScript== // @name MangaDex Reader fullscreen // @namespace Teasday // @version 0.3.4.1 // @license CC-BY-NC-SA-4.0 // @description Adds a fullscreen viewer to MangaDex // @author Teasday, Eva // @match https://mangadex.com/chapter/* // @icon https://mangadex.com/favicon.ico // @homepage https://ewasion.github.io/userscripts/mangadex-fullscreen/ // @updateURL https://raw.githubusercontent.com/ewasion/userscripts/master/mangadex-fullscreen/mangadex-fullscreen.meta.js // @downloadURL https://raw.githubusercontent.com/ewasion/userscripts/master/mangadex-fullscreen/mangadex-fullscreen.user.js // @grant none // ==/UserScript== (function() { 'use strict'; // add css const addStyle = function (css) { const head = document.getElementsByTagName('head')[0]; if (!head) return; const style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); }; addStyle(` #reader-container { position: relative; width: calc(100vw - 15px); left: calc(-50vw + 50%); padding: 0 5px; } #reader-container.fullscreen { z-index: 2000; position: absolute; top: 0; left: 0; right: 0; padding: 0; margin: 0; background: #111; display: flex; align-items: center; justify-content: center; flex-flow: column nowrap; height: 100%; min-height: 100%; width: auto; } #reader-container.fullscreen.fitwidth { height: auto; } img.reader { width: auto; max-height: calc(100vh - 50px); } #reader-container.fullscreen img.reader { height: 100%; max-height: 100%; min-height: 100%; max-width: none; } #reader-container.fitwidth img.reader { height: auto; max-height: none; max-width: 100%; } .reader-hover-menu { display: none; cursor: pointer; margin: 5px; font-size: 2em; color: #ddd; text-shadow: #000 1px 1px 4px; transition: all 0.4s; } #reader-container.fullscreen ~ .reader-hover-menu { z-index: 2020; position: fixed; display: block; top: 5px; opacity: 0.3; } #reader-container.fullscreen ~ .reader-hover-menu:hover { opacity: 1; background: rgba(0, 0, 0, .25); box-shadow: 0 0 15px 15px rgba(0, 0, 0, .25); border-radius: 50%; } #reader-container.fullscreen ~ .reader-hover-menu > div:hover { color: #fff; text-shadow: #fff 0 0 10px; transition: all 0.25s; } #reader-container.fullscreen ~ .reader-hover-menu.pull-right { right: 5px; float: right; text-align: right; } #reader-container.fullscreen ~ .reader-hover-menu.pull-left { left: 5px; float: left; text-align: left; } #reader-page-controls, #reader-page-controls div { position: absolute; top: 0; width: 100%; height: 100%; cursor: pointer; /* to help leave a little gap in the middle to make the image right-clickable */ pointer-events: none; } #reader-page-controls div { pointer-events: auto; } #reader-page-controls .prev-page { left: 0; width: 45%; } #reader-page-controls .next-page { right: 0; width: 45%; } #reader-page-controls .prev-chapter { left: 0; width: 15%; } #reader-page-controls .next-chapter { right: 0; width: 15%; } #reader-page-controls .prev-chapter, #reader-page-controls .next-chapter { opacity: 0; font-weight: bold; font-size: 5vh; background: radial-gradient(ellipse at center, rgba(10, 10, 10, .6) 0%, rgba(10, 10, 10, 0) 60%); transition: opacity 0.2s; display: flex; align-items: center; justify-content: center; color: rgba(255, 255, 255, .8); } #reader-page-controls .prev-chapter:before { content: '\xAB'; left: 0; } #reader-page-controls .next-chapter:before { content: '\xBB'; right: 0; } #reader-page-controls .prev-chapter:hover, #reader-page-controls .next-chapter:hover { opacity: 0.9; } .footer { height: auto; } .footer > p { margin-bottom: 0; } `); // control button data const controls = { fullscreen: { icon: 'expand-arrows-alt', titles: ['Enter fullscreen', 'Exit fullscreen'], shortcut: 'F'.charCodeAt(0), }, fitwidth: { icon: 'expand', titles: ['Fit to width (or auto)', 'Fit to height'], shortcut: 'R'.charCodeAt(0), } }; // add html const content = document.querySelector('#content'); const jumpCols = content.querySelectorAll('.row .col-sm-3'); const mangaLink = jumpCols[0].querySelector('a'); // works for now const sizeControls = document.createElement('div'); sizeControls.id = 'reader-size-controls'; sizeControls.classList.add('reader-hover-menu', 'pull-right'); sizeControls.innerHTML = Object.entries(controls).reduce(function(acc, ctrl) { return `${acc}
`; }, ''); const linkControls = document.createElement('div'); linkControls.id = 'reader-link-controls'; linkControls.classList.add('reader-hover-menu', 'pull-left'); linkControls.innerHTML = `
`; const newCol = document.createElement('div'); newCol.classList.add('col-sm-2'); newCol.innerHTML = Object.entries(controls).reduce(function(acc, ctrl, i) { return `${acc}`; }, ''); jumpCols[2].classList.replace('col-sm-3', 'col-sm-2'); jumpCols[3].classList.replace('col-sm-3', 'col-sm-2'); jumpCols[0].parentNode.appendChild(newCol); const readerContainer = document.createElement('div'); readerContainer.id = 'reader-container'; readerContainer.appendChild(document.querySelector('#current_page')); content.appendChild(readerContainer); content.appendChild(linkControls); content.appendChild(sizeControls); const pageControls = document.createElement('div'); pageControls.id = 'reader-page-controls'; pageControls.innerHTML = `
`; readerContainer.appendChild(pageControls); pageControls.querySelector('.prev-page').addEventListener('click', function(evt) { const cur = document.querySelector('[data-id="jump_page"] + .dropdown-menu .selected'); if (cur.previousElementSibling) { cur.previousElementSibling.firstElementChild.click(); } else { this.firstElementChild.click(); } }); pageControls.querySelector('.prev-chapter').addEventListener('click', function(evt) { evt.stopPropagation(); const cur = document.querySelector('[data-id="jump_chapter"] + .dropdown-menu .selected'); if (cur.previousElementSibling) { cur.previousElementSibling.firstElementChild.click(); } else { mangaLink.click(); } }); pageControls.querySelector('.next-page').addEventListener('click', function(evt) { const cur = document.querySelector('[data-id="jump_page"] + .dropdown-menu .selected'); if (cur.nextElementSibling) { cur.nextElementSibling.firstElementChild.click(); } else { this.firstElementChild.click(); } }); pageControls.querySelector('.next-chapter').addEventListener('click', function(evt) { evt.stopPropagation(); const cur = document.querySelector('[data-id="jump_chapter"] + .dropdown-menu .selected'); if (cur.nextElementSibling) { cur.nextElementSibling.firstElementChild.click(); } else { mangaLink.click(); } }); // actual js const updateCtrl = function(ctrl, val) { localStorage.setItem(`reader.${ctrl}`, val); for (const btn of document.querySelectorAll(`.control-${ctrl}`)) { btn.title = controls[ctrl].titles[val ? 1 : 0]; readerContainer.classList.toggle(`${ctrl}`, val); } }; const updateAll = function() { for (let ctrl of Object.keys(controls)) { updateCtrl(ctrl, localStorage.getItem(`reader.${ctrl}`) === 'true'); } }; const listenBtnClick = function(ctrl) { for (const btn of document.querySelectorAll(`.control-${ctrl}`)) { btn.addEventListener('click', function() { updateCtrl(ctrl, localStorage.getItem(`reader.${ctrl}`) !== 'true'); }, false); } }; Object.keys(controls).map(listenBtnClick); window.addEventListener('focus', updateAll, false); updateAll(); })();