Greasy Fork is available in English.

MangaDex Follows

Manage your follows page

目前為 2021-01-18 提交的版本,檢視 最新版本

// ==UserScript==
// @name         MangaDex Follows
// @namespace    https://greasyfork.org/es/scripts/411685-mangadex-follows
// @version      1.3.7.4.1
// @description  Manage your follows page
// @author       Australis
// @match        https://mangadex.org/*
// @exclude      https://mangadex.org/chapter/*
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle(".checkit::before {content: \"❌❌ \";}")

const debug = false

function log(string){
    if(debug) console.log(string)
}

log("Executing");

//DB Handling
//prefixes of implementation that we want to test
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;

//prefixes of window.IDB objects
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange

if (!window.indexedDB) {
    window.alert("Your browser doesn't support a stable version of IndexedDB. MangaDex Follows Script won't work for you")
}

const seriesDataExample = [
    { id: 149, name: '', status: '', last_read: 34, last_update: 1830012313}
];
var db;
var request = window.indexedDB.open("MDF", 2);

request.onerror = function(event) {
    console.log("error: ");
};

request.onsuccess = function(event) {
    db = request.result;
    log("success: "+ db);
    main();
};

request.onupgradeneeded = function(event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore("series", {keyPath: "id"});
    objectStore = db.createObjectStore("whitelist", {keyPath: "id", });
    objectStore = db.createObjectStore("blacklist", {keyPath: "id"});
    objectStore = db.createObjectStore("gap", {keyPath: "id"});

    //         for (var i in seriesDataExample) {
    //             objectStore.add(seriesDataExample[i]);
    //         }
}

function dbRead(variable,seriesID){
    var transaction = db.transaction([variable],"readwrite");
    var objectStore = transaction.objectStore(variable);
    var request = objectStore.get(seriesID);

    request.onerror = function(event) {
        log("Unable to retrieve "+variable+" from database!");
    };

    request.onsuccess = function(event) {
        // Do something with the request.result!
        if(request.result) {
            log(request.result);
        } else {
            log(variable+" couldn't be found in the database!");
            //add it
        }
    };
}

function dbReadAll(variable,callback) {
    var objectStore = db.transaction(variable).objectStore(variable);

    objectStore.openCursor().onsuccess = function(event) {
        var temp = []
        var cursor = event.target.result;

        if (cursor) {
            temp.push(cursor.value)
            cursor.continue();
        } else {
            log("No more entries!");
            callback(temp)
        }
    };
}

function dbReadAll2(variable,callback){
    var db
    log("dbReadAll2")
    request = window.indexedDB.open("MDF", 2)
    request.onsuccess = function(event) {
        db = request.result;
        console.log("success: "+ db);
        try{
            var objectStore = db.transaction(variable).objectStore(variable);
            var temp2 = []

            objectStore.openCursor().onsuccess = function(event) {
                var cursor = event.target.result;

                if (cursor) {
                    temp2.push(cursor.value)
                    cursor.continue();
                } else {
                    console.log("No more entries!");
                    //                 console.log(temp2)
                    return callback(temp2)
                }
            }
        }
        catch(e){
            log("Variable not available");
            return []
        }
    };
    request.onerror = function(event) {
        log("database error")
    }
}

i = -1
function dbReadAll3(variable,arrays){
    i++
    var db
    log("dbReadAll3 "+variable[i])
    var request = window.indexedDB.open("MDF", 2)
    var temp2 = []
    request.onsuccess = function(event) {
//         for(let i = 0; i < variable.length; i++){
            db = request.result;
            console.log("success: "+ db);
            try{
                var objectStore = db.transaction(variable[i]).objectStore(variable[i]);

                objectStore.openCursor().onsuccess = function(event) {
                    var cursor = event.target.result;

                    if (cursor) {
                        temp2.push(cursor.value)
                        cursor.continue();
                    } else {
                        console.log("No more entries!\n"+temp2);
                        //                 console.log(temp2)
                        switch(i){
                            case 0:
                                blacklist = temp2
                                return dbReadAll3(variable)
                                break
                            case 1:
                                whitelist = temp2
                                return dbReadAll3(variable)
                                break
                            case 2:
                                gap = temp2
                                break
                        }
                    }
                }

                objectStore.openCursor().onerror = function(event) {
                    console.log("cursor error "+variable[i])
                }
            }
            catch(e){
                log("Variable not available");
                switch(i){
                    case 0:
                        blacklist = temp2
                        return dbReadAll3(variable)
                        break
                    case 1:
                        whitelist = temp2
                        return dbReadAll3(variable)
                        break
                    case 2:
                        gap = temp2
                        break
                }
            }
//         }
    };
    request.onerror = function(event) {
        log("database error")
    }
}

function dbAdd(variable,seriesObj) {
    var request = db.transaction([variable], "readwrite").objectStore(variable).put(seriesObj);

    request.onsuccess = function(event) {
        log("Current "+variable+" has been added to database.");
    };

    request.onerror = function(event) {
        log("No duplicates allowed!!");
    }
}

function dbUpdate(variable,seriesObj){
    for(let s of seriesObj){
        dbAdd(variable,s)
    }
}

function dbRemove(variable,seriesID) {
    var request = db.transaction([variable], "readwrite").objectStore(variable).delete(seriesID);

    request.onsuccess = function(event) {
        log(variable+" "+seriesID+" has been removed from database.");
    };
}

//Variable handling
function LoadLists(input){
    let dalist = []
    function doThis(callback){
        dalist = dbR3adAll(input)
        callback()
    }
    function callback(){
        log(input+" loaded - "+dalist.length)
    }
    try{
        doThis(callback)
            .then(function(){return dalist})
    }
    catch(e){
        log("failed to load "+input)
        return dalist
    }
}

checker = []
let nodes = []

function Checker(node){
    if(checker.length == 0) checker = [node]
    else checker.push(node)
    log(node)
}

function CleanChecker(id,r){
    try{
        if(nodes.lenght == 0) nodes.push([id,[r]])
        else{
            add = true
            for(let n of nodes){
                log("a node")
                if(n[0] == id) {
                    log("existing node")
                    if(!n[1].includes(r)) n[1].push(r)
                    add = false
                }
            }
            if(add) nodes.push([id,[r]])
        }
    }catch(e){
        log("CleanChecker error: "+e)
    }
}

function Decider(){
    log("Decider")
    for(let n of nodes){
        if(n[1].length > 1){
            log("deciding")
            var save_c = 100000
            var save_v = 100000
            var save_r = false
            var best = []
            log(n[1])
            for(let m of n[1]){
                //                 log("save_c "+save_c)
                let dataset = m.children[2].childNodes[1].dataset
                log("chapter "+dataset.chapter)
                if(dataset.volume*1){
                    if(save_v > dataset.volume*1){
                        save_v = dataset.volume*1
                        save_c = dataset.chapter*1
                        save_r = m
                        best = []
                    }
                    else if(save_c > dataset.chapter*1){
                        log("next one?")
                        save_c = dataset.chapter*1
                        save_r = m
                        best = []
                    }
                    else if(save_c == dataset.chapter*1){
                        log("same")
                        if(!best.includes(save_r)) best.push(save_r)
                        if(!best.includes(m)) best.push(m)
                    }
                }
                else{
                    if(save_c > dataset.chapter*1){
                        log("next one?")
                        save_c = dataset.chapter*1
                        save_r = m
                        best = []
                    }
                    else if(save_c == dataset.chapter*1){
                        log("same")
                        if(!best.includes(save_r)) best.push(save_r)
                        if(!best.includes(m)) best.push(m)
                    }
                    else log("no coincidence")
                }
            }
            if(best.length){
                for(let b of best) CleanCheckerB(n[0]*1,b)
            }
            else CleanCheckerB(n[0]*1,save_r)
        }
        else {
            CleanCheckerB(n[0]*1,n[1][0])
        }
    }
    return true
}

function CleanCheckerB(id,row){
    try{
        for(var x=checker.length-1; x>=0; x--){
            if(checker[x][0] == id && checker[x][1] == row) checker.splice(x,1)
        }
    }catch(e){
        log("CleanChecker error: "+e)
        checker = []
    }
}

function Ahead(){
    try{
        if(checker.length){
            for(c of checker){
                let dataset = c[1].children[2].childNodes[1].dataset
                if(dataset.mangaId){
                    if(c[1].getElementsByClassName("chapter_mark_read_button grey")[0]) c[1].getElementsByClassName("chapter_mark_read_button grey")[0].setAttribute("class","chapter_mark_read_button")
                    else log(c)
                    if(c[1].getElementsByClassName("chapter_mark_read_button")[0]) c[1].getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: red")
                    else log(c)
                }
            }
        }
        checker = []
    }catch(e){
        log(e)
        log(c)
    }
}

function FS(array, number){//FindSeries
    return array.findIndex(q => q.id*1 == number*1)
}

blacklist = []
whitelist = []
gap = []
lista = 0

function main(){
    log("main")
    //     try{
//     new Promise(function() {

//         return dbReadAll2("blacklist",loadList)

//     }).then(function() { // (**)

//         return dbReadAll2("whitelist",loadList)

//     }).then(function() { // (***)

//         return dbReadAll2("gap",loadList)

//     }).then(function() {

//         return dbReadAll2("series",main2)

//     });
//     dbReadAll2("blacklist",loadList)
//     dbReadAll2("whitelist",loadList)
//     dbReadAll2("gap",loadList)
//     dbReadAll2("series",main2)
    dbReadAll3(["blacklist","whitelist","gap"],[blacklist,whitelist,gap])
    dbReadAll2("series",main2)
    // var enhance;
    try{
        if(JSON.parse(localStorage.getItem("enhance")) != null) enhance = JSON.parse(localStorage.getItem("enhance"))
        else enhance = true
    }
    catch(e){
        log("error with enhance")
        enhance = true
    }

    function loadList(res){
        switch(lista){
            case 0:
                lista++
                if(res == null) break
                blacklist = res
                break
            case 1:
                lista++
                if(res == null) break
                whitelist = res
                break
            case 2:
                lista++
                if(res == null) break
                gap = res
                break
        }
    }

    function main2(res){
        log("main2")
        series = res
        updater = []
        log("series loaded - "+series.length)
        //add titles from follows
        if(document.URL.includes('mangadex.org/follows/manga')){
            series_col = document.getElementsByClassName("manga-entry border-bottom");
            for(var s of series_col){
                var row = s.getElementsByClassName("row m-0")[1];
                var name = row.children[0].innerText;
                var state = row.children[2].innerText;
                var id = s.dataset.id;
                var node = {id: id, name: name, state: state, last_read: 0, last_update: 0}
                var add = true;
                if(series){
                    for(var m of series){
                        if(m.id*1 === id*1){
                            add = false;
                            if(m.state !== state) {
                                m.state = state;
                                if(updater.findIndex(q => q == m) < 0) updater.push(m)
                            }
                        }
                    }
                    if(add) {
                        series.push(node);
                        if(updater.findIndex(q => q == node) < 0) updater.push(node)
                    }
                }
                else {
                    series = [node]
                    updater = [node]
                }
            }
            //     localStorage.setItem('series',JSON.stringify(series))
            //                 dbUpdate("series",series)
            dbUpdate("series",updater)
        }

        else if(document.URL.includes('mangadex.org/follows')){
            //read chapters

            row = document.getElementsByClassName("chapter-container")[0].children;
            function Hide(DataSet){
                for(var b of blacklist){
                    if(b[0]*1 == DataSet.mangaId*1 && b[1]*1 == DataSet.group*1) return true
                }
                return false
            }
            function OnlyThis(DataSet){
                for(var w of whitelist){
                    if(w[0]*1 == DataSet.mangaId*1 && w[1]*1 != DataSet.group*1) return 0
                    if(w[0]*1 == DataSet.mangaId*1 && w[1]*1 == DataSet.group*1) return 1
                }
                return 2
            }

            let changeH = false
            let changeO = false
            function Process(){
                for(var r of row){
                    let dataset = r.children[2].childNodes[1].dataset;
                    if(dataset.mangaId){
                        let manga_id = dataset.mangaId*1;
                        if(enhance) try{
                            if(Hide(dataset)) {
                                title = r.children[0].innerHTML
                                r.setAttribute("style","display: none")
                                changeH = true
                            }
                            else if(changeH){
                                title2 = r.children[0].innerHTML
                                if(r.children[0].classList.contains("d-md-flex")) r.children[0].innerHTML = title
                                title = title2
                                changeH = false
                            }
                            if(!OnlyThis(dataset)) {
                                title = r.children[0].innerHTML
                                r.setAttribute("style","display: none")
                                changeO = true
                            }
                            else if(changeO){
                                title2 = r.children[0].innerHTML
                                if(r.children[0].classList.contains("d-md-flex")) r.children[0].innerHTML = title
                                title = title2
                                changeO = false
                            }
                        }catch(e){
                            log("enhance error, "+e)
                        }
                        try{
                            pointer = FS(series,manga_id)
                            if(series[pointer].last_read.toString().includes("v")){//if is divided by volume
                                if(series[pointer].last_read.toString() == "0v0") r.children[0].classList.value += " checkit"
                                else if(r.children[0].classList.value.includes("checkit")) log("ERASE THIS (vol version)!!")
                                log("a volume one, "+series[FS(series,manga_id)].last_read)
                                log(r)
                                q = series[pointer].last_read.split("v")
                                vol = q[0]
                                chap = q[1]
                                if(r.innerHTML.includes("mark_unread")){
                                    if(chap*1 < dataset.chapter*1 && vol*1 == dataset.volume*1) {
                                        log("new chapter")
                                        series[pointer].last_read = dataset.volume+"v"+dataset.chapter;
                                        if(updater.findIndex(q => q == series[pointer]) < 0) updater.push(series[pointer]);
                                    }
                                    if(vol < dataset.volume*1) {
                                        log("new volume")
                                        series[pointer].last_read = dataset.volume+"v"+dataset.chapter;
                                        if(updater.findIndex(q => q == series[pointer]) < 0) updater.push(series[pointer]);
                                    }
                                }
                                else if(enhance){
                                    if((chap*1 >= dataset.chapter*1 && vol*1 == dataset.volume*1) || vol*1 > dataset.volume*1){ //a duplicate
                                        r.getElementsByClassName("chapter_mark_read_button grey")[0].setAttribute("class","chapter_mark_read_button")
                                        if(gap.findIndex(q => q[0]*1 == manga_id) >= 0){
                                            let lgap = gap[gap.findIndex(q => q[0]*1 == dataset.mangaId)][1]
                                            log("a gapped one: ",lgap)
                                            log("current chapter: "+dataset.volume+"v"+dataset.chapter)
                                            if(chap*1 >= lgap[0].split("v")[1] && vol*1>= lgap[0].split("v")[0] &&
                                               lgap[1].split("v")[1] >= dataset.chapter*1 && vol*1<= lgap[1].split("v")[0]) r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: green")
                                        }
                                        else if(!(dataset.title.toLowerCase().includes("extra") || dataset.title.toLowerCase().includes("omake"))){
                                            r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: blue")
                                        }
                                        else r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: green")
                                    }
                                    else{//a new one
                                        if(chap*1 < dataset.chapter*1+1.11 && vol*1 == dataset.volume*1){//skipped chapter
                                            Checker([manga_id,r])
                                        }
                                        if(chap*1 < dataset.chapter*1 && vol*1 == dataset.volume*1 && dataset.chapter*1 <= chap*1+1.11){//next chapter
                                            //                             CleanChecker(manga_id)
                                            CleanChecker(manga_id,r)
                                        }
                                    }
                                }
                            }
                            else{//if not divided by volume
                                this_last = series[FS(series,manga_id)].last_read*1
                                if(r.innerHTML.includes("mark_unread")){
                                    if(this_last < dataset.chapter*1) {
                                        series[FS(series,manga_id)].last_read = dataset.chapter*1;
                                        if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                    }
                                }
                                else if(enhance){
                                    if(this_last == 0) r.children[0].classList.value += " checkit"
                                    else if(r.children[0].classList.value.includes("checkit")) log("ERASE THIS!!")
                                    if(this_last >= dataset.chapter*1){ //a duplicate
                                        log("a duplicate")
                                        if(r.getElementsByClassName("chapter_mark_read_button grey")[0]) r.getElementsByClassName("chapter_mark_read_button grey")[0].setAttribute("class","chapter_mark_read_button")
                                        if(gap.findIndex(q => q[0]*1 == manga_id) >= 0){
                                            let lgap = gap[gap.findIndex(q => q[0]*1 == dataset.mangaId)][1]
                                            log("a gapped one: ",lgap)
                                            log("current chapter: "+dataset.chapter)
                                            if(dataset.chapter*1 >= lgap[0] &&
                                               lgap[1] >= dataset.chapter*1) r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: green")
                                        }
                                        else if(dataset.title.toLowerCase().includes("extra") || dataset.title.toLowerCase().includes("omake") || (dataset.chapter*1)%1 == 0.5){
                                            r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: green")
                                        }
                                        else r.getElementsByClassName("chapter_mark_read_button")[0].setAttribute("style","color: blue")
                                    }
                                    else{//a new one
                                        if(this_last != 0
                                           && this_last < dataset.chapter*1+1.11){//skipped chapter
                                            //                             if((dataset.chapter*1)%1 && series[FS(series,manga_id)][3]*1+0.1 <= dataset.chapter*1) log("skipped parted chapter")
                                            Checker([manga_id,r])
                                        }
                                        if(this_last < dataset.chapter*1
                                           && dataset.chapter*1 <= this_last+1.11){//next chapter
                                            if((dataset.chapter*1)%1 && dataset.chapter*1 == Math.round((this_last+0.1+Number.EPSILON)*10)/10) {//Math.round((18.3+0.1+Number.EPSILON)*10)/10
                                                log("next parted chapter")
                                                CleanChecker(manga_id,r)
                                            }
                                            else if((dataset.chapter*1)%1 && dataset.chapter*1 == this_last+0.5) {
                                                log("next extra chapter")
                                                CleanChecker(manga_id,r)
                                            }
                                            else if((dataset.chapter*1)%1 && this_last-this_last%1 < dataset.chapter*1-(dataset.chapter*1)%1
                                                    && dataset.chapter*1 == this_last-this_last%1+1.1){
                                                log("next new parted chapter")
                                                CleanChecker(manga_id,r)
                                            }
                                            else if((dataset.chapter*1)%1 == 0) {
                                                log("regular next chapter")
                                                CleanChecker(manga_id,r)
                                            }
                                        }
                                    }
                                }
                            }

                        }catch(e){
                            //series not included
                            log("series not included?\n"+e)

                            log(dataset.mangaId+" "+r.children[0].innerText)
                            let this_last = 0
                            if(r.innerHTML.includes("mark_unread")) this_last = dataset.chapter*1;
                            if(FS(series,dataset.mangaId) == -1) {
                                cnode = {id: dataset.mangaId, name: r.children[0].innerText, state: ' Reading', last_read: this_last, last_update: dataset.timestamp}
                                series.push(cnode)
                                if(updater.findIndex(q => q == cnode) < 0) updater.push(cnode)
                            }
                        }
                    }//end if
                }//end for
                return true
            }
            if(Process()) Process() //a second one to reduce false positives
            if(Decider()) Ahead()
            //     localStorage.setItem('series',JSON.stringify(series))
            //                 dbUpdate("series",series)
            dbUpdate("series",updater)
        }

        if(document.URL.includes('/title/')){
            log("title")
            notVOL = false
            var follow_stat = document.getElementsByClassName("btn-group")[0].childNodes[0].childNodes[0].className;
            let mangaId = document.URL.split("/")[4]*1
            function ProcVol(){
                try{
                    for(let t of temp){
                        [lastv,lastc] = last.split("v")
                        if(t.dataset != null){
                            let dataset = t.dataset;
                            let manga_id = dataset.mangaId;
                            let vol = dataset.volume*1
                            if(vol == 0) {
                                notVOL = true
                                last = 0
                                Chapters()
                                return false
                            }
                            if(process && lastc == dataset.chapter*1 && lastv == vol){
                                if(!t.innerHTML.includes("mark_unread")){
                                    last = "0v0"
                                    log("reset")
                                }
                                else process = false
                            }
                            if(t.innerHTML.includes("mark_unread")) {
                                if(lastc < dataset.chapter*1 && lastv == vol) {//a new read chapter from same volume
                                    last = vol+"v"+dataset.chapter
                                    process = false
                                    series[FS(series,manga_id)].last_read = last;
                                    if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                }
                                if(lastv < vol){//a new volume is read
                                    log("new volume")
                                    last = vol+"v"+dataset.chapter
                                    series[FS(series,manga_id)].last_read = last;
                                    if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                }
                                if(time < dataset.timestamp*1){
                                    time = dataset.timestamp*1
                                    series[FS(series,manga_id)].last_update = dataset.timestamp*1
                                    if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                }
                            }
                        }//end if
                    }//end for
                    log("done ProcVol, current last: "+last)
                    //             localStorage.setItem('series',JSON.stringify(series))
                    //                         dbUpdate("series",series)
                    dbUpdate("series",updater)
                }catch(e){
                    log("ProcVol error\n"+e)
                }
            }
            function Chapters(){
                try{
                    if(last.toString().includes("v")){//is parted as independent chapters per volume
                        ProcVol()
                    }
                    else{//is default
                        for(let t of temp){
                            if(t.dataset != null){
                                let dataset = t.dataset;
                                let manga_id = dataset.mangaId;
                                let timestamp = dataset.timestamp
                                if(dataset.volume != null) {
                                    vol = dataset.volume*1
                                }
                                else {
                                    vol = 10000-1
                                    notVOL = true
                                }
                                if(checkvol && cvol > vol && !notVOL) {//volume change and not first iteration
                                    if(cchp <= dataset.chapter*1){ //if it's not a successive chapter
                                        log("volume!!")
                                        checkvol = false
                                        last = "0v0"
                                        ProcVol()
                                    }
                                }
                                if(!last.toString().includes("v")){
                                    if(process && last == dataset.chapter*1){
                                        if(!t.innerHTML.includes("mark_unread")){
                                            last = 0
                                            log("reset")
                                        }
                                        else process = false
                                    }
                                    if(t.innerHTML.includes("mark_unread")) {
                                        if(last < dataset.chapter*1) {
                                            process = false
                                            last = dataset.chapter*1;
                                            series[FS(series,manga_id)].last_read = dataset.chapter*1;
                                            if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                        }
                                        if(time < dataset.timestamp*1){
                                            time = dataset.timestamp*1
                                            series[FS(series,manga_id)].last_update = dataset.timestamp*1
                                            if(updater.findIndex(q => q == series[FS(series,manga_id)]) < 0) updater.push(series[FS(series,manga_id)])
                                        }
                                    }
                                    cvol = vol
                                    cchp = dataset.chapter*1
                                }//end if
                            }//end if
                        }//end for
                        log("new last: "+last)
                        //                 localStorage.setItem('series',JSON.stringify(series))
                        //                             dbUpdate("series",series)
                        dbUpdate("series",updater)
                        log("OK");
                    }//end else
                }catch(e){
                    log("Chapters error\n"+e)
                }
            }
            var temp = document.getElementsByClassName("chapter-row d-flex row no-gutters p-2 align-items-center border-bottom odd-row");
            var last = 0
            var time = 0
            var pos = FS(series,mangaId)
            if(pos >= 0) {
                last = series[pos].last_read
                time = series[pos].last_update
            }
            log("old last: "+last)
            process = true
            var cvol = 10000
            var cchp = 0
            checkvol = true
            let state = document.getElementsByClassName("btn dropdown-toggle")[0].getElementsByClassName("d-none d-xl-inline")[0].innerText
            let name = document.title.split(" (Title)")[0]
            let id = document.URL.split("/")[4]
            let node = {id: id, name: name, state: state, last_read: last, last_update: time}
            let add = true;
            let z = FS(series,id)
            if(z == -1){
                series.push(node)
                log(name+" added to series")
                if(updater.findIndex(q => q == node) < 0) updater.push(node)
            }
            else{
                if(series[z].state !== state) {
                    series[z].state = state;
                    if(updater.findIndex(q => q == series[z]) < 0) updater.push(series[z])
                }
            }
            if(!follow_stat.includes("fa-bookmark")){
                Chapters()
            }
        }

        if(document.URL.includes('mangadex.org/follows') && !document.URL.includes('mangadex.org/follows/manga')){
            seriesDL = []
            for(s of series){
                seriesDL.push([s.id,s.name,s.status,s.last_read,s.last_update])
            }
            let data = [['ID','Series name','Status','Last Read','Last Update']].concat(seriesDL);
            for(var d of data){
                if(d[1].includes("\"")) d[1] = d[1].replaceAll("\"","|")
                if(d[1].includes(";")) d[1] = d[1].replaceAll(";",":")
                if(d.length == 5 && d[5] != 0) d[4] = new Date(d[4]*1000)
                else d.push("Unknown")
            }

            // Building the CSV from the Data two-dimensional array
            // Each column is separated by ";" and new line "\n" for next row
            let csvContent = '';
            data.forEach(function(infoArray, index) {
                dataString = infoArray.join(';');
                csvContent += index < data.length ? dataString + '\n' : dataString;
            });

            // The download function takes a CSV string, the filename and mimeType as parameters
            // Scroll/look down at the bottom of this snippet to see how download is called
            let download = function(content, fileName, mimeType) {
                var a = document.createElement('a');
                mimeType = mimeType || 'application/octet-stream';

                if (navigator.msSaveBlob) { // IE10
                    navigator.msSaveBlob(new Blob([content], {
                        type: mimeType
                    }), fileName);
                } else if (URL && 'download' in a) { //html5 A[download]
                    a.href = URL.createObjectURL(new Blob([content], {
                        type: mimeType
                    }));
                    a.setAttribute('download', fileName);
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                } else {
                    location.href = 'data:application/octet-stream,' + encodeURIComponent(content); // only this mime type is supported
                }
            }

            document.download = function(){download(csvContent, 'MangaDex.csv', 'text/csv;encoding:utf-8')}

            let boton = document.createElement("li")
            boton.setAttribute("class","nav-item")
            boton.setAttribute("href","#")
            boton.setAttribute("onclick","download()")
            boton.appendChild(document.createElement("a"))
            boton.children[0].setAttribute("class","nav-link")
            boton.children[0].appendChild(document.createElement("span"))
            boton.children[0].children[0].setAttribute("class","fas fa-download fa-fw")
            boton.children[0].children[0].setAttribute("aria-hidden","true")
            boton.children[0].appendChild(document.createElement("span"))
            boton.children[0].children[1].setAttribute("class","d-none d-md-inline")
            boton.children[0].children[1].innerText = "Export"
            document.getElementsByClassName("nav nav-tabs")[0].insertBefore(boton,document.getElementsByClassName("nav nav-tabs")[0].children[4])

            let boton2 = document.createElement("li")
            boton2.appendChild(document.createElement("a"))
            boton2.firstChild.setAttribute("class","nav-link")
            boton2.firstChild.innerText = "Script Options"
            boton2.firstChild.setAttribute("onclick","Show()")
            document.getElementsByClassName("nav nav-tabs")[0].insertBefore(boton2,document.getElementsByClassName("nav nav-tabs")[0].children[5])
        }

        CreateMenu()
        document.getElementById("enhancer").checked = enhance
    }
    //     }
    //     catch(e){
    //         log("ERROR!!")
    //         log(e)
    //     }
}

function CreateMenu(){
    gg = document.createElement("style")
    gg.innerText = ".modal_row {display: grid;grid-gap: 5px;grid-template-columns: 20vw 20vw 5vw;}"
    gg.innerText+= ".modalX {display: none;position: fixed;z-index: 10;padding-top: 10vh;left: 0;top: 0;width: 99%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); color: white;}"
    gg.innerText+= ".modalo {position: relative;background-color: black;margin: auto;padding: 2vw;border: 1px solid #888;width: 80%;box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);-webkit-animation-name: animatetop;-webkit-animation-duration: 0.4s;animation-name: animatetop;animation-duration: 0.4s}"
    gg.innerText+= "@-webkit-keyframes animatetop {from {top:-300px; opacity:0} to {top:0; opacity:1}}"
    gg.innerText+= "@keyframes animatetop {from {top:-300px; opacity:0}to {top:0; opacity:1}}"
    gg.innerText+= ".closex {color: white;float: right;font-size: 28px;font-weight: bold;padding-right: 8vw;}"
    gg.innerText+= ".closex:hover,.close:focus {color: #000;text-decoration: none;cursor: pointer;}"
    gg.innerText+= ".half {width: 45%;display: inline-block;}"

    document.querySelector("head").appendChild(gg)

    modal = document.createElement("div")
    modal.setAttribute("class","modalX")
    modal.setAttribute("id","modalX")
    //     modal.setAttribute("style","margin: auto;width: 70vw;")
    closer = document.createElement("span")
    closer.setAttribute("class","closex")
    closer.innerText = "×"
    closer.setAttribute("onclick","CloseThis()")
    modal.appendChild(closer)

    form = document.createElement("div")
    elem1 = document.createElement("div") //checkbox
    elem2 = document.createElement("div") //whitelist
    elem3 = document.createElement("div") //blacklist
    elem4 = document.createElement("div") //gap
    modbut = document.createElement("button") //save button
    canbut = document.createElement("button") //cancel button

    modt1 = document.createElement("h2") //title1
    modt1.setAttribute("title","series & group ID are the numbers in their URLs, after /title/ and /group/ respectively")
    modt1.innerText = "Whitelist elements"
    modt2 = document.createElement("h2") //title2
    modt2.setAttribute("title","series & group ID are the numbers in their URLs, after /title/ and /group/ respectively")
    modt2.innerText = "Blacklist elements"
    modt3 = document.createElement("h2") //title3
    modt3.setAttribute("title","series ID are the numbers after /title/ in their URL, and the gap has to include the chapters available between the gap")
    modt3.innerText = "Gap list elements"

    form.setAttribute("class","modalo")
    form.setAttribute("id","ScriptForm")
    //     form.setAttribute("action","")

    elem1A = document.createElement("input")
    elem1A.setAttribute("type","checkbox")
    elem1A.setAttribute("id","enhancer")
    elem1A.setAttribute("name","enhancer")
    elem1A.setAttribute("style","margin:5px;")
    elem1A.setAttribute("checked",enhance)

    elem1B = document.createElement("label")
    elem1B.setAttribute("for","enhancer")
    elem1B.innerText = "\tHighlight unread chapters"
    elem1.appendChild(elem1A)
    elem1.appendChild(elem1B)

    elem2.setAttribute("id","whitelister")
    elem2.setAttribute("style","display:grid;grid-gap:5px;")

    modalrow1 = document.createElement("div")
    modalrow1.setAttribute("class","modal_row")
    title2A = document.createElement("span")
    title2B = document.createElement("span")
    title2A.innerText = "series ID"
    title2B.innerText = "group ID"
    modalrow1.appendChild(title2A)
    modalrow1.appendChild(title2B)
    elem2.appendChild(modt1)
    elem2.appendChild(modalrow1)

    function Row1(w){
        let modalrow2 = document.createElement("div")
        modalrow2.setAttribute("class","modal_row")
        let span2A = document.createElement("span")
        let input2A = document.createElement("input")
        input2A.setAttribute("type","number")
        input2A.setAttribute("name","mangaID")
        input2A.setAttribute("value",w[0])
        span2A.appendChild(input2A)
        let span2B = document.createElement("span")
        let input2B = document.createElement("input")
        input2B.setAttribute("type","number")
        input2B.setAttribute("name","groupID")
        input2B.setAttribute("value",w[1])
        span2B.appendChild(input2B)
        modalrow2.appendChild(span2A)
        modalrow2.appendChild(span2B)
        let del = document.createElement("p")
        del.innerText = "DELETE"
        del.setAttribute("onclick","this.parentElement.remove()")
        modalrow2.appendChild(del)
        return modalrow2
    }

    if(whitelist.length) for(let w of whitelist){
        elem2.appendChild(Row1(w))
        //         let modalrow2 = document.createElement("div")
        //         modalrow2.setAttribute("class","modal_row")
        //         let span2A = document.createElement("span")
        //         let input2A = document.createElement("input")
        //         input2A.setAttribute("type","number")
        //         input2A.setAttribute("name","mangaID")
        //         input2A.setAttribute("value",w[0])
        //         span2A.appendChild(input2A)
        //         let span2B = document.createElement("span")
        //         let input2B = document.createElement("input")
        //         input2B.setAttribute("type","number")
        //         input2B.setAttribute("name","groupID")
        //         input2B.setAttribute("value",w[1])
        //         span2B.appendChild(input2B)
        //         modalrow2.appendChild(span2A)
        //         modalrow2.appendChild(span2B)
        //         let del = document.createElement("p")
        //         del.innerText = "DELETE"
        //         del.setAttribute("onclick","this.parentElement.remove()")
        //         modalrow2.appendChild(del)
        //         elem2.appendChild(modalrow2)
    }
    //     else{
    //         modalrow2 = document.createElement("div")
    //         modalrow2.setAttribute("class","modal_row")
    //     }
    add = document.createElement("p")
    add.setAttribute("id","adder")
    add.innerText = "ADD NEW"
    add.setAttribute("onclick","this.remove();AddWL(this)")
    elem2.appendChild(add)


    elem3.setAttribute("id","blacklister")
    elem3.appendChild(modt2)
    elem3.appendChild(modalrow1.cloneNode(true))
    if(blacklist.length) for(let b of blacklist){
        elem3.appendChild(Row1(b))
        //         let modalrow2 = document.createElement("div")
        //         modalrow2.setAttribute("class","modal_row")
        //         let span2A = document.createElement("span")
        //         let input2A = document.createElement("input")
        //         input2A.setAttribute("type","number")
        //         input2A.setAttribute("name","mangaID")
        //         input2A.setAttribute("value",b[0])
        //         span2A.appendChild(input2A)
        //         let span2B = document.createElement("span")
        //         let input2B = document.createElement("input")
        //         input2B.setAttribute("type","number")
        //         input2B.setAttribute("name","groupID")
        //         input2B.setAttribute("value",b[1])
        //         span2B.appendChild(input2B)
        //         modalrow2.appendChild(span2A)
        //         modalrow2.appendChild(span2B)
        //         let del = document.createElement("p")
        //         del.innerText = "DELETE"
        //         del.setAttribute("onclick","this.parentElement.remove()")
        //         modalrow2.appendChild(del)
        //         elem3.appendChild(modalrow2)
    }
    //     else{
    //         modalrow2 = document.createElement("div")
    //         modalrow2.setAttribute("class","modal_row")
    //     }
    add = document.createElement("p")
    add.setAttribute("id","adder")
    add.innerText = "ADD NEW"
    add.setAttribute("onclick","this.remove();AddBL(this)")
    elem3.appendChild(add)

    elem4.setAttribute("id","gapper")
    modalrow1 = document.createElement("div")
    modalrow1.setAttribute("class","modal_row")
    let title4A = document.createElement("span")
    let title4B = document.createElement("span")
    let title4B1 = document.createElement("span")
    let title4B2 = document.createElement("span")
    title4B1.setAttribute("class","half")
    title4B2.setAttribute("class","half")
    title4A.innerText = "series ID"
    title4B1.innerText = "Gap start"
    title4B2.innerText = "Gap end"
    title4B.appendChild(title4B1)
    title4B.appendChild(title4B2)
    modalrow1.appendChild(title4A)
    modalrow1.appendChild(title4B)
    elem4.appendChild(modt3)
    elem4.appendChild(modalrow1)

    if(gap.length) for(let g of gap){
        let modalrow2 = document.createElement("div")
        modalrow2.setAttribute("class","modal_row")
        let span2A = document.createElement("span")
        let input2A = document.createElement("input")
        input2A.setAttribute("type","number")
        input2A.setAttribute("name","mangaIDgap")
        input2A.setAttribute("value",g[0])
        span2A.appendChild(input2A)
        let span2B = document.createElement("span")
        let span2B1 = document.createElement("span")
        let span2B2 = document.createElement("span")
        let input2B1 = document.createElement("input")
        input2B1.setAttribute("type","number")
        input2B1.setAttribute("class","half")
        input2B1.setAttribute("name","gap1")
        input2B1.setAttribute("value",g[1][0])
        span2B.appendChild(input2B1)
        span2B2 = document.createElement("span")
        let input2B2 = document.createElement("input")
        input2B2.setAttribute("type","number")
        input2B2.setAttribute("class","half")
        input2B2.setAttribute("name","gap2")
        input2B2.setAttribute("value",g[1][1])
        span2B.appendChild(input2B2)
        modalrow2.appendChild(span2A)
        modalrow2.appendChild(span2B)
        let del = document.createElement("p")
        del.innerText = "DELETE"
        del.setAttribute("onclick","this.parentElement.remove()")
        modalrow2.appendChild(del)
        elem4.appendChild(modalrow2)
    }
    //     else{
    //         modalrow2 = document.createElement("div")
    //         modalrow2.setAttribute("class","modal_row")
    //     }
    add = document.createElement("p")
    add.setAttribute("id","adder")
    add.innerText = "ADD NEW"
    add.setAttribute("onclick","this.remove();AddGap(this)")
    elem4.appendChild(add)

    form.appendChild(elem1)
    form.appendChild(elem2)
    form.appendChild(elem3)
    form.appendChild(elem4)

    modal.appendChild(form)

    buttons = document.createElement("div")
    modbut.setAttribute("onclick","UpdateValues()")
    modbut.innerText = "Save Changes & Reload"
    modbut.setAttribute("style","margin:auto;width:30%;display:block;")
    buttons.appendChild(modbut)

    canbut.setAttribute("onclick","CloseThis()")
    canbut.innerText = "Exit without Saving Changes"
    canbut.setAttribute("style","margin:auto;width:30%;display:block;")
    buttons.appendChild(canbut)
    form.appendChild(buttons)

    document.querySelector("body").appendChild(modal)
}

function NewRow1(){
    let modalrow2 = document.createElement("div")
    modalrow2.setAttribute("class","modal_row")
    let span2A = document.createElement("span")
    let input2A = document.createElement("input")
    input2A.setAttribute("type","number")
    input2A.setAttribute("name","mangaID")
    input2A.setAttribute("value",0)
    span2A.appendChild(input2A)
    let span2B = document.createElement("span")
    let input2B = document.createElement("input")
    input2B.setAttribute("type","number")
    input2B.setAttribute("name","groupID")
    input2B.setAttribute("value",0)
    span2B.appendChild(input2B)
    modalrow2.appendChild(span2A)
    modalrow2.appendChild(span2B)
    let del = document.createElement("p")
    del.innerText = "DELETE"
    del.setAttribute("onclick","this.parentElement.remove()")
    modalrow2.appendChild(del)
    return modalrow2
}

function NewRow2(){
    let modalrow2 = document.createElement("div")
    modalrow2.setAttribute("class","modal_row")
    let span2A = document.createElement("span")
    let input2A = document.createElement("input")
    input2A.setAttribute("type","number")
    input2A.setAttribute("name","mangaIDgap")
    input2A.setAttribute("value",0)
    span2A.appendChild(input2A)
    let span2B = document.createElement("span")
    let span2B1 = document.createElement("span")
    let span2B2 = document.createElement("span")
    let input2B1 = document.createElement("input")
    input2B1.setAttribute("type","number")
    input2B1.setAttribute("class","half")
    input2B1.setAttribute("name","gap1")
    input2B1.setAttribute("value",0)
    span2B.appendChild(input2B1)
    span2B2 = document.createElement("span")
    let input2B2 = document.createElement("input")
    input2B2.setAttribute("type","number")
    input2B2.setAttribute("class","half")
    input2B2.setAttribute("name","gap2")
    input2B2.setAttribute("value",0)
    span2B.appendChild(input2B2)
    modalrow2.appendChild(span2A)
    modalrow2.appendChild(span2B)
    let del = document.createElement("p")
    del.innerText = "DELETE"
    del.setAttribute("onclick","this.parentElement.remove()")
    modalrow2.appendChild(del)
    return modalrow2
}

function NewButton(ToDo){
    add = document.createElement("p")
    add.setAttribute("id","adder")
    add.setAttribute("onclick",ToDo)
    add.innerText = "ADD NEW"
    return add
}

document.Show = function(){
    modalX.style.display = "block";
}

document.CloseThis = function(){
    modalX.style.display = "none";
}

document.AddWL = function(element){
    element.remove()
    elem2.appendChild(NewRow1())
    elem2.appendChild(NewButton("this.remove();AddWL(this)"))
}
document.AddBL = function(element){
    element.remove()
    elem3.appendChild(NewRow1())
    elem3.appendChild(NewButton("this.remove();AddBL(this)"))
}
document.AddGap = function(element){
    element.remove()
    elem4.appendChild(NewRow2())
    elem4.appendChild(NewButton("this.remove();AddGap(this)"))
}

document.UpdateValues = function() {
    log("Updating")
    var elements = document.getElementById("ScriptForm").children;
    log(elements)
    enhance = document.getElementById("ScriptForm").getElementsByTagName("input")[0].checked
    obj1 =[];
    obj2 =[];
    obj3 =[];
    let wl = elements[1].getElementsByClassName("modal_row")
    let bl = elements[2].getElementsByClassName("modal_row")
    let gl = elements[3].getElementsByClassName("modal_row")
    for(let i = 1; i < wl.length; i++){
        let inputs = wl[i].getElementsByTagName("input")
        obj1.push([inputs.mangaID.value*1,inputs.groupID.value*1])
    }
    for(let i = 1; i < bl.length; i++){
        let inputs = bl[i].getElementsByTagName("input")
        obj2.push([inputs.mangaID.value*1,inputs.groupID.value*1])
    }
    for(let j = 1; j < gl.length; j++){
        let spans = gl[j].getElementsByTagName("input")
        obj3.push([spans.mangaIDgap.value*1,[spans.gap1.value*1,spans.gap2.value*1]])
    }

    //     localStorage.setItem('whitelist',JSON.stringify(obj1));
    //     localStorage.setItem('blacklist',JSON.stringify(obj2));
    //     localStorage.setItem('gap',JSON.stringify(obj3));
    localStorage.setItem('enhance',JSON.stringify(enhance))
    dbUpdate("whitelist",obj1)
    dbUpdate("blacklist",obj2)
    dbUpdate("gap",obj3)
    location.reload()
}