NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // // @name Batoto MyFollows // @version 15.04.08.1 // @description Filter your follows from comic search; info button and sorting in the old follows page; links between Batoto-MU-Mal and other features. // @namespace https://greasyfork.org/users/168 // @updateURL https://greasyfork.org/scripts/365-batoto-myfollows/code/Batoto%20MyFollows.meta.js // @downloadURL https://greasyfork.org/scripts/365-batoto-myfollows/code/Batoto%20MyFollows.user.js // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @include *://bato.to/* // @include *://www.mangaupdates.com/* // @include *://myanimelist.net/* // @run-at document-start // @noframes // ==/UserScript== //----------------------------------------------------- // Batoto //----------------------------------------------------- var batoto = { init: function() { if (GM_config.get('batoto_defaultToOldFollows')) { this.defaultToOldFollows(); } ready(function() { batoto.loadingImg.init(); if (/^\/$/.test(path)) { batoto.p_home.main(); } else if (/^\/myfollows/.test(path)) { batoto.p_newFollows.main(); } else if (/^\/follows_comics/.test(path)) { batoto.p_oldFollows.main(); } else if (/^\/search/.test(path)) { batoto.p_comicSearch.main(); } else if (/^\/comic\/_\/comics\/$/.test(path)) { batoto.p_comicDir.main(); } else if (/^\/comic\/_\/comics\/.+/.test(path)) { batoto.p_comic.main(); } else if (/^\/group\/_\/.\/.+/.test(path)) { batoto.p_group.main(); } else if (/^\/forums/.test(path)) { batoto.p_forums.main(); } batoto.addOptionsButton(); }); }, comicIdRegex: /bato\.to\/comic\/_\/.*\-r(\d+)\/?$/i, getComicId: function(url) { if (!url) return null; var id = url.match(batoto.comicIdRegex); return id !== null ? id[1] : id; }, chapterRegex: /(?:Vol\.([0-9.]+) )?Ch\.(?:(?: *- *)?([0-9.]+)([a-uA-U])?){0,2}(?: ?\)?[vV]([0-9]))?/, parseChapter: function(string) { var match = string.match(batoto.chapterRegex), obj = {}; if (!match) { return null; } obj.vol = match[1] === undefined ? false : match[1] * 1, obj.ch = match[2] === undefined ? true : match[2] * 1, obj.ch = match[3] === undefined ? obj.ch : obj.ch + (match[3].toLowerCase().charCodeAt(0) - 96) * 0.1, obj.ver = match[4] === undefined ? 1 : match[4] * 1; return obj; }, parseDate: function(string) { var now, num, month, hour, match = string.match(/(\d+) (\w{3})\w* (\d+) - (\d+):(\d+) ?(\w+)?/); if (match) { month = match[2]; if (month === 'Jan') month = 0; else if (month === 'Feb') month = 1; else if (month === 'Mar') month = 2; else if (month === 'Apr') month = 3; else if (month === 'May') month = 4; else if (month === 'Jun') month = 5; else if (month === 'Jul') month = 6; else if (month === 'Aug') month = 7; else if (month === 'Sep') month = 8; else if (month === 'Oct') month = 9; else if (month === 'Nov') month = 10; else if (month === 'Dec') month = 11; hour = match[4] === '12' ? 0 : match[4] * 1; hour = match[6] === 'AM' ? hour : hour + 12; return [match[3] * 1, month, match[1] * 1, hour, match[5] * 1]; } match = string.match(/(An?|\d+) (week|day|hour|minute)s? ago/); if (match) { now = new Date(); num = match[1] * 1 || 1; if (match[2] === 'week') now.setDate(now.getDate() - num * 7); else if (match[2] === 'day') now.setDate(now.getDate() - num); else if (match[2] === 'hour') now.setHours(now.getHours() - num); else if (match[2] === 'minute') now.setMinutes(now.getMinutes() - num); return [now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes()]; } match = string.match(/(Today|Yesterday), (\d+):(\d+) (\w+)/); if (match) { now = new Date(); if (match[1] === 'Yesterday') now.setDate(now.getDate() - 1); hour = match[2] === '12' ? 0 : match[2] * 1; hour = match[4] === 'AM' ? hour : hour + 12; return [now.getFullYear(), now.getMonth(), now.getDate(), hour, match[3] * 1]; } return [0, 0, 0, 0, 0]; }, theme: null, getTheme: function() { if (this.theme === null) { var storedTheme = getStorage('batoto_theme', true); if (this.isLoggedIn()) { ready(function() { var pageTheme = querySel('#new_skin_menucontent > .selected > a'), dropList = document.getElementById('new_skin_menucontent'); if (pageTheme) { pageTheme = pageTheme.textContent.toLowerCase().replace(/ .*/, ''); } else { pageTheme = 'subway'; } if (pageTheme !== storedTheme) { setStorage('batoto_theme', pageTheme, true); reload(); return; } dropList.addEventListener('click', function(e) { pageTheme = e.target.textContent.toLowerCase().replace(/ .*/, ''); setStorage('batoto_theme', pageTheme, true); }, true); }); } else { setStorage('batoto_theme', 'subway', true); } this.theme = storedTheme || 'subway'; } return this.theme; }, loggedIn: null, isLoggedIn: function() { if (this.loggedIn === null) { var storedLoggedIn = getStorage('batoto_logged_in', true); ready(function() { var pageLoggedIn = Window.ipb.vars['member_id'] !== 0; setStorage('batoto_logged_in', pageLoggedIn, true); if (!storedLoggedIn && pageLoggedIn === true) { reload(); } }); this.loggedIn = storedLoggedIn || false; } return this.loggedIn || false; }, defaultToOldFollows: function() { var oldFollows = 'http://bato.to/follows_comics'; if (path === '/myfollows' && location.search === '') { Window.stop(); location.replace(oldFollows); } ready(function() { document.getElementById('nav_menu_4_trigger').href = oldFollows; if (path === '/') { querySel('#hook_watched_items > div:last-child > a').href = oldFollows; } }); }, setComicPopup: function(selector) { //uses the same ipb function that previews the profiles var links = selector ? querySelAll(selector) : document.getElementsByTagName('a'), popupConf, anchor, id; for (var i = 0, len = links.length; i < len; i++) { anchor = links[i]; id = batoto.getComicId(anchor.getAttribute('href')); if (id !== null) { anchor.className += ' _hovertrigger'; anchor.setAttribute('hovercard-ref', 'comicPopup'); anchor.setAttribute('hovercard-id', id); } } popupConf = { 'w': '680px', 'delay': GM_config.get('batoto_comicsPopupDelay'), 'position': 'auto', 'ajaxUrl': Window.ipb.vars['home_url'] + '/comic_pop?', 'getId': true, 'setIdParam': 'id' }; if (typeof cloneInto === 'function') { popupConf = cloneInto(popupConf, unsafeWindow); } Window.ipb.hoverCardRegister.initialize('comicPopup', popupConf); }, followsList: { updating: false, update: function(callback) { if (!this.updating) { var self = this; self.updating = true; batoto.loadingImg.fadeIn(); var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState === 4) { if (request.status === 200) { var followsList = [], regex = /"View topic"><strong>(.*?)<\/strong>/g, title = regex.exec(request.responseText); while (title) { followsList.push(decodeHtmlEntity(title[1])); title = regex.exec(request.responseText); } setStorage('follows_list', toCharObject(followsList)); if (typeof callback === 'function') { callback(); } } batoto.loadingImg.fadeOut(); self.updating = false; } }; request.open('GET', '/follows_comics', true); request.send(); } } }, loadingImg: { init: function() { var div = document.getElementById('ajax_loading'); if (!div) { div = createElemHTML(Window.ipb.templates['ajax_loading']); div.style.display = 'none'; document.getElementById('ipboard_body').appendChild(div); } if (typeof cloneInto === 'function') { this.config = cloneInto(this.config, Window); } }, config: { duration: 0.25 }, queue: 0, effect: null, fadeIn: function() { this.queue++; if (this.queue === 1) { if (this.effect) { this.effect.cancel(); this.effect = null; } this.effect = new Window.Effect.Appear('ajax_loading', this.config); } }, fadeOut: function() { this.queue--; if (this.queue === 0) { if (this.effect) { this.effect.cancel(); this.effect = null; } this.effect = new Window.Effect.Fade('ajax_loading', this.config); } } }, addOptionsButton: function() { var btn = createElem('li'), anchor = createElem('a', { href: 'javascript:void(0)' }); anchor.textContent = 'Open BFM Options'; btn.appendChild(anchor); anchor.addEventListener('click', function() { GM_config.open(); }, false); querySel('#footer_utilities .ipsList_inline.left').appendChild(btn); } }; //------------------ // Specific pages //------------------ batoto.p_home = { main: function() { if (GM_config.get('batoto_followingIcon_home') && batoto.isLoggedIn()) { this.addFollowsIcons(); } }, addFollowsIcons: function() { var followsList = getStorage('follows_list'), icon = createElem('img', { title: 'Following', alt: '', src: getResource('batoto_followingIcon'), class: 'bmf_following_icon' }), titles = querySelAll("td > a[style='font-weight:bold;']"), comic; if (!followsList) { batoto.followsList.update(this.addFollowsIcons); return; } for (var i = 1, len = titles.length; i < len; i += 2) { comic = titles[i]; if (inCharObject(followsList, comic.textContent)) { comic.parentNode.insertBefore(icon.cloneNode(false), comic.previousSibling); } } } }; batoto.p_newFollows = { main: function() { if (!batoto.isLoggedIn()) return; this.saveFollowsList(); }, saveFollowsList: function() { var followsList = [], titles = document.getElementById('categories').parentNode.children; for (var i = 7, len = titles.length - 7; i < len; i++) { followsList.push(titles[i].textContent); } setStorage('follows_list', toCharObject(followsList)); } }; batoto.p_oldFollows = { main: function() { if (!batoto.isLoggedIn()) return; this.getComics(); this.saveFollowsList(); this.sorting.main(); if (GM_config.get('batoto_showTotalFollows')) { this.showTotalFollows(); } if (GM_config.get('batoto_addInfoBtns')) { this.info.addBtns(); } if (GM_config.get('batoto_addBoxToNewFollows')) { this.addBoxToNewFollows(); } if (GM_config.get('batoto_categorizeFollows')) { this.categorizeFollows(); } }, comics: null, getComics: function() { if (this.gotComics) return; var nodes = document.getElementsByClassName('ipb_table')[0].getElementsByTagName('tr'), len = nodes.length, comics = this.comics = new Array(len / 2); for (var i = 0; i < len; i += 2) { comics[i / 2] = { first: nodes[i], second: nodes[i + 1] }; } this.gotComics = true; }, getComicsTitle: function() { if (this.gotComicsTitle) return; var comics = this.comics; for (var i = 0, len = comics.length; i < len; i++) { comics[i].title = comics[i].first.getElementsByTagName('strong')[0].textContent; } this.gotComicsTitle = true; }, getComicsLastRead: function() { if (this.gotComicsLastRead) return; var comics = this.comics, comic, text, c; for (var i = 0, len = comics.length; i < len; i++) { comic = comics[i]; text = comic.first.lastElementChild.textContent; comic.lastReadDate = batoto.parseDate(text); c = batoto.parseChapter(text); if (c === null) { comic.lastReadCh = false; comic.lastReadVol = false; } else { comic.lastReadCh = c.ch; comic.lastReadVol = c.vol; } } this.gotComicsLastRead = true; }, getComicsLastUpdate: function() { if (this.gotComicsLastUpdate) return; var comics = this.comics, comic, dateText, chText, c; for (var i = 0, len = comics.length; i < len; i++) { comic = comics[i]; dateText = comic.second.lastElementChild.textContent; chText = comic.second.firstElementChild.textContent; comic.lastUpdateDate = batoto.parseDate(dateText); c = batoto.parseChapter(chText); if (c === null) { comic.lastUpdateCh = false; comic.lastUpdateVol = false; } else { comic.lastUpdateCh = c.ch; comic.lastUpdateVol = c.vol; } } this.gotComicsLastUpdate = true; }, getComicsUnreadCh: function() { if (this.gotUnreadCh) return; this.getComicsLastUpdate(); this.getComicsLastRead(); var comics = this.comics, comic; for (var i = 0, len = comics.length; i < len; i++) { comic = comics[i]; if (typeof comic.lastUpdateCh === 'number' && comic.lastReadCh === false) { comic.unreadChs = comic.lastUpdateCh; } else if (typeof comic.lastUpdateCh === 'number' && typeof comic.lastReadCh === 'number') { if (comic.lastUpdateCh === comic.lastReadCh) { comic.unreadChs = -0.03; } else { comic.unreadChs = comic.lastUpdateCh - comic.lastReadCh; } } else if (comic.lastUpdateCh === false && comic.lastReadCh === false) { comic.unreadChs = -0.04; } else if (comic.lastUpdateCh === true && comic.lastReadCh === false) { comic.unreadChs = -0.01; } else { comic.unreadChs = -0.02; } } this.gotUnreadCh = true; }, saveFollowsList: function() { var comics = this.comics, followsList = []; if (!this.gotComicsTitle) { this.getComicsTitle(); } for (var i = 0, len = comics.length; i < len; i++) { followsList.push(comics[i].title); } setStorage('follows_list', toCharObject(followsList)); }, showTotalFollows: function() { var total = String(this.comics.length), elem = document.createElement('strong'); elem.id = 'bmf_total_follows'; elem.textContent = total + ' Comics!'; document.getElementsByClassName('maintitle')[0].appendChild(elem); }, sorting: { modes: { "Last Update's Date": { prepare: function() { batoto.p_oldFollows.getComicsLastUpdate(); }, sortCallback: function(a, b) { var arrayA = a.lastUpdateDate, arrayB = b.lastUpdateDate, i; for (var i = 0; i < 5; i++) { if (arrayB[i] > arrayA[i]) return 1; if (arrayB[i] < arrayA[i]) return -1; } return 0; } }, "Last Update's Ch": { prepare: function() { batoto.p_oldFollows.getComicsLastUpdate(); }, sortCallback: function(a, b) { var A = a.lastUpdateCh, B = b.lastUpdateCh; if (A === B) { A = a.lastUpdateVol; B = b.lastUpdateVol; A = A === false ? -2 : (A === true ? -1 : A); B = B === false ? -2 : (B === true ? -1 : B); return B - A; } A = A === false ? -2 : (A === true ? -1 : A); B = B === false ? -2 : (B === true ? -1 : B); return B - A; } }, "Last Read's Date": { prepare: function() { batoto.p_oldFollows.getComicsLastRead(); }, sortCallback: function(a, b) { var arrayA = a.lastReadDate, arrayB = b.lastReadDate, i; for (var i = 0; i < 5; i++) { if (arrayB[i] > arrayA[i]) return 1; if (arrayB[i] < arrayA[i]) return -1; } return 0; } }, "Last Read's Ch": { prepare: function() { batoto.p_oldFollows.getComicsLastRead(); }, sortCallback: function(a, b) { var A = a.lastReadCh, B = b.lastReadCh; if (A === B) { A = a.lastReadVol; B = b.lastReadVol; A = A === false ? -2 : (A === true ? -1 : A); B = B === false ? -2 : (B === true ? -1 : B); return B - A; } A = A === false ? -2 : (A === true ? -1 : A); B = B === false ? -2 : (B === true ? -1 : B); return B - A; } }, 'Unread Chs': { prepare: function() { batoto.p_oldFollows.getComicsUnreadCh(); }, sortCallback: function(a, b) { return b.unreadChs - a.unreadChs; } }, 'Title': { prepare: function() { batoto.p_oldFollows.getComicsTitle(); }, sortCallback: function(a, b) { if (a.title > b.title) return 1; if (a.title < b.title) return -1; return 0; } } }, main: function() { if (GM_config.get('batoto_autoSort')) { this.run(GM_config.get('batoto_defaultSort')); } var self = this, mainTitle = document.getElementsByClassName('maintitle')[0], handler = function() { self.run(this.textContent); }, btnClass = 'bmf_sort_btn', mode, btn; if (GM_config.get('batoto_adaptSortButton')) { btnClass += ' f_icon'; } if (batoto.getTheme() === 'blood') { btnClass += ' ipsButton_secondary'; } else { btnClass += ' ipsButton'; } for (var modeName in self.modes) { mode = self.modes[modeName]; btn = createElem('button', { class: btnClass, href: 'javascript:void(0)' }); btn.textContent = modeName; btn.addEventListener('click', handler, false); mainTitle.appendChild(btn); } }, prev: null, reversed: false, run: function(modeName) { var comics = batoto.p_oldFollows.comics, modes = this.modes, mode = modes[modeName], revI; if (typeof mode.prepare === 'function') { mode.prepare(); } if (this.prev === modeName) { this.reversed = !this.reversed; } else { this.reversed = false; this.prev = modeName; } revI = this.reversed ? -1 : 1; comics.sort(function(a, b) { var i = mode.sortCallback(a, b) * revI; if (i === 0) { i = modes.Title.sortCallback(a, b); } return i; }); this.updateTable(true, true); }, updateTable: function(updateSort, updateClasses) { if (!updateSort && !updateClasses) return; var table = querySel('.clearfix > table'), tbody = table.firstElementChild, comics = batoto.p_oldFollows.comics, rowEven = false, node1, nodeInfo, node2; table.removeChild(tbody); for (var i = 0, len = comics.length; i < len; i++) { node1 = comics[i].first; nodeInfo = node1.nextElementSibling; node2 = comics[i].second; if (updateSort) { tbody.appendChild(node1); if (nodeInfo !== node2) { tbody.appendChild(nodeInfo); } tbody.appendChild(node2); } if (updateClasses) { node1.classList.toggle('row1', rowEven); node1.classList.toggle('row0', !rowEven); if (nodeInfo !== node2) { nodeInfo.classList.toggle('row1', rowEven); nodeInfo.classList.toggle('row0', !rowEven); } node2.classList.toggle('row1', rowEven); node2.classList.toggle('row0', !rowEven); rowEven = !rowEven; } } table.appendChild(tbody); } }, info: { addBtns: function() { var that = batoto.p_oldFollows, comics = that.comics, table = querySel('.clearfix > table > tbody'), infoBtn = createElem('a', { href: 'javascript:void(0)', class: 'bmf_info_button' }), title; for (var i = 0, len = comics.length; i < len; i++) { title = comics[i].first.getElementsByTagName('a')[1]; title.parentNode.insertBefore(infoBtn.cloneNode(false), title); } table.addEventListener('click', function(e) { if (e.target.className === 'bmf_info_button') { that.info.showBox(e.target); } }, false); }, showBox: function(infoBtn) { // modification of the function used by batoto: // bato.to/js/shortcuts_20131231.js //(prototype.js) var anchor = infoBtn.nextElementSibling, comicId = batoto.getComicId(anchor.getAttribute('href')), div = document.getElementById('cId_' + comicId); if (div === null) { div = this.addBoxContainer(infoBtn, comicId); } if (div.children.length === 0) { batoto.loadingImg.fadeIn(); var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState === 4) { batoto.loadingImg.fadeOut(); if (request.status === 200) { div.innerHTML = request.responseText; setTimeout(function() { div.style.display = ''; }, 30); } } }; request.open('GET', '/comic_pop?id=' + comicId, true); request.send(); } else if (div.style.display === '') { div.style.display = 'none'; } else { div.style.display = ''; } }, addBoxContainer: function(infoBtn, comicId) { var nextRow = infoBtn.parentNode.parentNode.nextElementSibling, div = createElem('div', { id: 'cId_' + comicId, style: 'display: none;' }), tr = createElem('tr', { class: nextRow.className.replace('altrow', '') + ' bmf_info_row' }), td = createElem('td', { colspan: '2', style: 'border-bottom-width:0 !important' }); td.appendChild(div); tr.appendChild(td); querySel('.ipb_table > tbody').insertBefore(tr, nextRow); infoBtn.parentNode.previousElementSibling.setAttribute('rowspan', '3'); return div; } }, categorizeFollows: function() { var rows = document.getElementsByTagName('table')[0].rows, read = 0, reading = 0, noReads = 0, className, row1, row2, link1, link2, viewOptionsBox, views; for (var i = 0, len = rows.length; i < len; i = i + 2) { row1 = rows[i]; row2 = rows[i + 1]; if (row1.children[2].textContent !== 'Last Read: No Record') { link1 = row1.children[2].children[0].getAttribute('href'); link2 = row2.firstElementChild.children[1].getAttribute('href'); if (link1 === link2) { className = ' bmf_read'; read++; } else { className = ' bmf_reading'; reading++; } } else { className = ' bmf_noreads'; noReads++; } row1.className = row2.className += className; } viewOptionsBox = createElemHTML( '<div class="general_box clearfix"><h3>View Settings</h3><div class="_sbcollapsable">' + '<a href="javascript:void(0)" onclick=\"$(\'view\').toggle();">Alter Settings</a>' + '<div id="view" style="display: none;">' + '<table><tbody>' + '<tr><td>All read (' + read + ')</td><td style="text-align: left; vertical-align: top; padding: 6px 0;">' + '<label><input id="show1" type="radio" name="read" value="show" checked="checked"> Show</label>' + '<label><input id="hide1" type="radio" name="read" value="hide" style="margin-left: 6px"> Hide</label></td></tr>' + '<tr><td>Reading (' + reading + ')</td><td style="text-align: left; vertical-align: top; padding: 6px 0;">' + '<label><input id="show2" type="radio" name="reading" value="show" checked="checked"> Show</label>' + '<label><input id="hide2" type="radio" name="reading" value="hide" style="margin-left: 6px"> Hide</label></td></tr>' + '<tr><td>No reads (' + noReads + ')</td><td style="text-align: left; vertical-align: top; padding: 6px 0;">' + '<label><input id="show3" type="radio" name="noreads" value="show" checked="checked"> Show</label>' + '<label><input id="hide3" type="radio" name="noreads" value="hide" style="margin-left: 6px"> Hide</label></td></tr>' + '</tbody></table></div></div>'); document.getElementById('index_stats').insertBefore(viewOptionsBox, null); views = function() { var table = document.getElementsByClassName('ipb_table')[0]; if (this.value === 'hide') { table.classList.add('bmf_hide_' + this.name); } else { table.classList.remove('bmf_hide_' + this.name); } }; for (var i = 1; i < 4; i++) { document.getElementById('show' + i).addEventListener('click', views, false); document.getElementById('hide' + i).addEventListener('click', views, false); } }, addBoxToNewFollows: function() { var box = document.createElement('div'); box.className = 'general_box alt clearfix'; box.innerHTML = '<h3><img src=" ' + Window.ipb.vars['rate_img_on'] + '" alt="">' + '\nFollows by Chapters (new follows)</h3>' + '<div class="recent_activity _sbcollapsable">' + '<div class="tab_toggle_content" style="text-align:center;">' + '<a href="/myfollows?noRedirect">Right here!</a></div></div>'; document.getElementById('index_stats').appendChild(box); } }; batoto.p_comicSearch = { main: function() { if (!batoto.isLoggedIn()) return; this.runMatch('last'); this.addUpdateFollowsBtn(); this.addExcludeOption(); this.watchForNewResults(); }, runMatch: function(table) { var comics = this.getMatchComics(table), addFollowingIcon = GM_config.get('batoto_followingIcon_search'); if (comics === false) { batoto.followsList.update(function() { batoto.p_comicSearch.runMatch('last'); }); return; } var icon = createElem('img', { title: 'Following', alt: '', src: getResource('batoto_followingIcon'), class: 'bmf_following_icon' }), matches = comics[0], mismatches = comics[1], comic, title; for (var i = 0, len = matches.length; i < len; i++) { comic = matches[i]; title = comic.firstChild; if (addFollowingIcon && title.childNodes.length === 2) { title.insertBefore(icon.cloneNode(false), title.firstChild.nextSibling); } comic.parentNode.parentNode.setAttribute('class', 'bmf_match'); } for (var i = 0, len = mismatches.length; i < len; i++) { // if the icon was added but then you unfollowed the comic // and updated the list, then css will hide the icon mismatches[i].parentNode.parentNode.setAttribute('class', 'bmf_mismatch'); } }, getMatchComics: function(table) { var followsList = getStorage('follows_list'), matches = [], mismatches = [], comics, comic, len, i; if (!followsList) { return false; } if (table === 'all') { comics = document.getElementsByTagName('strong'); i = 1; len = comics.length - 3; } else if (table === 'last') { table = document.getElementsByClassName('chapters_list').length - 1; comics = document.getElementsByClassName('chapters_list')[table].getElementsByTagName('strong'); i = 0; len = comics.length; } for (; i < len; i++) { comic = comics[i]; if (inCharObject(followsList, comic.textContent.trim())) { matches.push(comic); } else { mismatches.push(comic); } } return [matches, mismatches]; }, matchesVisibility: function(action) { if (action === 'hide') { document.getElementById('comic_search_results').className = 'bmf_hide_matches'; // hides the info boxes of matches if shown var infoBoxes = document.getElementsByClassName('ipsBox'), infoBox; for (var i = 0, len = infoBoxes.length; i < len; i++) { infoBox = infoBoxes[i].parentNode.parentNode; if (infoBox.previousElementSibling.className === 'bmf_match') { infoBox.style.display = 'none'; } } } else if (action === 'show') { document.getElementById('comic_search_results').className = ''; } }, addExcludeOption: function() { var self = this, optionsBar = document.getElementById('advanced_options').children[0], optionInput = document.createElement('tr'); optionInput.innerHTML = '<tr><td style="text-align: right; font-weight: bold;">Include MyFollows:</td>' + '<td style="text-align: left; vertical-align: top; padding: 8px 0;">' + '<label><input id="incl_follows" type="radio" name="follows" value="show" checked="checked"> Yes</label>' + '<label><input id="excl_follows" type="radio" name="follows" value="hide" style="margin-left: 4px"> No</label>' + '</td></tr>'; optionsBar.insertBefore(optionInput, optionsBar.children[6]); document.getElementById('incl_follows').addEventListener('click', function() { self.matchesHidden = false; self.matchesVisibility('show'); }, false); document.getElementById('excl_follows').addEventListener('click', function() { self.matchesHidden = true; self.matchesVisibility('hide'); }, false); }, watchForNewResults: function() { var self = this, tablesContainer = document.getElementById('comic_search_results'), observer = new MutationObserver(function(mutations) { if (mutations.length === 2 || mutations.length === 4) { self.runMatch('last'); } }); observer.observe(tablesContainer, { childList: true }); }, addUpdateFollowsBtn: function() { var self = this, searchBar = querySel('#comic_search_form > div > div'), updateBtn = createElem('a', { id: 'bmf_upd_follows_btn', class: 'input_submit', title: 'Update Follows', href: 'javascript:void(0)' }); updateBtn.textContent = 'Update Follows'; searchBar.appendChild(updateBtn); updateBtn.addEventListener('click', function() { batoto.followsList.update(function() { self.runMatch('all'); if (self.matchesHidden) { self.matchesVisibility('hide'); } }); }, false); } }; batoto.p_comicDir = { main: function() { if (GM_config.get('batoto_comicsPopup_comicDir')) { batoto.setComicPopup(); } } }; batoto.p_comic = { main: function() { if (GM_config.get('batoto_comicsPopup_comic')) { batoto.setComicPopup(); } if (GM_config.get('i_batoto_mu')) { this.addInterlinkBtn('mu', 'Search in MangaUpdates'); } if (GM_config.get('i_batoto_mal')) { this.addInterlinkBtn('mal', 'Search in MyAnimeList'); } if (GM_config.get('batoto_hyperlinkDesc')) { this.doHyperlinkDesc(); } if (GM_config.get('batoto_showMoreRecentBtn')) { this.addShowMoreRecentBtn(); } if (GM_config.get('batoto_autoOpenImgSpoilers') || GM_config.get('batoto_addSpoilersType')) { this.doSpoilersTweaks(); } if (GM_config.get('batoto_fixPageTitle')) { this.fixPageTitle(); } }, addInterlinkBtn: function(target, title) { var comicTitle = document.getElementsByClassName('ipsType_pagetitle')[0], comicName = comicTitle.textContent.replace(/\(doujinshi\)/gi, '(Doujin)'), iconUrl = getResource(target + '_icon'), button = createImgBtn(title, 'bmf_interlink_btn ipsButton_secondary', iconUrl); comicTitle.insertBefore(button, comicTitle.firstChild); interlinks.getUrl(target, comicName, location.href, function(url) { button.href = url; }); button.addEventListener('click', function(event) { if (!event.ctrlKey || !event.shiftKey) return; event.preventDefault(); interlinks.saveFromPrompt(location.href, target); }, false); }, doHyperlinkDesc: function() { var desc = document.getElementsByTagName('tbody')[0].children[6].children[1], regex = /(\b(https?):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[\-A-Z0-9+&@#\/%=~_|])/gim; desc.innerHTML = desc.innerHTML.replace(regex, '<a href="$1">$1</a>'); }, addShowMoreRecentBtn: function() { var btn = createElem('div', { id: 'bmf_show_recent_btn' }); var a = createElem('a', { class: 'input_submit', href: '/comic/_/comics/?sort_col=record_saved&sort_order=desc' }); a.textContent = 'Show More'; btn.appendChild(a); querySelAll('.general_box')[2].appendChild(btn); }, doSpoilersTweaks: function() { var spoilers = document.getElementsByClassName('bbc_spoiler_wrapper'), autoOpen = GM_config.get('batoto_autoOpenImgSpoilers'), addType = GM_config.get('batoto_addSpoilersType'), spoiler, imgs, text, type; for (var i = 0, len = spoilers.length; i < len; i++) { spoiler = spoilers[i]; imgs = spoiler.getElementsByClassName('bbc_img').length !== 0; text = spoiler.textContent.trim() !== ''; if (imgs && !text && autoOpen) { spoiler.previousElementSibling.value = 'Hide'; spoiler.firstElementChild.style.display = ''; } if (addType) { if (!imgs && text) { type = 'Spoiler (Text)'; } else if (imgs && text) { type = 'Spoiler (Text/Image)'; } else if (imgs && !text) { type = 'Spoiler (Image)'; } spoiler.parentNode.firstElementChild.textContent = type; } } }, fixPageTitle: function() { document.title = document.title.split('- Scanlations ')[0] + '- Batoto'; } }; batoto.p_group = { main: function() { if (GM_config.get('batoto_comicsPopup_group')) { batoto.setComicPopup(); } } }; batoto.p_forums = { main: function() { if (GM_config.get('batoto_comicsPopup_forums')) { batoto.setComicPopup(); } } }; //----------------------------------------------------- // MangaUpdates //----------------------------------------------------- var mu = { init: function() { ready(function() { var url = location.href; if (/\/series\.html\?(?:.+&|&?)id=[^&]+/.test(url)) { mu.p_comic.main(); } mu.addOptionsButton(); }); }, comicIdRegex: /mangaupdates\.com\/series\.html\?(?:.+&|&?)id=([^&]+)/i, getComicId: function(url) { if (!url) return null; var id = url.match(mu.comicIdRegex); return id !== null ? id[1] : id; }, addOptionsButton: function() { var trBtn = createElem('tr'), tr = createElem('tr'), td = createElem('td'), a = document.createElement('a'); trBtn.className = 'newsname', trBtn.appendChild(a); td.height = '15', tr.appendChild(td); a.textContent = 'Open BMF Opts'; a.style.color = 'rgb(49, 70, 110)'; a.addEventListener('click', function() { GM_config.open(); }, false); document.querySelector('#right_row2 tbody').appendChild(tr); document.querySelector('#right_row2 tbody').appendChild(trBtn); } }; //------------------ // Specific pages //------------------ mu.p_comic = { main: function() { if (GM_config.get('i_mu_mal')) { this.addInterlinkBtn('mal', ' Search in MyAnimeList'); } if (GM_config.get('i_batoto_mu')) { this.addInterlinkBtn('batoto', ' Search in Batoto'); } }, addInterlinkBtn: function(target, title) { var comicTitle = document.getElementsByClassName('releasestitle')[0], comicName = comicTitle.textContent; if (comicName.indexOf('(Novel)') === -1) { var button = createImgBtn(title, 'bmf_interlink_btn', getResource(target + '_icon')); comicTitle.parentNode.insertBefore(button, comicTitle); interlinks.getUrl(target, comicName, location.href, function(url) { button.href = url; }); button.addEventListener('click', function(event) { if (!event.ctrlKey || !event.shiftKey) return; event.preventDefault(); interlinks.saveFromPrompt(location.href, target); }, false); } } }; //----------------------------------------------------- // MyAnimeList //----------------------------------------------------- var mal = { init: function() { ready(function() { if (/manga/.test(path)) { mal.p_comic.main(); } mal.addOptionsButton(); }); }, comicIdRegex: /myanimelist\.net\/manga\/(\d+)/i, getComicId: function(url) { if (!url) return null; var id = url.match(mal.comicIdRegex); return id !== null ? id[1] : id; }, addOptionsButton: function() { var toolbox = querySel('#rightcontentunder'), btn = createElem('a', { href: 'javascript:void(0)' }); btn.textContent = 'Open BMF Options'; btn.addEventListener('click', function() { GM_config.open(); }, false); toolbox.insertBefore(btn, toolbox.lastElementChild); } }; mal.p_comic = { main: function() { if (GM_config.get('i_mu_mal')) { this.addInterlinkBtn('mu', 'Search in MangaUpdates'); } if (GM_config.get('i_batoto_mal')) { this.addInterlinkBtn('batoto', 'Search in Batoto'); } }, addInterlinkBtn: function(target, title) { var comicTitle = querySel('#contentWrapper h1 span').previousSibling, comicName = comicTitle.textContent, button = createImgBtn(title, 'bmf_interlink_btn', getResource(target + '_icon')); comicTitle.parentNode.insertBefore(button, comicTitle); interlinks.getUrl(target, comicName, location.href, function(url) { button.href = url; }); button.addEventListener('click', function(event) { if (!event.ctrlKey || !event.shiftKey) return; event.preventDefault(); interlinks.saveFromPrompt(location.href, target); }, false); } }; //----------------------------------------------------- // Interlinks between sites //----------------------------------------------------- var interlinks = { getUrl: function(targetName, comicName, sourceUrl, callback) { var target = this.sites[targetName], targetId, source, sourceName, sourceId, searchRequest, savedUrl, temporalUrl; if (sourceUrl) { for (sourceName in this.sites) { source = this.sites[sourceName]; if (source.urlRegex.test(sourceUrl)) { sourceId = source.getId(sourceUrl); if (sourceId) { savedUrl = this.getSaved(sourceName, sourceId, targetName); if (savedUrl) { callback(savedUrl, 'saved'); return; } } break; } } } if (!savedUrl) { if (GM_config.get('i_searchEngine') === 'Google') { searchRequest = googleRequest; } else if (GM_config.get('i_searchEngine') === 'DuckDuckGo') { searchRequest = duckRequest; } temporalUrl = searchRequest(comicName, target.queryUrl, function(res) { if (res.success && res.finalUrl.indexOf(target.queryUrl) !== -1) { callback(res.finalUrl, 'finalUrl'); targetId = target.getId(res.finalUrl); if (GM_config.get('i_saveUrls') && sourceId && targetId) { interlinks.save(sourceName, sourceId, targetName, targetId); } } else if (res.finalUrl !== 'not supported') { callback(res.searchUrl, 'search'); } }); callback(temporalUrl, 'temporal'); } }, getSaved: function(sourceName, sourceId, targetName) { var key = 'interlinks_' + sourceName + '->' + targetName, stored = getStorage(key), targetId; if (!stored) { return false; } targetId = stored[sourceId]; if (targetId) { return this.sites[targetName].comicUrl.replace('$comicId$', targetId); } return false; }, save: function(sourceName, sourceId, targetName, targetId) { var key = 'interlinks_' + sourceName + '->' + targetName, stored = getStorage(key); if (!stored) { stored = {}; } if (targetId === false) { delete stored[sourceId]; } else { stored[sourceId] = targetId; } setStorage(key, stored); }, saveFromPrompt: function(sourceUrl, targetName) { var sourceName, source, sourceId, userUrl, target, targetId; for (sourceName in this.sites) { source = this.sites[sourceName]; if (source.urlRegex.test(sourceUrl)) { sourceId = source.getId(sourceUrl); break; } } if (!sourceId) { alert('Sorry. Couldn\'t get the ID for the comic here.'); return; } userUrl = prompt('Write the URL for ' + targetName.toUpperCase() + ' (leave empty to remove):'); if (userUrl === null) return; userUrl = userUrl.trim(); if (userUrl === '') { this.save(sourceName, sourceId, targetName, false); alert('Removed successfully.'); return; } target = this.sites[targetName]; if (!target.urlRegex.test(userUrl)) { alert('Doesn\'t look like a valid URL for ' + targetName.toUpperCase() + '.'); return; } targetId = this.sites[targetName].getId(userUrl); if (!targetId) { alert('Sorry. Couldn\'t get the ID for the comic.'); return; } this.save(sourceName, sourceId, targetName, targetId); alert('Saved successfully.'); }, sites: { batoto: { urlRegex: /(bato\.to)|(batoto\.net)/i, getId: batoto.getComicId, comicUrl: 'http://bato.to/comic/_/comics/-r$comicId$', queryUrl: 'bato.to/comic/_/' }, mu: { urlRegex: /mangaupdates\.com/i, getId: mu.getComicId, comicUrl: 'https://www.mangaupdates.com/series.html?id=$comicId$', queryUrl: 'mangaupdates.com/series.html?' }, mal: { urlRegex: /myanimelist\.net/i, getId: mal.getComicId, comicUrl: 'http://myanimelist.net/manga/$comicId$', queryUrl: 'myanimelist.net/manga/' } } }; //----------------------------------------------------- // CSS //----------------------------------------------------- var CSS = { load: function(site) { if (!document.head) { setTimeout(function() { CSS.load(site); }, 1); return; } GM_addStyle(this[site]()); }, batoto: function() { var css = [ // various '.bmf_following_icon {', 'vertical-align: top;', 'margin-left: 1px; }', '.general_box.clearfix img {', 'vertical-align: bottom; }', '#pu_____hover___comicPopup_inner .ipsBox td {', 'padding: 4px; }', '#pu_____hover___comicPopup_inner .ipsBox td span ~ span {', 'display: none !important; }', '#pu_____hover___comicPopup_inner .ipsBox tr:last-child {', 'display: none; }', 'body table.ipb_table td {', 'padding: ' + GM_config.get('batoto_followRowsHeight') + '; }', 'body .chapters_list td {', 'padding: 3px 3px 4px 3px !important; }', // search '.bmf_hide_matches .bmf_match {', 'display: none; }', '.bmf_mismatch .bmf_following_icon {', 'display: none; }', '#bmf_upd_follows_btn {', 'position: absolute;', 'top: 10px;', 'left: 110px;', 'font-weight: normal; }', // old follows '.bmf_sort_btn {', 'float: right;', 'margin : 0 0 0 5px !important;', 'min-width: 50px !important;', 'height: 26px !important;', 'line-height: 26px !important;', 'padding: 0 11px !important;', batoto.getTheme() === 'sylo' ? 'font-size: 11px;' : '', 'font-family: helvetica,arial,sans-serif; }', '.clearfix > h3.maintitle {', 'line-height: 26px;', 'padding-top: 11px;', 'padding-bottom: 11px; }', '.bmf_hide_read .bmf_read, ', '.bmf_hide_reading .bmf_reading, ', '.bmf_hide_noreads .bmf_noreads {', 'display: none; }', '.bmf_read sup {', 'display: none; }', '.bmf_info_row > td {', 'padding: 0px !important;', 'border: none !important; }', '.bmf_info_row .ipsBox {', 'background-color: transparent; }', '.bmf_info_button {', 'padding: 0px 8px;', 'margin-right: 3px;', 'background-repeat: no-repeat;', 'background-position: center;', 'background-image: url(' + getResource('batoto_infoIcon') + '); }', '#bmf_total_follows {', 'font-size: 12px;', 'padding-left: 8px;', 'color: ' + GM_config.get('batoto_totalTextColor') + ';', 'display: inline;', 'vertical-align: baseline;', 'line-height: 12px; }', 'table .row0 ~ .row0 > td:last-child > div, table .row1 ~ .row1 > td:last-child > div {', 'vertical-align: -3px; }', 'table .row0 + .row0 > td > div, table .row1 + .row1 > td > div {', 'vertical-align: -5px; }', // comic page '.bmf_interlink_btn {', 'display: inline-block !important;', 'width: 28px;', 'height: 28px !important;', 'vertical-align: -2px !important;', 'text-align: center;', 'margin: 0px 8px 0px 0px !important;', 'padding: 0px !important; }', '.bmf_interlink_btn img {', 'padding: 6px;', 'margin: 0px !important;', 'vertical-align: -2px !important; }', '#bmf_show_recent_btn {', 'width: 100%;', 'margin-top: 5px;', 'position: absolute;', 'display: flex; }', '#bmf_show_recent_btn a {', 'margin: 0 auto;', 'font-weight: bold;', 'font-size: 12px; }' ].join(''); if (GM_config.get('batoto_hideBloodHeader') && batoto.getTheme() === 'blood') { css += [ '#branding { ', 'position: absolute !important; } ' ].join(''); } var customPrimary = readCookie('customPrimary'), customSecondary = readCookie('customSecondary'), customPattern = readCookie('customPattern'); if (batoto.getTheme() === 'subway' && (customPrimary || customSecondary || customPattern)) { if (customPrimary) { css += ".cpe #primary_nav, .cpe .maintitle, .cpe #community_app_menu > li.active > a, .cpe .col_c_icon img[src*='f_'], .cpe .f_icon, .cpe .topic_buttons li a, .cpe .pagination .pages li.active, .cpe #primary_extra_menucontent, .cpe #more_apps_menucontent, .cpe .post_block h3, .cpe .mini_pagination a, .cpe .user_controls li a, .cpe #vnc_filter_popup_close, .cpe #search .submit_input, .cpe .col_f_icon img, .cpe .ipsBadge_green, body.cpe #logo, .cpe #themeToggle, .cpe #primary_extra_menucontent, .cpe #more_apps_menucontent, .cpe .submenu_container{ background-color: #" + customPrimary + ';}.cpe .forum_name a, .cpe .subforums a{ color: #' + customPrimary + ';}'; } if (customSecondary) { css += 'body.cpe, #themeEditor #editPattern span, .cpe #socialLinks li, .cpe #secondary_navigation, .cpe ul.post_controls a:hover, .cpe .answerBadgeInPost, .cpe .ipsLikeButton.ipsLikeButton_enabled, .input_submit, .cpe .ipsTag{ background-color: #' + customSecondary + ';}.cpe ul.post_controls a{ color: #' + customSecondary + ';}'; } if (customPattern) { css += 'body.cpe, .cpe #logo, .cpe #primary_nav, .cpe #community_app_menu > li.active > a, .cpe #themeToggle {background-image: url(http://bato.to/forums/public/style_images/subway/patterns/' + customPattern + ');}'; } } return css; }, mu: function() { var css = [ // comic page '.bmf_interlink_btn {', 'background: #e4e4e4;', 'background: linear-gradient(to bottom ,#f6f6f6, #d7d7d7);', 'box-shadow: -1px 1px 0px 0px #dfdcdc inset, 1px -1px 0px 0px #bfbfbf inset;', 'border-radius: 4px;', 'display: inline-block;', 'width: 28px;', 'height: 28px;', 'margin-right: 5px;', 'vertical-align: -6px;', 'text-align: center; }', '.bmf_interlink_btn img {', 'padding: 5px;', 'vertical-align: 0;', 'margin-top: 1px; }' ].join(''); return css; }, mal: function() { var css = [ // comic page '.bmf_interlink_btn {', 'background: #e4e4e4;', 'background: linear-gradient(to bottom ,#f6f6f6, #d7d7d7);', 'box-shadow: -1px 1px 0px 0px #dfdcdc inset, 1px -1px 0px 0px #bfbfbf inset;', 'border-radius: 4px;', 'display: inline-block;', 'width: 28px;', 'height: 28px;', 'margin-right: 5px;', 'vertical-align: -6px;', 'text-align: center; }', '.bmf_interlink_btn img {', 'padding: 5px;', 'vertical-align: 0;', 'margin-top: 1px; }' ].join(''); return css; } }; //----------------------------------------------------- // Utility functions //----------------------------------------------------- function googleRequest(query, queryUrl, callback) { var encodedQuery = query.toLowerCase().replace(/\||-|~|"|\*|:/g, ' ').replace(/\s+/g, ' '), encodedQuery = encodeURIComponent(encodedQuery), encodedQueryUrl = encodeURIComponent(queryUrl), temporalUrl = 'https://www.google.com/webhp?#btnI=I&q=' + encodedQuery + '&sitesearch=' + encodedQueryUrl, requestUrl = 'https://www.google.com/search?btnI=I&q=' + encodedQuery + '&sitesearch=' + encodedQueryUrl, searchUrl = 'https://www.google.com/search?q=' + encodedQuery + '&sitesearch=' + encodedQueryUrl; GM_xmlhttpRequest({ method: 'HEAD', url: requestUrl, headers: { referer: 'https://www.google.com/' }, onload: function(response) { var finalUrl = response.finalUrl; callback({ 'success': (finalUrl && finalUrl.indexOf('https://www.google.com/') !== 0), 'query': query, 'queryUrl': queryUrl, 'finalUrl': finalUrl || 'not supported', 'searchUrl': searchUrl }); } }); return temporalUrl; } function duckRequest(query, queryUrl, callback) { var encodedQuery = query.toLowerCase().replace(/\\|"|!|-|:/g, ' ').replace(/\s+/g, ' '), encodedQuery = encodeURIComponent(encodedQuery), encodedQueryUrl = encodeURIComponent(queryUrl), requestUrl = 'https://duckduckgo.com/?kp=-1&q=%5C' + encodedQuery + '+site%3A' + encodedQueryUrl, searchUrl = 'https://duckduckgo.com/?kp=-1&q=' + encodedQuery + '+site%3A' + encodedQueryUrl; GM_xmlhttpRequest({ method: 'GET', url: requestUrl, onload: function(response) { var finalUrl = response.responseText.match(/&uddg=(.+?)'/); finalUrl = decodeURIComponent(finalUrl[1]); callback({ 'success': (finalUrl && finalUrl.indexOf('https://duckduckgo.com/') !== 0), 'query': query, 'queryUrl': queryUrl, 'finalUrl': finalUrl || false, 'searchUrl': searchUrl }); } }); // temporalUrl = requestUrl return requestUrl; } function getStorage(key, GM) { var value = GM ? GM_getValue('BMF_' + key) : localStorage.getItem('BMF_' + key); if (value !== undefined && value !== null) { value = JSON.parse(value); } return value; } function setStorage(key, value, GM) { if (GM) { GM_setValue('BMF_' + key, JSON.stringify(value)); } else { localStorage.setItem('BMF_' + key, JSON.stringify(value)); } } function decodeHtmlEntity(encoded) { var div = document.createElement('div'); div.innerHTML = encoded; return div.firstChild.nodeValue; } function createImgBtn(title, className, src, href) { var button = createElem('a', { title: title || '', class: className || '', href: href || 'javascript:void(0)' }); var img = createElem('img', { src: src, alt: '' }); button.appendChild(img); return button; } function querySel(selector, doc) { doc = doc || document; return doc.querySelector(selector); } function querySelAll(selector, doc, toArray) { doc = doc || document; var nodes = doc.querySelectorAll(selector); return toArray ? [].slice.call(nodes, 0) : nodes; } function createElem(tag, attributes, doc) { doc = doc || document; var elem = doc.createElement(tag); for (var attr in attributes) { elem.setAttribute(attr, attributes[attr]); } return elem; } function createElemHTML(html, doc) { doc = doc || document; var elem = doc.createElement('div'); elem.innerHTML = html; return elem.firstElementChild; } function toCharObject(stringArray) { var object = {}, firstChar; for (var i = 0, len = stringArray.length; i < len; i++) { firstChar = stringArray[i].charAt(0); object[firstChar] = object[firstChar] || []; object[firstChar].push(stringArray[i]); } return object; } function inCharObject(charObject, string) { var charArray = charObject[string.charAt(0)] || []; return (charArray.indexOf(string) !== -1); } function ready(callback) { ready.queue = ready.queue || []; ready.fired = ready.fired || false; if (ready.fired) { setTimeout(callback, 0); return; } else if (document.readyState === 'complete') { ready.fired = true; setTimeout(callback, 0); return; } if (!ready.whenReady) { ready.whenReady = function() { if (!ready.fired) { ready.fired = true; for (var i = 0; i < ready.queue.length; i++) { ready.queue[i].call(window); } ready.queue = []; } }; document.addEventListener('DOMContentLoaded', ready.whenReady, false); document.onreadystatechange = function() { if (document.readyState === 'complete') { ready.whenReady(); } }; } ready.queue.push(callback); } function reload() { Window.stop(); setTimeout(function() { location.reload(); }, 0); } function createCookie(name, value, days) { if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = '; expires=' + date.toGMTString(); } else var expires = ''; document.cookie = name + '=' + value + expires + '; path=/'; } function readCookie(name) { var nameEQ = name + '='; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } function eraseCookie(name) { createCookie(name, '', -1); } function getResource(key) { var res = getStorage('resource_' + key, true), config = GM_config.get(key); if ((!res || !res.base64) && config) { updateResource(key, config); return config; } else if (!config) { console.log('BMF: No resource nor config for:' + key); return null; } return res.base64; } function updateResource(key, url) { var res = getStorage('resource_' + key, true); if (GM_xmlhttpRequest && (!res || !res.base64 || res.url !== url)) { GM_xmlhttpRequest({ method: 'GET', url: url, overrideMimeType: 'text/plain; charset=x-user-defined', onload: function(response) { res = { url: url, base64: 'data:image/png;base64,' + btoa(response.responseText.replace(/[\u0100-\uffff]/g, function(c) { return String.fromCharCode(c.charCodeAt(0) & 0xff); })) }; setStorage('resource_' + key, res, true); }, onfail: function(response) { console.log('BMF: Could\'nt update resource:', key, url, response); } }); } } //----------------------------------------------------- // Run Script (GM_config and then BMF) //----------------------------------------------------- GM_config.init({ id: 'BMF_Options', title: 'BMF Options', css: '.section_desc { text-align: left !important; } .section_desc, .section_header { margin: 18px 0 6px !important; }', events: { save: function() { // update icons for (var id in GM_config.fields) { if (GM_config.fields[id].settings.label.match(/Icon URL:/)) { updateResource(id, GM_config.get(id), true); } } // update subway theme cookies var customPrimary = GM_config.get('batoto_s_customPrimary').trim(); if (customPrimary !== '') { eraseCookie('customPrimary'); createCookie('customPrimary', customPrimary, 360); } var customSecondary = GM_config.get('batoto_s_customSecondary').trim(); if (customSecondary !== '') { eraseCookie('customSecondary'); createCookie('customSecondary', customSecondary, 360); } } }, fields: { // Interlinks 'i_searchEngine': { 'label': 'Search Engine:', 'section': ['Interlinks'], 'options': [ 'Google', 'DuckDuckGo' ], 'type': 'select', 'default': 'Google' }, 'i_saveUrls': { 'type': 'checkbox', 'label': 'Save URLs.', 'title': 'You can also edit them manually by doing CTRL + SHIFT + CLICK on the buttons', 'default': true }, 'i_batoto_mu': { 'section': [undefined, 'Add links in the comic pages between:'], 'label': 'Batoto and Mangaupdates.', 'type': 'checkbox', 'default': true }, 'i_batoto_mal': { 'type': 'checkbox', 'label': 'Batoto and MyAnimeList.', 'default': true }, 'i_mu_mal': { 'type': 'checkbox', 'label': 'Mangaupdates and MyAnimeList.', 'default': true }, //====== Batoto ====== 'batoto_icon': { 'label': 'Icon URL:', 'section': ['Batoto'], 'type': 'text', 'size': 100, 'default': 'https://bato.to/forums/favicon.ico' }, 'batoto_infoIcon': { 'label': 'Info Icon URL:', 'type': 'text', 'size': 100, 'default': 'https://bato.to/forums/public/style_images/master/information.png' }, 'batoto_followingIcon': { 'label': 'Following Icon URL:', 'type': 'text', 'size': 100, 'default': 'https://bato.to/forums/public/style_images/master/star.png' }, 'batoto_comicsPopupDelay': { 'label': 'Comic popup/hovercard delay (ms):', 'title': 'Opens while hovering a URL to a comic page.', 'type': 'unsigned int', 'size': 5, 'default': 1000 }, 'batoto_defaultToOldFollows': { 'type': 'checkbox', 'label': "Set 'Old Follows' as default.", 'title': 'Replace links in the upper toolbar.', 'default': true }, // Subway Theme 'batoto_s_customPrimary': { 'label': 'Primary color (Hex):', 'section': [undefined, 'Subway Theme:'], 'type': 'text', 'size': 10, 'default': '59b6dd' }, 'batoto_s_customSecondary': { 'label': 'Secondary color (Hex):', 'type': 'text', 'size': 10, 'default': '333535' }, // Blood Theme 'batoto_hideBloodHeader': { 'section': [undefined, 'Blood Theme:'], 'type': 'checkbox', 'label': 'Make the header scroll with the page.', 'default': true }, // Home Page 'batoto_followingIcon_home': { 'section': [undefined, 'Home:'], 'type': 'checkbox', 'label': 'Add icon to follows.', 'default': true }, // Old Follows Page 'batoto_autoSort': { 'section': [undefined, 'Old Follows:'], 'type': 'checkbox', 'label': 'Sort follows automatically.', 'default': true }, 'batoto_defaultSort': { 'label': 'Default sort:', 'options': [ "Last Update's Date", "Last Update's Ch", "Last Read's Date", "Last Read's Ch", 'Unread Chs', 'Title' ], 'type': 'select', 'default': "Last Update's Date" }, 'batoto_adaptSortButton': { 'type': 'checkbox', 'label': 'Adapt the color of the sorting buttons to the theme (Subway).', 'default': true }, 'batoto_addInfoBtns': { 'type': 'checkbox', 'label': 'Add info button to follows.', 'default': true }, 'batoto_showTotalFollows': { 'type': 'checkbox', 'label': 'Add text showing the total of follows.', 'default': true }, 'batoto_totalTextColor': { 'label': 'Text color of the total of follows:', 'type': 'text', 'size': 15, 'default': '#EEEEEE' }, 'batoto_addBoxToNewFollows': { 'type': 'checkbox', 'label': "Add box linking to 'New Follows'.", 'default': true }, 'batoto_categorizeFollows': { 'type': 'checkbox', 'label': 'Categorize follows.', 'default': true }, 'batoto_followRowsHeight': { 'label': 'Height of the rows of follows.', 'type': 'text', 'size': 4, 'default': '12px' }, // Comic Search Page 'batoto_followingIcon_search': { 'section': [undefined, 'Comic Search:'], 'type': 'checkbox', 'label': 'Add icon to follows.', 'default': true }, // Comic Directory Page 'batoto_comicsPopup_comicDir': { 'section': [undefined, 'Comic Directory:'], 'type': 'checkbox', 'label': 'Enable comic info popup/hovercard.', 'title': 'Opens while hovering a URL to a comic page.', 'default': true }, // Comic Page 'batoto_hyperlinkDesc': { 'section': [undefined, 'Comic:'], 'type': 'checkbox', 'label': 'Hyperlink description.', 'default': true }, 'batoto_showMoreRecentBtn': { 'type': 'checkbox', 'label': "Add a 'Show more' button under 'Recently Added Comics'.", 'default': true }, 'batoto_comicsPopup_comic': { 'type': 'checkbox', 'label': 'Enable comic info popup/hovercard.', 'title': 'Opens while hovering a URL to a comic page.', 'default': true }, 'batoto_addSpoilersType': { 'type': 'checkbox', 'label': 'Add text indicating the content of each spoiler (Text/Image).', 'default': true }, 'batoto_autoOpenImgSpoilers': { 'type': 'checkbox', 'label': 'Auto open image spoilers.', 'default': true }, 'batoto_fixPageTitle': { 'type': 'checkbox', 'label': 'Shorter comic page title.', 'title': "Replaces '- Scanlations - Comic - Comic Directory - Batoto - Batoto Home.' with '- Batoto'.", 'default': true }, // Group Page 'batoto_comicsPopup_group': { 'section': [undefined, 'Group:'], 'type': 'checkbox', 'label': 'Enable comic info popup/hovercard.', 'title': 'Opens while hovering a URL to a comic page.', 'default': false }, // Forums 'batoto_comicsPopup_forums': { 'section': [undefined, 'Forums:'], 'type': 'checkbox', 'label': 'Enable comic info popup/hovercard.', 'title': 'Opens while hovering a URL to a comic page.', 'default': true }, //====== MangaUpdates ====== 'mu_icon': { 'label': 'Icon URL:', 'section': ['MangaUpdates'], 'type': 'text', 'size': 100, 'default': 'https://www.mangaupdates.com/favicon.ico' }, //====== MyAnimeList ====== 'mal_icon': { 'label': 'Icon URL:', 'section': ['MyAnimeList'], 'type': 'text', 'size': 100, 'default': 'http://cdn.myanimelist.net/images/faviconv5.ico' } } }); var Window = this.unsafeWindow || unsafeWindow || window, host = location.host, path = location.pathname; if (host === 'bato.to') { CSS.load('batoto'); batoto.init(); } else if (host === 'www.mangaupdates.com') { CSS.load('mu'); mu.init(); } else if (host === 'myanimelist.net') { CSS.load('mal'); mal.init(); } if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('Open BMF Options', function() { GM_config.open(); }); }