返回首頁 

Toonily Manga Loader

Forces Toonily to load all manga images at once and dynamically loads them into a single page strip with a stats window.


Install this script?
// ==UserScript==// @name         Toonily Manga Loader// @namespace    github.com/longkidkoolstar// @version      1.1// @description  Forces Toonily to load all manga images at once and dynamically loads them into a single page strip with a stats window.// @author       longkidkoolstar// @match        https://toonily.com/webtoon/*// @require      https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/js/all.min.js// @grant        GM.setValue// @grant        GM.getValue// @grant        GM.deleteValue// @license      MIT// ==/UserScript==(function() {'use strict';let imageUrls = [];let loadedImages = 0;let totalImages = 0;let reloadMode = false;// Function to simulate click on the "Load All Images" switchfunction enableLoadAllImages() {const loadAllImagesButton = document.querySelector('#btn-lazyload-controller');if (loadAllImagesButton && loadAllImagesButton.querySelector('.fa-toggle-off')) {loadAllImagesButton.click(); // Simulate clicking to enable "Load All Images"console.log("Forcing 'Load All Images'...");}}// Hooking into XMLHttpRequest to capture all image URLs from AJAX responsesfunction interceptAjaxRequests() {const originalOpen = XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open = function(method, url, ...args) {this.addEventListener('load', function() {if (url.includes('/chapters/manga')) {const parser = new DOMParser();const doc = parser.parseFromString(this.responseText, 'text/html');const imgs = doc.querySelectorAll('img.wp-manga-chapter-img');imgs.forEach(img => {const imgUrl = img.src.trim();if (!imageUrls.includes(imgUrl)) {imageUrls.push(imgUrl);}});}});return originalOpen.apply(this, [method, url, ...args]);};}// Function to remove all other HTML elements except for the manga containerfunction removeOtherElements() {const bodyChildren = Array.from(document.body.children);bodyChildren.forEach(child => {if (!child.id || child.id !== 'manga-container') {child.remove();}});}// Helper function to create a page container for each imagefunction createPageContainer(pageUrl) {const container = document.createElement('div');container.className = 'manga-page-container';const img = document.createElement('img');img.src = pageUrl;img.style.maxWidth = '100%';img.style.display = 'block';img.style.margin = '0px auto';  //Note: The 0px dictates the space between images and the auto center themimg.onload = function() {loadedImages++;updateStats(); // Update the stats when an image is fully loaded};addClickEventToImage(img);container.appendChild(img);return container;}// Function to extract already loaded image URLs from the pagefunction extractImageUrlsFromPage() {const images = document.querySelectorAll('.reading-content img.wp-manga-chapter-img');images.forEach(img => {const src = img.src.trim();if (src.startsWith('https://cdn.toonily.com/chapters/manga') && !imageUrls.includes(src)) {imageUrls.push(src);}});totalImages = imageUrls.length;}// Helper function to create an exit buttonfunction createExitButton() {const exitButton = document.createElement('button');exitButton.textContent = 'Exit';exitButton.style.backgroundColor = '#e74c3c';exitButton.style.color = '#fff';exitButton.style.border = 'none';exitButton.style.padding = '10px';exitButton.style.margin = '10px auto';exitButton.style.display = 'block';  // Center the buttonexitButton.style.cursor = 'pointer';exitButton.style.borderRadius = '5px';exitButton.addEventListener('click', function() {window.location.reload();  // Reload the page when clicked});return exitButton;}// Function to load all manga images into a single stripfunction loadMangaImages() {const mangaContainer = document.createElement('div');mangaContainer.id = 'manga-container';document.body.appendChild(mangaContainer);imageUrls.forEach((url, index) => {const pageContainer = createPageContainer(url);// Add exit button to the first loaded pageif (index === 0) {const topExitButton = createExitButton();mangaContainer.appendChild(topExitButton);}mangaContainer.appendChild(pageContainer);// Add exit button to the last loaded pageif (index === imageUrls.length - 1) {const bottomExitButton = createExitButton();mangaContainer.appendChild(bottomExitButton);}});removeOtherElements(); // Remove all other page elements after loading}// Function to update the stats windowfunction updateStats() {const statsPages = document.querySelector('.ml-stats-pages');const loadingImages = document.querySelector('.ml-loading-images');const totalImagesDisplay = document.querySelector('.ml-total-images');if (statsPages) statsPages.textContent = `${loadedImages}/${totalImages} loaded`;if (loadingImages) loadingImages.textContent = `${totalImages - loadedImages} images loading`;if (totalImagesDisplay) totalImagesDisplay.textContent = `${totalImages} images in chapter`;}// Function to create the stats windowasync function createStatsWindow() {const statsWindow = document.createElement('div');statsWindow.className = 'ml-stats';statsWindow.style.position = 'fixed';statsWindow.style.bottom = '10px';statsWindow.style.right = '10px';statsWindow.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';statsWindow.style.padding = '1px';statsWindow.style.color = 'white';statsWindow.style.borderRadius = '8px';statsWindow.style.zIndex = '9999';statsWindow.style.transition = 'opacity 0.3s';statsWindow.style.opacity = '0.5';statsWindow.addEventListener('mouseenter', function() {statsWindow.style.opacity = '1';});statsWindow.addEventListener('mouseleave', function() {statsWindow.style.opacity = '0.5';});const statsWrapper = document.createElement('div');statsWrapper.style.display = 'flex';statsWrapper.style.alignItems = 'center'; // Center verticallyconst collapseButton = document.createElement('span');collapseButton.className = 'ml-stats-collapse';collapseButton.title = 'Hide stats';collapseButton.textContent = '>>';collapseButton.style.cursor = 'pointer';collapseButton.style.marginRight = '10px'; // Space between button and contentcollapseButton.addEventListener('click', async function() {contentContainer.style.display = contentContainer.style.display === 'none' ? 'block' : 'none';collapseButton.textContent = contentContainer.style.display === 'none' ? '<<' : '>>';await GM.setValue('statsCollapsed', contentContainer.style.display === 'none');});(async () => {const isCollapsed = await GM.getValue('statsCollapsed', false);contentContainer.style.display = isCollapsed ? 'none' : 'block';collapseButton.textContent = isCollapsed ? '<<' : '>>';})();const contentContainer = document.createElement('div');contentContainer.className = 'ml-stats-content';const statsText = document.createElement('span');statsText.className = 'ml-stats-pages';statsText.textContent = `0/0 loaded`; // Initial statsconst infoButton = document.createElement('i');infoButton.innerHTML = '<i class="fas fa-question-circle"></i>';infoButton.title = 'See userscript information and help';infoButton.style.marginLeft = '5px';infoButton.style.marginRight = '5px'; // Add space to the rightinfoButton.addEventListener('click', function() {alert('This userscript loads manga pages in a single view. Click on an image to reload.');});const moreStatsButton = document.createElement('i');moreStatsButton.innerHTML = '<i class="fas fa-chart-pie"></i>';moreStatsButton.title = 'See detailed page stats';moreStatsButton.style.marginRight = '5px'; // Add space to the rightmoreStatsButton.addEventListener('click', function() {const statsBox = document.querySelector('.ml-floating-msg');statsBox.style.display = statsBox.style.display === 'block' ? 'none' : 'block';});const refreshButton = document.createElement('i');refreshButton.innerHTML = '<i class="fas fa-sync-alt"></i>';refreshButton.title = 'Click an image to reload it.';refreshButton.addEventListener('click', function() {reloadMode = !reloadMode;refreshButton.style.color = reloadMode ? 'orange' : '';console.log(`Reload mode is now ${reloadMode ? 'enabled' : 'disabled'}.`);});const miniExitButton = document.createElement('button');miniExitButton.innerHTML = '<i class="fas fa-sign-out-alt"></i>';miniExitButton.title = 'Exit the Manga Loader';miniExitButton.style.marginLeft = '10px'; // Space between other buttonsminiExitButton.style.backgroundColor = '#e74c3c';  // Red color for the buttonminiExitButton.style.color = '#fff';miniExitButton.style.border = 'none';miniExitButton.style.padding = '1px 5px';miniExitButton.style.borderRadius = '5px';miniExitButton.style.cursor = 'pointer';miniExitButton.addEventListener('click', function() {window.location.reload();  // Refresh the page});contentContainer.appendChild(statsText);contentContainer.appendChild(infoButton);contentContainer.appendChild(moreStatsButton);contentContainer.appendChild(refreshButton);contentContainer.appendChild(miniExitButton);  // Add mini exit button to the contentstatsWrapper.appendChild(collapseButton);statsWrapper.appendChild(contentContainer);statsWindow.appendChild(statsWrapper);const statsBox = document.createElement('pre');statsBox.className = 'ml-box ml-floating-msg';statsBox.innerHTML = `<strong>Stats:</strong><br><span class="ml-loading-images">0 images loading</span><br><span class="ml-total-images">0 images in chapter</span><br><span class="ml-loaded-pages">0 pages parsed</span>`;statsBox.style.display = 'none'; // Initially hiddenstatsWindow.appendChild(statsBox);document.body.appendChild(statsWindow);}// Add the click event to imagesfunction addClickEventToImage(image) {image.addEventListener('click', function() {if (reloadMode) {const imgSrc = image.dataset.src || image.src;image.src = ''; // Clear the src to trigger reloadsetTimeout(() => {image.src = imgSrc; // Retry loading after clearing}, 100); // Short delay to ensure proper reload}});}// Create and add the "Load Manga" buttonconst loadMangaButton = document.createElement('button');loadMangaButton.textContent = 'Load Manga';loadMangaButton.style.position = 'fixed';loadMangaButton.style.bottom = '10px';loadMangaButton.style.right = '10px';loadMangaButton.style.zIndex = '9999';loadMangaButton.style.padding = '10px';loadMangaButton.style.backgroundColor = '#f39c12';loadMangaButton.style.border = 'none';loadMangaButton.style.borderRadius = '5px';loadMangaButton.style.cursor = 'pointer';// Add hover effect to the buttonloadMangaButton.addEventListener('mouseover', function() {loadMangaButton.style.backgroundColor = '#ff5500';});loadMangaButton.addEventListener('mouseout', function() {loadMangaButton.style.backgroundColor = '#f39c12';});const mangaInfo = document.querySelector("body > div.wrap > div > div > div > div.profile-manga.summary-layout-1 > div > div > div > div.tab-summary");if (!mangaInfo) {document.body.appendChild(loadMangaButton);loadMangaButton.addEventListener('click', async function() {enableLoadAllImages(); // Force the site to load all imagesinterceptAjaxRequests(); // Hook into AJAX requests to capture image URLsextractImageUrlsFromPage(); // Initially extract image URLs from the pageloadMangaImages(); // Load all collected imagesloadMangaButton.remove(); // Remove the button after loadingawait createStatsWindow(); // Create the stats window});}// Wait for the DOM to finish loading before adding the buttonwindow.addEventListener('DOMContentLoaded', function() {if (!mangaInfo) {document.body.appendChild(loadMangaButton);}});})();