Instant Wakamemo

わかめて上で動作するわかめてメモのようなものです。

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Instant Wakamemo
// @namespace    http://mobajinro.s178.xrea.com/wakamemo/
// @version      1.5.3
// @description  わかめて上で動作するわかめてメモのようなものです。
// @author       udop_
// @match        http://jinrou.dip.jp/~jinrou/cgi_jinro.cgi
// @match        http://61.215.66.131/~jinrou/cgi_jinro.cgi
// @match        http://www7a.biglobe.ne.jp/~kuri/cgi_jinro.cgi
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @run-at       document-start
// ==/UserScript==

;(function ($) {
    "use strict"

    if (document.body.bgColor == "#fee3aa") {
        //村画面でないときは出さない
        return false
    }

    //**********************************************
    // 変数・クラスの宣言
    //**********************************************

    Array.prototype.fillundef = function (def, lastindex) {
        lastindex = lastindex + 1 || this.length
        for (let i = 0; i < lastindex; i++) {
            if (this[i] === null || this[i] === undefined) {
                this[i] = JSON.parse(JSON.stringify(def))
            }
        }
    }

    function range(a, b) {
        var ar = []
        a = a || playerInfo.length
        b = b || undefined
        if (!b) {
            for (let i = 0; i < a; i++) ar.push(i)
        } else {
            for (let i = a; i < b; i++) ar.push(i)
        }
        return ar
    }

    var playerInfo,
        indexOfName,
        discussLog,
        deathLog,
        inputMode,
        votetimes,
        newestDay,
        today,
        setting,
        colorSetting
    var playertable = $("table").eq(1)
    var textareatable = $("table[cellspacing=0]").eq(-1)
    var discusstable = $("table[cellpadding=0]").not("table.CLSTABLE2").last()
    var infotable = $("table[cellspacing=0]").eq(0)
    var commandtable = $("table[cellspacing=0]").eq(-2)
    var textarea = $("textarea").eq(0)
    var body = $("body")
    var isdaytime = document.body.bgColor != "#000000"

    var settingDefault = {
        rewrite_css: {
            value: "yes",
            option: { yes: "はい", no: "いいえ" },
            name: "見た目を変更する",
        },
        auto_import_log: {
            value: "onetime",
            option: { none: "しない", onetime: "投票時", alltime: "常時" },
            name: "自動ログ取得のタイミング",
        },
        alert_vote: {
            value: "onetime",
            name: "未投票時に警告する",
            option: { yes: "はい", no: "いいえ" },
        },
        send_support: {
            value: "ctrl",
            name: "支援キー+ENTERで送信",
            option: { none: "しない", ctrl: "CTRL", shift: "SHIFT" },
        },
        autoreload_interval: {
            value: "30",
            name: "自動更新(観戦時のみ)",
            option: { 10: "10秒", 20: "20秒", 30: "30秒", 60: "60分" },
        },
        theme_color: {
            value: "navy",
            name: "テーマカラー",
            option: {
                crimson: "紅",
                darkorange: "オレンジ",
                darkgreen: "緑",
                navy: "蒼",
                purple: "紫",
                sienna: "茶",
            },
        },
        layout: {
            value: "no",
            name: "表を横に並べる(横幅と相談)",
            option: { yes: "はい", no: "いいえ" },
        },
        grayregion: {
            value: "no",
            name: "人外と推理した占い師の結果は<br>完グレ判定に使用しない",
            option: { yes: "はい", no: "いいえ" },
        },
        coloringName: {
            value: "no",
            name: "役職で色分けする(試験運用)",
            option: { yes: "はい", no: "いいえ" },
        },
    }

    var nameColor = {
        black: "なし",
        red: "赤",
        pink: "ピンク",
        blue: "青",
        green: "緑",
        purple: "紫",
        brown: "茶",
        gaming: "虹色",
    }
    var colorSettingDefault = {
        gray: {
            value: "black",
            name: "グレー",
            option: nameColor,
        },
        fortune: {
            value: "black",
            name: "占い",
            option: nameColor,
        },
        necro: {
            value: "black",
            name: "霊能",
            option: nameColor,
        },
        share: {
            value: "black",
            name: "共有",
            option: nameColor,
        },
        guard: {
            value: "black",
            name: "狩人",
            option: nameColor,
        },
        cat: {
            value: "black",
            name: "猫又",
            option: nameColor,
        },
        beast: {
            value: "black",
            name: "人外",
            option: nameColor,
        },
    }
    var data = {
        playerInfo: [],
        indexOfName: {},
        isAutoReload: false,
        discussLog: [],
        deathLog: { exec: [], bite: [] },
        isfilter: false,
        inputMode: "simple",
        villageno: 0,
        setting: settingDefault,
        votetimes: [],
        importlogday: 0,
    }
    var color = {
        crimson: { main: "crimson", sub: "#EC365A" },
        darkorange: { main: "darkorange", sub: "#FF9F32" },
        darkgreen: { main: "darkgreen", sub: "#009300" },
        navy: { main: "navy", sub: "#0000b2" },
        purple: { main: "purple", sub: "#B200B2" },
        sienna: { main: "sienna", sub: "#C66538" },
    }

    class Tr {
        constructor(id, cl) {
            this.id = ""
            this.cl = ""
            if (id) this.id = id
            if (cl) this.cl = cl
            this.tds = []
        }
        add(val, cl) {
            var c = cl ? `class='${cl}'` : ""
            var td = `<td ${c}>${val}</td>`
            this.tds.push(td)
        }
        addhtml(html) {
            this.tds.push(html)
        }
        text() {
            var tr = "<tr"
            if (this.id) tr = tr + " id='" + this.id + "'"
            if (this.cl) tr = tr + " class='" + this.cl + "'"
            tr += ">"
            tr += this.tds.join("")
            tr += "</tr>"
            return tr
        }
        appendTo(jQueryObject) {
            jQueryObject.append(this.text())
        }
    }

    var COLORLIST = {
        0: "選択▼",
        1: "明灰",
        2: "暗灰",
        3: "黄色",
        4: "オレンジ",
        5: "赤",
        6: "水色",
        7: "青",
        8: "黄緑",
        9: "紫",
        10: "桃色",
        11: "肌色",
        12: "茶色",
        13: "緑",
        14: "若草色",
        15: "真紅",
        16: "薄茶色",
        17: "藍色",
        18: "蒼",
        19: "ピンク",
        20: "銀色",
        21: "薄紫",
        22: "象牙色",
        23: "黒",
    }

    //**********************************************
    //  Session関係
    //**********************************************

    function load() {
        if (localStorage.getItem("memodata")) {
            data = JSON.parse(localStorage.getItem("memodata"))
        }

        playerInfo = data.playerInfo
        indexOfName = data.indexOfName
        discussLog = data.discussLog
        deathLog = data.deathLog
        votetimes = data.votetimes

        var old_setting = data.setting
        data.setting = {}
        for (let k in settingDefault) {
            data.setting[k] = old_setting && k in old_setting ? old_setting[k] : settingDefault[k]
        }
        setting = data.setting

        old_setting = data.colorSetting || colorSettingDefault
        data.colorSetting = {}
        for (let k in colorSettingDefault) {
            if (old_setting && k in old_setting) {
                data.colorSetting[k] = old_setting[k]
            } else {
                data.colorSetting[k] = colorSettingDefault[k]
            }
        }
        colorSetting = data.colorSetting
        save()
    }

    function save() {
        localStorage.setItem("memodata", JSON.stringify(data))
    }

    //**********************************************
    //   import関係
    //**********************************************

    function importLog() {
        updateplayerInfo()
        importDiscussLog()
    }

    function updateplayerInfo() {
        //プレイヤー情報を取得
        //death…「能力がCOできなくなった日」を指す 3日目吊りなら4日目。3日目噛まれなら3日目

        if (discussLog.length >= 2) {
            playertable.find("td:odd").each(function (i, v) {
                if (!$(v).html()) return false
                playerInfo[i].vital = $(v).html().includes("生存中") ? "alive" : "death"
            })
        } else {
            data.playerInfo = []
            playerInfo = data.playerInfo
            data.indexOfName = {}
            indexOfName = data.indexOfName
            playertable.find("td:odd").each(function (i, v) {
                var html = $(v).html()
                if (!html) return false

                var name = html.split("<br>")[0]
                var vital = html.includes("生存中") ? "alive" : "death"
                playerInfo.push({
                    no: i,
                    name: name,
                    vital: vital,
                    job: "gray",
                    reasoning: "gray",
                    jobresult: [],
                    vote: [],
                    death: 99,
                })
                data.indexOfName[name] = i
            })
        }
        save()
    }

    function importDiscussLog() {
        //ログ取り込み
        if (isdaytime) {
            discussLog[today] = []
            discusstable.find("tr").each(function (i, v) {
                if ($(v).children().length != 2) return true
                discussLog[today].push({
                    name: $(v).children().eq(0).find("b").eq(0).html(),
                    namehtml: $(v).children().eq(0).html(),
                    content: $(v).children().eq(1).html(),
                })
            })
        }

        //投票結果・死亡ログ
        var votelog = []
        discusstable.find("td[colspan='2']").each(function (i, v) {
            if (
                /(無残な姿で発見|死体で発見|村民協議の結果処刑|突然死|猫又の呪い)/.test($(v).text())
            ) {
                importDeath(v)
            }
            if (/\d{1,2}日目 投票結果。/.test($(v).text())) {
                votelog.unshift(v)
            }
        })
        if (votelog.length) importVote(votelog)

        filterlog(99, newestDay)
        save()
    }

    function importDeath(log) {
        //死体を記録
        var day = today
        var deathday = today
        var no = indexOfName[$(log).find("b").eq(0).text()]
        var text = $(log).text()
        var reason = ""
        if (/無残な姿で発見/.test(text)) {
            reason = "bite"
        } else if (/死体で発見/.test(text)) {
            reason = "note"
        } else if (/村民協議の結果|猫又の呪い/.test(text)) {
            reason = "exec"
            isdaytime ? day-- : deathday++
        } else if (/突然死/.test(text)) {
            reason = "sudden"
            deathday++
        }
        playerInfo[no].death = deathday

        if (!deathLog[reason]) deathLog[reason] = []
        deathLog[reason].fillundef([], day)

        if (!deathLog[reason][day].includes(no)) {
            deathLog[reason][day].push(no)
        }
    }

    function importVote(logs) {
        //投票を取り込む
        var day = $(logs[0])
            .text()
            .match(/(\d{1,2})日目 投票結果。/)
        day = day[1] - 1

        for (var player of playerInfo) {
            player.vote[day] = []
            player.vote.fillundef("-", logs.length)
        }

        for (let times = 0; times < logs.length; times++) {
            $(logs[times])
                .find("tr")
                .each(function (i, vote) {
                    var voter = indexOfName[$(vote).find("b").eq(0).text()]
                    var target = $(vote).find("b").eq(1).text()
                    playerInfo[voter].vote[day][times] = target
                })
        }
        votetimes[day] = logs.length
        votetimes.fillundef(0)
    }

    //**********************************************
    //   refresh関係
    //**********************************************

    function refresh() {
        //再表示まとめて
        refreshDiscussLog()
        refreshPlayerInfoTable()
        refreshVoteTable()
        refreshSummary()
    }

    function refreshPlayerInfoTable() {
        //プレイヤー情報更新 くっそ長い

        //初期化
        playerInfoTable.empty()

        if (!playerInfo.length) return false

        var playersList = { 99: "" }
        for (let i of range()) {
            playersList[i] = playerInfo[i].name
        }
        var joblist = {
            gray: "",
            fortune: "占い",
            necro: "霊能",
            share: "共有",
            guard: "狩人",
            cat: "猫又",
            beast: "人外",
        }
        var reasoninglist = {
            gray: "",
            real: "真",
            fake: "偽",
            villager: "村人",
            madman: "狂人",
            wolf: "人狼",
            fox: "妖狐",
        }
        var resultlist = { notinput: "", white: "○", black: "●" }

        //-------------------------名前列
        var namerow = new Tr("", "namerow")
        namerow.add("<a id='filterlink_99_99'>全ログ</a>")
        for (let player of playerInfo) {
            namerow.add(
                `<a id='filterlink_${player.no}_99'>${player.name}</a>`,
                `player_${player.no}`
            )
        }
        namerow.appendTo(playerInfoTable)

        //-------------------------発言数列
        for (var day = 1; day <= newestDay; day++) {
            var row = new Tr("", "talknumrow")
            row.add(`<a id=filterlink_99_${day}>${day + 1}日目</a>`)
            for (let player of playerInfo) {
                var talknum = $("#log_day" + day).find("tr.talk_player" + player.no).length
                if (talknum === 0) talknum = ""
                row.add(
                    `<a id=filterlink_${player.no}_${day}>${talknum}</a>`,
                    "player_" + player.no
                )
            }
            row.appendTo(playerInfoTable)
        }

        //-------------------------CO列
        var jobrow = new Tr("", "jobrow")
        jobrow.add("CO")
        for (let player of playerInfo) {
            let select = createSelectBox(joblist, player.job, {
                id: `player_${player.no}_job`,
                class: "jobselect",
            })
            jobrow.add(select, "player_" + player.no)
        }
        jobrow.appendTo(playerInfoTable)

        //-------------------------推理列
        jobrow = new Tr("", "jobrow")
        jobrow.add("推理")
        for (let player of playerInfo) {
            let select = createSelectBox(reasoninglist, player.reasoning, {
                id: `player_${player.no}_reasoning`,
                class: "reasoningselect",
            })
            jobrow.add(select, "player_" + player.no)
        }
        playerInfoTable.append(jobrow.text())

        //-------------------------役職結果列
        for (day = 1; day <= newestDay; day++) {
            var resultrow = new Tr("result_" + day, "resultrow")
            resultrow.add(`占霊結果 ${day + 1}日目`)

            for (let player of playerInfo) {
                if (
                    (player.job == "fortune" && day < player.death) ||
                    (player.job == "necro" && day < player.death && day > 1)
                ) {
                    //占い師、霊能者で、結果があるなら
                    let select1 = createSelectBox(playersList, 99, {
                        id: `target_${player.no}_${day}`,
                        class: "jobtarget",
                    })
                    let select2 = createSelectBox(resultlist, "notinput", {
                        id: `judge_${player.no}_${day}`,
                        class: "jobjudge",
                    })
                    resultrow.add(select1 + select2, "player_" + player.no)
                } else {
                    resultrow.add("", "player_" + player.no)
                }
            }
            playerInfoTable.append(resultrow.text())
        }
        refreshJobResult()
        switchAliveFilter()
        switchInputMode()
        coloring()

        //絞込機能つける
        $("#playerInfoTable a").on("click", function (e) {
            var id = $(this).attr("id").split("_")
            filterlog(id[1], id[2])
        })

        //変更は逐一反映
        $("select.jobselect").on("change", function () {
            var no = $(this).attr("id").split("_")[1] - 0
            playerInfo[no].job = $(this).val()

            refreshPlayerInfoTable()
            refreshJobInitial()
            refreshSummary()
            if (setting.coloringName.value == "yes") setNameColor()
            save()
        })

        $("select.reasoningselect").on("change", function () {
            var no = $(this).attr("id").split("_")[1] - 0
            playerInfo[no].reasoning = $(this).val()

            refreshJobInitial()
            coloring()
            save()
        })

        $("select.jobtarget").on("change", function (e) {
            var id, no, day
            ;[id, no, day] = $(this).attr("id").split("_")

            playerInfo[no].jobresult.fillundef({ target: 99, judge: "notinput" }, +day)
            playerInfo[no].jobresult[day].target = $(this).val() - 0

            refreshSummary()
            coloring()
            save()
        })

        $("select.jobjudge").on("change", function (e) {
            var id, no, day
            ;[id, no, day] = $(this).attr("id").split("_")

            playerInfo[no].jobresult.fillundef({ target: 99, judge: "notinput" }, +day)
            playerInfo[no].jobresult[day].judge = $(this).val()
            refreshSummary()
            coloring()
            save()
        })
    }

    function refreshJobResult() {
        var fortunes = playerInfo.filter((p) => {
            return p.job == "fortune"
        })
        for (let fortune of fortunes) {
            for (let day = 1; day < Math.min(fortune.death, newestDay + 1); day++) {
                if (fortune.jobresult[day]) {
                    //既に結果が入力されているとき
                    $("#target_" + fortune.no + "_" + day).val(fortune.jobresult[day].target)
                    $("#judge_" + fortune.no + "_" + day).val(fortune.jobresult[day].judge)
                }
            }
        }

        var necros = playerInfo.filter((p) => {
            return p.job == "necro"
        })
        for (let necro of necros) {
            for (let day = 2; day < Math.min(necro.death, newestDay + 1); day++) {
                if (necro.jobresult[day]) {
                    //既に結果が入力されているとき
                    $("#target_" + necro.no + "_" + day).val(necro.jobresult[day].target)
                    $("#judge_" + necro.no + "_" + day).val(necro.jobresult[day].judge)
                } else if (deathLog.exec[day - 1]) {
                    $("#target_" + necro.no + "_" + day).val(deathLog.exec[day - 1])
                }
            }
        }
    }

    function refreshDiscussLog() {
        discussLogTable.empty()
        if (!discussLog.length) return false

        discussLog.forEach(function (logs, day) {
            if (!logs) return

            var tbody = $("<tbody></tbody>", { id: "log_day" + day })
            var trs = `<tr class="systemlog"><td colspan="2">${day + 1}日目</td></tr>`

            for (var log of logs) {
                var cl = log.name in indexOfName ? "talk_player" + indexOfName[log.name] : ""
                trs += `<tr class="${cl}"><td>${log.namehtml}<span class='jobinitial'></span></td><td>${log.content}</td></tr>`
            }
            tbody.append(trs).prependTo(discussLogTable)
        })
        refreshJobInitial()
    }

    function refreshJobInitial() {
        var jobinitial = {
            fortune: "占",
            necro: "霊",
            share: "共",
            cat: "猫",
            guard: "狩",
            wolf: "狼",
            madman: "狂",
            fox: "狐",
            villager: "村",
            real: "真",
            fake: "偽",
            beast: "外",
            gray: "",
        }
        for (var player of playerInfo) {
            var job = jobinitial[player.reasoning] + jobinitial[player.job]
            $("tr.talk_player" + player.no + " span").html(job)
        }
    }

    function refreshVoteTable() {
        //投票テーブルリライト
        voteTable.empty()

        var tr = new Tr()
        tr.add("プレイヤー")
        for (var day = 1; day <= newestDay; day++) {
            let colspan = votetimes[day] || 1
            tr.addhtml(`<td colspan="${colspan}">${day + 1}日目</td>`)
        }
        voteTable.append(tr.text())

        for (var player of playerInfo) {
            tr = new Tr()
            tr.add(player.name)
            for (day = 1; day <= newestDay; day++) {
                let times = votetimes[day] || 1
                for (let i = 0; i < times; i++) {
                    var text = player.vote[day] && player.vote[day][i] ? player.vote[day][i] : "-"
                    tr.add(text)
                }
            }
            voteTable.append(tr.text())
        }
    }

    function refreshSummary() {
        var color = { notinput: "?", white: "○", black: "●" }
        var reasons = ["bite", "note", "exec", "sudden"]
        var reasonflavor = { bite: "無残", note: "デスノ", exec: "処刑", sudden: "突然死" }

        summaryTable.empty()

        var tr = new Tr()
        tr.addhtml("<td colspan='2'></td>")
        for (var day = 1; day <= newestDay; day++) {
            tr.add("" + (day + 1) + "日目")
        }
        tr.appendTo(summaryTable)

        var fn = playerInfo.filter((player) => {
            return player.job == "fortune"
        })

        for (let i = 0; i < fn.length; i++) {
            let player = fn[i]
            tr = new Tr()
            if (i == 0) tr.addhtml(`<td rowspan=${fn.length}>占い師</td>`)
            tr.add(player.name)

            for (day = 1; day <= newestDay; day++) {
                let name = "",
                    judge = ""
                if (player.jobresult[day] && player.jobresult[day].target != 99) {
                    name = playerInfo[player.jobresult[day].target].name
                    judge = color[player.jobresult[day].judge]
                }
                tr.add(name + judge)
            }
            tr.appendTo(summaryTable)
        }

        fn = playerInfo.filter((player) => {
            return player.job == "necro"
        })

        for (let i = 0; i < fn.length; i++) {
            let player = fn[i]
            tr = new Tr()
            if (i == 0) tr.addhtml(`<td rowspan=${fn.length}>霊能者</td>`)
            tr.add(player.name)
            for (let day = 1; day <= newestDay; day++) {
                let name = "",
                    judge = ""
                if (player.jobresult[day] && player.jobresult[day].target != 99) {
                    name = playerInfo[player.jobresult[day].target].name
                    judge = color[player.jobresult[day].judge]
                }
                tr.add(name + judge)
            }
            tr.appendTo(summaryTable)
        }

        for (var reason of reasons) {
            if (!deathLog[reason]) continue

            tr = new Tr()
            if (reason == "bite") tr.addhtml("<td rowspan='4'>死亡ログ</td>")
            tr.add(reasonflavor[reason])

            for (day = 1; day <= newestDay; day++) {
                var text = "-"
                if (deathLog[reason][day]) {
                    text = deathLog[reason][day].map((x) => playerInfo[x].name).join("<br>")
                }
                tr.add(text)
            }
            tr.appendTo(summaryTable)
        }
    }

    //**********************************************
    //   表示
    //**********************************************

    var switchDispArea = function (_this) {
        //メモのログ/投票表示切り替え
        $("div.tab").removeClass("active")
        $(_this).addClass("active")

        $("#memoBody > div").hide()
        var mode = $(_this).data("value")
        $(`#${mode}Area`).show()
    }

    function switchAliveFilter(_this) {
        if (_this) {
            data.isfilter = $(_this).data("value") == "on"
            save()
        }
        $("div.select.filter").removeClass("active")
        data.isfilter
            ? $("#showAliveButton").addClass("active")
            : $("#showAllButton").addClass("active")
        for (var player of playerInfo) {
            if (!data.isfilter || player.vital == "alive" || player.job != "gray") {
                $("td.player_" + player.no).show()
            } else {
                $("td.player_" + player.no).hide()
            }
        }
    }

    function switchInputMode(_this) {
        if (_this) {
            data.inputMode = $(_this).data("value")
            save()
        }
        $("div.select.inputmode").removeClass("active")
        $(`#input${data.inputMode}Button`).addClass("active")
        for (var day = 1; day <= newestDay; day++) {
            if (data.inputMode == "full" || (data.inputMode == "simple" && day == newestDay)) {
                $("#result_" + day).show()
            } else {
                $("#result_" + day).hide()
            }
        }
    }

    function filterlog(player, day) {
        if (player < 99) {
            $("#discussLogTable tr").hide()
            $("tr.systemlog").show()
            $("tr.talk_player" + player).show()
        } else {
            $("#discussLogTable tr").show()
        }

        if (day < 99) {
            $("#discussLogTable tbody").hide()
            $("#log_day" + day).show()
        } else {
            $("#discussLogTable tbody").show()
        }
    }

    function coloring() {
        var notgray = range().filter((i) => playerInfo[i].job != "gray")

        var fortunes = playerInfo.filter((player) => player.job == "fortune")
        if (setting.grayregion.value == "yes") {
            fortunes = fortunes.filter((fortune) => {
                return fortune.reasoning == "gray" || fortune.reasoning == "real"
            })
        }
        fortunes.forEach(function (player) {
            notgray = notgray.concat(player.jobresult.map((p) => p.target))
        })

        var graylist = range().filter((no) => {
            return !notgray.includes(no)
        })

        $("tr.namerow td").removeClass("death").removeClass("gray")
        for (var player of playerInfo) {
            if (player.vital == "death") {
                $("tr.namerow .player_" + player.no).addClass("death")
            } else if (graylist.includes(player.no)) {
                $("tr.namerow .player_" + player.no).addClass("gray")
            }
        }
    }

    function reset() {
        data = {
            playerInfo: [],
            indexOfName: {},
            isAutoReload: false,
            importlogday: 0,
            discussLog: [],
            deathLog: { exec: [], bite: [] },
            isfilter: false,
            inputMode: "simple",
            villageno: getVillageno(),
            setting: data.setting,
            votetimes: [],
        }
        playerInfo = data.playerInfo
        indexOfName = data.indexOfName
        discussLog = data.discussLog
        deathLog = data.deathLog
        setting = data.setting
        votetimes = data.votetimes
        save()
    }

    //**********************************************
    //   乱数表
    //**********************************************

    function rnd(n, digit) {
        //0~n-1の整数乱数 digitを指定するとゼロパディング
        digit = digit || false
        var r = Math.floor(Math.random() * n)
        if (digit) r = padding(r, digit)
        return r
    }

    function padding(num, digit) {
        //ゼロパディング
        return ("0000000000" + num).slice(-digit)
    }

    function makernd() {
        //四桁乱数
        return rnd(10000, 4)
    }

    function makematrix(num) {
        //乱数表
        var l = []
        var mat = ""
        for (var i = 0; i < num; i++) {
            l[i] = padding(i + 1, 2)
        }
        for (i = 0; i < num; i++) {
            var r = rnd(num - i)
            mat += l[r]
            mat += i % 5 == 4 ? "\n" : " / "
            for (var j = r; j < num - 1; j++) {
                l[j] = l[j + 1]
            }
        }
        return mat
    }

    function makerndjob(num) {
        //役職対応
        var jobs = ["村 人", "占い師", "霊能者", "狩 人", "共有者", "狂 人", "背徳者"]
        var cir = ["①", "②", "③", "④", "⑤", "⑥", "⑦"]
        var result = ""
        var isimo = num == 15 || num == 19
        var n = isimo ? 7 : 6
        var i, r
        if (isimo) {
            result += cir[rnd(n)] + "\n\n"
        }
        var l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        for (i = 0; i < n; i++) {
            r = rnd(10 - i)
            result = result + jobs[i] + ":" + l[r] + rnd(100, 2) + "\n"
            for (var j = r; j <= num - 1; j++) {
                l[j] = l[j + 1]
            }
        }
        return result
    }

    //**********************************************
    //   その他小物
    //**********************************************

    function getToday() {
        //表示されている日付チェック
        var day = /<font size="\+2">(\d{1,2})/.exec(body.html())
        today = day ? day[1] - 1 : 0
        newestDay = Math.max(today, discussLog.length - 1)
    }

    function getVillageno() {
        var vno = $("title").text().slice(0, 6) - 0
        return vno
    }

    function createSelectBox(option, selected, attr) {
        //optionを持つselectを作る。str。optionは{value: innerHTML}の形式で
        let attrtext = ""
        if (attr) {
            for (let k in attr) {
                attrtext = attrtext + ` ${k}="${attr[k]}"`
            }
        }
        var s = `<select${attrtext}>`
        for (var i in option) {
            let issl = i == selected ? "selected" : ""
            s += `<option value="${i}" ${issl}>${option[i]}</option>`
        }
        s += "</select>"
        return s
    }

    function editSpeakField(text) {
        //発言欄をtextにする
        textarea.val(text)
        memoContainer.hide()
    }

    function popupMessage(text) {
        //メッセージ
        messageArea.text(text).show()
        setTimeout(function () {
            messageArea.hide()
        }, 1500)
    }

    //**********************************************
    //   便利設定
    //**********************************************
    var left
    function setAlertVote() {
        //未投票アラート
        if ($("font[size=6]").size()) {
            warningArea.show()
            var cmbplayer = $("select[name=CMBPLAYER]").clone()
            var votebutton = $("<input />", {
                type: "button",
                value: "投票",
                on: {
                    click: function () {
                        $("select").eq(0).val("VOTE")
                        document.forms[0].submit()
                    },
                },
            })
            left = counts
            warningArea.html("未投票です! あと<span id='left'></span>秒")
            warningArea.append(cmbplayer)
            warningArea.append(votebutton)
            cmbplayer.on("change", function () {
                $("select[name=CMBPLAYER]").val($(this).val())
            })
            $("#left").html(left)
            setInterval(function () {
                left--
                $("#left").html(left)
            }, 1000)
        }
    }

    function receiveKeyResponse() {
        //キー入力を受け付けるかどうか
        $(window).on("keydown", function (e) {
            if (setting.send_support.value == "ctrl" && e.ctrlKey && e.keyCode == 13) {
                document.forms[0].submit()
            } else if (setting.send_support.value == "shift" && e.shiftKey && e.keyCode == 13) {
                document.forms[0].submit()
            }
        })
    }

    function dispSuggest() {
        var colorselect = createSelectBox(COLORLIST, 0, { id: "colorlist" })
        var coloredit = `<td class="coloredit">アイコン色:</td><td class="coloredit">${colorselect}</td>`
        var iconsupport =
            "<td class='iconsupport'><input type='button' id='pasteurl' value='/../../imgbbs/img/'></td>"

        commandtable.find("td").eq(1).after(coloredit).after(iconsupport)
        commandtable.find("td").eq(-2).addClass("cmbplayer")
        commandtable.find("td").eq(-1).addClass("cmbplayer")

        $("#colorlist").on("change", function () {
            editSpeakField($(this).val())
        })
        $("#pasteurl").on("click", function () {
            editSpeakField("/../../imgbbs/img/")
        })
        $("select")
            .eq(0)
            .on("change", function () {
                $("td.coloredit").hide()
                $("td.iconsupport").hide()
                $("td.cmbplayer").hide()
                if ($(this).val() == "ICONCHG") {
                    $("td.coloredit").show()
                } else if ($(this).val() == "BCONCHG") {
                    $("td.iconsupport").show()
                } else {
                    $("td.cmbplayer").show()
                }
            })
    }

    function highlightDeathnote() {
        var td = infotable.find("td:last")
        if (/アナタの家の前に/.test(td.html())) {
            td.css("color", "red")
        }
    }

    function setAutoImportLog() {
        if (!data.importlogday) data.importlogday = 0
        if (setting.auto_import_log.value == "alltime") {
            importLog()
        } else if (setting.auto_import_log.value == "onetime") {
            if (today > data.importlogday && /<font size="\+2">投票/.test(body.html())) {
                data.importlogday = today
                importLog()
                save()
                popupMessage("ログを取り込みました。")
            }
        }
    }

    var autoReloadFlg

    function setAutoReload() {
        autoReloadFlg = setTimeout(function () {
            textarea.val("")
            document.forms[0].submit()
        }, setting.autoreload_interval.value * 1000)
    }

    function castASpellOnMe() {
        var q = $("#caspequery").val()
        var num = $("#caspenum").val()
        if (num > 10) num = 10
        $.post(
            "http://mobajinro.s178.xrea.com/caspe/getWaffle.php",
            {
                query: q,
                num: num,
            },
            function (data) {
                var txt = data.replace(/\n+/g, "<br>")
                var nos = txt.match(/\d{4}/g)
                if (nos) {
                    for (var no of nos) {
                        var src = no
                        if (no - 0 > 4674) src = "/../../imgbbs/img/" + no
                        txt = txt.replace(
                            no,
                            `<img data-no='${no}' src='http://jinrou.dip.jp/~jinrou/img/alive_${src}.gif'>`
                        )
                    }
                }
                $("#casperesult").html(txt)
                $("#casperesult img").on("click", function () {
                    $("#iconno").val($(this).data("no"))
                    $("#casperesult img").removeClass("iconselected")
                    $(this).addClass("iconselected")
                })
            }
        )
    }

    function setClipboard(text, disc) {
        //クリップボードにコピー 発言欄初期化する注意
        textarea.val(text).select()
        document.execCommand("copy")
        textarea.val("")
    }

    function setNameColor() {
        if (setting.coloringName.value != "yes") return false
        $("b").each((i, e) => {
            let name = $(e).text()
            if (name in indexOfName) {
                let i = indexOfName[name]
                let job = playerInfo[i].job
                $(e).removeClass().addClass(colorSetting[job].value)
            }
        })
    }

    //**********************************************
    //   個人設定の読み込み
    //**********************************************

    load()

    //**********************************************
    //   cssの追加と書き換え
    //**********************************************
    var style = []

    //見た目変更
    if (setting.rewrite_css.value == "yes") {
        style = [
            ".CLSTABLE tr td:nth-of-type(even) {font-size: 12px;line-height: 110%;padding: 2px;}",
            ".CLSTABLE tr td:nth-of-type(odd) {font-size: 0px;padding: 2px;}",
            'body[bgcolor="#000000"] font[color="#6666aa"] {color: #ccccff;}',
            'font[size="-1"] {font-size: 9pt;}',
            "img {padding: 0;}",
            "input,select {font-size: 9pt;}",
            "table {font-size: 13px;}",
            'textarea {font-family: "Meiryo";font-size: 11px;min-height: 100px;}',
            'table[cellpadding="0"] tr td:nth-of-type(2){word-break:break-all;}',
        ]
    }

    //追加分
    style = style.concat([
        `:root{--theme-color:${color[setting.theme_color.value].main}; --sub-color:${
            color[setting.theme_color.value].sub
        };}`,
        "*{box-sizing:border-box;}",
        "body{margin:0;}",
        "form{margin:8px;}",
        "#memoContainer{display:none; width:100%; height:100%; position:fixed; top:0px; left:0px; background-color:rgba(180,180,180,0.8); padding:15px; overflow:auto;}",
        "#floatButtonArea{position:fixed; right:15px; top:15px;}",
        "#floatButtonArea > div{margin:0px 2px; display:inline-block; vertical-align: top; width:110px;  box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4);}",
        "#messageArea{width:400px; height:80px; position:fixed; left:50%; transform: translate(-50%, 0); top:15px; display:none;}",
        "#messageArea{text-align:center;font-size:14px; color:black;vertical-align:middle;padding-top:5px;}",
        "#warningArea{width:100%; height:40px; padding:5px; position:fixed; bottom:0; display:none; background-color:darkorange; text-align:center; font-size:16px; color:white; font-weight:bold;}",
        "#left{font-size:30px;}",
        "#warningArea select, #warningArea input{font-size:11pt; vertical-align:middle;}",

        "#memoMenu{width:100%; margin: 0 auto; font-size:10px;}",
        "#memoMenu input, #memoMenu select, #buttonArea input{font-size:11px;}",
        "#memoTab{margin-top:15px;}",
        "#memoBody{width:100%; height:calc(100% - 63px); margin: 0 auto; }",

        "#logArea,#voteArea{width:100%; height:100%; overflow:auto; margin: 0 auto; background-color:white; padding:10px; border-radius:0px 8px 8px 8px ;}",
        "#voteArea{display:none;}",

        "#discussLogTable{border-collapse:collapse;}",
        "#discussLogTable td{text-align:left;vertical-align:top; color:black; word-break:break-all; font-size:9pt; line-height:140%; padding:2px;}",
        "#discussLogTable font{font-size:9pt;}",
        "#discussLogTable tr td:first-of-type{min-width:150px;}",
        "#discussLogTable tr.systemlog td{font-weight:bold;background-color:var(--theme-color) !important; color:white; text-align:center;}",
        "#playerInfoTable a{text-decoration:underline; color:blue; cursor:pointer;}",
        "#playerInfoTable tr.namerow td.death {background-color:pink;}",
        "#playerInfoTable tr.namerow td.gray {background-color:#e3e3e3;}",
        "#voteTable, #summaryTable{font-size:11px; border-collapse:collapse; margin-bottom:10px;color:black;}",
        "#voteTable td, #summaryTable td{border:1px solid #666; padding:2px; }",
        "#toolArea_hid {display:none;}",
        "#setting, #colorSetting{font-size:13px; position:fixed; right:10px; bottom:10px; display:none; width:400px; height:300px; background-color:white;}",
        "#setting input[type=number], #setting input[type=text]{width:60px;}",
        ".coloredit, .iconsupport{display:none;}",
        ".voiceloud {padding:0px 5px;}",
        ".voiceloud div:not(:first-of-type){margin-top:5px;}",
        ".voice {width:30px;height:30px;font-size:16px;border:1px solid black; border-radius:2px;background-color:white;line-height:28px;text-align:center;color:black; cursor:pointer;}",
        ".voice.voice_selected{border:3px solid red; line-height:24px;}",
        "#caspe{display:none; position:fixed; right:10px; bottom:10px; width:400px; height:300px; solid #333; overflow:auto; font-size:9pt;}",
        "#caspe input[type=text]{width:100px;}",
        "#caspe input[type=number]{width:50px}",
        "#caspe, #setting,#colorSetting, #messageArea{box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); border-top:16px solid var(--theme-color); background-color:#efefef; padding:10px;}",
        "#floatButtonArea a, .button{width:110px; color:white; background-color:var(--theme-color); cursor:pointer; display:inline-block; font-size:12px; font-weight:bold; line-height:24px; text-align:center;}",
        "div.button{margin:0px 2px; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); padding:0px 3px;}",
        ".tab{width:150px; color:white; background-color:var(--theme-color);cursor:pointer;  display:inline-block; font-size:12px; line-height:24px; text-align:center; border-radius:8px 8px 0 0;}",
        ".tab.active{color:var(--theme-color); background-color:white;font-weight:bold;}",
        "#floatButtonArea a:hover, div.button:hover{background-color:var(--sub-color);}",
        ".closebutton{position:absolute; right:5px; top:5px;}",
        "#caspe img{padding:2px;}",
        "#caspe img.iconselected{border:2px solid var(--theme-color); padding:0px;}",
        ".jobinitial{user-select:none; color:var(--theme-color); font-weight:bold; font-size:80%;}",
        ".jobinitial:not(:empty):before{content:'[';}",
        ".jobinitial:not(:empty):after{content:']';}",
        ".black{} .pink{color:deeppink;} .red{color:red;} .green{color:green;}",
        ".purple{color:purple;} .brown{color:brown;} .blue{color:blue;}",
        ".gaming{background:linear-gradient(to right, #f33,#ff3,#3f3,#3ff,#33f,#f3f,#f33) ;-webkit-background-clip: text; -webkit-text-fill-color:transparent;}",
    ])
    if (setting.layout.value == "yes") {
        style = style.concat([
            "#logArea{display: flex; flex-direction:column; flex-wrap:wrap;}",
            "#playerInfoArea{height:calc(100% - 50px); overflow:auto hidden; width:50%; padding:5px;}",
            "#playerInfoArea select{font-size:8.5pt;}",
            "#buttonArea{height:50px;font-size:12px;  padding:5px;}",
            "#buttonArea > div{ margin-bottom:5px;}",
            "#discussLogArea{width:50%; overflow:auto; padding:5px;}",
            ".select{color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; text-align:center; display:inline-block;width:100px;}",
            ".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
            "#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
            "#buttonArea > div > div.select:lastof-type{margin-right:5px;}",
            "#playerInfoTable{background-color:white; text-align:center; font-size:8pt; color:black; width:100%; margin:0px 2px 2px 0px;}",
            "#playerInfoTable tbody{display:flex; flex-direction:row;border-spacing:0;}",
            "#playerInfoTable tr{display:flex; flex-direction:column; flex:0 0 40px;}",
            "#playerInfoTable tr.namerow{display:flex; flex-direction:column; flex:0 0 80px; position:sticky; left:0;}",
            "#playerInfoTable tr.namerow td{background-color:white;}",
            "#playerInfoTable tr.resultrow{display:flex; flex-direction:column; flex:0 0 140px;}",
            "#playerInfoTable td{border-right:#666 solid 1px;border-bottom:#666 solid 1px;padding:1px; display:block; width:auto; height:1.8em; }",
            "#playerInfoTable tr:first-of-type td{border-left:#666 solid 1px;}",
            "#playerInfoTable tr td:first-of-type{border-top:#666 solid 1px;}",
        ])
    } else {
        style = style.concat([
            "#buttonArea{padding:10px;line-height:24px;font-size:9pt;}",
            "#buttonArea > div{display:inline-block;}",
            "#playerInfoArea{overflow:auto;}",
            ".select{width:100px; color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; display:inline-block; font-size:12px; line-height:20px; text-align:center;}",
            ".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
            "#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
            "#buttonArea > div > div.select:last-of-type{margin-right:5px;}",
            "#playerInfoTable {background-color:white;text-align:center; font-size:8pt;border-collapse:collapse; color:black; margin:0 auto;}",
            "#playerInfoTable td{border:#666 solid 1px; padding:1px; }",
            "#playerInfoTable tr.namerow{height:35px;}",
            "#playerInfoTable tr td:first-child{min-width:50px;}",
        ])
    }
    $("<style></style>").html(style.join("\n")).appendTo($("head"))

    //**********************************************
    //   ツール用のもろもろを追加
    //**********************************************

    //大枠
    var memoContainer = $("<div></div>", { id: "memoContainer" }).appendTo(body)
    var messageArea = $("<div></div>", { id: "messageArea" }).appendTo(body)
    var warningArea = $("<div></div>", { id: "warningArea" }).appendTo(body)
    var floatButtonArea = $("<div></div>", { id: "floatButtonArea" }).appendTo(body)
    var settingArea = $("<div></div>", { id: "setting" }).appendTo(body)
    var colorSettingArea = $("<div></div>", { id: "colorSetting" }).appendTo(body)
    var caspe = $("<div></div>", { id: "caspe" }).appendTo(body)

    //ブロック
    var memoMenu = $("<div></div>", { id: "memoMenu" }).appendTo(memoContainer)
    var memoTab = $("<div></div>", { id: "memoTab" }).appendTo(memoContainer)
    var memoBody = $("<div></div>", { id: "memoBody" }).appendTo(memoContainer)

    //各div
    var logArea = $("<div></div>", { id: "logArea" }).appendTo(memoBody)
    var voteArea = $("<div></div>", { id: "voteArea" }).appendTo(memoBody)
    var playerInfoArea = $("<div></div>", { id: "playerInfoArea" }).appendTo(logArea)
    var buttonArea = $("<div></div>", { id: "buttonArea" }).appendTo(logArea)
    var discussLogArea = $("<div></div>", { id: "discussLogArea" }).appendTo(logArea)

    //テーブル
    var playerInfoTable = $("<table>", { id: "playerInfoTable" }).appendTo(playerInfoArea)
    var discussLogTable = $("<table>", { id: "discussLogTable" }).appendTo(discussLogArea)
    var voteTable = $("<table>", { id: "voteTable" }).appendTo(voteArea)
    var summaryTable = $("<table>", { id: "summaryTable" }).appendTo(voteArea)

    //観戦時のみ自動更新ボタン
    if ($("td.CLSTD01").eq(1).text() == "◆ 再表示") {
        var onoff = data.isAutoReload ? "ON" : "OFF"
        floatButtonArea.append("<div><a id='autoReload'>自動更新:" + onoff + "</a></div>")
        if (data.isAutoReload) setAutoReload()

        $("#autoReload").click(function () {
            data.isAutoReload = !data.isAutoReload
            save()
            var onoff = data.isAutoReload ? "ON" : "OFF"
            $("#autoReload").text("自動更新:" + onoff)
            popupMessage("自動更新を" + onoff + "にしました。")

            if (data.isAutoReload) {
                setAutoReload()
            } else {
                clearTimeout(autoReloadFlg)
            }
        })
    }

    floatButtonArea.append(
        [
            "<div id='toolArea'><a>ツール</a>",
            "<div id='toolArea_hid'>",
            "<a id='tool1'>四桁乱数</a>",
            "<a id='tool2'>乱数表</a>",
            "<a id='tool3'>役職一覧と丸数字</a>",
            "<a id='dispcaspe'>きゃすぺ</a>",
            "<a id='dispsetting'>設定</a>",
            "<a id='dispcolorsetting'>色設定</a></div>",
            "</div>",
        ].join("\n")
    )
    floatButtonArea.append("<div><a id='toggleButton'>メモ表示/非表示</a></div>")

    //共通のボタン
    memoMenu.append("<div class='button' id='importButton'>ログの取り込み</div>")
    memoMenu.append("<div class='button' id='resetButton'>リセット</div>")
    memoMenu.append("<div class='button' id='reloadButton'>更新</div>")

    //タブ
    memoTab.append("<div class='tab active' id='logDispButton' data-value='log'>発言ログ</div>")
    memoTab.append("<div class='tab' id='voteDispButton' data-value='vote'>投票履歴</div>")

    //各ブロックのボタン
    buttonArea.append(
        [
            "<div>絞り込み",
            "<div class='select filter' id='showAllButton' data-value='off'>全員表示</div>",
            "<div class='select filter' id='showAliveButton' data-value='on'>生存+役職のみ</div>",
            "</div>",
        ].join("")
    )
    buttonArea.append(
        [
            "<div>役職入力",
            "<div class='select inputmode' id='inputnoneButton' data-value='none'>なし</div>",
            "<div class='select inputmode' id='inputsimpleButton' data-value='simple'>最新のみ</div>",
            "<div class='select inputmode' id='inputfullButton' data-value='full'>全日</div>",
            "</div>",
        ].join("")
    )

    var settingTable = $("<table></table>", { id: "settingtable" }).appendTo(settingArea)
    settingArea.append(
        `<div class="closebutton"><input type="button" onclick="document.getElementById('setting').style.display='none';" value='閉じる'></div>`
    )

    for (var k in setting) {
        var item = setting[k]
        var tr = new Tr()
        tr.add(item.name)
        tr.add(createSelectBox(item.option, item.value, { id: k }))
        tr.appendTo(settingTable)
    }

    var colorSettingTable = $("<table></table>", { id: "settingtable" }).appendTo(colorSettingArea)
    colorSettingArea.append(
        `<div class="closebutton"><input type="button" onclick="document.getElementById('colorSetting').style.display='none';" value='閉じる'></div>`
    )

    for (k in colorSetting) {
        item = colorSetting[k]
        tr = new Tr()
        tr.add(item.name)
        tr.add(createSelectBox(item.option, item.value, { id: "color_" + k }))
        tr.appendTo(colorSettingTable)
    }

    caspe.html(
        [
            "キーワード:<input type='text' id='caspequery'>数:<input type='number' id='caspenum' value='1'><input type='button' id='castaspellonme' value='きゃすぺ'>",
            "<div id='casperesult'></div>",
            "<input type='button' id='changeicon' value='選択したアイコンを設定'>",
            "<input type='hidden' id='iconno' value=''>",
            `<div class="closebutton"><input type='button' onclick='document.getElementById("caspe").style.display = "none";' value='閉じる'></div>`,
        ].join("\n")
    )

    //**********************************************
    //   ロードが完了したら仕込み
    //**********************************************

    $(function () {
        playertable = $("table").eq(1)
        textareatable = $("table[cellspacing=0]").eq(-1)
        discusstable = $("table[cellpadding=0]").not("table.CLSTABLE2").last()
        infotable = $("table[cellspacing=0]").eq(0)
        commandtable = $("table[cellspacing=0]").eq(-2)
        textarea = $("textarea").eq(0)
        body = $("body")

        var voicebutton =
            "<td class='voiceloud'><div class='voice' data-value='MSG'>普</div><div class='voice' data-value='MSG2'><strong>強</strong></div><div class='voice' data-value='MSG3'><span style='color:#6666ee;'>弱</span></div></td>"
        var submitbutton =
            "<td><input type='submit' value='行動/更新' style='height:100px; width:150px;'></td>"
        textareatable.find("td:last").after(submitbutton).after(voicebutton)

        switchInputMode()
        switchAliveFilter()

        $("#toggleButton").on("click", () => {
            memoContainer.toggle()
        })
        $("#importButton").on("click", function () {
            importLog()
            refresh()
        })
        $("div.tab").on("click", function () {
            switchDispArea(this)
        })
        $("#resetButton").on("click", function () {
            if (window.confirm("ログをすべてリセットします。本当によろしいですか?")) {
                reset()
                getToday()
                refresh()
            }
        })
        $("#reloadButton").on("click", function () {
            textarea.val("")
            document.forms[0].submit()
        })

        $("div.select.filter").on("click", function () {
            switchAliveFilter(this)
        })

        $("div.select.inputmode").on("click", function () {
            switchInputMode(this)
        })

        $("#toolArea").hover(
            () => {
                $("#toolArea_hid").show()
            },
            () => {
                $("#toolArea_hid").hide()
            }
        )
        $("#tool1").on("click", () => {
            setClipboard(makernd())
            popupMessage("コピーしました:4桁乱数")
        })
        $("#tool2").on("click", () => {
            updateplayerInfo()
            setClipboard(makematrix(playerInfo.length))
            popupMessage("コピーしました:乱数表")
        })
        $("#tool3").on("click", () => {
            updateplayerInfo()
            setClipboard(makerndjob(playerInfo.length))
            popupMessage("コピーしました:役職一覧")
        })
        $("#dispcaspe").on("click", () => {
            $("#caspe").show()
        })
        $("#dispsetting").on("click", () => {
            $("#setting").show()
        })
        $("#dispcolorsetting").on("click", () => {
            $("#colorSetting").show()
        })
        $("#setting select").on("change", function () {
            setting[$(this).attr("id")].value = $(this).val()
            save()
        })
        $("#colorSetting select").on("change", function () {
            let id = $(this).attr("id").slice(6)
            colorSetting[id].value = $(this).val()
            save()
            setNameColor()
        })
        $("#castaspellonme").on("click", function () {
            castASpellOnMe()
        })
        $("#changeicon").on("click", function () {
            var no = $("#iconno").val()
            if (no == "") return false
            if (no - 0 > 4674) no = "/../../imgbbs/img/" + no
            editSpeakField(no)
            $("select").eq(0).val("BCONCHG")
            document.forms[0].submit()
        })

        textarea.focus()

        $("div.voice").click(function () {
            $("select").eq(0).val($(this).data("value"))
            $("div.voice").removeClass("voice_selected")
            $(this).addClass("voice_selected")
        })

        $(window).on("keydown", function (e) {
            if (e.keyCode == 27) {
                memoContainer.hide()
            }
        })

        if (localStorage.debug == "on") {
            memoMenu.append("<div class='button' id='debugButton'>デバッグ用</div>")
            $("#debugButton").click(() => {
                console.log(data, today)
            })
        }

        getToday()
        dispSuggest()
        highlightDeathnote()

        if (setting.auto_import_log.value != "none") setAutoImportLog()
        if (setting.alert_vote.value == "yes") setAlertVote()
        if (setting.send_support.value != "none") receiveKeyResponse()
        if (setting.coloringName.value == "yes") setNameColor()
        if (data.villageno != getVillageno()) reset()

        refresh()

        if (!localStorage.memoVersion || localStorage.memoVersion != "1.4.2") {
            localStorage.memoVersion = "1.5.0"
            var notice = $("<div></div>", { id: "notice" }).appendTo(body)
            /*
            notice.attr("style","position:fixed;left:calc(50% - 200px);bottom:calc(50% - 100px); width:400px; height:200px;box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); border-top:16px solid var(--theme-color); background-color:#efefef; padding:10px; font-size:10pt;")
            notice.html("<span style='font-size:12pt'>ウドメモver1.4に伴うお知らせ</span><br>未投票時にメモを見ながら投票できる機能を追加しましたが、この機能は<b>テストできていません。</b>必ず投票状態になったことを<b>目視確認</b>してください。また正常に動かなかった場合は教えてね(はーと)<br>")
            notice.append($("<input>",{on:{click:function(){$("#notice").hide();}}, value:"表示しない", type:"button"}))
            */
        }
    })
})(jQuery)