// ==UserScript== // // @name Batoto MyFollows // @description Allows you to filter your follows from comis search, links to MU and MAL from comic page, hides blood header, and other things. // @namespace https://greasyfork.org/users/168 // @include http://www.batoto.net/* // @include https://www.batoto.net/* // @exclude http://www.batoto.net/ads/* // @exclude https://www.batoto.net/ads/* // @include http://www.mangaupdates.com/series.html?* // @include https://www.mangaupdates.com/series.html?* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_getResourceURL // @run-at document-start // @version 14.08.25.01 // @resource followingIconR https://www.batoto.net/forums/public/style_images/Sylo/star.png // @resource muIconR https://www.mangaupdates.com/favicon.ico // @resource malIconR http://cdn.myanimelist.net/images/faviconv5.ico // @resource bIconR https://www.batoto.net/forums/favicon.ico // @resource infoIconR https://www.batoto.net/forums/public/style_images/master/information.png // @downloadURL none // ==/UserScript== var settings = { addIconsInHomepage: true, addIconsInSearch: true, followingIcon: GM_getResourceURL('followingIconR'), mangaupdatesLinkInComic: true, muIcon: GM_getResourceURL('muIconR'), malLinkInComic: true, malIcon: GM_getResourceURL('malIconR'), batotoLinkInMU: true, bIcon: GM_getResourceURL('bIconR'), infoIcon: 'https://www.batoto.net/forums/public/style_images/master/information.png', infoButtonOldFollows: true, durationInfoSlide: 0.0, //0.2 default batoto showTotalFollows: true, textColor: '#20B2AA', redirectToOldFollows: false, comicsPopupComicPage: true, comicsPopupForum: true, popupDelay: 1000, hideBloodHeader: true, hyperlinkDescription: true }; var BMF = {}, utils = {}, layouts = {}; BMF.init = function() { layouts.setStyles(); if (window.location.host == 'www.batoto.net') { if (settings.redirectToOldFollows) { BMF.redirectToOldFollows(); } window.addEventListener('DOMContentLoaded', BMF.mainBatoto, false); } else if (window.location.host == 'www.mangaupdates.com') { window.addEventListener('DOMContentLoaded', BMF.mainMangaUpdates, false); } }; BMF.isBlood = function() { var checkBlood = function() { var themeImg = document.getElementsByTagName('link')[1].href, currentIsBlood = themeImg.indexOf('Blood_Images') != -1, storageIsBlood = utils.getStorage('usingBlood') == true; if (currentIsBlood != storageIsBlood) { utils.setStorage('usingBlood', currentIsBlood); window.location.reload(); } }; window.addEventListener('DOMContentLoaded', checkBlood, false); BMF.isBlood = function() { return BMF.isBlood.value; }; BMF.isBlood.value = utils.getStorage('usingBlood') || false; return BMF.isBlood(); }; BMF.redirectToOldFollows = function() { if (window.location.href == 'http://www.batoto.net/myfollows') { window.location.replace('/follows_comics'); } var replaceLinks = function() { document.getElementById('nav_menu_4_trigger').href = '/follows_comics'; if (window.location.pathname == '/') { document.getElementById('hook_watched_items').children[2].firstChild.href = '/follows_comics'; } }; window.addEventListener('DOMContentLoaded', replaceLinks, false); }; BMF.mainBatoto = function() { var path = window.location.pathname; if (path == '/') { if (settings.addIconsInHomepage) BMF.addFollowIconsHome(); } else if (path == '/myfollows') { BMF.saveFollowsList('new'); } else if (path == '/follows_comics') { BMF.saveFollowsList('old'); if (settings.infoButtonOldFollows) BMF.addInfoOldFollows(); var newFollowsLinkBox = layouts.newFollowsLinkBox(); document.getElementById('index_stats').appendChild(newFollowsLinkBox); BMF.categorizeFollows(); if (settings.showTotalFollows) BMF.addTotalFollows(); } else if (path == '/search') { BMF.followsInSearch(); } else if (path.indexOf('/comic/_/comics') == 0) { if (settings.mangaupdatesLinkInComic) BMF.addSearchInMUButton(); if (settings.malLinkInComic) BMF.addSearchInMALButton(); if (settings.comicsPopupComicPage) BMF.setComicPopup(); if (settings.hyperlinkDescription) { var desc = document.getElementsByTagName('tbody')[0].children[6].children[1], regExp = /(https?:\/\/([-\w\.]+)+(:\d+)?(\/([-\w\/_\.]*(\?\S+)?)?)?)/ig; desc.innerHTML = desc.innerHTML.replace(regExp, "$1"); } } else if (path.indeOf('/forums') == 0) { if (settings.comicsPopupForum) BMF.setComicPopup(); } }; BMF.addFollowIconsHome = function() { var followsList = utils.getStorage('followsList'), icon = utils.newImg('Following', settings.followingIcon, 'following_icon'), current = document.getElementById('loading_row_1'), i = 1; while (current) { if (followsList.indexOf(current.previousElementSibling.textContent) != -1) { current.parentNode.insertBefore(icon.cloneNode(false), current.parentNode.childNodes[2]); } i = i + 1; current = document.getElementById('loading_row_' + i.toString()); } }; BMF.saveFollowsList = function(page) { var followsList = [], comics, i, len; if (page == 'new') { comics = document.getElementsByClassName("clearfix")[9].children; i = 8; } else if (page == 'old') { comics = document.getElementsByTagName('strong'); i = 1; } len = comics.length - 3; for (undefined; i < len; i++) { followsList.push(comics[i].textContent); } utils.setStorage('followsList', followsList); }; BMF.addSearchInMUButton = function() { var title = document.getElementsByClassName('ipsType_pagetitle')[0], titleText = title.textContent.replace(/\(Doujinshi\)|\(doujinshi\)/g, ''), button = utils.newAnchorImg('Search in MangaUpdates', settings.muIcon, 'bmf_button'); title.insertBefore(button, title.firstChild); utils.googleRedirectAnchor(button, 'mangaupdates.com/series.html?id=', titleText); }; BMF.addSearchInMALButton = function() { var title = document.getElementsByClassName('ipsType_pagetitle')[0], titleText = title.textContent.replace(/\(Doujinshi\)|\(doujinshi\)/g, '(Doujin)'), button = utils.newAnchorImg('Search in MyAnimeList', settings.malIcon, 'bmf_button'); title.insertBefore(button, title.firstChild); utils.googleRedirectAnchor(button, 'myanimelist.net/manga', titleText); }; BMF.setComicPopup = function() { //uses a ipb function (the same that previews the profiles) var links = [].slice.call(document.getElementsByTagName('a')), length = links.length, anchor, href; for (var i = 0; i < length; i++) { anchor = links[i]; href = anchor.href; if (href.indexOf('http://www.batoto.net/comic/_/') == 0 && href.search(/\?|#/) == -1) { anchor.className += ' _hovertrigger'; anchor.setAttribute('hovercard-ref', 'comicPopup'); anchor.setAttribute('hovercard-id', utils.getComicId(href)); } } ipb.hoverCardRegister.initialize('comicPopup', { 'w': '680px', 'delay': settings.popupDelay, 'position': 'auto', 'ajaxUrl': 'http://www.batoto.net/comic_pop?', 'getId': true, 'setIdParam': 'id' }); }; BMF.addInfoOldFollows = function() { var titles = [].slice.call(document.getElementsByTagName('strong'), 0), len = titles.length - 3, infoButton = document.createElement('a'), title; infoButton.href = 'javascript:void(0)'; infoButton.className = 'info_button'; for (var i = 1; i < len; i++) { title = titles[i].parentNode; title.parentNode.insertBefore(infoButton.cloneNode(false), title); title.previousSibling.addEventListener('click', BMF.showInfoOldFollows, false); } }; BMF.showInfoOldFollows = function() { // modification of the function used by batoto // batoto.net/js/shortcuts_20131231.js //(prototype.js) var that = this, comicid = utils.getComicId(that.nextSibling.href), divid = 'cId_' + comicid; if ($(divid) && $(divid).visible()) { Effect.SlideUp(divid, { duration: settings.durationInfoSlide }); } else if ($(divid) && $(divid).innerHTML != '') { Effect.SlideDown(divid, { duration: settings.durationInfoSlide }); } else { new Ajax.Updater(divid, ipb.vars['home_url'] + '/comic_pop', { parameters: { id: comicid }, method: 'get', onLoaded: function() { var tr = document.createElement('tr'), td = document.createElement('td'), div = document.createElement('div'), table = utils.getNode(that, "pa", 3), nextRow = utils.getNode(that, "pa", 2, "neE", 1); div.id = divid; div.style.display = 'none'; tr.className = 'info_row'; if (BMF.isBlood()) { if (that.parentNode.parentNode.className == 'row1') { div.className += ' grey_row'; } else { div.className += ' white_row'; } } td.setAttribute('colspan', '2'); td.className = 'info_col'; td.appendChild(div); tr.className = nextRow.className.replace(/row0|row1|altrow/g, ''); tr.appendChild(td); table.insertBefore(tr, nextRow); that.parentNode.previousElementSibling.setAttribute('rowspan', '3'); }, onSuccess: function() { setTimeout(function() { new Effect.SlideDown(divid, { duration: settings.durationInfoSlide }); }, 50); } }); } }; BMF.updateFollowsList = function() { //only if there are no request being made currently var loading = document.getElementById('ajax_loading'); if (!loading || loading.style.display == 'none') { new Ajax.Request('/follows_comics', { method: 'get', onSuccess: function(response) { var followsArray = [], regExp = /topic">(.*?)<\/strong><\/a>/g, comic = regExp.exec(response.responseText); while (comic) { followsArray.push(utils.decodeHtmlEntity(comic[1])); comic = regExp.exec(response.responseText); } utils.setStorage('followsList', followsArray); //updates all the previous comic results BMF.matchFollowsSearch('all'); if (settings.addIconsInSearch) BMF.addIconsToMatches(); if (BMF.hideMatches) BMF.changeMatchesDisplay('hide'); } }); } }; BMF.categorizeFollows = function() { var rows = document.getElementsByTagName('table')[0].rows, len = rows.length, read = 0, reading = 0, noReads = 0, className, row1, row2, link1, link2; for (var a = 0; a < len; a = a + 2) { row1 = rows[a]; row2 = rows[a + 1]; if (row1.children[2].textContent != 'Last Read: No Record') { link1 = row1.children[2].children[0].href; link2 = row2.firstElementChild.children[1].href; if (link1 == link2) { className = ' bmf_read'; read++; } else { className = ' bmf_reading'; reading++; } } else { className = ' bmf_noreads'; noReads++; } row1.className = row2.className += className; } var viewOptionsBox = layouts.viewOptionsBox(read, reading, noReads); document.getElementById("index_stats").insertBefore(viewOptionsBox, null); var views = function() { var table = document.getElementsByClassName('ipb_table')[0]; if (this.value == 'hide') { table.classList.add('h_' + this.name); } else { table.classList.remove('h_' + this.name); } }; for (var e = 1; e < 4; e++) { document.getElementById('show' + e).addEventListener('click', views, false); document.getElementById('hide' + e).addEventListener('click', views, false); } }; BMF.addTotalFollows = function() { var total = document.getElementsByTagName('strong').length - 3, text = document.createElement('strong'); text.className = 'total_follows'; text.textContent = 'You are following ' + total + ' comics!'; document.getElementsByClassName('maintitle')[0].appendChild(text); }; BMF.followsInSearch = function() { BMF.matchFollowsSearch(); if (settings.addIconsInSearch) BMF.addIconsToMatches(); //button to update your follows list var searchBar = document.getElementsByClassName('input_submit')[2].parentNode, updateButton = document.createElement('a'); updateButton.href = '
Alter Settings' + '
'; return box; }; layouts.newFollowsLinkBox = function() { var box = document.createElement('div'); box.className = 'general_box alt clearfix'; box.innerHTML = '

Follows by Chapters (new follows)

' + '
'; return box; }; utils.setStorage = function(key, value) { localStorage.setItem('BMF_' + key, JSON.stringify(value)); }; utils.getStorage = function(key) { return JSON.parse(localStorage.getItem('BMF_' + key)); }; utils.decodeHtmlEntity = function(encoded) { var div = document.createElement('div'); div.innerHTML = encoded; return div.firstChild.nodeValue; }; utils.newAnchorImg = function(title, src, className, href) { var button = document.createElement('a'), img = utils.newImg(title, src); button.className = className; button.href = href || 'javascript:void(0)'; button.appendChild(img); return button; }; utils.newImg = function(title, src, className) { var icon = document.createElement('img'); icon.alt = ''; icon.title = title; icon.src = src; icon.className = className || ''; return icon; }; utils.getComicId = function(url) { var temp = url.substring(url.lastIndexOf('r') + 1); return temp; }; utils.getNode = function() { var args = arguments, node = args[0], len = args.length, a, action, times, b; for (a = 1; a < len; a = a + 2) { action = args[a]; times = args[a + 1]; b = 0; if (action == 'pa') { //undefined because of jslint for (undefined; b < times; b++) { node = node.parentNode; } } else if (action == 'fiE') { for (undefined; b < times; b++) { node = node.firstElementChild; } } else if (action == 'prE') { for (undefined; b < times; b++) { node = node.previousElementSibling; } } else if (action == 'neE') { for (undefined; b < times; b++) { node = node.nextElementSibling; } } else if (action == 'fi') { for (undefined; b < times; b++) { node = node.firstChild; } } else if (action == 'pr') { for (undefined; b < times; b++) { node = node.previousSibling; } } else if (action == 'ne') { for (undefined; b < times; b++) { node = node.nextSibling; } } } return node; }; utils.googleRedirectAnchor = function(button, site, query) { //removes extra whitespaces, search operators and encode non latin characters query = encodeURIComponent(query.replace(/-|"|\*|\:/g, ' ').replace(/\s+/g, ' ')); //first url sends you first to google and then redirects you, //this causes sometimes a half rendering of 'google.com' first. //second url can give you the direct link to the target site, //but only through a request with google as referer and response.finalUrl (GM_) var redirectURL = 'https://www.google.com/webhp?#btnI=I&sitesearch=' + site + '&q=' + query, requestURL = 'https://www.google.com/search?btnI=I&sitesearch=' + site + '&q=' + query; button.href = redirectURL; GM_xmlhttpRequest({ method: 'HEAD', url: requestURL, headers: { Referer: 'https://www.google.com/' }, onload: function(response) { if (response.finalUrl.indexOf(site) != -1) { button.href = response.finalUrl; } else { button.href = requestURL; } } }); }; BMF.init();