// ==UserScript== // @name My Userscript : 显示当前网站所有可用的UserJS脚本,Show Site All UserJS // @name:zh My Userscript : 显示当前网站所有可用的UserJS脚本 // @name:zh-CN My Userscript : 显示当前网站所有可用的UserJS脚本 // @name:zh-TW My Userscript : 顯示當前網站所有可用的UserJS腳本 // @name:ja My Userscript:現在のサイトの利用可能なすべてのUserJSスクリプトを表示する // @name:ru-RU My Userscript : Показать пользовательские скрипты (UserJS) для сайта. // @name:ru My Userscript : Показать пользовательские скрипты (UserJS) для сайта. // @namespace https://github.com/jae-jae/Userscript-Plus // @version 3.0 // @description 显示当前网站的所有可用UserJS(Tampermonkey)脚本,一键安装。Show current site all UserJS,The easier way to install UserJs for Tampermonkey. // @description:zh 显示当前网站的所有可用UserJS(Tampermonkey)脚本,一键安装。 // @description:zh-CN 显示当前网站的所有可用UserJS(Tampermonkey)脚本,一键安装。 // @description:zh-TW 顯示當前網站的所有可用UserJS(Tampermonkey)腳本,一键安装。 // @description:ja 現在のサイトで利用可能なすべてのUserJS(Tampermonkey)スクリプトを表示します。 // @description:ru-RU Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey. // @description:ru Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey. // @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/psl@1.9.0/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 // @downloadURL https://update.greasyfork.icu/scripts/548869/My%20Userscript%20%3A%20%E6%98%BE%E7%A4%BA%E5%BD%93%E5%89%8D%E7%BD%91%E7%AB%99%E6%89%80%E6%9C%89%E5%8F%AF%E7%94%A8%E7%9A%84UserJS%E8%84%9A%E6%9C%AC%EF%BC%8CShow%20Site%20All%20UserJS.user.js // @updateURL https://update.greasyfork.icu/scripts/548869/My%20Userscript%20%3A%20%E6%98%BE%E7%A4%BA%E5%BD%93%E5%89%8D%E7%BD%91%E7%AB%99%E6%89%80%E6%9C%89%E5%8F%AF%E7%94%A8%E7%9A%84UserJS%E8%84%9A%E6%9C%AC%EF%BC%8CShow%20Site%20All%20UserJS.meta.js // ==/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 = '
可匹配的脚本
当前网站可用脚本: 0
网站: ' + this.host + '
排序方式:
'; this.tplFloatBtn = '
UserJS
⚙️
'; this.tplSettings = '
My Userscript 设置
'; } _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; My 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 = '
未找到可用的脚本
'; return; } var listHtml = ''; scriptList.forEach(function(script) { listHtml += '
'; // 脚本名称 listHtml += '
' + (script.name || '未知脚本').replace(//g, '>') + '
'; // 脚本描述 listHtml += '
' + (script.description || '无描述').replace(//g, '>') + '
'; // 脚本元信息 listHtml += '
'; // 总安装量 if (script.total_installs) { listHtml += '总安装: ' + _this.formatNumber(script.total_installs) + ''; } // 今日安装量 if (script.daily_installs) { listHtml += '今日: ' + _this.formatNumber(script.daily_installs) + ''; } // 评分 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 += '好评: ' + goodPercent + '%'; } // 更新时间 if (script.code_updated_at) { listHtml += '更新: ' + _this.formatDate(script.code_updated_at) + ''; } // 版本信息 if (script.version) { listHtml += 'v' + script.version + ''; } // 作者信息 if (script.users && script.users.length > 0) { listHtml += '作者: ' + script.users[0].name + ''; } listHtml += '
'; // 操作按钮 listHtml += '
'; listHtml += '查看详情'; if (script.code_url) { listHtml += '安装脚本'; } listHtml += '
'; listHtml += '
'; }); 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 = '
未找到可用的脚本
'; } }).catch(function(error) { console.error('获取脚本列表失败:', error); listContainer.innerHTML = '
获取脚本列表失败
'; }); // 添加排序选择器事件 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(); } })();