// ==UserScript== // @name New MangaDex Follows // @namespace https://greasyfork.org/scripts/430295-new-mangadex-follows // @version 1.1.2 // @description Manage your follows // @author Australis // @match https://mangadex.org/* // @icon https://www.google.com/s2/favicons?domain=mangadex.org // @grant none // @downloadURL none // ==/UserScript== if(localStorage.getItem("seriesdex") == null) seriesdex = [] else seriesdex = JSON.parse(localStorage.getItem("seriesdex")) var classChapter = "flex chapter" //class of the elements containing the chapters var classFeed = "chapter-feed__chapters-list" //class containing the chapter cluster var grouptagclass = "group-tag line-clamp-1 -my-1" var newStyle = newInner("style","text/css",".hideme {\ndisplay: none;\n}") document.querySelector("head").append(newStyle) var filter = JSON.parse(localStorage.getItem("filter")) if(filter == undefined || filter == null) { console.log("filter: "+JSON.stringify(filter)) filter = true localStorage.setItem("filter",JSON.stringify(filter)) } whitelist = [] blacklist = [] function GetNumero(text){ for(let t of text.split(" ")){ console.log(t) if((t*1)>0) return t*1 } return NaN } function ScriptForm(){ if(document.URL.includes("https://mangadex.org/titles/feed") && !document.getElementById("script_btn")){ console.log("executing ScriptForm") var options_loc = "flex items-center mb-4" if(/*!document.getElementById("script_btn") && */document.getElementsByClassName(options_loc).length){ var newButton = newInner("div","",'') document.getElementsByClassName(options_loc)[0].append(newButton.children[0]) document.getElementsByClassName(options_loc)[0].append(newButton.children[0]) console.log("form added") } else setTimeout(ScriptForm,500) } } window.NMDF = function(){ var daBody = document.getElementById("script_body") document.getElementById("filter").checked = filter if(daBody.style.display == "none"){ //including event listener daBody.style.display = "block" } else{ daBody.style.display = "none" //removing listener } } window.SC = function(){ filter = document.getElementById("filter").checked localStorage.setItem("filter",JSON.stringify(filter)) document.getElementById("script_body").style.display = "none" } window.EXIT = function(){ document.getElementById("script_body").style.display = "none" } function FS(array, number){//FindSeries return array.findIndex(q => q.id == number) } function HideThis(array,series,group){ if(array.findIndex(q => q[0] == series && q[1] == group) >= 0) return true return false } function OnlyThis(array,series,group){ if(array.findIndex(q => q[0] == series && q[1] == group) < 0) return 0 //it's not there if(array.findIndex(q => q[0] == series && q[1] != group) >= 0) return 1 //the series is listed but not this group if(array.findIndex(q => q[0] == series && q[1] == group) >= 0) return 2 //the series is listed with this group return 3 //series is not listed } button_reload = 0 disURL = "" function NextNext(array,pointer,next){ if(next[0] < array[pointer].nextc*1 && array[pointer].last < next[0]) { array[pointer].nextc = next[0] array[pointer].nextl = next[1] } } function newInner(tag,classN,inner){ var temp = document.createElement(tag) if(classN == "text/css") temp.type = classN else temp.className = classN temp.innerHTML = inner return temp } function cloneDOM(tag,nodo){ var temp = document.createElement(tag) temp = nodo.cloneNode(true) return temp } function newDOM(tag,classN,style){ var temp = document.createElement(tag) temp.className = classN temp.style.display = style return temp } function HasBeenRead(nodo){ if(!nodo.getElementsByTagName("svg")[0].className.baseVal.includes(" feather ")) return true else return false } function ChangeToBL(nodo){ if(nodo.parentElement.getElementsByClassName("showme").length) console.log("still blacklisted?")//InverseIt(nodo.parentElement)//HideItAgain else { var parche = newInner("a","font-bold truncate showme blacklisted","BLACKLISTED") nodo.parentElement.append(parche)//removeAttribute("href") nodo.className += " hideme" } } function InverseIt(nodo){//ShowItgain let hideme = nodo.getElementsByClassName("hideme")[0] let showme = nodo.getElementsByClassName("showme")[0] if(!!hideme && !!showme){ console.log("inversing in process") hideme.className = nodo.children[2].className.replace("hideme","showme") showme.className = nodo.children[3].className.replace("showme","hideme") } } function HideItAgain(nodo){ console.log("Hide It Again in process") let hideme = nodo.getElementsByClassName("hideme")[0] let showme = nodo.getElementsByClassName("showme")[0] hideme.className = nodo.children[2].className.replace("hideme","showme") showme.className = nodo.children[3].className.replace("showme","hideme") } function getID(nodo){ return nodo.getElementsByTagName("a")[0].href.split("/")[4] } function getID2(nodo){ return nodo.getElementsByTagName("a")[1].href.split("/")[4] } function ProcessGroups(process,batch,i,id_series){//todo: fix syntaxis to consider every scenario if(filter){ var repeated = false var icono,newdiv var groupclass = "md:col-span-2" var externals = "flex items-center" var groupflex = batch[i].getElementsByClassName(groupclass)[0] //get the groups of this chapters if(!groupflex) groupflex = batch[i].getElementsByClassName(externals)[1] //external chapters if(batch[i].getElementsByClassName("nmf-single").length) { groupflex = batch[i].getElementsByClassName("group-tag") //already processed repeated = true } else{ if(groupflex.getElementsByTagName("svg").length) icono = cloneDOM("svg",groupflex.getElementsByTagName("svg")[0]) else icono = newInner("div","",'').firstChild newdiv = newDOM("div",groupclass+" nmf-groups","inline-grid") } function GFP(groupflex,z,process,batch,i,id_series){ let wl = false let bl = false var gid = 0 if(groupflex.innerText == "No Group") gid = 0 else { if(groupflex.children[z].href) gid = groupflex.children[z].href.split("/")[4] else if(groupflex.children[z].parentElement.href) gid = groupflex.children[z].parentElement.href.split("/")[4] //external links else { groupflex = groupflex.getElementsByClassName("nmf-single")[z].children[1] }//console.log(groupflex.children[z]) } //check if should be shown if(HideThis(blacklist,id_series,gid)) { console.log("blacklisted! hidethis") process += false bl = true //ChangeToBL(batch[i].getElementsByTagName("a")[0]) } else if(OnlyThis(whitelist,id_series,gid) == 1) { console.log("not in whitelist!") process += false } else if(OnlyThis(whitelist,id_series,gid) == 2) { console.log("whitelisted!") process += true wl = true } else{ process += true } //add buttons if(!groupflex.getElementsByClassName("nmf-single").length){ var single = newDOM("div","nmf-single","flex") var newicono if(!!icono) { newicono= cloneDOM("svg",icono) single.append(newicono) } var dagroup = document.createElement("a") dagroup = groupflex.children[z].cloneNode(true) if(dagroup.tagName != "A") { dagroup = newInner("a",grouptagclass,groupflex.children[z].innerText) if(!!groupflex.children[z].parentElement.href) dagroup.href = groupflex.children[z].parentElement.href //for external } dagroup.style.maxWidth = "80%" dagroup.title = groupflex.children[z].innerText single.append(dagroup) var executor if(wl) executor = newInner("div","list-options","") else if(bl) executor = newInner("div","list-options","") else executor = newInner("div","list-options","") single.append(executor) newdiv.append(single) } return process } try{ if(!repeated){ for(let z=1; z= 0) NextNext(seriesdex,pseries,[nextc,nextl]) else seriesdex.push({id:id_series, name:l.firstChild.innerText, last:last, nextc:nextc, nextl:nextl}) let index = FS(follows,id_series) if(index >= 0){ if(seriesdex[pseries].last < last || seriesdex[pseries].last == null) seriesdex[pseries].last = last if(nextc > last){ NextNext(follows,index,[nextc,nextl]) NextNext(seriesdex,pseries,[nextc,nextl]) } if(l != follows[index].pointer) { while(l.getElementsByClassName(classChapter)[0] != undefined) follows[index].pointer.getElementsByClassName(classChapter)[0].append(l.getElementsByClassName(classChapter)[0]) } } else{ follows.push({id:id_series, ch:last, url:link, nextc:nextc, nextl:nextl, pointer:l}) } if(pseries == -1) pseries = FS(seriesdex,id_series) if(seriesdex[pseries].nextc == 0 || seriesdex[pseries].nextc == null || seriesdex[pseries].nextc == 10000 || (seriesdex[pseries].nextc > nextc && seriesdex[pseries].last < nextc)){ seriesdex[pseries].nextc = nextc seriesdex[pseries].nextl = nextl } } var feedone = "mb-4" function FollowsFeed(){ if(JSON.parse(localStorage.getItem("whitelist"))) whitelist = JSON.parse(localStorage.getItem("whitelist")) if(JSON.parse(localStorage.getItem("blacklist"))) blacklist = JSON.parse(localStorage.getItem("blacklist")) if(document.URL.includes("titles/feed")) { var feedwhole = "mb-12" if(document.getElementsByClassName(feedwhole).length){//if page is loaded console.log("FollowsFeed") lista = document.getElementsByClassName(feedwhole)[0].getElementsByClassName(feedone) console.log("lista loaded") for(let i = seriesdex.length-1; i>=0; i--){//to correct a previous bug if(seriesdex[i].id.includes("/")) seriesdex.splice(i,1) } for(let l of lista){//check the list try{ var id_series = getID(l)//.getElementsByTagName("a")[0].href.split("/")[4] var batch = l.getElementsByClassName(classChapter) var chap, link, read, nextc, nextl, last nextc = 10000 nextl = "" let pseries = FS(seriesdex,id_series) if(pseries < 0) last = -10 else last = seriesdex[pseries].last if(batch.length){//if there's chapters for(let i=0; i last && nextc > current){ nextc = current nextl = batch[i].getElementsByTagName("a")[0].href.split("chapter/")[1] } } else if(HasBeenRead(batch[i])){ if(last < current) last = current } if(filter) InverseIt(batch[i].getElementsByTagName("a")[0].parentElement) } else{//has to be hidden if(filter) ChangeToBL(batch[i].getElementsByTagName("a")[0]) } } } DecideNext(pseries,nextc,nextl,last,link,id_series,l) } catch(e){ console.log("error processing lista") console.log(e) } } for(let f of follows){//consider chapters that aren't together let avail = f.pointer.getElementsByClassName(classChapter) let lastest = -10 let newest = 10000 let link = null let current = FS(seriesdex,f.id) if(current >= 0){ lastest = seriesdex[current].last newest = seriesdex[current].nextc } for(let a of avail){ if(!a.getElementsByClassName("continue").length){ let elem = a.getElementsByTagName("a")[0] let disONE = elem.innerText.replace("\n"," ").split(" ")[1]*1 try{ JSON.parse(disONE)//for external links } catch(e){ console.log("external link") disONE = elem.innerText.replace("\n"," ").split(" ")[2]*1 if(Number.isNaN(disONE)) disONE = 0 } let unread = (!HasBeenRead(a)) if(disONE > lastest && !unread) lastest = disONE if(unread && newest > disONE && disONE > lastest) { newest = disONE link = elem.href.split("chapter/")[1] } } } f.ch = lastest if(newest > lastest) { f.nextc = newest f.nextl = link } else{ f.nextc = 10000 f.nextl = null } if(lastest > seriesdex[current].last) { seriesdex[current].last = lastest } else{ if(seriesdex[current].nextc != f.nextc){ seriesdex[current].nextc = f.nextc seriesdex[current].nextl = f.nextl } } } for(let l of lista){//add the continue link if necessary let elem = l.getElementsByTagName("a")[0] if(elem){ //l.firstChild.getElementsByTagName("a")[0] let daONE = seriesdex[FS(seriesdex,elem.href.split("/")[4])] let extrainfo = "" if(l.getElementsByClassName(classFeed).length == 0) l.style.display = "none" else if(daONE.nextc > daONE.last && daONE.nextl){ if(daONE.last != -10 && (daONE.nextc - daONE.last >= 1.2)) extrainfo = " (WARNING: skipped chapters)" let newbutton = newInner("div",classFeed,"") if(l.getElementsByClassName("continue").length == 0) l.getElementsByClassName("chapter-feed__chapters")[0].insertBefore(newbutton,l.getElementsByClassName(classFeed)[0]) else l.getElementsByClassName("continue")[0].parentElement.innerHTML = newbutton.innerHTML } if((daONE.nextl == null || daONE.nextl == "") && l.getElementsByClassName("continue")[0] != undefined) l.getElementsByClassName("continue")[0].parentElement.remove() } } for(let n=follows.length-1; n >= 0; n--){//put unread series on top of the list if(follows[n].pointer.getElementsByClassName("continue")[0] != undefined) lista[0].parentElement.insertBefore(follows[n].pointer,lista[0]) } localStorage.setItem("seriesdex",JSON.stringify(seriesdex)) } else repeat = setTimeout(FollowsFeed,5000) } } function Clickear(){ hola = document.getElementsByClassName("follows__content mb-12")[0].firstChild.children state = true try{ for(let h of hola){ if(h.innerHTML != ""){ if(h.firstChild.firstChild.lastChild.tagName == "BUTTON") { h.firstChild.firstChild.lastChild.click() } else state = false } } if(state) setTimeout(Clickear,2500) else FillTitles() } catch(e){ setTimeout(Clickear,2500) } } function Llenar(X){ let temp = [] console.log("now filling "+X.firstChild.firstChild.firstChild.innerText) for(let t of X.getElementsByClassName("manga-card")){ var id = getID(t)//.getElementsByTagName("a")[0].href.split("/")[4] var name = t.getElementsByTagName("a")[0].innerText.replaceAll("\n ","").replaceAll("\n ","") temp.push({id:id, name:name, last:null, nextc:null, nextl:null }) if(temp.length == 0) temp = [{id:id, name:name, last:null, nextc:null, nextl:null}] } console.log("Done Filling!!") return temp } function NewButton(){ try{ newb = document.createElement("button") newb.innerHTML = "" if(!document.getElementById("fill-btn")) document.getElementsByClassName("controls mb-auto ml-auto")[0].appendChild(newb) dl = document.createElement("div") dl.innerHTML="