<!DOCTYPE html>
<html lang="cn">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 不携带 referer -->
    <meta name="referrer" content="never">
    <title>B站排行</title>
    <link rel="stylesheet" href="./assets/css/main.css">
    <style>
        #title {
            margin-bottom: 0;
        }

        #dynamic-note {
            color: grey;
            margin: 5px auto;
            font-size: 13px;
        }

        td {
            font-size: 12px;

            max-width: 200px;
            word-wrap: break-word;
        }
    </style>
</head>

<body>
    <div style="text-align: center;">
        <h1 id="title">B站排行榜</h1>
        <p id="dynamic-note"></p>
        <hr>
        <div style="display: none;">
            显示字段:
            <label for="show_emoticon">
                <input type="checkbox" class="filter_checkbox" name="show_emoticon" id="show_emoticon"
                    detailed-checked="true">热搜表情
            </label>
            <label for="show_num">
                <input type="checkbox" class="filter_checkbox" name="show_num" id="show_num" checked="true"
                    concise-checked="true" detailed-checked="true">热度
            </label>
            <label for="show_category">
                <input type="checkbox" class="filter_checkbox" name="show_category" id="show_category" checked="true"
                    detailed-checked="true">分类
            </label>
            <label for="show_onboard_time">
                <input type="checkbox" class="filter_checkbox" name="show_onboard_time" id="show_onboard_time"
                    checked="true" detailed-checked="true">上线时间
            </label>
            <label for="show_is_new">
                <input type="checkbox" class="filter_checkbox" name="show_is_new" id="show_is_new"
                    detailed-checked="true">是否新热搜
            </label>
            <label for="show_detail">
                <input type="checkbox" class="filter_checkbox" name="show_detail" id="show_detail"
                    detailed-checked="true">热搜详情
            </label>
            <label for="show_mid">
                <input type="checkbox" class="filter_checkbox" name="show_mid" id="show_mid">mid
            </label>
            <br>
            <button id="btn_show_all">全选</button>
            <button id="btn_show_none">全不选</button>
            |
            <button id="btn_show_concise">简洁</button>
            <button id="btn_show_default">普通</button>
            <button id="btn_show_detailed">详细</button>
        </div>
    </div>
    <p id="latestUpdateTime" style="font-size: 12px; display: inline-block; vertical-align: middle;"></p>
    <nobr>
        <button id="btn_refresh">重新拉取</button>
    </nobr>
    <nobr>
        <label for="auto_refresh">
            <input type="checkbox" name="auto_refresh" id="auto_refresh">自动拉取<span id="auto_refresh_countdown"></span>
        </label>
    </nobr>
    <nobr>
        <span id="update-finish-info" style="color: green; font-weight: bold; display: none;">拉取成功,数据已更新</span>
    </nobr>
    <table id="list"></table>
    <div class="bottom-placeholder">
        <p>
            — 到底啦 —<br>
            数据来源: https://www.bilibili.com/v/popular/rank/all
        </p>
    </div>

    <script>
        /**
         * 全局变量
         */
        // 拉取下来的数据
        let hotBandData;

        // 按钮
        const btnShowAll = document.getElementById('btn_show_all');
        const btnShowNone = document.getElementById('btn_show_none');
        const btnShowConcise = document.getElementById('btn_show_concise');
        const btnShowDefault = document.getElementById('btn_show_default');
        const btnShowDetailed = document.getElementById('btn_show_detailed');

        const btnRefresh = document.getElementById('btn_refresh');

        // 复选框
        const filterCheckbox = document.getElementsByClassName("filter_checkbox");

        const showEmoticon = document.getElementById("show_emoticon");
        const showNum = document.getElementById("show_num");
        const showCategory = document.getElementById("show_category");
        const showOnboardTime = document.getElementById("show_onboard_time");
        const showIsNew = document.getElementById("show_is_new");
        const showDetail = document.getElementById("show_detail");
        const showMid = document.getElementById("show_mid");

        const autoRefresh = document.getElementById("auto_refresh");


        // 绑定按钮点击事件
        btnShowAll.addEventListener('click', function () {
            for (let i = 0; i < filterCheckbox.length; i++) {
                const element = filterCheckbox[i];
                element.checked = true;
            }
            render();
        });
        btnShowNone.addEventListener('click', function () {
            for (let i = 0; i < filterCheckbox.length; i++) {
                const element = filterCheckbox[i];
                element.checked = false;
            }
            render();
        });
        btnShowConcise.addEventListener('click', function () {
            for (let i = 0; i < filterCheckbox.length; i++) {
                const element = filterCheckbox[i];
                element.checked = element.getAttribute('concise-checked') === 'true';
            }
            render();
        });
        btnShowDefault.addEventListener('click', function () {
            for (let i = 0; i < filterCheckbox.length; i++) {
                const element = filterCheckbox[i];
                element.checked = element.getAttribute('checked') === 'true';
            }
            render();
        });
        btnShowDetailed.addEventListener('click', function () {
            for (let i = 0; i < filterCheckbox.length; i++) {
                const element = filterCheckbox[i];
                element.checked = element.getAttribute('detailed-checked') === 'true';
            }
            render();
        });

        btnRefresh.onclick = function () {
            getData();
            document.getElementById("update-finish-info").style.display = "";
            // btnRefresh.style.display = "none";
            btnRefresh.style.visibility = "hidden";
            setTimeout(function () {
                document.getElementById("update-finish-info").style.display = "none";
                // btnRefresh.style.display = "";
                btnRefresh.style.visibility = "";
            }, 1000);
        };

        // 绑定复选框改变事件
        for (let i = 0; i < filterCheckbox.length; i++) {
            // console.log(filterCheckbox[i]);
            filterCheckbox[i].onchange = function () {
                render();
            };
        }

        let autoRefreshIntreval = null;
        const autoRefreshCountDownElement = document.getElementById('auto_refresh_countdown');
        const countDown = 20; // 自动拉取间隔时间,单位:秒
        let autoRefreshCountDown = countDown;
        autoRefresh.onchange = function () {
            if (autoRefresh.checked) {
                btnRefresh.style.display = "none";
                btnRefresh.click();
                autoRefreshIntreval = setInterval(function () {
                    if ((--autoRefreshCountDown) > 0) {
                        autoRefreshCountDownElement.innerHTML = `(${autoRefreshCountDown}s)`;
                    } else {
                        autoRefreshCountDown = countDown;
                        btnRefresh.click();
                        autoRefreshCountDownElement.innerHTML = ``;
                    }
                }, 1000);
            } else {
                clearInterval(autoRefreshIntreval);
                btnRefresh.style.display = "";
                autoRefreshCountDownElement.innerHTML = ``;
            }
        };

        // 根据屏幕判断要显示哪些字段
        // 此时还未拉取数据,所以进入 render 函数会直接返回,不会多次渲染
        let initWidth = document.body.offsetWidth;
        // console.log(initWidth);
        /* if (initWidth < 400) {
            btnShowNone.click();
            btnShowNone.innerHTML += "(默认)";
        } else */ if (initWidth < 600) {
            btnShowConcise.click();
            btnShowConcise.innerHTML += "(默认)";
        } else if (initWidth < 1900) {
            btnShowDefault.click();
            btnShowDefault.innerHTML += "(默认)";
        } else {
            btnShowDetailed.click();
            btnShowDetailed.innerHTML += "(默认)";
        }

        // 网页加载后加载榜单
        getData();

        // 定时刷新
        // setInterval(getData, 10 * 1000);

        function getData() {
            var xhr = new XMLHttpRequest();
            xhr.open("GET", "../data/bilibili-rank/latest.json?t=" + Date.now(), true);
            xhr.send();
            xhr.onreadystatechange = function () {
                if (xhr.readyState !== 4) return;

                if (xhr.status == 200) {
                    try {
                        hotBandData = JSON.parse(xhr.responseText);
                        if (!hotBandData.data || typeof hotBandData.data !== 'object')
                            throw new Error("data is undefined or not an object");
                    } catch (e) {
                        console.error("[error]", "\n", e, "\n", "\n", "[xhr.responseText]", "\n", xhr.responseText);
                        alert("latest.json 文件解析失败,请检查文件");
                        return;
                    }
                    console.log(hotBandData);

                    // 更新动态 note
                    document.getElementById("dynamic-note").innerHTML = hotBandData.note;

                    // 更新时间
                    document.getElementById("latestUpdateTime").innerHTML =
                        "数据拉取时间:" + new Date().toLocaleString() + "<br/>" +
                        "热榜更新时间:" + new Date(hotBandData.update_time).toLocaleString();

                    // 渲染榜单
                    render();
                } else if (xhr.status == 404) {
                    alert("data 目录下未找到 latest.json 文件,可能的原因:\n您还没有运行脚本拉取数据,请先运行脚本,然后刷新页面");
                }
            }
        }

        function render() {
            if (!hotBandData) return;

            /**
             * 渲染热搜列表
             */
            let hotBandList = hotBandData.data;

            var str = [];
            // 渲染表格
            str.push(`<thead>
                <tr class="thead" style="top: 0; background-color: white; position: sticky;">

                    <td>编号</td>
                    <td>标题</td>
                    <td>时长</td>
                    <td>封面</td>
                    <td>第一帧</td>
                    <td>视频ID</td>
                    <td>分类</td>
                    <td>发布时间</td>
                    <td>简介</td>
                    <td>作者(mid)</td>
                    <td>统计</td>
                    <td>视频宽高</td>
                    <td>定位</td>

                    <td>videos</td>
                    <td>tid</td>
                    <td>copyright</td>
                    <td>state</td>
                    <td>rights</td>
                    <td>dynamic</td>
                    <td>score</td>

                    <td>mission_id</td>
                    <td>season_id</td>
                    <td>up_from_v2</td>
                    <td>others</td>
                </tr>
            </thead>`);
            str.push(`<tbody>`);
            for (var i = 0; i < hotBandList.length; i++) {
                const hotBand = hotBandList[i];
                let link = hotBand.short_link == hotBand.short_link_v2
                    ? hotBand.short_link
                    : `${hotBand.short_link}<br/>${hotBand.short_link_v2}`;

                /**
                 * 视频总秒数转化为友好显示时间
                 */
                // refer: https://blog.csdn.net/weixin_43838488/article/details/122337474
                function formatZero(num, len) {
                    if (String(num).length > len) {
                        return num;
                    }
                    return (Array(len).join(0) + num).slice(-len)
                }

                let duration = hotBand.duration - 1; // 根据观测,基本上所有视频都是少1s(有些少了2s或者其他),所以这里减1
                let durationArr = [0, 0, 0];
                durationArr[0] = Math.floor(duration / (60 * 60));
                durationArr[1] = Math.floor((duration - durationArr[0] * 60 * 60) / 60);
                durationArr[2] = Math.floor(duration - durationArr[0] * 60 * 60 - durationArr[1] * 60);
                let durationStr = "";
                if (durationArr[0] === 0) {
                    // 小时为0
                    durationStr = `${formatZero(durationArr[1], 2)}:${formatZero(durationArr[2], 2)}`;
                } else {
                    // 小时不为0
                    durationStr = `${formatZero(durationArr[0], 2)}:${formatZero(durationArr[1], 2)}:${formatZero(durationArr[2], 2)}`;
                }

                //功能:求最大公约数
                //参数: x 、y   number
                //返回值: number
                function gcd(x, y) {
                    if (isNaN(x) || isNaN(y)) return null;

                    if (x % y === 0) {
                        return y;
                    }
                    return gcd(y, x % y)
                    //三目运算符写法:
                    //return x % y === 0 ? y : gcd(y , x % y) ;
                }
                let dimension_gcd = gcd(hotBand.dimension.width, hotBand.dimension.height);

                str.push(`<tr>
                    <!-- 编号 -->
                    <td>${i + 1}</td>

                    <!-- 标题 -->
                    <td>
                        <a href="${link}" target="_blank">${hotBand.title}</a>
                    </td>

                    <!-- 时长 -->
                    <td>${durationStr}<br>(${hotBand.duration}s)</td>

                    <!-- 封面 -->
                    <td>
                        <img src="${hotBand.pic}" style="width: 120px;"/>
                    </td>

                    <!-- 第一帧 -->
                    <td>
                        <img src="${hotBand.first_frame}" style="width: 120px;"/>
                    </td>

                    <!-- aid -->
                    <td style="text-align: left;">
                        <nobr>aid: ${hotBand.aid}</nobr>
                        <nobr>bvid: ${hotBand.bvid}</nobr>
                        <nobr>cid: ${hotBand.cid}</nobr>
                    </td>

                    <!-- 分类 -->
                    <td>${hotBand.tname}</td>

                    <!-- 发布时间 -->
                    <td style="font-size: 10px;">
                        <nobr>pubdate: ${new Date(hotBand.pubdate * 1000).toLocaleString()}</nobr>
                        <nobr>ctime: ${new Date(hotBand.ctime * 1000).toLocaleString()}</nobr>
                    </td>

                    <!-- 简介 -->
                    <td>${hotBand.desc}</td>

                    <!-- 作者 -->
                    <td>
                        <img src="${hotBand.owner.face}" style="width: 30px;"/><br>
                        ${hotBand.owner.name}<br>
                        (${hotBand.owner.mid})
                        <!-- ${JSON.stringify(hotBand.owner)} -->
                    </td>

                    <!-- 统计 -->
                    <td style="text-align: left;">
                        <nobr>播放: ${hotBand.stat.view}</nobr>
                        <nobr>弹幕: ${hotBand.stat.danmaku}</nobr>
                        <nobr>评论: ${hotBand.stat.reply}</nobr>
                        <nobr>喜欢: ${hotBand.stat.favorite}</nobr>
                        <nobr>投币: ${hotBand.stat.coin}</nobr>
                        <nobr>分享: ${hotBand.stat.share}</nobr>
                        <!--<nobr>当前排名: ${hotBand.stat.now_rank}</nobr>-->
                        <nobr>历史<!--最高-->排名: ${hotBand.stat.his_rank}</nobr>
                        <nobr>喜欢数: ${hotBand.stat.like}</nobr>
                        <nobr>不喜欢数: ${hotBand.stat.dislike}</nobr>
                        <!-- ${JSON.stringify(hotBand.stat)} -->
                    </td>

                    <!-- 视频宽高 -->
                    <td>
                        ${hotBand.dimension.width}:${hotBand.dimension.height}<br>
                        (${hotBand.dimension.width / dimension_gcd}:${hotBand.dimension.height / dimension_gcd})
                        <!-- ${hotBand.dimension.rotate} -->
                        <!-- ${JSON.stringify(hotBand.dimension)} -->
                    </td>

                    <!-- 定位 -->
                    <td>
                        <nobr>${hotBand.pub_location}</nobr>
                    </td>


                    <!-- videos -->
                    <td>${hotBand.videos}</td>
                    <!-- tid -->
                    <td>${hotBand.tid}</td>
                    <!-- copyright -->
                    <td>${hotBand.copyright}</td>
                    <!-- state -->
                    <td>${hotBand.state}</td>
                    <!-- rights -->
                    <td>${JSON.stringify(hotBand.rights)}</td>
                    <!-- dynamic -->
                    <td>${hotBand.dynamic}</td>
                    <!-- score -->
                    <td>${hotBand.score}</td>

                    <!-- mission_id -->
                    <td>${hotBand.mission_id ? hotBand.mission_id : ''}</td>
                    <!-- season_id -->
                    <td>${hotBand.season_id ? hotBand.season_id : ''}</td>
                    <!-- up_from_v2 -->
                    <td>${hotBand.up_from_v2 ? hotBand.up_from_v2 : ''}</td>
                    <!-- others -->
                    <td>
                        ${hotBand.others
                        ? `<div style="max-height: 200px; overflow: scroll;"><span>${JSON.stringify(hotBand.others)}</span></div>`
                        : ''}
                    </td>
                </tr >`);
            }
            str.push(`</tbody>`);
            document.getElementById('list').innerHTML = str.join('');
        }
    </script>
</body>

</html>