Userscript+ : 顯示當前網站所有可用的UserJS腳本 Jaeger

顯示當前網站的所有可用UserJS(Tampermonkey)腳本,一键安装,修复了BUG。

// ==UserScript==
// @name         Userscript+ : 显示当前网站所有可用的UserJS脚本,Show Site All UserJS
// @name:zh      Userscript+ : 显示当前网站所有可用的UserJS脚本 Jaeger
// @name:zh-CN   Userscript+ : 显示当前网站所有可用的UserJS脚本 Jaeger
// @name:zh-TW   Userscript+ : 顯示當前網站所有可用的UserJS腳本 Jaeger
// @name:ja   Userscript +:現在のサイトの利用可能なすべてのUserJSスクリプトを表示するJaeger
// @name:ru-RU   Userscript+ : Показать пользовательские скрипты (UserJS) для сайта. Jaeger
// @name:ru   Userscript+ : Показать пользовательские скрипты (UserJS) для сайта. Jaeger
// @namespace    https://github.com/jae-jae/Userscript-Plus
// @version      2.7.2
// @description         显示当前网站的所有可用UserJS(Tampermonkey)脚本,,一键安装,修复了BUG。Show current site all UserJS,The easier way to install UserJs for Tampermonkey.
// @description:zh      显示当前网站的所有可用UserJS(Tampermonkey)脚本,一键安装,修复了BUG。
// @description:zh-CN   显示当前网站的所有可用UserJS(Tampermonkey)脚本,一键安装,修复了BUG。
// @description:zh-TW   顯示當前網站的所有可用UserJS(Tampermonkey)腳本,一键安装,修复了BUG。
// @description:ja   現在のサイトで利用可能なすべてのUserJS(Tampermonkey)スクリプトを表示します。
// @description:ru-RU   Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey.
// @description:ru   Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey.
// @author       Jaeger <[email protected]>
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3ggEBCQHM3fXsAAAAVdJREFUOMudkz2qwkAUhc/goBaGJBgUtBCZyj0ILkpwAW7Bws4yO3AHLiCtEFD8KVREkoiFxZzX5A2KGfN4F04zMN+ce+5c4LMUgDmANYBnrnV+plBSi+FwyHq9TgA2LQpvCiEiABwMBtzv95RSfoNEHy8DYBzHrNVqVEr9BWKcqNFoxF6vx3a7zc1mYyC73a4MogBg7vs+z+czO50OW60Wt9stK5UKp9Mpj8cjq9WqDTBHnjAdxzGQZrPJw+HA31oulzbAWgLoA0CWZVBKIY5jzGYzdLtdE9DlcrFNrY98zobqOA6TJKHW2jg4nU5sNBpFDp6mhVe5rsvVasUwDHm9Xqm15u12o+/7Hy0gD8KatOd5vN/v1FozTVN6nkchxFuI6hsAAIMg4OPxMJCXdtTbR7JJCMEgCJhlGUlyPB4XfumozInrupxMJpRSRtZlKoNYl+m/6/wDuWAjtPfsQuwAAAAASUVORK5CYII=
// @include        *
// @require     https://code.jquery.com/jquery-3.6.0.min.js
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/psl.min.js
// @resource     count  https://greasyfork.org/scripts/by-site.json
// @grant        GM_xmlhttpRequest
// @grant        GM_getResourceText
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// @noframes
// @connect     cdn.bootcss.com
// @connect     raw.githubusercontent.com
// @connect     gist.githubusercontent.com
// @connect     cdnjs.cloudflare.com
// @connect     greasyfork.org
// @connect     cdn.jsdelivr.net
// @run-at      document-end
// ==/UserScript==

unsafeWindow.GmAjax = GM_xmlhttpRequest;

(function() {

    'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var FetchUserjs = function () {
    function FetchUserjs() {
        _classCallCheck(this, FetchUserjs);

        this.host = this.getMainHost();
        this.showTime = 10;
        this.quietKey = 'jae_fetch_userjs_quiet';
        this.countKey = 'jae_fetch_userjs_count';
        this.settingsKey = 'jae_fetch_userjs_settings';
        this.isVisible = false;
        this.currentScriptList = []; // 存储当前的脚本列表
        this.settings = this.loadSettings();
        this.tplBox = '<div id="jae_userscript_box"><style>.jae-userscript{position:fixed;width:500px;top:20px;right:20px;z-index:9999999999;height:auto;background:#fff;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.15);padding:15px;border: 2px solid #4CAF50;}.jae-userscript-shadow{box-shadow:0 1px 4px rgba(0,0,0,.3), 0px 0 20px rgba(0,0,0,.1) inset}.jae-userscript-shadow::before,.jae-userscript-shadow::after{content:"";position:absolute;z-index:-1}.jae-userscript-shadow::before,.jae-userscript-shadow::after{content:"";position:absolute;z-index:-1;bottom:15px;left:10px;width:50%;height:20%}.jae-userscript-shadow::before,.jae-userscript-shadow::after{content:"";position:absolute;z-index:-1;bottom:15px;left:10px;width:50%;height:20%;box-shadow:0 15px 10px rgba(0,0,0,.7);transform:rotate(-3deg)}.jae-userscript-shadow::after{right:10px;left:auto;transform:rotate(3deg)}.script-list{max-height:450px;overflow-y:auto;padding:10px}.script-item{padding:15px;margin:10px 0;border:1px solid #e0e0e0;border-radius:6px;background:#fafafa;transition:all 0.3s;}.script-item:hover{background-color:#f0f8ff;border-color:#4CAF50;box-shadow:0 2px 8px rgba(76,175,80,0.2);}.script-item:last-child{border-bottom:1px solid #e0e0e0}.script-name{font-weight:bold;color:#333;margin-bottom:8px;font-size:15px;line-height:1.4;}.script-desc{font-size:13px;color:#666;margin-bottom:10px;line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}.script-meta{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;font-size:12px;}.script-meta-item{background:#e8f5e8;color:#2e7d32;padding:3px 8px;border-radius:12px;}.script-meta-item.installs{background:#e3f2fd;color:#1565c0;}.script-meta-item.rating{background:#fff3e0;color:#f57c00;}.script-meta-item.updated{background:#f3e5f5;color:#7b1fa2;}.script-actions{display:flex;gap:8px;}.script-install{display:inline-block;padding:8px 12px;background:#4CAF50;color:white;text-decoration:none;border-radius:4px;font-size:13px;transition:background-color 0.3s;text-align:center;flex:1;}.script-install:hover{background:#45a049;color:white;text-decoration:none;}.script-install.secondary{background:#2196F3;}.script-install.secondary:hover{background:#1976D2;}.close-btn{position:absolute;top:10px;right:10px;background:#f0f0f0;border:none;border-radius:50%;width:24px;height:24px;font-size:16px;cursor:pointer;transition:background-color 0.3s;}.close-btn:hover{background:#e0e0e0;}.sort-controls{padding:10px;background:#f8f9fa;border-bottom:1px solid #eee;display:flex;align-items:center;gap:10px;flex-wrap:wrap;}.sort-label{font-size:13px;color:#666;font-weight:bold;}.sort-select{padding:5px 8px;border:1px solid #ddd;border-radius:4px;font-size:12px;background:white;cursor:pointer;transition:border-color 0.3s;}.sort-select:hover{border-color:#4CAF50;}.sort-select:focus{outline:none;border-color:#4CAF50;box-shadow:0 0 0 2px rgba(76,175,80,0.2);}</style><div class="jae-userscript"><div class="script-header" style="padding:10px;background:#f8f9fa;border-bottom:1px solid #eee;position:relative;"><div style="font-weight:bold;font-size:16px;margin-bottom:5px;">可匹配的脚本</div><div style="font-size:12px;color:#666;">当前网站可用脚本: <span id="script-count">0</span> 个</div><div style="font-size:12px;color:#666;">网站: ' + this.host + '</div><button class="close-btn" id="close-script-box">&times;</button></div><div class="sort-controls"><span class="sort-label">排序方式:</span><select class="sort-select" id="sort-method"><option value="default">默认排序</option><option value="installs_desc">安装量(高到低)</option><option value="installs_asc">安装量(低到高)</option><option value="daily_desc">今日安装(高到低)</option><option value="updated_desc">更新时间(最新)</option><option value="updated_asc">更新时间(最旧)</option><option value="rating_desc">好评率(高到低)</option><option value="name_asc">名称(A-Z)</option></select></div><div class="script-list"></div></div></div>';
        this.tplFloatBtn = '<div id="jae_userscript_float_btn" style="position:fixed;top:50%;right:0;z-index:9999999998;width:40px;height:80px;background:#4CAF50;color:white;border:none;border-radius:8px 0 0 8px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.2);transition:all 0.3s;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;writing-mode:vertical-lr;" title="显示UserJS脚本">UserJS</div><div id="jae_settings_btn" style="position:fixed;top:calc(50% + 90px);right:0;z-index:9999999998;width:40px;height:40px;background:#FF9800;color:white;border:none;border-radius:8px 0 0 8px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.2);transition:all 0.3s;display:flex;align-items:center;justify-content:center;font-size:16px;" title="设置">⚙️</div>';
        this.tplSettings = '<div id="jae_settings_box" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:9999999999;width:400px;background:#fff;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,0.3);padding:20px;border:2px solid #FF9800;"><div style="font-weight:bold;font-size:18px;margin-bottom:15px;color:#333;text-align:center;">UserJS+ 设置</div><div style="margin-bottom:20px;"><label style="display:flex;align-items:center;cursor:pointer;padding:10px;border-radius:4px;transition:background-color 0.3s;" onmouseover="this.style.backgroundColor=\'#f5f5f5\'" onmouseout="this.style.backgroundColor=\'transparent\'"><input type="checkbox" id="auto-show-setting" style="margin-right:10px;transform:scale(1.2);"><div><div style="font-weight:bold;color:#333;margin-bottom:3px;">自动显示脚本浮窗</div><div style="font-size:12px;color:#666;">在有可用脚本的网站上自动显示浮窗</div></div></label></div><div style="display:flex;gap:10px;justify-content:center;"><button id="save-settings" style="padding:8px 20px;background:#4CAF50;color:white;border:none;border-radius:4px;cursor:pointer;font-weight:bold;transition:background-color 0.3s;" onmouseover="this.style.backgroundColor=\'#45a049\'" onmouseout="this.style.backgroundColor=\'#4CAF50\'">保存</button><button id="cancel-settings" style="padding:8px 20px;background:#f44336;color:white;border:none;border-radius:4px;cursor:pointer;font-weight:bold;transition:background-color 0.3s;margin-left:10px;" onmouseover="this.style.backgroundColor=\'#da190b\'" onmouseout="this.style.backgroundColor=\'#f44336\'">取消</button></div></div>';
    }

    _createClass(FetchUserjs, [{
        key: 'getMainHost',
        value: function getMainHost() {
            var host = window.location.hostname;
            try {
                // 检查psl是否可用
                if (typeof psl !== 'undefined' && psl && typeof psl.get === 'function') {
                    return psl.get(host) || host.split('.').splice(-2).join('.');
                } else {
                    // 如果psl不可用,使用备用方法
                    return host.split('.').splice(-2).join('.');
                }
            } catch (e) {
                // 发生错误时使用备用方法
                return host.split('.').splice(-2).join('.');
            }
        }
    }, {
        key: 'getCountData',
        value: function getCountData(host) {
            try {
                var countData = GM_getResourceText('count');
                if (!countData) {
                    console.warn('无法获取脚本数量数据');
                    return 0;
                }
                countData = JSON.parse(countData);
                var count = countData[host] || 0;
                sessionStorage.setItem(this.countKey, count);
                return count;
            } catch (e) {
                console.error('获取脚本数量数据失败:', e);
                return 0;
            }
        }
    }, {
        key: 'fetchScriptList',
        value: function fetchScriptList(host) {
            try {
                // 使用Greasy Fork API获取脚本列表
                var apiUrl = 'https://greasyfork.org/zh-CN/scripts/by-site/' + encodeURIComponent(host) + '.json';

                return new Promise(function(resolve, reject) {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: apiUrl,
                        headers: {
                            'User-Agent': 'Mozilla/5.0 (compatible; Userscript+)'
                        },
                        timeout: 10000,
                        onload: function(response) {
                            try {
                                if (response.status === 200) {
                                    var scriptList = JSON.parse(response.responseText);
                                    // 确保返回的是数组
                                    if (Array.isArray(scriptList)) {
                                        resolve(scriptList);
                                    } else {
                                        console.warn('API返回数据格式不正确');
                                        resolve([]);
                                    }
                                } else {
                                    console.error('获取脚本列表失败,状态码:', response.status);
                                    resolve([]);
                                }
                            } catch (e) {
                                console.error('解析脚本列表失败:', e);
                                resolve([]);
                            }
                        },
                        onerror: function(error) {
                            console.error('请求脚本列表失败:', error);
                            resolve([]);
                        },
                        ontimeout: function() {
                            console.error('请求脚本列表超时');
                            resolve([]);
                        }
                    });
                });
            } catch (e) {
                console.error('获取脚本列表异常:', e);
                return Promise.resolve([]);
            }
        }
    }, {
        key: 'formatDate',
        value: function formatDate(dateStr) {
            try {
                var date = new Date(dateStr);
                var now = new Date();
                var diffTime = Math.abs(now - date);
                var diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

                if (diffDays === 1) {
                    return '昨天';
                } else if (diffDays < 7) {
                    return diffDays + '天前';
                } else if (diffDays < 30) {
                    return Math.ceil(diffDays / 7) + '周前';
                } else if (diffDays < 365) {
                    return Math.ceil(diffDays / 30) + '个月前';
                } else {
                    return Math.ceil(diffDays / 365) + '年前';
                }
            } catch (e) {
                return dateStr;
            }
        }
    }, {
        key: 'formatNumber',
        value: function formatNumber(num) {
            if (!num) return '0';
            if (num >= 1000000) {
                return (num / 1000000).toFixed(1) + 'M';
            } else if (num >= 1000) {
                return (num / 1000).toFixed(1) + 'K';
            }
            return num.toString();
        }
    }, {
        key: 'loadSettings',
        value: function loadSettings() {
            try {
                var settings = localStorage.getItem(this.settingsKey);
                if (settings) {
                    return JSON.parse(settings);
                } else {
                    // 默认设置:不自动显示浮窗
                    return {
                        autoShow: false
                    };
                }
            } catch (e) {
                console.error('加载设置失败:', e);
                return {
                    autoShow: false
                };
            }
        }
    }, {
        key: 'saveSettings',
        value: function saveSettings(settings) {
            try {
                localStorage.setItem(this.settingsKey, JSON.stringify(settings));
                this.settings = settings;
                return true;
            } catch (e) {
                console.error('保存设置失败:', e);
                return false;
            }
        }
    }, {
        key: 'showSettings',
        value: function showSettings() {
            var _this = this;

            // 检查是否已存在设置窗口
            if (document.getElementById('jae_settings_box')) {
                return;
            }

            document.body.insertAdjacentHTML('beforeend', this.tplSettings);

            var settingsBox = document.getElementById('jae_settings_box');
            var autoShowCheckbox = document.getElementById('auto-show-setting');
            var saveBtn = document.getElementById('save-settings');
            var cancelBtn = document.getElementById('cancel-settings');

            // 设置当前值
            autoShowCheckbox.checked = this.settings.autoShow;

            // 保存设置
            saveBtn.addEventListener('click', function() {
                var newSettings = {
                    autoShow: autoShowCheckbox.checked
                };

                if (_this.saveSettings(newSettings)) {
                    alert('设置已保存!');
                    settingsBox.remove();
                } else {
                    alert('保存设置失败!');
                }
            });

            // 取消设置
            cancelBtn.addEventListener('click', function() {
                settingsBox.remove();
            });

            // 点击背景关闭
            settingsBox.addEventListener('click', function(e) {
                if (e.target === settingsBox) {
                    settingsBox.remove();
                }
            });
        }
    }, {
        key: 'sortScripts',
        value: function sortScripts(scriptList, sortMethod) {
            var sorted = scriptList.slice(); // 创建副本避免修改原数组

            switch (sortMethod) {
                case 'installs_desc':
                    sorted.sort(function(a, b) {
                        return (b.total_installs || 0) - (a.total_installs || 0);
                    });
                    break;
                case 'installs_asc':
                    sorted.sort(function(a, b) {
                        return (a.total_installs || 0) - (b.total_installs || 0);
                    });
                    break;
                case 'daily_desc':
                    sorted.sort(function(a, b) {
                        return (b.daily_installs || 0) - (a.daily_installs || 0);
                    });
                    break;
                case 'updated_desc':
                    sorted.sort(function(a, b) {
                        var dateA = new Date(a.code_updated_at || 0);
                        var dateB = new Date(b.code_updated_at || 0);
                        return dateB - dateA;
                    });
                    break;
                case 'updated_asc':
                    sorted.sort(function(a, b) {
                        var dateA = new Date(a.code_updated_at || 0);
                        var dateB = new Date(b.code_updated_at || 0);
                        return dateA - dateB;
                    });
                    break;
                case 'rating_desc':
                    sorted.sort(function(a, b) {
                        var ratingA = 0;
                        var ratingB = 0;

                        if (a.good_ratings && a.ok_ratings && a.bad_ratings) {
                            var totalA = a.good_ratings + a.ok_ratings + a.bad_ratings;
                            ratingA = totalA > 0 ? (a.good_ratings / totalA) * 100 : 0;
                        }

                        if (b.good_ratings && b.ok_ratings && b.bad_ratings) {
                            var totalB = b.good_ratings + b.ok_ratings + b.bad_ratings;
                            ratingB = totalB > 0 ? (b.good_ratings / totalB) * 100 : 0;
                        }

                        return ratingB - ratingA;
                    });
                    break;
                case 'name_asc':
                    sorted.sort(function(a, b) {
                        var nameA = (a.name || '').toLowerCase();
                        var nameB = (b.name || '').toLowerCase();
                        return nameA.localeCompare(nameB);
                    });
                    break;
                default:
                    // 默认排序,不做任何操作
                    break;
            }

            return sorted;
        }
    }, {
        key: 'renderScriptList',
        value: function renderScriptList(scriptList, container) {
            var _this = this;

            if (!scriptList || scriptList.length === 0) {
                container.innerHTML = '<div style="padding:20px;text-align:center;color:#666;">未找到可用的脚本</div>';
                return;
            }

            var listHtml = '';
            scriptList.forEach(function(script) {
                listHtml += '<div class="script-item">';

                // 脚本名称
                listHtml += '<div class="script-name">' + (script.name || '未知脚本').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</div>';

                // 脚本描述
                listHtml += '<div class="script-desc">' + (script.description || '无描述').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</div>';

                // 脚本元信息
                listHtml += '<div class="script-meta">';

                // 总安装量
                if (script.total_installs) {
                    listHtml += '<span class="script-meta-item installs">总安装: ' + _this.formatNumber(script.total_installs) + '</span>';
                }

                // 今日安装量
                if (script.daily_installs) {
                    listHtml += '<span class="script-meta-item installs">今日: ' + _this.formatNumber(script.daily_installs) + '</span>';
                }

                // 评分
                if (script.good_ratings && script.ok_ratings && script.bad_ratings) {
                    var totalRatings = script.good_ratings + script.ok_ratings + script.bad_ratings;
                    var goodPercent = Math.round((script.good_ratings / totalRatings) * 100);
                    listHtml += '<span class="script-meta-item rating">好评: ' + goodPercent + '%</span>';
                }

                // 更新时间
                if (script.code_updated_at) {
                    listHtml += '<span class="script-meta-item updated">更新: ' + _this.formatDate(script.code_updated_at) + '</span>';
                }

                // 版本信息
                if (script.version) {
                    listHtml += '<span class="script-meta-item">v' + script.version + '</span>';
                }

                // 作者信息
                if (script.users && script.users.length > 0) {
                    listHtml += '<span class="script-meta-item">作者: ' + script.users[0].name + '</span>';
                }

                listHtml += '</div>';

                // 操作按钮
                listHtml += '<div class="script-actions">';
                listHtml += '<a href="https://greasyfork.org/zh-CN/scripts/' + script.id + '" target="_blank" class="script-install secondary">查看详情</a>';
                if (script.code_url) {
                    listHtml += '<a href="' + script.code_url + '" target="_blank" class="script-install">安装脚本</a>';
                }
                listHtml += '</div>';

                listHtml += '</div>';
            });

            container.innerHTML = listHtml;
        }
    }, {
        key: 'setSize',
        value: function setSize(w, h) {
            var element = document.querySelector('.jae-userscript');
            if (element) {
                element.style.width = w + 'px';
                element.style.height = h + 'px';
            }
        }
    }, {
        key: 'addEventListener',
        value: function addEventListener(eventName, handler) {
            document.getElementById('jae_userscript_box').addEventListener(eventName, handler);
        }
    }, {
        key: 'bindEvent',
        value: function bindEvent() {
            var _this = this;

            // 移除自动消失功能,改为手动关闭
            // this.timeId = setTimeout(function () {
            //     var box = document.getElementById('jae_userscript_box');
            //     if (box) {
            //         box.remove();
            //     }
            // }, this.showTime * 1000);

            this.addEventListener('max', function () {
                _this.setSize(860, 492);
                var element = document.querySelector('.jae-userscript');
                if (element) {
                    element.classList.add('jae-userscript-shadow');
                }
                // clearTimeout(_this.timeId);
            });

            this.addEventListener('min', function () {
                setTimeout(function () {
                    var element = document.querySelector('.jae-userscript');
                    if (element) {
                        element.classList.remove('jae-userscript-shadow');
                    }
                    _this.setSize(370, 56);
                }, 500);
            });

            this.addEventListener('close', function () {
                var box = document.getElementById('jae_userscript_box');
                if (box) {
                    box.style.display = 'none';
                    _this.isVisible = false;
                }
            });

            this.addEventListener('loading', function () {
                // clearTimeout(_this.timeId);
            });
        }
    }, {
        key: 'createFloatButton',
        value: function createFloatButton() {
            var _this = this;

            // 检查是否已存在浮动按钮
            if (document.getElementById('jae_userscript_float_btn')) {
                return;
            }

            document.body.insertAdjacentHTML('beforeend', this.tplFloatBtn);

            var floatBtn = document.getElementById('jae_userscript_float_btn');
            var settingsBtn = document.getElementById('jae_settings_btn');

            if (floatBtn) {
                floatBtn.addEventListener('click', function() {
                    _this.showScriptBox();
                });

                // 鼠标悬停效果
                floatBtn.addEventListener('mouseenter', function() {
                    this.style.backgroundColor = '#45a049';
                    this.style.transform = 'translateX(-5px)';
                });

                floatBtn.addEventListener('mouseleave', function() {
                    this.style.backgroundColor = '#4CAF50';
                    this.style.transform = 'translateX(0)';
                });
            }

            if (settingsBtn) {
                settingsBtn.addEventListener('click', function() {
                    _this.showSettings();
                });

                // 鼠标悬停效果
                settingsBtn.addEventListener('mouseenter', function() {
                    this.style.backgroundColor = '#F57C00';
                    this.style.transform = 'translateX(-5px)';
                });

                settingsBtn.addEventListener('mouseleave', function() {
                    this.style.backgroundColor = '#FF9800';
                    this.style.transform = 'translateX(0)';
                });
            }
        }
    }, {
        key: 'showScriptBox',
        value: function showScriptBox() {
            var box = document.getElementById('jae_userscript_box');
            if (box) {
                box.style.display = 'block';
                this.isVisible = true;
            } else {
                this.renderScriptBox();
            }
        }
    }, {
        key: 'execFrameJs',
        value: function execFrameJs(frameWindow) {
            // 此方法已移除,因为不再使用外部UI框架
            console.log('execFrameJs method deprecated');
        }
    }, {
        key: 'render',
        value: function render() {
            var _this = this;

            // 始终创建浮动按钮
            this.createFloatButton();

            // 根据设置决定是否自动显示浮窗
            if (this.settings.autoShow && !this.isQuiet) {
                var count = this.getCountData(this.host);
                if (count > 0) {
                    this.renderScriptBox();
                }
            }
        }
    }, {
        key: 'renderScriptBox',
        value: function renderScriptBox() {
            var _this = this;

            // 如果已存在则直接显示
            var existingBox = document.getElementById('jae_userscript_box');
            if (existingBox) {
                existingBox.style.display = 'block';
                this.isVisible = true;
                return;
            }

            document.body.insertAdjacentHTML('beforeend', this.tplBox);
            this.isVisible = true;

            // 更新脚本计数
            var count = this.getCountData(this.host);
            document.getElementById('script-count').textContent = count;

            // 获取并显示脚本列表
            var dom = document.getElementById('jae_userscript_box');
            var listContainer = dom.querySelector('.script-list');
            var sortSelect = dom.querySelector('#sort-method');
            listContainer.innerHTML = '加载中...';

            this.fetchScriptList(this.host).then(function(scriptList) {
                if (scriptList && scriptList.length > 0) {
                    // 更新实际脚本数量并存储脚本列表
                    document.getElementById('script-count').textContent = scriptList.length;
                    _this.currentScriptList = scriptList;

                    // 初始渲染(默认排序)
                    _this.renderScriptList(scriptList, listContainer);
                } else {
                    document.getElementById('script-count').textContent = '0';
                    _this.currentScriptList = [];
                    listContainer.innerHTML = '<div style="padding:20px;text-align:center;color:#666;">未找到可用的脚本</div>';
                }
            }).catch(function(error) {
                console.error('获取脚本列表失败:', error);
                listContainer.innerHTML = '<div style="padding:20px;text-align:center;color:#666;">获取脚本列表失败</div>';
            });

            // 添加排序选择器事件
            sortSelect.addEventListener('change', function() {
                var sortMethod = this.value;
                if (_this.currentScriptList.length > 0) {
                    var sortedList = _this.sortScripts(_this.currentScriptList, sortMethod);
                    _this.renderScriptList(sortedList, listContainer);
                }
            });

            // 添加关闭按钮事件
            document.getElementById('close-script-box').addEventListener('click', function() {
                _this.isVisible = false;
                document.getElementById('jae_userscript_box').style.display = 'none';
            });

            this.bindEvent();
        }
    }, {
        key: 'isQuiet',
        get: function get() {
            var quiet = sessionStorage.getItem(this.quietKey);
            return quiet ? true : false;
        }
    }]);

    return FetchUserjs;
}();

// 确保页面加载完成后执行
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        var fu = new FetchUserjs();
        fu.render();
    });
} else {
    var fu = new FetchUserjs();
    fu.render();
}

})();