LinuxDo自定义🛠️

为 LinuxDo 设置 快速收藏、点击数可视化、图像缩放、小图显示、自定义徽标、去除模糊、详情展开、页面加宽 等功能。

当前为 2024-08-02 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name LinuxDo自定义🛠️
// @name:en     LinuxDo Custom🛠️
// @name:zh-CN  LinuxDo自定义🛠️
// @description 为 LinuxDo 设置 快速收藏、点击数可视化、图像缩放、小图显示、自定义徽标、去除模糊、详情展开、页面加宽 等功能。
// @description:en Adds customizable features such as logos, click count visualization, image resize, and quick bookmarking to LinuxDo
// @description:zh-CN 为 LinuxDo 设置 快速收藏、点击数可视化、图像缩放、小图显示、自定义徽标、去除模糊、详情展开、页面加宽 等功能。
// @version      0.4.7
// @author       Yearly
// @match        https://linux.do/*
// @icon        data:image/svg+xml;base64,PHN2ZyAgdmlld0JveD0iMCAtMiAzNiAzNiIgdmVyc2lvbj0iMS4xIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCBtZWV0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICA8cmVjdCB5PSItMiIgd2lkdGg9IjM2IiBoZWlnaHQ9IjM2IiByeD0iMTUlIiBmaWxsPSIjMDAwIi8+CiAgPHBhdGggZmlsbD0iI2VlZSIgIGQ9Ik0zMy42OCwxNS40SDIyLjIzQTMuNjgsMy42OCwwLDAsMSwxOSw5Ljg5bC40LS42OUg0VjdIMjAuNzFsMS4xNS0ySDRBMiwyLDAsMCwwLDIsN1YyOWEyLDIsMCwwLDAsMiwySDMyYTIsMiwwLDAsMCwyLTJWMTUuMzhaTTE2LjgsMTkuODNsLTEwLDQuNTlWMjEuNzhsNi41MS0zLTYuNTEtM1YxMy4xN2wxMCw0LjU5Wm02LjYsNS41N0gxN1YyM2g2LjRaIiBjbGFzcz0iY2xyLWktc29saWQtLWFsZXJ0ZWQgY2xyLWktc29saWQtcGF0aC0xLS1hbGVydGVkIi8+CiAgPHBhdGggZmlsbD0iI0ZEMSIgZD0iTTI2Ljg1LDEuMTQsMjEuMTMsMTFBMS4yOCwxLjI4LDAsMCwwLDIyLjIzLDEzSDMzLjY4QTEuMjgsMS4yOCwwLDAsMCwzNC43OCwxMUwyOS4wNiwxLjE0QTEuMjgsMS4yOCwwLDAsMCwyNi44NSwxLjE0WiIgY2xhc3M9ImNsci1pLXNvbGlkLS1hbGVydGVkIGNsci1pLXNvbGlkLXBhdGgtMi0tYWxlcnRlZCBjbHItaS1hbGVydCIvPgo8L3N2Zz4K
// @license      MIT
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @namespace    http://tampermonkey.net/
// @supportURL   https://greasyfork.org/scripts/499029
// @homepageURL  https://greasyfork.org/scripts/499029
// ==/UserScript==

(function() {
    var settings = {};

    const default_main_icon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtbGFiZWw9IkxpbnV4IiB2aWV3Qm94PSIwIDAgNTAwIDUwMCIgZmlsbD0iIzMzMyI+PHJlY3Qgd2lkdGg9IjQ1MCIgaGVpZ2h0PSI0NTAiIHJ4PSIxNSUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYxIDE5MGMtNiAxNC00OCA1OC00NCAxMDIgMTYgMTg0IDcyIDYwIDE1NiAxMDYgMCAwIDE1MC04NCAzMC0yMjAtMzQtNDgtNC04Ni0yNi0xMThzLTYwLTM0LTg4LTQgMTIgNzQtMjggMTM0Ii8+PHBhdGggZD0iTTMwOSAyODJzMTgtMzYtMTYtNjJjMzIgMzQgMTIgNjQgMTIgNjRoLTZjLTItNzAtMjAtMzItNDYtMTU2IDMwLTM0LTI4LTY0LTI4LThoLTE4YzItNDgtNDAtMjQtMTYgMTAtMiA3NC00NiAxMDQtNDYgMTU2LTE0LTM2IDEyLTY0IDEyLTY0cy0zNiAzMC0xNCA3NCA2MiAzNCAzNCA1NGM0NCAzMCAxMTIgMTAgMTEwLTU0IDItMTYgNDQtMTAgNDgtNnMtNi04LTI2LThNMTk3IDEyNmMtMTQtNC0xMC0yMi00LTIyczE2IDE0IDQgMjJtMzggMmMtMTAtMTQtMi0yOCA4LTI2czEwIDI2LTggMjYiIGZpbGw9IiNlZWUiLz48ZyBmaWxsPSIjZmMyIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMiI+PHBhdGggZD0ibTE0MyAzMDIgNDIgNjBjMjIgMTQgMTAgNzAtNTAgNDItMzQtMTAtNjItOC02Ni0yNnM4LTIwIDYtMjhjLTgtNDQgMjgtMjIgMzgtNDRzMTAtMzIgMzAtNG0yMjQgMjhjLTgtMTIgMC0zNC0yOC0zMi0xMiAyNC00NiA0OC00OCAwLTIwIDAtNiA0OC0xNCA3MC0xOCA1NCAzNCA1OCA1NiAzMmw1Mi0zNmM0LTYgMTAtMTItMTgtMzRNMTgzIDE0NmMtNi0xMiAyMi0yOCAzMi0yOHMyNCA4IDM4IDEyIDggMTggNCAyMC0yNiAyMC00MiAyMC0yMC0xNi0zMi0yNCIvPjxwYXRoIGQ9Ik0xODMgMTQ0YzE2IDEyIDM0IDIyIDcwLTYiLz48L2c+PHBhdGggZD0iTTIwNSAxMzJjLTQgMCAyLTQgNC0ybTE0IDJjMi0yLTItNC02LTIiLz48L3N2Zz4="

    const default_wide_icon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMTAiIGhlaWdodD0iMTEwIiB2aWV3Ym94PSItNSAtNSAzMTAgMTEwIj48cmVjdCB3aWR0aD0iOTkiIGhlaWdodD0iOTkiIHJ4PSIxMDAlIiBmaWxsPSIjRjFGMUYxIi8+PHRleHQgeD0iMjQiIHk9Ijc2IiBmb250LXNpemU9Ijc3IiBmaWxsPSIjRmMzIiBmb250LXdlaWdodD0iYm9sZCI+TDwvdGV4dD48dGV4dCB4PSI3MCIgeT0iNzUiIGZvbnQtc2l6ZT0iNDYiIGZpbGw9IiM4ODgiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZXR0ZXItc3BhY2luZz0iNSI+SU5VWDwvdGV4dD48dGV4dCB4PSIyMDUiIHk9Ijc3IiBmb250LXNpemU9IjcwIiBmaWxsPSIjRUVFIiBmb250LXdlaWdodD0iNjYwIj5EbzwvdGV4dD48L3N2Zz4=";

    const settingsConfig = {
        quick_mark    : { type: 'checkbox', label: '快速收藏  ', default: true, style:'', info:'在帖子上增加一个⭐用于快速收藏到书签' },
        cnts_colorful : { type: 'checkbox', label: '点击数可视化', default: true, style:'', info:'点击数彩色高亮,数越大,颜色越红' },
        image_view    : { type: 'checkbox', label: '增强大图查看', default: true, style:'', info:'在大图查看时,支持滚轮缩放和鼠标拖动位置' },
        spoiler_noblur: { type: 'checkbox', label: '去除模糊', default: false, style:'', info:'去除剧透字段的模糊,使其直接显示' },
        details_open  : { type: 'checkbox', label: '详情展开', default: false, style:'', info:'直接展开被折叠的详情' },
        wider_page    : { type: 'checkbox', label: '超宽显示', default: false, style:'', info:'让页面显示尽量宽' },
        thin_header   : { type: 'checkbox', label: '窄的顶栏', default: false, style:'', info:'让(Header)顶栏变窄' },
        topic_scroll  : { type: 'checkbox', label: '帖子限高', default: true, style:'', info:'帖子内容限高,太长的帖子会自动滚动' },

        order_created : { type: 'checkbox', label: '按创建排序', default: true, style:'', info:'首页导航的【新】改成新创建排序' },

        image_mini    : { type: 'checkbox', label: '显示小图', default: false, style:'', info:'让帖子中的图都变小,在鼠标悬停时显示大图' },
        image_mini_H  : { type: 'number', label: '  小图高度', default: "70", dependsOn: 'image_mini', style:'font-size:15px; margin-top:10px;' , info:'(单位px,建议设为大于50的数)' },
        image_mini_W  : { type: 'number', label: '  小图宽度', default: "70", dependsOn: 'image_mini', style:'font-size:15px; margin-top:10px;' , info:'(单位px,建议设为大于50的数)' },

        icon_custom   : { type: 'checkbox', label: '自定义图标', default: false, style:'' , info:'始皇说不建议这样,所以我让鼠标悬停时能看眼原LOGO' },
        icon_main     : { type: 'text', label: '  主图标URL', default: default_main_icon, dependsOn: 'icon_custom', style:'font-size:15px; margin-top:10px;', info:'' },
        icon_wide     : { type: 'text', label: '  宽图标URL', default: default_wide_icon, dependsOn: 'icon_custom', style:'font-size:15px; margin-top:10px;', info:'' },


    };

    Object.keys(settingsConfig).forEach(key => {
        settings[key] = GM_getValue(key, settingsConfig[key].default);
    });

    GM_registerMenuCommand('Custom Settings', openSettings);


    function openSettings() {
        if (document.querySelector('div#linuxdo-custom-setting')) {
            return;
        }
        const shadow = document.createElement('div');
        shadow.style = `position: fixed; top: 0%; left: 0%; z-index:8888; width:100vw; height:100vh; background: #6668;`;
        const panel = document.createElement('div');
        panel.style = `position: fixed; top: 45%; left: 50%; z-index:9999; transform: translate(-50%, -50%); background: white; padding:15px 25px; border: 1px solid #ccc; color:#000;`;
        panel.id = "linuxdo-custom-setting"
        let html = `
            <style type="text/css">
              :scope label {color:#666; font-size:16px; display:flex; justify-content:space-between; align-items:center; margin-top:25px;}
              :scope label span {color:#6bc; font-size:12px; font-weight:normal; padding:0 6px; margin-right:auto;}
              :scope label input[type=text] {width:300px; padding:1px; margin:0 5px 0 0; font-size:14px;}
              :scope label input[type=number] {width:60px; padding:1px; margin:0 5px; text-align:center;}
              :scope label input[type=checkbox] {background:pink; margin:0 5px;}
              :scope label input[disabled] {background: #CCC;}
              :scope label button {user-select: none; color: #333; padding: 2px 10px; margin-top:10px; border-radius:5px;}
            </style>
            <h2 style="text-align:center; margin-top:.5rem;">———— Settings ————</h2>
            `;
        Object.keys(settingsConfig).forEach(key => {
            const cfg = settingsConfig[key];
            const val = settings[key];
            const checked = cfg.type === 'checkbox' && val ? 'checked' : '';
            const disabled = cfg.dependsOn && !settings[cfg.dependsOn] ? 'disabled' : '';
            html += `<label style="${cfg.style}">${cfg.label}<span>${cfg.info}</span><input type="${cfg.type}" id="ujs_set_${key}" value="${val}" ${checked} ${disabled} ></label>`;
        });
        html += `<label><button id="ld_userjs_apply" style="font-weight: bold; background:#ACE">保存并刷新</button>
           <span></span><button id="ld_userjs_save">仅保存</button>
           <span></span><button id="ld_userjs_reset">重置</button>
           <span></span><button id="ld_userjs_close">取消</button></label>`;
        panel.innerHTML = html;

        document.body.append(shadow, panel);

        Object.keys(settingsConfig).forEach(key => {
            if (settingsConfig[key].dependsOn) {
                document.getElementById(`ujs_set_${settingsConfig[key].dependsOn}`).addEventListener('change', updateDependencies);
            }
        });

        function updateDependencies() {
            Object.keys(settingsConfig).forEach(key => {
                if (settingsConfig[key].dependsOn) {
                    document.getElementById(`ujs_set_${key}`).disabled = !document.getElementById(`ujs_set_${settingsConfig[key].dependsOn}`).checked;
                }
            });
        }

        document.querySelector('button#ld_userjs_save').addEventListener('click', () => {
            Object.keys(settingsConfig).forEach(key => {
                const element = document.getElementById(`ujs_set_${key}`);
                settings[key] = element.type === 'checkbox' ? element.checked : element.value;
                GM_setValue(key, settings[key]);
            });
            alert('Settings saved!');
            panel.remove();
        });

        document.querySelector('button#ld_userjs_apply').addEventListener('click', () => {
            Object.keys(settingsConfig).forEach(key => {
                const element = document.getElementById(`ujs_set_${key}`);
                settings[key] = element.type === 'checkbox' ? element.checked : element.value;
                GM_setValue(key, settings[key]);
            });
            window.location.reload();
        });

        document.querySelector('button#ld_userjs_reset').addEventListener('click', () => {
            Object.keys(settingsConfig).forEach(key => {
                GM_deleteValue(key);
            });
            window.location.reload();
        });

        function setting_hide() {
            panel.remove();
            shadow.remove();
        }


        document.querySelector('button#ld_userjs_close').addEventListener('click', () => setting_hide());

        shadow.onclick = () => setting_hide();

        updateDependencies();
    }

    // Function 1: Custom Logo
    if (settings.icon_custom) {
        GM_addStyle(`
            #site-logo {
                object-fit: scale-down;
                object-position: -999vw;
                background-size: cover;
                background-repeat: no-repeat;
                background-image: url('${settings.icon_main}');
                opacity: 1;
                transition: opacity 0.5s ease;
            }
            #site-logo.logo-big {
                background-image: url('${settings.icon_wide}');
            }
            #site-logo.logo-mobile {
                background-image: url('${settings.icon_wide}');
            }
            #site-logo:hover {
                object-position: unset;
                background-image: none;
            }
        `);

        function replaceIcon() {
            document.querySelector('link[rel="icon"]').href = settings.icon_main;
        }

        const observer = new MutationObserver(replaceIcon);
        observer.observe(document.head, { childList: true, subtree: true });

        replaceIcon();
    }

    // Function 2: Click Counts Visualization
    if (settings.cnts_colorful) {
        (function countsColorful() {
            const badges = document.querySelectorAll("span.badge.badge-notification.clicks");
            let values = Array.from(badges, badge => parseInt(badge.title || badge.textContent));
            let maxValue = Math.max(...values);
            let minValue = Math.min(...values);
            if (maxValue < 100 || (maxValue - minValue < 10)) maxValue = maxValue * 1.5;
            badges.forEach(badge => {
                if (!badge.style.backgroundColor) {
                    const number = parseInt(badge.title || badge.textContent);
                    const hue = 180 - (number / maxValue) * 180;
                    badge.style.backgroundColor = `hsl(${hue}, 50%, 50%)`;
                    badge.style.color = "#fff";
                    const sl = document.createElement('span');
                    sl.style = `height: 1em; display: inline-block; float: right; background: hsl(${hue}, 50%, 50%); width: ${100 * (number / maxValue)}px;`;
                    badge.after(sl);
                }
            });
            setTimeout(countsColorful, 1500);
        })();
    }

    // Function 3: Image Resize and Drag
    if (settings.image_view) {
        let sizePercent = 80;
        let isDragging = false;
        let startX, startY, initialX, initialY;

        function adjustSize(event) {
            let contentImg = document.querySelector('section#discourse-lightbox img');
            if (contentImg) {
                let delta = event.deltaY > 0 ? -10 : 10;
                sizePercent += delta;
                if (sizePercent > 300) sizePercent = 300;
                if (sizePercent < 5) sizePercent = 5;

                contentImg.style.width = sizePercent + '%';
                contentImg.style.maxWidth = sizePercent + '%';
                contentImg.style.maxHeight = sizePercent + '200%';
            }
        }

        function startDrag(event) {
            let contentImg = document.querySelector('section#discourse-lightbox img');
            if (contentImg) {
                isDragging = true;
                startX = event.clientX;
                startY = event.clientY;
                initialX = contentImg.offsetLeft;
                initialY = contentImg.offsetTop;
                event.preventDefault();
            }
        }

        function drag(event) {
            if (isDragging) {
                let contentImg = document.querySelector('section#discourse-lightbox img');
                if (contentImg) {
                    let dx = event.clientX - startX;
                    let dy = event.clientY - startY;
                    contentImg.style.left = (initialX + dx) + 'px';
                    contentImg.style.top = (initialY + dy) + 'px';
                }
            }
        }

        function stopDrag(event) {
            isDragging = false;
        }

        let observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    let contentImg = document.querySelector('section#discourse-lightbox img');
                    if (contentImg) {
                        document.querySelector('section#discourse-lightbox').onwheel = adjustSize;
                        contentImg.onmousedown = startDrag;
                        contentImg.onmouseup = stopDrag;
                        contentImg.onmousemove = drag;
                        contentImg.style.cursor = "move";

                        function stopClickEvent(event) {
                            event.stopImmediatePropagation();
                            event.preventDefault();
                        }
                        contentImg.addEventListener('click', stopClickEvent, true);
                    }
                });
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });

    }

    // Function 4: Quick Bookmark
    if (settings.quick_mark) {
        const starSvg = `<svg class="svg-icon" aria-hidden="true" style="text-indent: 1px; transform: scale(1); width:18px; height:18px;">
             <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
             <path d="M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z"></path></svg></svg> `;
        let markMap = new Map();

        function handleResponse(xhr, successCallback, errorCallback) {
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        successCallback(xhr);
                    } else {
                        errorCallback(xhr);
                    }
                }
            };
        }

        function deleteStarMark(mark_btn, data_id) {
            if (markMap.has(data_id)) {
                const mark_id = markMap.get(data_id);
                var xhr = new XMLHttpRequest();
                xhr.open('DELETE', `/bookmarks/${mark_id}`, true);
                xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');
                xhr.setRequestHeader("x-csrf-token", document.head.querySelector("meta[name=csrf-token]")?.content);

                handleResponse(xhr, (xhr) => {
                    mark_btn.style.color = '#777';
                    mark_btn.title = "收藏";
                    mark_btn.onclick = () => addStarMark(mark_btn, data_id);
                }, (xhr) => {
                    alert('删除失败!' + xhr.statusText + "\n" + TryParseJson(xhr.responseText));
                });

                xhr.send();
            }
        }

        function TryParseJson(str) {
            try {
                const jsonObj = JSON.parse(str);
                return JSON.stringify(jsonObj, null, 1);
            } catch (error) {
                return str;
            }
        }

        function addStarMark(mark_btn, data_id) {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/bookmarks', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
            xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');
            xhr.setRequestHeader('discourse-logged-in', ' true');
            xhr.setRequestHeader('discourse-present', ' true');
            xhr.setRequestHeader("x-csrf-token", document.head.querySelector("meta[name=csrf-token]")?.content);
            const postData = `name=%E6%94%B6%E8%97%8F&auto_delete_preference=3&bookmarkable_id=${data_id}&bookmarkable_type=Post`;

            handleResponse(xhr, (xhr) => {
                mark_btn.style.color = '#fdd459';
                mark_btn.title = "删除收藏";
                mark_btn.onclick = () => deleteStarMark(mark_btn, data_id);
            }, (xhr) => {
                alert('收藏失败!' + xhr.statusText + "\n" + TryParseJson(xhr.responseText));
            });

            xhr.send(postData);
        }

        function addMarkBtn() {
            let articles = document.querySelectorAll("article[data-post-id]");
            if (articles.length <= 0) return;

            articles.forEach(article => {
                const target = article.querySelector("div.topic-body.clearfix > div.regular.contents > section > nav > div.actions");
                if (target && !article.querySelector("div.topic-body.clearfix > div.regular.contents > section > nav > span.star-bookmark")) {
                    const dataPostId = article.getAttribute('data-post-id');
                    const starButton = document.createElement('span');

                    starButton.innerHTML = starSvg;
                    starButton.className = "star-bookmark";
                    starButton.style.cursor = 'pointer';
                    starButton.style.margin = '0px 12px';

                    if (markMap.has(dataPostId)) {
                        starButton.style.color = '#fdd459';
                        starButton.title = "删除收藏";
                        starButton.onclick = () => deleteStarMark(starButton, dataPostId);
                    } else {
                        starButton.style.color = '#777';
                        starButton.title = "收藏";
                        starButton.onclick = () => addStarMark(starButton, dataPostId);
                    }
                    target.after(starButton);
                }
            });
        }

        function getStarMark() {
            let articles = document.querySelectorAll("article[data-post-id]");
            if (articles.length <= 0) return;

            const currentUserElement = document.querySelector('#current-user button');
            const currentUsername = currentUserElement ? currentUserElement.getAttribute('href').replace('/u/', '') : null;

            const xhr = new XMLHttpRequest();
            xhr.open('GET', `/u/${currentUsername}/user-menu-bookmarks`, true);
            xhr.setRequestHeader("x-csrf-token", document.head.querySelector("meta[name=csrf-token]")?.content);

            handleResponse(xhr, (xhr) => {
                var response = JSON.parse(xhr.responseText);
                response.bookmarks.forEach(mark => {
                    markMap.set(mark.bookmarkable_id.toString(), mark.id.toString());
                });
                addMarkBtn();
            }, (xhr) => {
                console.error('GET请求失败:', xhr.statusText);
            });

            xhr.send();
        }

        let lastUpdateMarkTime = 0;
        let lastUpdateButnTime = 0;
        function mutationCallback() {
            const currentTime = Date.now();
            if (currentTime - lastUpdateMarkTime > 9000) {
                setTimeout(getStarMark, 500);
                lastUpdateMarkTime = currentTime;
            }
            if (currentTime - lastUpdateButnTime > 1000) {
                setTimeout(addMarkBtn, 500);
                lastUpdateButnTime = currentTime;
            }
        }

        const mainNode = document.querySelector("#main-outlet");
        if (mainNode) {
            const observer = new MutationObserver(mutationCallback);
            observer.observe(mainNode, { childList: true, subtree: true });
        }

        getStarMark();
    }

    // Function 5: mini article image show
    if (settings.image_mini) {
        let _H = parseInt(settings.image_mini_H);
        let _W = parseInt(settings.image_mini_W);//  transition: max-width 0.5s ease-in-out, max-height 0.5s ease-in-out;

        GM_addStyle(`
        article div.topic-body div.regular.contents img:not(.thumbnail):not(.ytp-thumbnail-image):not(.emoji) {
           max-width : ${_W}px;
           max-height : ${_H}px;
           object-fit: contain;
        }
        `)

        var imageMiniTimer = setInterval(function() {
            var images = document.querySelectorAll('article div.topic-body div.regular.contents img:not(.thumbnail):not(.ytp-thumbnail-image):not(.emoji)');

            if (images.length >= 1) {

                for (var i = 0; i < images.length; i++) {
                    let img = images[i];
                    let image_src = null;
                    let src_height = null;

                    let urls = img.getAttribute('srcset')
                    if (urls) {
                        urls = urls.match(/https:\/\/[^,\s]+/g);
                        image_src = urls[urls.length - 1];
                    }else{
                        image_src = img.src;
                    }

                    src_height = img.naturalHeight || img.height;

                    if (img.parentElement.matches('a.lightbox')) {
                        img = img.parentElement;
                        if (!image_src) {
                            image_src = img.getAttribute('href');
                        }
                    }
                    //console.log(image_src)
                    img.image_src = image_src;
                    img.src_height = src_height;

                    let previewDiv = null;

                    if (document.getElementById('hover-preview-img') == null) {
                        previewDiv = document.createElement('div');
                        previewDiv.id = 'hover-preview-img';
                        previewDiv.style = 'position: fixed; z-index:999; top:-10px; max-width: 0px; max-height 0px; opacity: 0; transition: max-width 0.3s ease-in-out, max-height 0.3s ease-in-out, left 0.3s ease-in-out , opacity 0.3s ease-in-out , top 0.3s ease-in-out;'; // display:none;
                        document.body.appendChild(previewDiv);
                        let fullSizeImg = document.createElement('img');
                        fullSizeImg.className = 'full-size-image';
                        previewDiv.appendChild(fullSizeImg);
                    } else {
                        previewDiv = document.getElementById('hover-preview-img');
                    }

                    img.addEventListener('mouseenter', function(event) {
                        let previewDiv = document.getElementById('hover-preview-img');
                        let fullSizeImg = previewDiv.querySelector('.full-size-image');
                        previewDiv.style.display = 'block';
                        previewDiv.style.background="#FFFE";
                        previewDiv.style.boxShadow="1px 1px 5px #555";
                        previewDiv.style.padding="0px";

                        previewDiv.style.left= event.clientX + 20 + 'px';
                        previewDiv.style.maxWidth = '99vw';
                        previewDiv.style.maxHeight = '99vh';

                        this.title="";
                        fullSizeImg.src = this.image_src;
                        fullSizeImg.style.width = '';
                        fullSizeImg.style.height = '';
                        fullSizeImg.style.maxWidth = '100%';
                        fullSizeImg.style.maxHeight = '100%';
                        previewDiv.style.top = event.clientY - this.src_height/2 + 'px';
                        previewDiv.style.opacity = 1;

                        fullSizeImg.onload = function() {
                            console.log(previewDiv.offsetTop , fullSizeImg.naturalHeight , window.innerHeight);
                            if (previewDiv.offsetTop + fullSizeImg.naturalHeight > window.innerHeight - 5) {
                                previewDiv.style.top = window.innerHeight - 5 - fullSizeImg.naturalHeight + 'px';
                            }
                        };

                    });

                    img.addEventListener('mouseleave', function() {
                        let previewDiv = document.getElementById('hover-preview-img');
                        previewDiv.style.top = "-10px";
                        previewDiv.style.maxWidth = "0px";
                        previewDiv.style.maxHeight = "0px";
                        previewDiv.style.opacity = 0;
                    });
                }
            }
        }, 1000);
    }

    // Function 6: remove spoiler blurred
    if (settings.spoiler_noblur) {
        GM_addStyle(`
        .spoiler-blurred {
          filter: drop-shadow(0px 0px 3px #BBB)!important;
        }

        .spoiler-blurred img {
          filter: drop-shadow(0px 0px 3px #BBB)!important;
        }

        `)
    }

    // Function 7: details open
    if (settings.details_open) {
        function open_detail() {
            let details = document.querySelectorAll("article details");
            details.forEach(detail => {
                if (detail.opened != true) {
                    detail.open = true;
                    detail.opened = true;
                }
            });
            setTimeout(open_detail, 990);
        }
        setTimeout(open_detail, 900);
    }

    // Function 8: wider page
    if (settings.wider_page) {
        GM_addStyle(`
        #main-outlet-wrapper {
          max-width: 100%!important;
        }
        body.has-sidebar-page header.d-header > div.wrap {
          max-width: 100%!important;
        }
        .topic-body {
          width: 100%!important;
        }
        `)
    }

    // Function 9: thin_header
    if (settings.thin_header) {
        GM_addStyle(`
        .d-header  {
          height: 2.5em !important;
        }
        .d-header .extra-info-wrapper .title-wrapper {
          display: flex;
          flex-direction: row;
        }
        .d-header div.title-wrapper > h1.header-title {
          width: auto;
          font-size: large;
        }
        .d-header #site-logo {
           height: 2em !important;
        }
        .d-header .d-header-icons .icon img.avatar {
           height: 2em !important;
        }
        `)
    }

    // Function 10: topic contents scroll
    if (settings.topic_scroll) {

        GM_addStyle(`
        article div.topic-body .regular.contents .cooked {
            max-height: 60vh;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: #aaaa #1111;
        }
        article div.topic-body .regular.contents .cooked ::-webkit-scrollbar-track {
            background: #1111;
        }
        article div.topic-body .regular.contents .cooked ::-webkit-scrollbar-thumb {
            background: #aaaa;
        }
        article div.topic-body .regular.contents .cooked ::-webkit-scrollbar-thumb:hover {
            background: #0008;
        }
        `)
    }

    // Function 11: order by Created
    if (settings.order_created) {

        function orderByCreated() {
            console.log("order")
            const a_new = document.querySelector("ul#navigation-bar > li.new.ember-view.nav-item_new > a");
            if ( a_new && a_new.href.endsWith("/new")) {
                console.log("order_created")
                a_new.parentNode.title = "按最新创建排序";
                a_new.href = a_new.href.replace("/new","/latest?order=created");
                a_new.style.filter="drop-shadow(0px 0px 1px var(--quaternary))"; // #8FF8

            }
            setTimeout(orderByCreated, 990);
        }
        setTimeout(orderByCreated, 900);
    }

})();