ニコニコ静画、簡単NGスクリプト

申し訳ないが見たくないイラストはNG

// ==UserScript==
// @name         ニコニコ静画、簡単NGスクリプト
// @namespace    http://tampermonkey.net/
// @version      1.814
// @description  申し訳ないが見たくないイラストはNG
// @author       cbxm
// @match        https://seiga.nicovideo.jp/tag/*
// @match        https://seiga.nicovideo.jp/seiga/*
// @match        https://seiga.nicovideo.jp/watch/*
// @match        https://seiga.nicovideo.jp/my/personalize*
// @match        https://nicoad.nicovideo.jp/widget/*
// @match        https://seiga.nicovideo.jp/search/*
// @match        https://seiga.nicovideo.jp/illust/list
// @match        https://seiga.nicovideo.jp/shunga/list
// @connect      seiga.nicovideo.jp
// @connect      nicoad.nicovideo.jp
// @grant        GM.xmlHttpRequest
// @grant        GM.getValue
// @grant        GM.setValue
// @run-at document-start
// @license MIT
// ==/UserScript==
(async () => {
    "use strict";
    ;
    ;
    //汎用関数
    class Util {
        static WaitDocumentElement() {
            return new Promise(r => {
                if (document.documentElement != null) {
                    return r();
                }
                window.addEventListener("DOMContentLoaded", () => {
                    return r();
                });
            });
        }
        //https://stackoverflow.com/questions/69368851/type-safe-way-of-narrowing-type-of-arrays-by-length-when-nouncheckedindexedacces
        //TypeScriptくんを納得させるための関数
        static HasLengthAtLeast(arr, len) {
            return arr != null && arr.length >= len;
        }
        //TypeScriptくんを納得させるための関数
        static IsLength(arr, len) {
            return arr != null && arr.length == len;
        }
        //xmlString=未探査部分
        static XmlToObj(xmlString) {
            const F = (xmlString, obj) => {
                //タグを抜き出す
                let tagMatchs = null;
                while (true) {
                    tagMatchs = xmlString.match(/<([^>]+)>/); //タグを探す
                    //タグがないということはそれが値になる
                    if (tagMatchs == null) {
                        return xmlString;
                    }
                    if (tagMatchs[1]?.[tagMatchs[1].length - 1] == "/") {
                        xmlString = xmlString.replace(/<[^>]+>([^]*)/, "$1");
                    }
                    else {
                        break;
                    }
                }
                if (!Util.HasLengthAtLeast(tagMatchs, 2)) {
                    return xmlString;
                }
                const tag = tagMatchs[1];
                //タグの内側とその先を抜き出す
                const matchChildlen = [];
                while (true) {
                    const matchs = xmlString.match(new RegExp(`^[^<]*<${tag}>([^]+?)<\/${tag}>([^]*)`));
                    if (matchs == null || !Util.HasLengthAtLeast(matchs, 3)) {
                        break;
                    }
                    matchChildlen.push(matchs[1]);
                    xmlString = matchs[2];
                }
                //タグあったのにマッチしなかったおかしい
                if (matchChildlen.length == 0) {
                    return obj;
                }
                //そのタグが一つしかないとき、オブジェクトになる
                if (Util.IsLength(matchChildlen, 1)) {
                    //子を探す
                    obj[tag] = F(matchChildlen[0], {});
                }
                //そのタグが複数あるとき、配列になる
                if (matchChildlen.length > 1) {
                    obj = [];
                    for (let i = 0; i < matchChildlen.length; i++) {
                        //子を探す
                        obj[i] = F(matchChildlen[i], {});
                    }
                }
                //兄弟を探す
                F(xmlString, obj);
                return obj;
            };
            //初期化で<xml>を取り除く
            xmlString = xmlString.replace(/\s*<[^>]+>([^]+)/, "$1");
            return F(xmlString, {});
        }
        static HtmlToDocument(str) {
            const parser = new DOMParser();
            return parser.parseFromString(str, "text/html");
        }
        static HtmlToChildNodes(str) {
            return this.HtmlToDocument(str).body.childNodes;
        }
        static HtmlToElement(str) {
            return this.HtmlToDocument(str).body.firstElementChild;
        }
        static HtmlToSVG(s) {
            var div = document.createElementNS('https://www.w3.org/1999/xhtml', 'div');
            div.innerHTML = '<svg xmlns="https://www.w3.org/2000/svg">' + s + '</svg>';
            var frag = document.createDocumentFragment();
            while (div.firstChild?.firstChild)
                frag.appendChild(div.firstChild.firstChild);
            return frag;
        }
        static Wait(ms) {
            return new Promise(r => setTimeout(() => r(null), ms));
        }
        static WithTimeOut(p, ms) {
            return Promise.race([p, this.Wait(ms)]);
        }
        static async Retry(p, retryCount, wait, predicate) {
            for (let i = 0; i < retryCount; i++) {
                const result = await p();
                if (predicate(result)) {
                    return result;
                }
                //console.log("wait...");
                await Util.Wait(wait);
            }
            return null;
        }
        static async Download(url, name) {
            const link = document.createElement("a");
            document.body.appendChild(link);
            link.download = name;
            link.href = url;
            link.click();
            //すぐに消すと反応しないとか
            await this.Wait(100);
            document.body.removeChild(link);
        }
        static GMFetchText(url, optopns) {
            //console.log(url);
            return new Promise((resolve, reject) => {
                GM.xmlHttpRequest({
                    url: url,
                    ...optopns,
                    onload: (response) => {
                        // リダイレクトを処理(Chromeだと処理できないバグ?https://github.com/Tampermonkey/tampermonkey/issues/2134)
                        if (response.responseText == null && response.status >= 300 && response.status < 400) {
                            if (url != response.finalUrl) {
                                return this.GMFetchText(response.finalUrl, optopns);
                            }
                            console.error("リダイレクトを処理できませんでした");
                        }
                        resolve(response.responseText);
                    },
                    onerror: (e) => {
                        console.error(e);
                        reject("");
                    }
                });
            });
        }
        static Unique(array) {
            return Array.from(new Set(array));
        }
        static IsElementInViewport(el) {
            var rect = el.getBoundingClientRect();
            return (rect.top >= 0 &&
                rect.left >= 0 &&
                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
                rect.right <= (window.innerWidth || document.documentElement.clientWidth));
        }
        static SetCookie(key, value, option = "") {
            window.document.cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value) + "; " + option;
        }
        static SetCookieKeyAndValue(keyAndValue) {
            window.document.cookie = keyAndValue;
        }
        static GetAllCookie() {
            const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
            let keyVal = [];
            for (let i = 0; i < cookies.length; i++) {
                //cookies[i]=cookies[i].replace(/^\s/,"");     ???
                const s = cookies[i].match(/^(.*?)=(.*?)$/);
                if (Util.HasLengthAtLeast(s, 3)) {
                    keyVal.push({ key: s[1], val: s[2] });
                }
            }
            return keyVal;
        }
        static GetCookie(key) {
            const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
            for (var c of cookies) {
                if (RegExp(key).test(c)) {
                    return c;
                }
            }
            return null;
        }
        static GetCookieVal(key) {
            const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
            for (var c of cookies) {
                const matched = c.match(`${key}=([^]*)`);
                if (Util.HasLengthAtLeast(matched, 2)) {
                    return matched[1];
                }
            }
            return null;
        }
        static DeleteCookie(key) {
            window.document.cookie = encodeURI(key) + "=; max-age=0";
        }
    }
    ;
    class Fetcher {
        static GMFetchText(url) {
            return Util.GMFetchText(url, {
                method: "GET"
            });
        }
        static async FetchIllustDatas(ids) {
            if (ids.length == 0) {
                return { illusts: [], userIds: [] };
            }
            const url = `https:\/\/seiga.nicovideo.jp/api/illust/info?id_list=${ids.join()}`;
            const res = await this.GMFetchText(url);
            const obj = Util.XmlToObj(res);
            if (obj.response == undefined) {
                return { illusts: [], userIds: [] };
            }
            const list = Array.isArray(obj.response.image_list) ? obj.response.image_list : [obj.response.image_list.image];
            const illusts = [];
            for (let i = 0; i < list.length; i++) {
                illusts[i] = {
                    illustId: list[i].id,
                    created: new Date(list[i].created)
                };
            }
            return {
                illusts: illusts,
                userIds: list.map(l => l.user_id)
            };
        }
        static async FetchUserName(userId) {
            const url = "https://seiga.nicovideo.jp/api/user/info?id=" + userId;
            const json = Util.XmlToObj(await this.GMFetchText(url));
            if (json.response == undefined) {
                return { illusts: [], userIds: [] };
            }
            return json.response.user.nickname;
        }
        static async FetchUserId(illustId) {
            const url = "https://seiga.nicovideo.jp/api/illust/info?id=im" + illustId;
            const resultText = await this.GMFetchText(url);
            const json = Util.XmlToObj(resultText);
            if (json.response == undefined) {
                return { illusts: [], userIds: [] };
            }
            return json.response.image.user_id;
        }
    }
    ;
    class Storage {
        constructor(storageName) {
            this.storageName = "";
            this.storageName = storageName;
        }
        async GetStorageData(defaultValue = null) {
            const text = await GM.getValue(this.storageName, null);
            return text != null ? JSON.parse(decodeURIComponent(text)) : defaultValue;
        }
        async SetStorageData(data) {
            await GM.setValue(this.storageName, encodeURIComponent(JSON.stringify(data)));
        }
    }
    ;
    ;
    //MutationObserver使った便利関数
    class Observer {
        static Wait(predicate, parent = document, option = null) {
            return new Promise(r => {
                if (option == null) {
                    option = {
                        childList: true,
                        subtree: true
                    };
                }
                const mutationObserver = new MutationObserver((mrs) => {
                    if (predicate(mrs)) {
                        mutationObserver.disconnect();
                        r(mrs);
                        return;
                    }
                });
                mutationObserver.observe(parent, option);
            });
        }
        ;
        static WaitAddedNodes(predicate, parent, option = null) {
            return new Promise(r => {
                if (option == null) {
                    option = {
                        childList: true,
                        subtree: true
                    };
                }
                const mutationObserver = new MutationObserver(async (mrs) => {
                    //console.log(document.head.innerHTML);
                    //console.log(document.body.innerHTML);
                    const result = [];
                    for (let node of mrs) {
                        //console.log(added);
                        for (let i = 0; i < node.addedNodes.length; i++) {
                            result.push(...await predicate(node.addedNodes[i]));
                        }
                    }
                    if (result.length != 0) {
                        mutationObserver.disconnect();
                        r(result);
                        return;
                    }
                });
                mutationObserver.observe(parent, option);
            });
        }
        ;
        static WaitAddedNode(predicate, parent, option = null) {
            return new Promise(r => {
                if (option == null) {
                    option = {
                        childList: true,
                        subtree: true
                    };
                }
                if (option.childList == undefined && option.attributes == undefined && option.characterData == undefined) {
                    option.childList = true;
                    option.subtree = true;
                }
                const mutationObserver = new MutationObserver(async (mrs) => {
                    //console.log(document.head.innerHTML);
                    //console.log(document.body.innerHTML);
                    for (let node of mrs) {
                        //console.log(added);
                        for (let i = 0; i < node.addedNodes.length; i++) {
                            const ret = await predicate(node.addedNodes[i]);
                            if (ret != null) {
                                mutationObserver.disconnect();
                                r(ret);
                                return;
                            }
                        }
                    }
                });
                mutationObserver.observe(parent, option);
            });
        }
        ;
        static async DefinitelyGetElementById(id, parent = document.documentElement, option = null) {
            if (!(option?.doNotNormalCheck ?? false)) {
                const e = document.getElementById(id);
                if (e != null) {
                    return e;
                }
            }
            return this.WaitAddedNode(e => (e instanceof Element && e.id == id) ? e : null, parent, option);
        }
        //getElementsByClassNameをつかうけど単体
        static async DefinitelyGetElementByClassName(className, parent = document.documentElement, option = null) {
            if (!(option?.doNotNormalCheck ?? false)) {
                const e = parent.getElementsByClassName(className)[0];
                if (e != null) {
                    return e;
                }
            }
            return this.WaitAddedNode(e => {
                if (e instanceof Element) {
                    if (e.classList.contains(className)) {
                        return e;
                    }
                    if (option?.isDeepSearch ?? false) {
                        const c = e.getElementsByClassName(className);
                        if (c.length != 0) {
                            return c[0];
                        }
                    }
                }
                return null;
            }, parent, option);
        }
        //getElementsByTagNameをつかうけど単体
        static async DefinitelyGetElementByTagName(tagName, parent = document.documentElement, option = null) {
            tagName = tagName.toUpperCase();
            if (!(option?.doNotNormalCheck ?? false)) {
                const e = parent.getElementsByTagName(tagName)[0];
                if (e != null) {
                    return e;
                }
            }
            return this.WaitAddedNode(e => {
                if (e instanceof Element) {
                    if (e.tagName == tagName) {
                        return e;
                    }
                    if (option?.isDeepSearch ?? false) {
                        const c = e.getElementsByTagName(tagName);
                        if (c.length != 0) {
                            return c[0];
                        }
                    }
                }
                return null;
            }, parent, option);
        }
        static async DefinitelyGetElementsByClassName(className, parent = document.documentElement, option = null) {
            if (!(option?.doNotNormalCheck ?? false)) {
                const e = parent.getElementsByClassName(className);
                if (e.length != 0) {
                    return Array.from(e);
                }
            }
            return this.WaitAddedNodes(e => {
                const ret = [];
                if (e instanceof Element) {
                    if (e.classList.contains(className)) {
                        ret.push(e);
                    }
                    if (option?.isDeepSearch ?? false) {
                        ret.push(...Array.from(e.getElementsByClassName(className)));
                    }
                }
                return ret;
            }, parent, option);
        }
        static async DefinitelyGetElementsByTagName(tagName, parent = document.documentElement, option = null) {
            tagName = tagName.toUpperCase();
            if (!(option?.doNotNormalCheck ?? false)) {
                const e = parent.getElementsByTagName(tagName);
                if (e.length != 0) {
                    return Array.from(e);
                }
            }
            return this.WaitAddedNodes(e => {
                const ret = [];
                if (e instanceof Element) {
                    if (e.tagName == tagName) {
                        ret.push(e);
                    }
                    if (option?.isDeepSearch ?? false) {
                        ret.push(...Array.from(e.getElementsByTagName(tagName)));
                    }
                }
                return ret;
            }, parent, option);
        }
    }
    ;
    //暫定OK、暫定荒らし、確定OK、確定荒らし
    //type Status = "OK" | "NG" | "LOK" | "LNG"
    let VirtualPageType;
    (function (VirtualPageType) {
        VirtualPageType[VirtualPageType["None"] = -1] = "None";
        VirtualPageType[VirtualPageType["TAG_SEARCH"] = 0] = "TAG_SEARCH";
        VirtualPageType[VirtualPageType["ILLUST"] = 1] = "ILLUST";
        VirtualPageType[VirtualPageType["PERSONALIZE"] = 2] = "PERSONALIZE";
        VirtualPageType[VirtualPageType["ADS"] = 3] = "ADS";
        VirtualPageType[VirtualPageType["KEYWORD_SEARCH"] = 4] = "KEYWORD_SEARCH";
        VirtualPageType[VirtualPageType["MAX"] = 5] = "MAX";
    })(VirtualPageType || (VirtualPageType = {}));
    ;
    ;
    ;
    const pageInfos = [
        {
            type: VirtualPageType.TAG_SEARCH,
            regex: /https:\/\/seiga.nicovideo.jp\/tag\/.*/,
            name: "タグ検索",
        },
        {
            type: VirtualPageType.ILLUST,
            regex: /https:\/\/seiga.nicovideo.jp\/(seiga|watch)\/.*/,
            name: "イラストページ",
        },
        {
            type: VirtualPageType.PERSONALIZE,
            regex: /https:\/\/seiga.nicovideo.jp\/my\/personalize.*/,
            name: "定点観測",
        },
        {
            type: VirtualPageType.ADS,
            regex: /https:\/\/nicoad.nicovideo.jp\/widget\/.*/,
            name: "ニコニ広告",
        },
        {
            type: VirtualPageType.KEYWORD_SEARCH,
            regex: /https:\/\/seiga.nicovideo.jp\/search\/.*/,
            name: "キーワード検索",
        },
        {
            type: VirtualPageType.TAG_SEARCH,
            regex: /https:\/\/seiga.nicovideo.jp\/illust\/list/,
            name: "静画イラスト一覧",
        },
        {
            type: VirtualPageType.KEYWORD_SEARCH,
            regex: /https:\/\/seiga.nicovideo.jp\/shunga\/list/,
            name: "春画イラスト一覧",
        }
    ];
    const virtualPageInfos = [
        {
            illustListName: "item_list",
            illustItemName: "list_item",
        },
        {
            illustListName: "item_list",
            illustItemName: "list_item_cutout",
        },
        {
            illustListName: "list_body",
            illustItemName: "illust_thumb",
        },
        {
            // 空ならul,ilを使う
            illustListName: "",
            illustItemName: "",
        },
        {
            illustListName: "illust_pict_all",
            illustItemName: "illust_list_img",
        }
    ];
    let Status;
    (function (Status) {
        Status[Status["NONE"] = 0] = "NONE";
        Status[Status["OK"] = 1] = "OK";
        Status[Status["NG"] = 2] = "NG";
        Status[Status["WHITE"] = 3] = "WHITE";
        Status[Status["BLACK"] = 4] = "BLACK";
        Status[Status["AUTO"] = 5] = "AUTO";
        Status[Status["MAX"] = 6] = "MAX";
    })(Status || (Status = {}));
    class Main {
        constructor() {
            this.cache = new Map();
            this.illustInfos = [];
            this.illustListElements = new Set();
            this.selectedList = [];
            this.cacheStorage = new Storage("NICONICO_RENTO_ARASI_NG_DATA_CACHE");
            this.optionStorage = new Storage("NICONICO_RENTO_ARASI_NG_OPTION_CACHE");
            this.imgIntersectionObserver = new IntersectionObserver(entries => {
                for (let e of entries) {
                    if (e.intersectionRatio > 0) {
                        const img = e.target;
                        if (img.src != null && img.dataset != null && img.dataset.src != null) {
                            img.src = img.dataset.src;
                        }
                        this.imgIntersectionObserver.unobserve(img);
                    }
                }
            });
        }
        ListToMap(list) {
            for (let o of list) {
                o.name ?? (o.name = "");
            }
            if (list[0] != null && list[0].userId != null) {
                return new Map(list.map(o => {
                    const userId = o.userId;
                    delete o.userId;
                    for (let illust of o.illusts) {
                        illust.illustId = illust.id;
                        delete illust.id;
                    }
                    return [userId, o];
                }));
            }
            return new Map(list);
        }
        async GetStorageData() {
            const obj = await this.cacheStorage.GetStorageData([]);
            this.cache = this.ListToMap(obj);
            //console.log(this.cache);
            const defaultOption = {
                usePages: [true, true, true, true, true, true, true],
                judge: {
                    isJudge: false,
                    time: 1 * 60 * 60 * 1000, //一時間
                    postCount: 5,
                    period: 30,
                    isAutoNGHidden: false
                },
                createUserLink: true,
                createBlackWhiteButton: true,
                okCacheMax: 1000, //どのくらいがいいのかわからない
            };
            this.option = await this.optionStorage.GetStorageData(defaultOption);
            if (this.option.usePages == undefined) {
                this.option.usePages = defaultOption.usePages;
            }
            for (let i = 0; i < VirtualPageType.MAX; i++) {
                if (this.option.usePages[i] == undefined) {
                    this.option.usePages[i] = defaultOption.usePages[i];
                }
            }
            if (this.option.judge == undefined) {
                this.option.judge = defaultOption.judge;
            }
            if (this.option.judge.time == undefined) {
                this.option.judge.time = defaultOption.judge.time;
            }
            if (this.option.judge.postCount == undefined) {
                this.option.judge.postCount = defaultOption.judge.postCount;
            }
            if (this.option.judge.period == undefined) {
                this.option.judge.period = defaultOption.judge.period;
            }
            if (this.option.judge.isJudge == undefined) {
                this.option.judge.isJudge = defaultOption.judge.isJudge;
            }
            if (this.option.judge.isAutoNGHidden == undefined) {
                this.option.judge.isAutoNGHidden = defaultOption.judge.isAutoNGHidden;
            }
            if (this.option.createUserLink == undefined) {
                this.option.createUserLink = defaultOption.createUserLink;
            }
            if (this.option.createBlackWhiteButton == undefined) {
                this.option.createBlackWhiteButton = defaultOption.createBlackWhiteButton;
            }
            if (this.option.okCacheMax == undefined) {
                this.option.okCacheMax = defaultOption.okCacheMax;
            }
            //console.log(this.option);
        }
        GetInfo(illustId) {
            for (let [userId, user] of this.cache) {
                if (user.illusts == null) {
                    console.log(userId, user);
                    user.illusts = [];
                }
                for (let illust of user.illusts) {
                    if (illust.illustId == illustId) {
                        return { userId: userId, user: user, illust: illust };
                    }
                }
            }
            return undefined;
        }
        CheckAutoNG(user) {
            if (user.illusts.length == 0 || user.status != Status.OK) {
                return;
            }
            const createdNumbers = user.illusts.map(illust => {
                if (typeof illust.created == "string") {
                    illust.created = new Date(illust.created);
                }
                return illust.created.getTime();
            });
            //新しい順
            const sorted = createdNumbers.sort((a, b) => b - a);
            const currentDateNumber = new Date().getTime();
            const periodMS = this.option.judge.period * 86400000;
            for (let i = 0; i < sorted.length; i++) {
                if (periodMS != 0 && sorted[i] <= currentDateNumber - periodMS) {
                    break;
                }
                let j = i + 1;
                let postCount = 1;
                while (true) {
                    if (j >= sorted.length || sorted[i] - sorted[j] > this.option.judge.time || (periodMS != 0 && sorted[j] <= currentDateNumber - periodMS)) {
                        break;
                    }
                    j++;
                    postCount++;
                }
                if (postCount >= this.option.judge.postCount) {
                    user.status = Status.AUTO;
                    return;
                }
            }
        }
        ;
        GetIllustIds(itemListElement) {
            const illustIdElements = itemListElement.getElementsByTagName("img");
            const illustIds = new Set();
            for (let i = 0; i < illustIdElements.length; i++) {
                // https://lohas.nicoseiga.jp//thumb/xxxxxxuz?yyyyyy から xxxxx を抜き出す
                const idMatchs = illustIdElements[i].src.match(/(\d+).+?(?:\?\d+)?$/);
                if (idMatchs == null) {
                    continue;
                }
                const id = idMatchs[idMatchs.length - 1];
                illustIds.add(id);
            }
            return [...illustIds];
        }
        DrawList() {
            if (this.optionDialog == null) {
                return;
            }
            const list = this.optionDialog.getElementsByClassName("scrollUL")[0];
            const onlyCurrentPageCheckbox = this.optionDialog.getElementsByClassName("onlyCurrentPageCheckbox")[0];
            const listStatusSelect = this.optionDialog.getElementsByClassName("listStatusSelect")[0];
            if (list == undefined || onlyCurrentPageCheckbox == undefined || listStatusSelect == undefined) {
                return;
            }
            const status = listStatusSelect.value == "ALL" ? "" : Status[listStatusSelect.value];
            list.innerHTML = "";
            for (let [userId, user] of this.cache) {
                if (status != "" && user.status != status) {
                    continue;
                }
                const info = this.illustInfos.find(info => info.userId == userId);
                let sampleIllustId = (info == undefined) ? undefined : info.illust.illustId;
                if (onlyCurrentPageCheckbox.checked && sampleIllustId == undefined) {
                    continue;
                }
                if (sampleIllustId == undefined && user.illusts[0] != undefined) {
                    sampleIllustId = user.illusts[0].illustId;
                }
                const div = document.createElement("div");
                div.style.height = "70px";
                div.style.display = "flex";
                div.style.flexDirection = "column";
                div.className = "userInfoItem";
                list.appendChild(div);
                div.addEventListener("mouseup", e => this.ClickList(div));
                {
                    const nameIdDiv = document.createElement("div");
                    nameIdDiv.style.top = "relative";
                    nameIdDiv.style.position = "4px";
                    div.appendChild(nameIdDiv);
                    {
                        const nameSpan = document.createElement("span");
                        nameSpan.className = "userName";
                        nameSpan.textContent = user.name;
                        nameSpan.style.fontSize = "130%";
                        nameSpan.style.color = "black";
                        nameSpan.style.width = "66px";
                        nameSpan.style.height = "24px";
                        nameSpan.style.padding = "3px";
                        nameIdDiv.appendChild(nameSpan);
                        const idSpan = document.createElement("span");
                        idSpan.className = "userId";
                        idSpan.textContent = userId;
                        idSpan.style.fontSize = "130%";
                        idSpan.style.color = "black";
                        idSpan.style.width = "66px";
                        idSpan.style.padding = "3px";
                        nameIdDiv.appendChild(idSpan);
                    }
                    const userAndSampleImgDiv = document.createElement("div");
                    div.appendChild(userAndSampleImgDiv);
                    {
                        const aUser = document.createElement("a");
                        aUser.href = `https:\/\/seiga.nicovideo.jp/user/illust/${userId}`;
                        userAndSampleImgDiv.appendChild(aUser);
                        {
                            const imgUser = document.createElement("img");
                            imgUser.dataset.src = `https:\/\/secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/${Math.floor(parseInt(userId) / 10000)}/${userId}.jpg`;
                            imgUser.style.height = "40px";
                            imgUser.style.position = "relative";
                            imgUser.style.padding = "0 20px 0 10px";
                            imgUser.style.top = "-5px";
                            this.imgIntersectionObserver.observe(imgUser);
                            aUser.appendChild(imgUser);
                            imgUser.addEventListener("error", () => {
                                imgUser.src = "https:\/\/secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg";
                            });
                        }
                        if (sampleIllustId != undefined) {
                            const aSample = document.createElement("a");
                            aSample.href = `https:/\/seiga.nicovideo.jp/seiga/im${sampleIllustId}`;
                            userAndSampleImgDiv.appendChild(aSample);
                            {
                                const imgSample = document.createElement("img");
                                imgSample.dataset.src = `https:\/\/lohas.nicoseiga.jp\/\/thumb/${sampleIllustId}c`;
                                imgSample.style.height = "30px";
                                this.imgIntersectionObserver.observe(imgSample);
                                imgSample.style.position = "relative";
                                imgSample.style.top = "-5px";
                                aSample.appendChild(imgSample);
                                const bigSample = document.createElement("img");
                                bigSample.dataset.src = `https:\/\/lohas.nicoseiga.jp\/\/thumb/${sampleIllustId}c`;
                                bigSample.style.height = "100px";
                                this.imgIntersectionObserver.observe(bigSample);
                                bigSample.style.pointerEvents = "none";
                                bigSample.style.position = "absolute";
                                bigSample.style.zIndex = "110";
                                imgSample.addEventListener("mouseover", () => {
                                    const clientRect = imgSample.getBoundingClientRect();
                                    const x = window.scrollX + clientRect.left + imgSample.width / 2 - 50;
                                    const y = window.scrollY + clientRect.top + imgSample.height / 2 - 50;
                                    bigSample.style.top = y + "px";
                                    bigSample.style.left = x + "px";
                                    document.body.appendChild(bigSample);
                                });
                                imgSample.addEventListener("mouseleave", () => {
                                    bigSample.remove();
                                });
                            }
                        }
                    }
                }
            }
        }
        ClickList(target) {
            if (target != null) {
                if (this.selectedList.includes(target)) {
                    target.style.backgroundColor = "";
                    this.selectedList = this.selectedList.filter(s => s != target);
                }
                else {
                    target.style.backgroundColor = "rgba(0, 140, 255, 0.5)";
                    this.selectedList.push(target);
                }
            }
        }
        async SetOptionButton() {
            if (document.getElementById("optionSpan") != null) {
                return;
            }
            const optionSpan = document.createElement("span");
            optionSpan.id = "optionSpan";
            optionSpan.style.margin = "0 10px";
            optionSpan.style.lineHeight = "29px";
            if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
                const nextSibling = await Util.WithTimeOut(Observer.DefinitelyGetElementByClassName("search_tab_border"), 10000);
                if (nextSibling == null) {
                    return;
                }
                nextSibling.insertAdjacentElement("beforebegin", optionSpan);
            }
            else {
                const parent = await Util.WithTimeOut(Observer.DefinitelyGetElementByClassName("sg_pankuzu"), 10000);
                if (parent == null) {
                    return;
                }
                parent.appendChild(optionSpan);
            }
            {
                const optionButton = document.createElement("input");
                optionButton.type = "button";
                optionButton.value = "簡単NGスクリプト";
                optionButton.style.backgroundColor = "yellow";
                optionButton.style.padding = "1px 10px";
                optionButton.style.fontSize = "110%";
                optionButton.style.cssText += "color: black !important;";
                optionButton.addEventListener("click", () => {
                    if (this.optionDialog.parentElement == null) {
                        optionSpan.appendChild(this.optionDialog);
                        return;
                    }
                    this.optionDialog.style.display = (this.optionDialog.style.display == "none") ? "block" : "none";
                });
                optionSpan.appendChild(optionButton);
                this.optionDialog = document.createElement("div");
                this.optionDialog.style.backgroundColor = "white";
                this.optionDialog.style.position = "absolute";
                this.optionDialog.style.padding = "5px";
                this.optionDialog.style.marginLeft = "10px";
                this.optionDialog.style.zIndex = "100";
                this.optionDialog.style.border = "2px solid";
                {
                    const list1 = document.createElement("div");
                    list1.style.display = "flex";
                    list1.style.paddingTop = "5px";
                    list1.style.paddingBottom = "10px";
                    this.optionDialog.appendChild(list1);
                    {
                        const listStatusSelect = document.createElement("select");
                        listStatusSelect.className = "listStatusSelect";
                        listStatusSelect.style.margin = "5px";
                        list1.appendChild(listStatusSelect);
                        for (let i = 1; i <= Status.MAX; i++) {
                            const option = document.createElement("option");
                            const text = i == Status.MAX ? "ALL" : Status[i];
                            option.value = text;
                            option.textContent = text;
                            listStatusSelect.appendChild(option);
                        }
                        listStatusSelect.addEventListener("change", () => {
                            while (this.selectedList.length != 0) {
                                const element = this.selectedList.pop();
                                if (element != undefined) {
                                    element.style.backgroundColor = "";
                                }
                            }
                            this.DrawList();
                        });
                        const onlyCurrentPageLabel = document.createElement("label");
                        onlyCurrentPageLabel.style.color = "black";
                        onlyCurrentPageLabel.style.padding = "3px";
                        onlyCurrentPageLabel.style.display = "flex";
                        list1.appendChild(onlyCurrentPageLabel);
                        {
                            const onlyCurrentPageCheckbox = document.createElement("input");
                            onlyCurrentPageCheckbox.type = "checkbox";
                            onlyCurrentPageCheckbox.className = "onlyCurrentPageCheckbox";
                            onlyCurrentPageCheckbox.checked = true;
                            onlyCurrentPageCheckbox.style.padding = "3px";
                            onlyCurrentPageCheckbox.style.margin = "10px";
                            onlyCurrentPageCheckbox.style.marginRight = "3px";
                            onlyCurrentPageCheckbox.style.marginLeft = "0px";
                            onlyCurrentPageLabel.appendChild(onlyCurrentPageCheckbox);
                            onlyCurrentPageCheckbox.addEventListener("change", () => this.DrawList());
                            const onlyCurrentPageText = document.createElement("div");
                            onlyCurrentPageText.textContent = "このページだけ";
                            onlyCurrentPageText.style.color = "black";
                            onlyCurrentPageLabel.appendChild(onlyCurrentPageText);
                        }
                        const allSelect = document.createElement("input");
                        allSelect.type = "button";
                        allSelect.value = "全選択";
                        allSelect.style.color = "black";
                        allSelect.style.fontSize = "120%";
                        allSelect.style.padding = "0 5px";
                        allSelect.style.margin = "3px";
                        list1.appendChild(allSelect);
                        allSelect.addEventListener("click", () => {
                            const infos = Array.from(document.getElementsByClassName("userInfoItem"));
                            for (let info of infos) {
                                this.ClickList(info);
                            }
                        });
                        const detailButton = document.createElement("input");
                        detailButton.type = "button";
                        detailButton.value = "設定";
                        detailButton.style.color = "black";
                        detailButton.style.fontSize = "120%";
                        detailButton.style.margin = "3px";
                        detailButton.style.marginLeft = "45px";
                        detailButton.style.padding = "0 10px";
                        list1.appendChild(detailButton);
                        detailButton.addEventListener("click", () => detailDialog.style.display = (detailDialog.style.display == "none") ? "block" : "none");
                        const detailDialog = document.createElement("div");
                        detailDialog.style.backgroundColor = "white";
                        detailDialog.style.display = "none";
                        detailDialog.style.position = "absolute";
                        detailDialog.style.paddingLeft = "10px";
                        detailDialog.style.zIndex = "100";
                        detailDialog.style.border = "2px solid";
                        detailDialog.style.left = "360px";
                        detailDialog.style.top = "10px";
                        detailDialog.style.minWidth = "350px";
                        list1.appendChild(detailDialog);
                        const useSettingH3 = document.createElement("h1");
                        useSettingH3.textContent = "使うところ";
                        useSettingH3.style.fontSize = "140%";
                        useSettingH3.style.marginTop = "10px";
                        useSettingH3.style.color = "black";
                        detailDialog.appendChild(useSettingH3);
                        const setUseListDiv = document.createElement("div");
                        setUseListDiv.style.marginBottom = "10px";
                        setUseListDiv.style.display = "flex";
                        setUseListDiv.style.flexWrap = "wrap";
                        detailDialog.appendChild(setUseListDiv);
                        {
                            for (let i = 0; i < pageInfos.length; i++) {
                                const setUseLabel = document.createElement("label");
                                setUseLabel.style.display = "inline-block";
                                setUseListDiv.appendChild(setUseLabel);
                                {
                                    const setUsePageCheckbox = document.createElement("input");
                                    setUsePageCheckbox.type = "checkbox";
                                    setUsePageCheckbox.checked = this.option.usePages[i];
                                    setUsePageCheckbox.style.padding = "3px";
                                    setUsePageCheckbox.style.margin = "10px";
                                    setUsePageCheckbox.style.marginRight = "3px";
                                    setUseLabel.appendChild(setUsePageCheckbox);
                                    setUsePageCheckbox.addEventListener("change", async () => {
                                        this.option.usePages[i] = setUsePageCheckbox.checked;
                                        await this.optionStorage.SetStorageData(this.option);
                                    });
                                    const setUsePageText = document.createElement("span");
                                    setUsePageText.textContent = pageInfos[i].name;
                                    setUsePageText.style.padding = "3px";
                                    setUsePageText.style.fontSize = "120%";
                                    setUsePageText.style.color = "black";
                                    setUseLabel.appendChild(setUsePageText);
                                }
                            }
                        }
                        const otherSettingH3 = document.createElement("h1");
                        otherSettingH3.textContent = "イラストサムネ";
                        otherSettingH3.style.fontSize = "140%";
                        otherSettingH3.style.marginTop = "10px";
                        otherSettingH3.style.color = "black";
                        detailDialog.appendChild(otherSettingH3);
                        const setCreateUserLinkDiv = document.createElement("div");
                        setCreateUserLinkDiv.style.display = "flex";
                        detailDialog.appendChild(setCreateUserLinkDiv);
                        {
                            const setCreateUserLinkChackbox = document.createElement("input");
                            setCreateUserLinkChackbox.type = "checkbox";
                            setCreateUserLinkChackbox.id = "createUserLink";
                            setCreateUserLinkChackbox.checked = this.option.createUserLink;
                            setCreateUserLinkChackbox.style.padding = "3px";
                            setCreateUserLinkChackbox.style.margin = "10px";
                            setCreateUserLinkChackbox.style.marginRight = "3px";
                            setCreateUserLinkDiv.appendChild(setCreateUserLinkChackbox);
                            setCreateUserLinkChackbox.addEventListener("change", async () => {
                                this.option.createUserLink = setCreateUserLinkChackbox.checked;
                                await this.optionStorage.SetStorageData(this.option);
                            });
                            const setCreateUserLinkDivLabel = document.createElement("label");
                            setCreateUserLinkDivLabel.htmlFor = "createUserLink";
                            setCreateUserLinkDivLabel.textContent = "ユーザー名をユーザーページへのリンクにする";
                            setCreateUserLinkDivLabel.style.color = "black";
                            setCreateUserLinkDivLabel.style.padding = "3px";
                            setCreateUserLinkDivLabel.style.fontSize = "120%";
                            setCreateUserLinkDiv.appendChild(setCreateUserLinkDivLabel);
                        }
                        const setCreateBlackWhiteButtonDiv = document.createElement("div");
                        setCreateBlackWhiteButtonDiv.style.display = "flex";
                        detailDialog.appendChild(setCreateBlackWhiteButtonDiv);
                        {
                            const setCreateBlackWhiteButtonChackbox = document.createElement("input");
                            setCreateBlackWhiteButtonChackbox.type = "checkbox";
                            setCreateBlackWhiteButtonChackbox.id = "setCreateBlackWhiteButton";
                            setCreateBlackWhiteButtonChackbox.checked = this.option.createBlackWhiteButton;
                            setCreateBlackWhiteButtonChackbox.style.padding = "3px";
                            setCreateBlackWhiteButtonChackbox.style.margin = "10px";
                            setCreateBlackWhiteButtonChackbox.style.marginRight = "3px";
                            setCreateBlackWhiteButtonDiv.appendChild(setCreateBlackWhiteButtonChackbox);
                            setCreateBlackWhiteButtonChackbox.addEventListener("change", async () => {
                                this.option.createBlackWhiteButton = setCreateBlackWhiteButtonChackbox.checked;
                                await this.optionStorage.SetStorageData(this.option);
                            });
                            const setCreateBlackWhiteButtonLabel = document.createElement("label");
                            setCreateBlackWhiteButtonLabel.htmlFor = "setCreateBlackWhiteButton";
                            setCreateBlackWhiteButtonLabel.textContent = "白黒ボタンを付ける";
                            setCreateBlackWhiteButtonLabel.style.color = "black";
                            setCreateBlackWhiteButtonLabel.style.padding = "3px";
                            setCreateBlackWhiteButtonLabel.style.fontSize = "120%";
                            setCreateBlackWhiteButtonDiv.appendChild(setCreateBlackWhiteButtonLabel);
                        }
                        const otherSettingH4 = document.createElement("h1");
                        otherSettingH4.textContent = "連投自動NG";
                        otherSettingH4.style.fontSize = "140%";
                        otherSettingH4.style.marginTop = "10px";
                        otherSettingH4.style.color = "black";
                        detailDialog.appendChild(otherSettingH4);
                        const judgeRigorCover = document.createElement("div");
                        const setIsJudgeDiv = document.createElement("div");
                        setIsJudgeDiv.style.display = "flex";
                        detailDialog.appendChild(setIsJudgeDiv);
                        {
                            const isJudgeCheckbox = document.createElement("input");
                            isJudgeCheckbox.type = "checkbox";
                            isJudgeCheckbox.id = "isJudgeCheckbox";
                            isJudgeCheckbox.checked = this.option.judge.isJudge;
                            isJudgeCheckbox.style.padding = "3px";
                            isJudgeCheckbox.style.margin = "10px";
                            isJudgeCheckbox.style.marginRight = "3px";
                            setIsJudgeDiv.appendChild(isJudgeCheckbox);
                            isJudgeCheckbox.addEventListener("change", async () => {
                                this.option.judge.isJudge = isJudgeCheckbox.checked;
                                if (this.option.judge.isJudge) {
                                    judgeRigorCover.style.visibility = "hidden";
                                }
                                else {
                                    judgeRigorCover.style.visibility = "visible";
                                }
                                await this.optionStorage.SetStorageData(this.option);
                            });
                            const isJudgeLabel = document.createElement("label");
                            isJudgeLabel.htmlFor = "isJudgeCheckbox";
                            isJudgeLabel.textContent = "有効にする";
                            isJudgeLabel.style.color = "black";
                            isJudgeLabel.style.padding = "3px";
                            isJudgeLabel.style.fontSize = "120%";
                            setIsJudgeDiv.appendChild(isJudgeLabel);
                        }
                        const setJudgeDiv = document.createElement("div");
                        setJudgeDiv.style.padding = "5px";
                        setJudgeDiv.style.position = "relative";
                        detailDialog.appendChild(setJudgeDiv);
                        {
                            const setJudgeRigorDiv = document.createElement("div");
                            setJudgeRigorDiv.style.padding = "0px 10px 5px 10px";
                            setJudgeDiv.appendChild(setJudgeRigorDiv);
                            {
                                const setJudgeTime = document.createElement("input");
                                setJudgeTime.type = "time";
                                setJudgeTime.style.height = "20px";
                                setJudgeTime.style.fontSize = "120%";
                                const hour = ('00' + Math.floor(this.option.judge.time / 60 / 1000 / 60).toString()).slice(-2);
                                const minutes = ('00' + (this.option.judge.time / 60 / 1000 % 60).toString()).slice(-2);
                                setJudgeTime.value = `${hour}:${minutes}`;
                                setJudgeTime.addEventListener("change", async () => {
                                    const [h, m] = setJudgeTime.value.split(":").map(s => parseInt(s));
                                    const ms = ((h * 60) + m) * 60 * 1000;
                                    if (ms >= 1) {
                                        this.option.judge.time = ms;
                                        await this.optionStorage.SetStorageData(this.option);
                                    }
                                    else {
                                        const hour = ('00' + Math.floor(this.option.judge.time / 60 / 1000 / 60).toString()).slice(-2);
                                        const minutes = ('00' + (this.option.judge.time / 60 / 1000 % 60).toString()).slice(-2);
                                        setJudgeTime.value = `${hour}:${minutes}`;
                                    }
                                });
                                setJudgeRigorDiv.appendChild(setJudgeTime);
                                const setJudgeText1 = document.createElement("span");
                                setJudgeText1.textContent = "以内に";
                                setJudgeText1.style.color = "black";
                                setJudgeText1.style.fontSize = "15px";
                                setJudgeRigorDiv.appendChild(setJudgeText1);
                                const setJudgePostCount = document.createElement("input");
                                setJudgePostCount.type = "number";
                                setJudgePostCount.value = this.option.judge.postCount.toString();
                                setJudgePostCount.min = "2";
                                setJudgePostCount.style.width = "40px";
                                setJudgePostCount.style.height = "20px";
                                setJudgePostCount.style.fontSize = "120%";
                                setJudgePostCount.addEventListener("change", async () => {
                                    const num = parseInt(setJudgePostCount.value);
                                    if (num >= 2) {
                                        this.option.judge.postCount = num;
                                        await this.optionStorage.SetStorageData(this.option);
                                    }
                                    else {
                                        this.option.judge.postCount = 2;
                                        setJudgePostCount.value = this.option.judge.postCount.toString();
                                    }
                                });
                                setJudgeRigorDiv.appendChild(setJudgePostCount);
                                const setJudgeText2 = document.createElement("span");
                                setJudgeText2.textContent = "回投稿で仮荒らし認定";
                                setJudgeText2.style.color = "black";
                                setJudgeText2.style.fontSize = "15px";
                                setJudgeRigorDiv.appendChild(setJudgeText2);
                            }
                            const setJudgePeriodDiv = document.createElement("div");
                            setJudgePeriodDiv.style.padding = "0px 10px 5px 10px";
                            setJudgeDiv.appendChild(setJudgePeriodDiv);
                            {
                                //日
                                const setJudgePeriod = document.createElement("input");
                                setJudgePeriod.style.marginRight = "5px";
                                setJudgePeriod.type = "text";
                                setJudgePeriod.style.width = "40px";
                                setJudgePeriod.style.height = "18px";
                                setJudgePeriod.style.fontSize = "120%";
                                setJudgePeriod.value = this.option.judge.period.toString();
                                setJudgePeriodDiv.appendChild(setJudgePeriod);
                                setJudgePeriod.addEventListener("change", async () => {
                                    const num = Number(setJudgePeriod.value);
                                    if (num >= 0) {
                                        this.option.judge.period = num;
                                        await this.optionStorage.SetStorageData(this.option);
                                    }
                                    else {
                                        this.option.judge.period = 0;
                                        setJudgePeriod.value = this.option.judge.period.toString();
                                    }
                                });
                                const setJudgePeriodText = document.createElement("span");
                                setJudgePeriodText.textContent = "日前のイラストまで対象(0で無限)";
                                setJudgePeriodText.style.color = "black";
                                setJudgePeriodText.style.fontSize = "15px";
                                setJudgePeriodDiv.appendChild(setJudgePeriodText);
                            }
                            const setAutoNGHiddenDiv = document.createElement("div");
                            setAutoNGHiddenDiv.style.padding = "0px 10px";
                            setJudgeDiv.appendChild(setAutoNGHiddenDiv);
                            {
                                const setAutoNGHiddenLabel = document.createElement("label");
                                setAutoNGHiddenDiv.appendChild(setAutoNGHiddenLabel);
                                {
                                    const setAutoNGHiddenCheckbox = document.createElement("input");
                                    setAutoNGHiddenCheckbox.style.padding = "3px";
                                    setAutoNGHiddenCheckbox.style.marginRight = "5px";
                                    setAutoNGHiddenCheckbox.type = "checkbox";
                                    setAutoNGHiddenCheckbox.checked = this.option.judge.isAutoNGHidden;
                                    setAutoNGHiddenLabel.appendChild(setAutoNGHiddenCheckbox);
                                    setAutoNGHiddenCheckbox.addEventListener("change", async () => {
                                        this.option.judge.isAutoNGHidden = setAutoNGHiddenCheckbox.checked;
                                        for (let info of this.illustInfos) {
                                            this.UpdateIllust(info);
                                            this.DrawBlackWhiteButton(info);
                                        }
                                        this.UpdateIllustList();
                                        await this.optionStorage.SetStorageData(this.option);
                                    });
                                    const setAutoNGHiddenText = document.createElement("span");
                                    setAutoNGHiddenText.textContent = "自動NGしたのを非表示にする";
                                    setAutoNGHiddenText.style.color = "black";
                                    setAutoNGHiddenText.style.fontSize = "15px";
                                    setAutoNGHiddenLabel.appendChild(setAutoNGHiddenText);
                                }
                            }
                            judgeRigorCover.style.backgroundColor = "gray";
                            judgeRigorCover.style.width = "320px";
                            judgeRigorCover.style.height = "100%";
                            judgeRigorCover.style.zIndex = "1000";
                            judgeRigorCover.style.opacity = "0.5";
                            judgeRigorCover.style.position = "absolute";
                            judgeRigorCover.style.top = "0";
                            setJudgeDiv.appendChild(judgeRigorCover);
                            if (this.option.judge.isJudge) {
                                judgeRigorCover.style.visibility = "hidden";
                            }
                            else {
                                judgeRigorCover.style.visibility = "visible";
                            }
                        }
                        const otherSettingH5 = document.createElement("h1");
                        otherSettingH5.textContent = "その他";
                        otherSettingH5.style.fontSize = "140%";
                        otherSettingH5.style.marginTop = "10px";
                        otherSettingH5.style.color = "black";
                        detailDialog.appendChild(otherSettingH5);
                        //const setToOKPeriodDiv = document.createElement("div");
                        //setToOKPeriodDiv.style.padding = "5px";
                        //detailDialog.appendChild(setToOKPeriodDiv);
                        //{
                        //    const setToOKPeriodText1 = document.createElement("div");
                        //    setToOKPeriodText1.textContent = "取得したなかで最新絵が";
                        //    setToOKPeriodText1.style.color = "black";
                        //    setToOKPeriodText1.style.fontSize = "15px";
                        //    setToOKPeriodDiv.appendChild(setToOKPeriodText1);
                        //    const setToOKPeriodText2 = document.createElement("div");
                        //    setToOKPeriodText2.textContent = "これより前のものなら未分類化(0で無効)";
                        //    setToOKPeriodText2.style.color = "black";
                        //    setToOKPeriodText2.style.fontSize = "15px";
                        //    setToOKPeriodDiv.appendChild(setToOKPeriodText2);
                        //    for (let i = Status.OK; i < Status.MAX; i++) {
                        //        const setToOKPeriodStatusDiv = document.createElement("div");
                        //        setToOKPeriodDiv.appendChild(setToOKPeriodStatusDiv);
                        //        const setToOKPeriodStatusNameText = document.createElement("span");
                        //        setToOKPeriodStatusNameText.textContent = Status[i]+": ";
                        //        setToOKPeriodStatusNameText.style.color = "black";
                        //        setToOKPeriodStatusNameText.style.fontSize = "15px";
                        //        setToOKPeriodStatusDiv.appendChild(setToOKPeriodStatusNameText);
                        //        if (i == Status.OK) {
                        //            const setToOKPeriodStatusOKText = document.createElement("span");
                        //            setToOKPeriodStatusOKText.textContent = "これが未分類リスト";
                        //            setToOKPeriodStatusOKText.style.color = "black";
                        //            setToOKPeriodStatusOKText.style.fontSize = "15px";
                        //            setToOKPeriodStatusDiv.appendChild(setToOKPeriodStatusOKText);
                        //            continue;
                        //        }
                        //        //日
                        //        const setToOKPeriod = document.createElement("input");
                        //        setToOKPeriod.style.marginRight = "5px";
                        //        setToOKPeriod.type = "text";
                        //        setToOKPeriod.style.width = "40px";
                        //        setToOKPeriod.style.height = "5px";
                        //        setToOKPeriod.style.fontSize = "120%";
                        //        setToOKPeriod.value = this.option.judge.period.toString();
                        //        setToOKPeriodStatusDiv.appendChild(setToOKPeriod);
                        //        setToOKPeriod.addEventListener("change", async () => {
                        //            const num = Number(setToOKPeriod.value);
                        //            if (num >= 0) {
                        //                this.option.judge.period = num;
                        //                await this.optionStorage.SetStorageData(this.option);
                        //            } else {
                        //                this.option.judge.period = 0;
                        //                setToOKPeriod.value = this.option.judge.period.toString();
                        //            }
                        //        });
                        //        const setToOKPeriodText = document.createElement("span");
                        //        setToOKPeriodText.textContent = "日";
                        //        setToOKPeriodText.style.color = "black";
                        //        setToOKPeriodText.style.fontSize = "15px";
                        //        setToOKPeriodStatusDiv.appendChild(setToOKPeriodText);
                        //    }
                        //}
                        const setOKCacheMaxFlex = document.createElement("div");
                        setOKCacheMaxFlex.style.padding = "5px";
                        detailDialog.appendChild(setOKCacheMaxFlex);
                        {
                            const setOKCacheMaxText1 = document.createElement("span");
                            setOKCacheMaxText1.textContent = "OKユーザーのキャッシュ最大数:";
                            setOKCacheMaxText1.style.color = "black";
                            setOKCacheMaxText1.style.fontSize = "15px";
                            setOKCacheMaxFlex.appendChild(setOKCacheMaxText1);
                            const setOKCacheMax = document.createElement("input");
                            setOKCacheMax.type = "number";
                            setOKCacheMax.value = this.option.okCacheMax.toString();
                            setOKCacheMax.style.width = "80px";
                            setOKCacheMax.min = "100";
                            setOKCacheMax.style.height = "20px";
                            setOKCacheMax.style.fontSize = "120%";
                            setOKCacheMax.addEventListener("change", async () => {
                                const num = parseInt(setOKCacheMax.value);
                                if (num >= 100) {
                                    this.option.okCacheMax = num;
                                    await this.optionStorage.SetStorageData(this.option);
                                }
                                else {
                                    this.option.okCacheMax = 100;
                                    setOKCacheMax.value = this.option.okCacheMax.toString();
                                }
                            });
                            setOKCacheMaxFlex.appendChild(setOKCacheMax);
                        }
                    }
                    const list2 = document.createElement("div");
                    list2.style.position = "relative";
                    list2.style.display = "flex";
                    this.optionDialog.appendChild(list2);
                    {
                        const userInfoList = document.createElement("ul");
                        userInfoList.className = "scrollUL";
                        userInfoList.style.overflowY = "scroll";
                        userInfoList.style.overflowX = "hidden";
                        userInfoList.style.height = "400px";
                        userInfoList.style.width = "250px";
                        list2.appendChild(userInfoList);
                        const buttonList = document.createElement("ul");
                        buttonList.style.width = "90px";
                        list2.appendChild(buttonList);
                        {
                            const moveButtonList = document.createElement("div");
                            moveButtonList.style.marginTop = "20px";
                            moveButtonList.style.marginBottom = "10px";
                            buttonList.appendChild(moveButtonList);
                            {
                                for (let i = Status.OK; i < Status.MAX; i++) {
                                    const div = document.createElement("div");
                                    moveButtonList.appendChild(div);
                                    {
                                        const toButton = document.createElement("input");
                                        toButton.type = "button";
                                        toButton.style.padding = "3px";
                                        toButton.style.fontSize = "130%";
                                        toButton.style.margin = "3px";
                                        toButton.value = "→ " + Status[i];
                                        toButton.name = Status[i];
                                        div.appendChild(toButton);
                                        toButton.addEventListener("click", async () => {
                                            while (this.selectedList.length != 0) {
                                                const element = this.selectedList.pop();
                                                if (element == undefined) {
                                                    continue;
                                                }
                                                element.style.backgroundColor = "";
                                                const userId = element.getElementsByClassName("userId")[0].textContent;
                                                if (userId == undefined) {
                                                    continue;
                                                }
                                                const user = this.cache.get(userId);
                                                if (user != undefined) {
                                                    user.status = Status[toButton.name];
                                                }
                                            }
                                            for (let info of this.illustInfos) {
                                                this.UpdateIllust(info);
                                                this.DrawBlackWhiteButton(info);
                                            }
                                            this.UpdateIllustList();
                                            this.DrawList();
                                            await this.cacheStorage.SetStorageData([...this.cache]);
                                        });
                                    }
                                }
                            }
                            const DeleteSelectedUser = () => {
                                while (this.selectedList.length != 0) {
                                    const element = this.selectedList.pop();
                                    if (element == undefined) {
                                        continue;
                                    }
                                    const userId = element.getElementsByClassName("userId")[0].textContent;
                                    if (userId == undefined) {
                                        continue;
                                    }
                                    this.cache.delete(userId);
                                    const infos = this.illustInfos.filter(info => info.userId == userId);
                                    for (let info of infos) {
                                        info.user.status = Status.WHITE;
                                        this.UpdateIllust(info);
                                        this.DrawBlackWhiteButton(info);
                                    }
                                    this.UpdateIllustList();
                                    this.illustInfos = this.illustInfos.filter(info => info.userId != userId);
                                }
                            };
                            const div = document.createElement("div");
                            buttonList.appendChild(div);
                            {
                                const selectedCacheClearButton = document.createElement("input");
                                selectedCacheClearButton.type = "button";
                                selectedCacheClearButton.style.padding = "3px";
                                selectedCacheClearButton.style.fontSize = "120%";
                                selectedCacheClearButton.style.margin = "3px";
                                selectedCacheClearButton.style.marginTop = "5px";
                                selectedCacheClearButton.style.backgroundColor = "yellow";
                                selectedCacheClearButton.style.cssText += "color: black !important";
                                selectedCacheClearButton.value = "→ DELETE";
                                div.appendChild(selectedCacheClearButton);
                                selectedCacheClearButton.addEventListener("click", async () => {
                                    if (!window.confirm("選択したアイテムのキャッシュクリアしていいですか?\nホワイト・ブラックリストも削除されます。")) {
                                        return;
                                    }
                                    DeleteSelectedUser();
                                    this.DrawList();
                                    await this.cacheStorage.SetStorageData([...this.cache]);
                                });
                            }
                            const div2 = document.createElement("div");
                            buttonList.appendChild(div2);
                            {
                                const allCacheClearButton = document.createElement("input");
                                allCacheClearButton.type = "button";
                                allCacheClearButton.style.padding = "3px";
                                allCacheClearButton.style.fontSize = "120%";
                                allCacheClearButton.style.margin = "3px";
                                allCacheClearButton.style.backgroundColor = "red";
                                allCacheClearButton.value = "ALL DELETE";
                                div2.appendChild(allCacheClearButton);
                                allCacheClearButton.addEventListener("click", async () => {
                                    if (!window.confirm("全キャッシュクリアしていいですか?\nホワイト・ブラックリストも削除されます。")) {
                                        return;
                                    }
                                    for (let info of this.illustInfos) {
                                        info.user.status = Status.WHITE;
                                        this.UpdateIllust(info);
                                        this.DrawBlackWhiteButton(info);
                                    }
                                    this.illustInfos = [];
                                    this.UpdateIllustList();
                                    this.cache.clear();
                                    this.DrawList();
                                    await this.cacheStorage.SetStorageData([...this.cache]);
                                });
                            }
                            const div3 = document.createElement("div");
                            buttonList.appendChild(div3);
                            {
                                const reStartButton = document.createElement("input");
                                reStartButton.type = "button";
                                reStartButton.style.padding = "3px";
                                reStartButton.style.fontSize = "120%";
                                reStartButton.style.margin = "3px";
                                reStartButton.style.marginTop = "10px";
                                reStartButton.style.backgroundColor = "green";
                                reStartButton.style.cssText += "color: white !important";
                                reStartButton.value = "RE START";
                                div3.appendChild(reStartButton);
                                reStartButton.addEventListener("click", async () => {
                                    await this.Run();
                                });
                            }
                            const div4 = document.createElement("div");
                            div4.style.marginTop = "10px";
                            div4.style.marginBottom = "10px";
                            buttonList.appendChild(div4);
                            {
                                const importDiv = document.createElement("div");
                                importDiv.style.position = "relative";
                                div4.appendChild(importDiv);
                                {
                                    const importButton = document.createElement("input");
                                    importButton.type = "button";
                                    importButton.style.padding = "3px";
                                    importButton.style.fontSize = "120%";
                                    importButton.style.margin = "3px";
                                    importButton.style.marginTop = "10px";
                                    importButton.value = "← IMPORT";
                                    importDiv.appendChild(importButton);
                                    const importFile = document.createElement("input");
                                    importFile.type = "file";
                                    importFile.style.position = "absolute";
                                    importFile.style.opacity = "0";
                                    importFile.style.width = "80px";
                                    importFile.style.top = "8px";
                                    importFile.style.left = "0";
                                    importFile.accept = "text/plain";
                                    importFile.style.padding = "0";
                                    importDiv.appendChild(importFile);
                                    importFile.addEventListener("change", async (e) => {
                                        if (e.target == null) {
                                            return;
                                        }
                                        const files = e.target.files;
                                        if (files == null) {
                                            return;
                                        }
                                        const file = files[0];
                                        if (file.type != "text/plain") {
                                            alert("テキストファイルを入れてください。");
                                            return;
                                        }
                                        if (!window.confirm("インポートしていいですか?\nインポートする前に、今選択しているユーザーは削除されます。")) {
                                            return;
                                        }
                                        DeleteSelectedUser();
                                        this.DrawList();
                                        const reader = new FileReader();
                                        reader.readAsText(file);
                                        reader.onload = async () => {
                                            if (typeof reader.result != "string") {
                                                return;
                                            }
                                            const importUsers = this.ListToMap(JSON.parse(reader.result));
                                            for (let [imUserId, imUser] of importUsers) {
                                                for (let illust of imUser.illusts) {
                                                    illust.created = new Date(illust.created);
                                                }
                                            }
                                            for (let [imUserId, imUser] of importUsers) {
                                                if (imUser == null) {
                                                    continue;
                                                }
                                                const cachedUser = this.cache.get(imUserId);
                                                if (cachedUser == undefined) {
                                                    this.cache.set(imUserId, imUser);
                                                }
                                                else {
                                                    cachedUser.status = imUser.status;
                                                    for (let illust of cachedUser.illusts) {
                                                        if (cachedUser.illusts.some(c => c.illustId == illust.illustId)) {
                                                            continue;
                                                        }
                                                        if (illust == null) {
                                                            continue;
                                                        }
                                                        cachedUser.illusts.push(illust);
                                                    }
                                                }
                                            }
                                            await this.cacheStorage.SetStorageData([...this.cache]);
                                            this.Run();
                                        };
                                    });
                                }
                                const exportEiv = document.createElement("div");
                                div4.appendChild(exportEiv);
                                {
                                    const reStartButton = document.createElement("input");
                                    reStartButton.type = "button";
                                    reStartButton.style.padding = "3px";
                                    reStartButton.style.fontSize = "120%";
                                    reStartButton.style.margin = "3px";
                                    reStartButton.style.marginTop = "5px";
                                    reStartButton.value = "→ EXPORT";
                                    exportEiv.appendChild(reStartButton);
                                    reStartButton.addEventListener("click", async () => {
                                        const selectedUsers = new Map();
                                        for (let element of this.selectedList) {
                                            if (element == undefined) {
                                                continue;
                                            }
                                            const userId = element.getElementsByClassName("userId")[0].textContent;
                                            if (userId == null) {
                                                continue;
                                            }
                                            const user = this.cache.get(userId);
                                            if (user != undefined) {
                                                selectedUsers.set(userId, user);
                                            }
                                        }
                                        if (selectedUsers.size == 0) {
                                            alert("出力するユーザーを選択してください");
                                            return;
                                        }
                                        const listStatusSelect = this.optionDialog.getElementsByClassName("listStatusSelect")[0];
                                        const status = listStatusSelect.value;
                                        const blob = new Blob([JSON.stringify(selectedUsers)], { type: "text/plain" });
                                        const dlUrl = URL.createObjectURL(blob);
                                        await Util.Download(dlUrl, `niconicoNG_${status}.txt`);
                                    });
                                }
                            }
                        }
                    }
                }
            }
        }
        UpdateIllust(info) {
            const img = info.element.getElementsByTagName("img")[0] ?? info.element.getElementsByClassName("image-layer")[0];
            if (info.user.status == Status.OK || info.user.status == Status.WHITE) {
                if (img != null) {
                    img.style.filter = "brightness(1)";
                }
                if (info.element.parentElement == null) {
                    info.parent.appendChild(info.element);
                }
            }
            if (info.user.status == Status.NG || (info.user.status == Status.AUTO && !this.option.judge.isAutoNGHidden)) {
                if (img != null) {
                    img.style.filter = "brightness(0.3)";
                }
                info.parent.appendChild(info.element);
            }
            if (info.user.status == Status.BLACK || (info.user.status == Status.AUTO && this.option.judge.isAutoNGHidden)) {
                info.element.remove();
            }
        }
        UpdateIllustList() {
            for (let illustListElement of this.illustListElements) {
                if (this.currentPage == VirtualPageType.ILLUST) {
                    for (let moreLink of Array.from(illustListElement.getElementsByClassName("list_more_link"))) {
                        if (moreLink.parentElement == null) {
                            continue;
                        }
                        moreLink.parentElement.appendChild(moreLink);
                    }
                }
                if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
                    const brs = illustListElement.getElementsByTagName("br");
                    while (brs.length) {
                        brs[0].remove();
                    }
                    for (var i = 0; i < illustListElement.childElementCount; i++) {
                        if ((i % 6) != 4) {
                            continue;
                        }
                        illustListElement.children[i].insertAdjacentHTML("afterend", "<br clear='all'>");
                    }
                    illustListElement.insertAdjacentHTML("beforeend", "<br clear='all'>");
                }
                if (this.currentPage == VirtualPageType.PERSONALIZE) {
                    const brs = Array.from(illustListElement.getElementsByTagName("br"));
                    for (let br of brs) {
                        br.remove();
                    }
                    const items = Array.from(illustListElement.getElementsByClassName(virtualPageInfos[this.currentPage].illustItemName));
                    for (let i = 0; i < items.length; i++) {
                        if ((i + 1) % 4 == 0 || i == items.length - 1) {
                            const br = document.createElement("br");
                            br.clear = "all";
                            items[i].insertAdjacentElement("afterend", br);
                        }
                    }
                }
                if (this.currentPage == VirtualPageType.ADS) {
                    const ds = illustListElement.getElementsByClassName("ADS_Dammy");
                    while (ds.length)
                        ds[0].remove();
                    if (1 < illustListElement.childElementCount) {
                        for (var i = illustListElement.childElementCount; i < 3; i++) {
                            const dammy = document.createElement("div");
                            dammy.classList.add("ADS_Dammy");
                            dammy.style.width = illustListElement.children[0].clientWidth + "px";
                            illustListElement.appendChild(dammy);
                        }
                    }
                }
            }
        }
        CreateUserLink(illustInfo) {
            if (this.currentPage == VirtualPageType.PERSONALIZE || !this.option.createUserLink || illustInfo.element.getElementsByClassName("userLink").length > 0) {
                return;
            }
            const userElement = illustInfo.element.getElementsByClassName("user")[0];
            if (userElement == null) {
                return;
            }
            const userA = document.createElement("a");
            userA.className = "userLink";
            userA.href = "https://seiga.nicovideo.jp/user/illust/" + illustInfo.userId;
            userA.style.left = "0";
            userA.style.zIndex = "10";
            userA.style.right = "10px";
            userA.style.position = "absolute";
            userA.style.border = "0";
            userA.style.opacity = "0";
            userA.addEventListener("mouseover", () => {
                userA.style.border = "solid 1px silver";
                userA.style.opacity = "0.3";
            });
            userA.addEventListener("mouseleave", () => {
                userA.style.border = "0";
                userA.style.opacity = "0";
            });
            if (this.currentPage == VirtualPageType.TAG_SEARCH) {
                userA.style.height = "10px";
                userA.style.top = "34px";
                userA.style.backgroundColor = "silver";
            }
            if (this.currentPage == VirtualPageType.ILLUST) {
                userA.style.height = "20px";
                userA.style.top = "20px";
                userA.style.backgroundColor = "black";
            }
            userElement.style.position = "relative";
            userElement.style.zIndex = "20";
            userElement.style.pointerEvents = "none";
            userElement.insertAdjacentElement("beforebegin", userA);
        }
        async DrawBlackWhiteButton(illustInfo) {
            if (!this.option.createBlackWhiteButton) {
                return;
            }
            if (illustInfo.user.status == Status.BLACK || illustInfo.user.status == Status.WHITE) {
                if (illustInfo.user.status == Status.WHITE) {
                    const list = Array.from(illustInfo.element.getElementsByClassName("toListButton"));
                    for (let l of list) {
                        l.remove();
                    }
                }
                return;
            }
            if (illustInfo.element.getElementsByClassName("toListButton").length > 0) {
                return;
            }
            const whiteButton = document.createElement("input");
            const blackButton = document.createElement("input");
            whiteButton.style.zIndex = "20";
            whiteButton.style.visibility = "hidden";
            whiteButton.style.cursor = "default";
            if (this.currentPage == VirtualPageType.TAG_SEARCH) {
                whiteButton.style.left = "117px";
                whiteButton.style.top = "-30px";
                whiteButton.style.width = "40px";
                whiteButton.style.height = "25px";
                whiteButton.style.position = "relative";
            }
            if (this.currentPage == VirtualPageType.ILLUST) {
                whiteButton.style.left = "54px";
                whiteButton.style.top = "-19px";
                whiteButton.style.width = "30px";
                whiteButton.style.height = "19px";
                whiteButton.style.position = "relative";
            }
            if (this.currentPage == VirtualPageType.PERSONALIZE) {
                whiteButton.style.top = "240px";
                whiteButton.style.width = "40px";
                whiteButton.style.height = "25px";
                whiteButton.style.position = "absolute";
                illustInfo.element.style.position = "relative";
                illustInfo.element.style.height = "258px";
            }
            if (this.currentPage == VirtualPageType.ADS) {
                whiteButton.style.top = "85px";
                whiteButton.style.width = "30px";
                whiteButton.style.height = "19px";
                whiteButton.style.position = "absolute";
                whiteButton.style.border = "2px solid #736b5e";
                illustInfo.element.style.position = "relative";
            }
            if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
                whiteButton.style.top = "144px";
                whiteButton.style.width = "30px";
                whiteButton.style.height = "19px";
                whiteButton.style.position = "absolute";
                illustInfo.element.style.position = "relative";
            }
            //上記のスタイルを両方に適用
            blackButton.style.cssText = whiteButton.style.cssText;
            whiteButton.type = "button";
            blackButton.type = "button";
            whiteButton.className = "toListButton";
            blackButton.className = "toListButton";
            whiteButton.name = "white";
            blackButton.name = "black";
            whiteButton.style.cssText += `background-color : white !important;`;
            blackButton.style.cssText += `background-color : black !important;`;
            if (this.currentPage == VirtualPageType.PERSONALIZE) {
                whiteButton.style.left = "77px";
                blackButton.style.left = "117px";
            }
            if (this.currentPage == VirtualPageType.ADS) {
                whiteButton.style.left = "135px";
                blackButton.style.left = "165px";
            }
            if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
                whiteButton.style.right = "28px";
                blackButton.style.right = "-2px";
            }
            whiteButton.addEventListener("contextmenu", async (e) => {
                e.preventDefault();
                illustInfo.user.status = Status.OK;
                for (let info of this.illustInfos) {
                    this.UpdateIllust(info);
                }
                this.UpdateIllustList();
                this.DrawList();
                await this.cacheStorage.SetStorageData([...this.cache]);
            });
            whiteButton.addEventListener("click", async () => {
                illustInfo.user.status = Status.WHITE;
                for (let info of this.illustInfos) {
                    this.UpdateIllust(info);
                    const buttons = info.element.getElementsByClassName("toListButton");
                    while (buttons.length != 0) {
                        buttons[0].remove();
                    }
                }
                this.UpdateIllustList();
                this.DrawList();
                await this.cacheStorage.SetStorageData([...this.cache]);
            });
            blackButton.addEventListener("contextmenu", async (e) => {
                e.preventDefault();
                illustInfo.user.status = Status.NG;
                for (let info of this.illustInfos) {
                    this.UpdateIllust(info);
                }
                this.UpdateIllustList();
                this.DrawList();
                await this.cacheStorage.SetStorageData([...this.cache]);
            });
            blackButton.addEventListener("click", async () => {
                illustInfo.user.status = Status.BLACK;
                for (let info of this.illustInfos) {
                    this.UpdateIllust(info);
                }
                this.UpdateIllustList();
                this.DrawList();
                await this.cacheStorage.SetStorageData([...this.cache]);
            });
            if (this.currentPage == VirtualPageType.TAG_SEARCH) {
                const infoElement = illustInfo.element.getElementsByClassName("illust_count")[0];
                blackButton.addEventListener("mouseover", () => {
                    infoElement.style.opacity = "1";
                });
                blackButton.addEventListener("mouseleave", () => {
                    infoElement.style.opacity = "";
                });
                whiteButton.addEventListener("mouseover", () => {
                    infoElement.style.opacity = "1";
                });
                whiteButton.addEventListener("mouseleave", () => {
                    infoElement.style.opacity = "";
                });
            }
            if (this.currentPage == VirtualPageType.ILLUST) {
                const infoElement = illustInfo.element.getElementsByClassName("illust_info")[0];
                blackButton.addEventListener("mouseover", () => {
                    infoElement.style.bottom = "0px";
                });
                blackButton.addEventListener("mouseleave", () => {
                    infoElement.style.bottom = "";
                });
                whiteButton.addEventListener("mouseover", () => {
                    infoElement.style.bottom = "0px";
                });
                whiteButton.addEventListener("mouseleave", () => {
                    infoElement.style.bottom = "";
                });
            }
            illustInfo.element.addEventListener("mouseover", () => {
                blackButton.style.visibility = "visible";
                whiteButton.style.visibility = "visible";
            });
            illustInfo.element.addEventListener("touchstart", () => {
                blackButton.style.visibility = "visible";
                whiteButton.style.visibility = "visible";
            });
            illustInfo.element.addEventListener("mouseleave", () => {
                blackButton.style.visibility = "hidden";
                whiteButton.style.visibility = "hidden";
            });
            illustInfo.element.addEventListener("touchend", () => {
                blackButton.style.visibility = "hidden";
                whiteButton.style.visibility = "hidden";
            });
            if (this.currentPage == VirtualPageType.ADS) {
                illustInfo.element.insertAdjacentElement("afterbegin", blackButton);
                illustInfo.element.insertAdjacentElement("afterbegin", whiteButton);
                return;
            }
            illustInfo.element.appendChild(whiteButton);
            illustInfo.element.appendChild(blackButton);
        }
        async AddInfos(illustListElement) {
            const illustItemName = virtualPageInfos[this.currentPage].illustItemName;
            let illustElements;
            if (this.currentPage == VirtualPageType.KEYWORD_SEARCH || this.currentPage == VirtualPageType.PERSONALIZE) {
                illustElements = Array.from(illustListElement.getElementsByClassName(illustItemName));
            }
            else {
                illustElements = Array.from(illustListElement.getElementsByTagName("li"))
                    .filter(e => illustItemName == "" || e.classList.contains(illustItemName) || e.firstElementChild?.classList.contains(illustItemName));
            }
            const illustIds = this.GetIllustIds(illustListElement);
            const names = Array.from(illustListElement.getElementsByClassName("user"));
            //console.log(illustIds);
            //console.log(illustElements);
            //console.log(names);
            //キャッシュからの情報と合わせて追加(もうこれ分かんねぇこともある)
            for (let i = 0; i < illustIds.length; i++) {
                if (this.illustInfos.some(info => info.illust.illustId == illustIds[i] && info.element == illustElements[i])) {
                    continue;
                }
                if (illustElements[i] == null) {
                    continue;
                }
                const info = this.GetInfo(illustIds[i]);
                if (info != undefined && names[i]?.textContent != null) {
                    info.user.name = names[i].textContent ?? info.user.name;
                }
                this.illustInfos.push({
                    userId: info == undefined ? "" : info.userId,
                    illust: info == undefined ? { created: "", illustId: illustIds[i] } : info.illust,
                    user: info == undefined ? { illusts: [], status: Status.NONE, name: names[i]?.textContent ?? "" } : info.user,
                    element: illustElements[i],
                    parent: illustListElement
                });
            }
        }
        SetCurrentPage(url) {
            for (let i = 0; i < pageInfos.length; i++) {
                if (pageInfos[i].regex.test(url)) {
                    this.currentPage = pageInfos[i].type;
                    return this.option.usePages[i];
                }
            }
            this.currentPage = VirtualPageType.None;
            return false;
        }
        GetPage() {
            return this.currentPage;
        }
        //メインクラス、メイン関数の肥大化もう始まってる!
        async Run(illustListElements) {
            const illustListName = virtualPageInfos[this.currentPage].illustListName;
            let firstIllustListElement;
            if (illustListName == "") {
                firstIllustListElement = await Observer.DefinitelyGetElementByTagName("ul", undefined, { isDeepSearch: true });
            }
            else {
                firstIllustListElement = await Observer.DefinitelyGetElementByClassName(illustListName, undefined, { isDeepSearch: true });
            }
            if (this.currentPage == VirtualPageType.ADS) {
                await Observer.DefinitelyGetElementByTagName("li", undefined, { isDeepSearch: true });
                if (firstIllustListElement) {
                    firstIllustListElement.style.visibility = "hidden";
                    firstIllustListElement.style.overflow = "hidden";
                }
            }
            await Util.WithTimeOut(Observer.DefinitelyGetElementById("footer"), 1000);
            if (illustListElements == null) {
                if (illustListName == "") {
                    illustListElements = Array.from(document.getElementsByTagName("ul"));
                }
                else {
                    illustListElements = Array.from(document.getElementsByClassName(illustListName));
                }
            }
            for (let illustListElement of illustListElements) {
                illustListElement.style.visibility = "hidden";
                await this.AddInfos(illustListElement);
                this.illustListElements.add(illustListElement);
            }
            //console.log("infos", this.illustInfos, this.illustInfos.length);
            //誰のイラストかこれもう分かんねぇやつ達
            const unkownInfos = this.illustInfos.filter(info => info.userId == "");
            //console.log("unkownInfos", unkownInfos);
            //この戻り値なんかダサい・・・ダサくない?
            const result = await Fetcher.FetchIllustDatas(unkownInfos.map(info => info.illust.illustId));
            //誰のかこれもう分かんねぇやつらとキャッシュまで!?の情報更新
            for (let i = 0; i < unkownInfos.length; i++) {
                if (result.illusts[i] == null) {
                    //alert("null!!!");
                    //console.log(result);
                    //debugger;
                    continue;
                }
                unkownInfos[i].illust = result.illusts[i];
                unkownInfos[i].userId = result.userIds[i];
                let cachedUser = this.cache.get(result.userIds[i]);
                if (cachedUser == undefined) {
                    if (unkownInfos[i].user == null) {
                        //alert("null!!!");
                        //console.log(unkownInfos);
                        //debugger;
                        continue;
                    }
                    unkownInfos[i].user.status = Status.OK;
                    this.cache.set(unkownInfos[i].userId, unkownInfos[i].user);
                }
                else {
                    ////キャッシュ使ったら後ろにしとく
                    this.cache.delete(result.userIds[i]);
                    this.cache.set(result.userIds[i], cachedUser);
                    unkownInfos[i].user = cachedUser;
                }
                if (!unkownInfos[i].user.illusts.some(illust => illust.illustId == result.illusts[i].illustId)) {
                    unkownInfos[i].user.illusts.push(result.illusts[i]);
                }
            }
            // IDが見つからないものを削除する
            this.illustInfos = this.illustInfos.filter(info => info.userId != "");
            //増えすぎたキャッシュ削除
            if (this.cache.size > 0) {
                let okCount = 0;
                for (let [userId, user] of this.cache) {
                    if (user.status == Status.OK) {
                        okCount++;
                    }
                }
                for (let [userId, user] of this.cache) {
                    if (okCount < this.option.okCacheMax) {
                        break;
                    }
                    //OK以外消さない
                    //今使ってたら消さない
                    if (user.status == Status.OK && !this.illustInfos.some(info => info.userId == userId)) {
                        this.cache.delete(userId);
                        okCount--;
                    }
                }
            }
            //console.log(result);
            //ブラック,ホワイトリストにないイラストエレメントにボタン追加
            for (let illustInfo of this.illustInfos) {
                this.DrawBlackWhiteButton(illustInfo);
                this.CreateUserLink(illustInfo);
            }
            if (this.option.judge.isJudge) {
                //投稿者の荒らし判定更新 ↓これは重複排除
                for (let c of [...new Set(this.illustInfos.map(u => u.user))]) {
                    this.CheckAutoNG(c);
                }
            }
            await this.cacheStorage.SetStorageData([...this.cache]);
            for (let info of this.illustInfos) {
                this.UpdateIllust(info);
            }
            this.UpdateIllustList();
            for (let illustListElement of illustListElements) {
                illustListElement.style.visibility = "visible";
            }
            await this.SetOptionButton();
            this.DrawList();
        }
        async StartObserve() {
            const illustListName = virtualPageInfos[this.currentPage].illustListName;
            if (illustListName == "") {
                return;
            }
            const illustListParent = (await Observer.DefinitelyGetElementByClassName(illustListName)).parentNode;
            const mutationObserver = new MutationObserver(async (mrs) => {
                for (let mr of mrs) {
                    for (let i = 0; i < mr.addedNodes.length; i++) {
                        const element = mr.addedNodes[i];
                        if (element.classList == null) {
                            continue;
                        }
                        if (element.classList.contains(illustListName)) {
                            await this.Run([element]);
                        }
                    }
                }
            });
            mutationObserver.observe(illustListParent ?? document, {
                childList: true,
                subtree: true
            });
        }
    }
    ;
    const main = new Main();
    await main.GetStorageData();
    const isUseNG = main.SetCurrentPage(location.href);
    await Util.WaitDocumentElement();
    main.SetOptionButton();
    if (!isUseNG) {
        return;
    }
    await main.Run();
    await main.StartObserve();
})();