- // ==UserScript==
- // @name Manga Loader
- // @namespace http://www.fuzetsu.com/MangaLoader
- // @version 1.0.5
- // @description Loads entire chapter into the page in a long strip format
- // @copyright 2014+, fuzetsu
- // @match http://www.batoto.net/read/*
- // @match http://mangafox.me/manga/*/*/*
- // @match http://readms.com/r/*/*/*/*
- // @match http://g.e-hentai.org/s/*/*
- // @match http://exhentai.org/s/*/*
- // @match http://www.fakku.net/*/*/read*
- // @match http://www.mangareader.net/*/*
- // @match http://www.mangahere.co/manga/*/*
- // @match http://www.mangapanda.com/*/*
- // @match http://mangadeer.com/manga/*/*/*/*
- // @match http://mngacow.com/*/*
- // ==/UserScript==
-
- // set to true for manga load without prompt
- var BM_MODE = false;
-
- var scriptName = 'Manga Loader';
-
- /**
- Sample Implementation:
- {
- match: "http://domain.com/.*" // the url to react to for manga loading
- , img: '#image' // css selector to get the page's manga image
- , next: '#next_page' // css selector to get the link to the next page
- , numpages: '#page_select' // css selector to get the number of pages. elements like (select, span, etc)
- , curpage: '#page_select' // css selector to get the current page. usually the same as numPages if it's a select element
- , nextchap: '#next_chap' // css selector to get the link to the next chapter
- , prevchap: '#prev_chap' // same as above except for previous
- , wait: 3000 // how many ms to wait before auto loading (to wait for elements to load)
-
- Any of the CSS selectors can be functions instead that return the desired value.
- }
- */
-
- var implementations = [
- { // Batoto
- match: "http://www.batoto.net/read/.*"
- , img: '#comic_page'
- , next: '#full_image + div > a'
- , numpages: '#page_select'
- , curpage: '#page_select'
- , nextchap: 'select[name=chapter_select]'
- , prevchap: 'select[name=chapter_select]'
- , invchap: true
- }
- , { // MangaPanda
- match: "http://www.mangapanda.com/.*/[0-9]*"
- , img: '#img'
- , next: '.next a'
- , numpages: '#pageMenu'
- , curpage: '#pageMenu'
- , nextchap: 'td.c5 + td a'
- , prevchap: 'table.c6 tr:last-child td:last-child a'
- }
- , { // MangaFox
- match: "http://mangafox.me/manga/.*/.*/.*"
- , img: '#image'
- , next: '.next_page'
- , numpages: 'select.m'
- , curpage: 'select.m'
- , nextchap: '#chnav p + p a'
- , prevchap: '#chnav a'
- }
- , { // MangaStream
- match: "http://readms.com/r/.*/.*/.*/.*"
- , img: '#manga-page'
- , next: '.next a'
- , numpages: function() {
- var lastPage = getEl('.subnav-wrapper .controls .btn-group:last-child ul li:last-child');
- return parseInt(lastPage.textContent.match(/[0-9]/g).join(''), 10);
- }
- }
- , { // MangaReader
- match: "http://www.mangareader.net/.*/.*"
- , img:'#img'
- , next: '.next a'
- , numpages: '#pageMenu'
- , curpage: '#pageMenu'
- , nextchap: 'td.c5 + td a'
- , prevchap: 'table.c6 tr:last-child td:last-child a'
- }
- , { // MangaCow
- match: "^http://mngacow\.com/.*/[0-9]*"
- , img: '.prw > a > img'
- , next: '.prw > a'
- , numpages: 'select.cbo_wpm_pag'
- , curpage: 'select.cbo_wpm_pag'
- , nextchap: function(prev) {
- var chapSel = getEl('select.cbo_wpm_chp');
- var nextChap = chapSel.options[chapSel.selectedIndex + (prev ? 1 : -1)];
- if(nextChap) {
- return 'http://mngacow.com/' + window.location.pathname.slice(1, window.location.pathname.slice(1).indexOf('/') + 2) + nextChap.value;
- }
- }
- , prevchap: function() {
- return this.nextchap(true);
- }
- }
- , { // MangaHere
- match: "^http://www.mangahere.co/manga/.*/.*"
- , img: '#viewer img'
- , next: '.next_page'
- , numpages: 'select.wid60'
- , curpage: 'select.wid60'
- , nextchap: '#top_chapter_list'
- , prevchap: '#top_chapter_list'
- , wait: 2000
- }
- , { // MangaDeer
- match: "^http://mangadeer\.com/manga/.*"
- , img: '.img-link > img'
- , next: '.page > span:last-child > a'
- , numpages: '#sel_page_1'
- , curpage: '#sel_page_1'
- , nextchap: function(prev) {
- var ddl = getEl('#sel_book_1');
- var index = ddl.selectedIndex + (prev ? -1 : 1);
- if(index >= ddl.options.length) return;
- var mangaName = window.location.href.slice(window.location.href.indexOf('manga/') + 6);
- mangaName = mangaName.slice(0, mangaName.indexOf('/'));
- return 'http://mangadeer.com/manga/' + mangaName + ddl.options[index].value + '/1';
- }
- , prevchap: function() {
- return this.nextchap(true);
- }
- }
- , { // GEH/EXH
- match: "http://(g.e-hentai|exhentai).org/s/.*/.*"
- , img: '.sni > a > img, #img'
- , next: '.sni > a, #i3 a'
- }
- , { // Fakku (doesn' work...)
- match: "^http://www.fakku.net/.*/.*/read#page=[0-9]*"
- , img: '.current-page'
- , next: '#image a'
- , numpages: '.drop'
- , curpage: '.drop'
- }
- ];
-
- var log = function(msg, type) {
- type = type || 'log';
- if(type === 'exit') {
- throw scriptName + ' exit: ' + msg;
- } else {
- console[type](scriptName + ' ' + type + ': ', msg);
- }
- };
-
- var getEl = function(q, c) {
- if(!q) return;
- return (c || document).querySelector(q);
- };
-
- var storeGet = function(key) {
- if(typeof GM_getValue === "undefined") {
- return localStorage.getItem(key);
- }
- return GM_getValue(key);
- };
-
- var storeSet = function(key, value) {
- if(typeof GM_setValue === "undefined") {
- return localStorage.setItem(key, value);
- }
- return GM_setValue(key, value);
- };
-
- var storeDel = function(key) {
- if(typeof GM_deleteValue === "undefined") {
- return localStorage.removeItem(key);
- }
- return GM_deleteValue(key);
- };
-
- var extractInfo = function(selector, mod, context) {
- selector = this[selector];
- if(typeof selector === 'function') {
- return selector.call(this);
- }
- var elem = getEl(selector, context)
- , option;
- mod = mod || {};
- if(elem) {
- switch (elem.nodeName.toLowerCase()) {
- case 'img':
- return elem.getAttribute('src');
- case 'a':
- return elem.getAttribute('href');
- case 'ul':
- return elem.children.length;
- case 'select':
- switch(mod.type) {
- case 'index':
- return elem.options.selectedIndex;
- case 'value':
- option = elem.options[elem.options.selectedIndex + (mod.val || 0)] || {};
- return option.value;
- default:
- return elem.options.length;
- }
- break;
- default:
- return elem.textContent;
- }
- }
- };
-
- var toStyleStr = function(obj) {
- var stack = []
- , key;
- for(key in obj) {
- if(obj.hasOwnProperty(key)) {
- stack.push(key + ':' + obj[key]);
- }
- }
- return stack.join(';');
- };
-
- var createButton = function(text, action, styleStr) {
- var button = document.createElement('button');
- button.textContent = text;
- button.onclick = action;
- button.setAttribute('style', styleStr || '');
- return button;
- };
-
- var getViewer = function(prevChapter, nextChapter) {
- var viewerCss = toStyleStr({
- 'background-color': 'black'
- , 'text-align': 'center'
- , 'font': '.9em sans-serif'
- })
- , imagesCss = toStyleStr({
- 'margin': '5px 0'
- })
- , navCss = toStyleStr({
- 'text-decoration': 'none'
- , 'color': 'black'
- , 'background': 'linear-gradient(white, #ccc)'
- , 'padding': '3px 10px'
- , 'border': '1px solid #ccc'
- , 'border-radius': '5px'
- })
- ;
- // clear all styles and scripts
- document.head.innerHTML = '';
- // and navigation
- var nav = (prevChapter ? '<a href="' + prevChapter + '" style="' + navCss + '" class="ml-chap-nav">Prev Chapter</a> ' : '') +
- (storeGet('mAutoload') ? '' : '<a href="" style="' + navCss + '">Exit</a> ') +
- (nextChapter ? '<a href="' + nextChapter + '" style="' + navCss + '" class="ml-chap-nav">Next Chapter</a>' : '');
- document.body.innerHTML = nav + '<div id="images" style="' + imagesCss + '"></div>' + nav;
- // set the viewer css
- document.body.setAttribute('style', viewerCss);
- // set up listeners for chapter navigation
- document.addEventListener('click', function(evt) {
- if(evt.target.className.indexOf('ml-chap-nav') !== -1) {
- log('next chapter will autoload');
- storeSet('autoload', 'yes');
- }
- }, false);
- return getEl('#images');
- };
-
- var imageCss = toStyleStr({
- 'max-width': '100%'
- , 'display': 'block'
- , 'margin': '3px auto'
- });
-
- var addImage = function(src, loc, callback) {
- var image = new Image();
- image.onerror = function() {
- log('failed to load ' + src);
- image.remove();
- };
- image.onload = callback;
- image.src = src;
- image.setAttribute('style', imageCss);
- loc.appendChild(image);
- };
-
- var loadManga = function(imp) {
- var ex = extractInfo.bind(imp)
- , url = ex('img')
- , nextUrl = ex('next')
- , numPages = ex('numpages')
- , curPage = ex('curpage', {type:'index'}) || 1
- , nextChapter = ex('nextchap', {type:'value', val: (imp.invchap && -1) || 1})
- , prevChapter = ex('prevchap', {type:'value', val: (imp.invchap && 1) || -1})
- , xhr = new XMLHttpRequest()
- , d = document.implementation.createHTMLDocument()
- , getPageInfo = function() {
- var page = d.body;
- d.body.innerHTML = xhr.response;
- try {
- // find image and append
- addImage(ex('img', null, page), loc);
- // find next link and load it
- loadNextPage(ex('next', null, page));
- } catch(e) {
- log('error getting details from next page, assuming end of chapter.');
- }
- }
- , loadNextPage = function(url) {
- if(++curPage > numPages) {
- log('reached "numPages" ' + numPages + ', assuming end of chapter');
- return;
- }
- if(lastUrl === url) {
- log('last url is the same as current, assuming end of chapter');
- return;
- }
- lastUrl = url;
- xhr.open('get', url);
- xhr.onload = getPageInfo;
- xhr.onerror = function() {
- log('failed to load page, aborting', 'error');
- };
- xhr.send();
- }
- , lastUrl, loc
- ;
-
- if(!url || !nextUrl) {
- log('failed to retrieve ' + (!url ? 'image url' : 'next page url'), 'exit');
- }
-
- loc = getViewer(prevChapter, nextChapter);
-
- addImage(url, loc);
- loadNextPage(nextUrl);
-
- };
-
- var pageUrl = window.location.href
- , btnLoadCss = toStyleStr({
- 'position': 'fixed'
- , 'bottom': 0
- , 'right': 0
- , 'padding': '5px'
- , 'margin': '0 10px 10px 0'
- , 'z-index': '1000'
- })
- , btnLoad
- ;
-
- // used when switching chapters
- var autoload = storeGet('autoload');
- // manually set by user in menu
- var mAutoload = storeGet('mAutoload') || false;
-
- // register menu command
- typeof GM_registerMenuCommand === 'function' && GM_registerMenuCommand((mAutoload ? 'Disable' : 'Enable') + ' manga autoload', function() {
- storeSet('mAutoload', !mAutoload);
- window.location.reload();
- });
-
- // clear autoload
- storeDel('autoload');
-
- log('starting...');
-
- var success = implementations.some(function(imp) {
- if(imp.match && (new RegExp(imp.match, 'i')).test(pageUrl)) {
- if(BM_MODE || mAutoload || autoload) {
- setTimeout(loadManga.bind(null, imp),imp.wait || 0);
- return true;
- }
- // append button to dom that will trigger the page load
- btnLoad = createButton('Load Manga', function(evt) {
- loadManga(imp);
- this.remove();
- }, btnLoadCss);
- document.body.appendChild(btnLoad);
- return true;
- }
- });
-
- if(!success) {
- log('no implementation for ' + pageUrl, 'error');
- }