// ==UserScript== // @name Magic Userscript+ : Show Site All UserJS // @name:zh Magic Userscript+ : 显示当前网站所有可用的UserJS脚本 Jaeger // @name:zh-CN Magic Userscript+ : 显示当前网站所有可用的UserJS脚本 Jaeger // @name:zh-TW Magic Userscript+ : 顯示當前網站所有可用的UserJS腳本 Jaeger // @name:ja Magic Userscript+ : 現在のサイトの利用可能なすべてのUserJSスクリプトを表示するJaeger // @name:ru-RU Magic Userscript+ : Показать пользовательские скрипты (UserJS) для сайта. Jaeger // @name:ru Magic Userscript+ : Показать пользовательские скрипты (UserJS) для сайта. Jaeger // @description Show current site all UserJS, the easier way to install UserJs for Tampermonkey. // @description:zh 显示当前网站的所有可用UserJS(Tampermonkey)脚本,交流QQ群:104267383 // @description:zh-CN 显示当前网站的所有可用UserJS(Tampermonkey)脚本,交流QQ群:104267383 // @description:zh-TW 顯示當前網站的所有可用UserJS(Tampermonkey)腳本,交流QQ群:104267383 // @description:ja 現在のサイトで利用可能なすべてのUserJS(Tampermonkey)スクリプトを表示します。 // @description:ru-RU Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey. // @description:ru Показывает пользовательские скрипты (UserJS) для сайта. Легкий способ установить пользовательские скрипты для Tampermonkey. // @author Magic // @version 5.11.19 // @icon data:image/png;base64,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 // @supportURL https://github.com/magicoflolis/Userscript-Plus/issues/new // @namespace https://github.com/magicoflolis/Userscript-Plus // @homepageURL https://github.com/magicoflolis/Userscript-Plus // @license MIT // @connect greasyfork.org // @connect sleazyfork.org // @connect github.com // @connect openuserjs.org // @match https://*/* // @grant GM_xmlhttpRequest // @grant GM_openInTab // @grant GM_getValue // @grant GM_setValue // @grant GM_info // @compatible chrome // @compatible firefox // @compatible edge // @compatible opera // @compatible safari // @noframes // @run-at document-start // @downloadURL none // ==/UserScript== /** * Injected stylesheet * https://github.com/magicoflolis/Userscript-Plus/tree/master/src/sass */ const main_css = `*{scrollbar-color:#fff #2e323d;scrollbar-width:thin}@supports not (scrollbar-width: thin){* ::-webkit-scrollbar{width:1.4vw;height:3.3vh}* ::-webkit-scrollbar-track{background-color:#2e323d;border-radius:10px;margin-top:3px;margin-bottom:3px;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}* ::-webkit-scrollbar-thumb{border-radius:10px;background-color:#fff;background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent)}* ::-webkit-scrollbar-thumb:hover{background-color:#fff}}*:not(.mujs-iframe){background:#495060;color:#fff}magic-userjs{line-height:normal}.magicuserjs-cfg{line-height:1.5}body.webext-page,.main{font-size:14px}mujs-column,mujs-row{display:flex}mujs-column,mujs-row{gap:10px}@media screen and (max-width: 800px){mujs-column{flex-flow:row wrap}}mujs-row{flex-direction:column}magic-userjs{cursor:default}.hidden{display:none !important;z-index:-1 !important}.main{width:100%;width:-moz-available;width:-webkit-fill-available;background:#495060 !important;border:1px solid rgba(0,0,0,0);border-radius:10px;font-family:Arial,Helvetica,sans-serif}@media screen and (max-height: 450px){.main:not(.webext-page){height:100% !important;bottom:0rem !important;margin-left:0rem !important;margin-right:0rem !important;right:0rem !important}}.main.expanded{height:100% !important;bottom:0rem !important}.main:not(.webext-page){position:fixed;height:492px}.main:not(.webext-page):not(.expanded){margin-left:1rem;margin-right:1rem;right:1rem;bottom:1rem}.main:not(.webext-page):not(.expanded).auto-height{height:auto}.main:not(.hidden){z-index:100000000000000000 !important;display:flex !important;flex-direction:column !important}.count{background:rgba(0,0,0,0)}.mainframe{background:rgba(0,0,0,0);position:fixed;bottom:1rem;right:1rem}.mainframe:not(.hidden){z-index:100000000000000000 !important;display:block}.mainframe count-frame{width:2em;height:1em}count-frame{border-radius:16px;padding:0 .25em;border:2px solid rgba(0,0,0,0);font-size:16px;font-weight:400;display:inline-block;text-align:center;min-width:1em}.magicuserjs-header{order:0;display:flex;gap:10px;border-bottom:1px solid #fff;border-top-left-radius:10px;border-top-right-radius:10px;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;padding:10px;font-size:1em;place-content:space-between}.magicuserjs-body{overflow-x:hidden;order:1}.magicuserjs-body .magicuserjs-ratings{padding:0 .25em;border:1px solid #fff;border-radius:10px}.magicuserjs-body magicuserjs-btn svg{fill:#fff;width:14px;height:14px;background:rgba(0,0,0,0)}.magicuserjs-cfg,.magicuserjs-body{border:1px solid rgba(0,0,0,0);border-bottom-left-radius:10px;border-bottom-right-radius:10px}@media screen and (max-width: 1150px){.magicuserjs-cfg{margin:0px auto 1rem auto !important}}.magicuserjs-cfg{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}@media screen and (max-height: 812px){.magicuserjs-cfg:not(.webext-page){flex-wrap:wrap;flex-direction:row !important}}.magicuserjs-cfg mujs-section>label{display:flex;justify-content:space-between}.magicuserjs-cfg mujs-section>label input[type*=number]{position:relative;border-radius:4px;border:1px solid #fff}.magicuserjs-cfg .magicuserjs-inlab{position:relative;width:38px}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]{display:none}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]:checked+label{margin-left:0;background-color:rgba(255,255,255,.568)}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]:checked+label:before{right:0px}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]#greasyfork:checked+label,.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]#sleazyfork:checked+label{background-color:rgba(0,183,255,.568)}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]#openuserjs:checked+label{background-color:rgba(237,63,20,.568)}.magicuserjs-cfg .magicuserjs-inlab input[type*=checkbox]#github:checked+label{background-color:rgba(36,41,47,.568)}.magicuserjs-cfg .magicuserjs-inlab label{padding:0;display:block;overflow:hidden;height:16px;border-radius:20px;border:1px solid #fff;background-color:#495060}.magicuserjs-cfg .magicuserjs-inlab label:before{content:"";display:block;width:20px;height:20px;margin:-2px;background:#fff;position:absolute;top:0;right:20px;border-radius:20px}.magicuserjs-cfg #blacklist{overflow-y:auto;background:#000;color:#fff;resize:vertical;outline:none;border-style:none;font-family:monospace}.magicuserjs-cfg #blacklist:focus{outline:none}.magicuserjs-cfg:not(.webext-page){order:2;margin:0px 25rem 1rem 25rem}table{width:100%;width:-moz-available;width:-webkit-fill-available}@media screen and (max-width: 800px){table thead>tr{display:grid;grid-auto-flow:column}}@media screen and (max-width: 500px){table thead>tr{display:none !important}}table th,table td{border-bottom:1px solid #fff}table td.magicuserjs-uframe,table td.magicuserjs-list,table td.install-btn{text-align:center}table th{position:-webkit-sticky;position:sticky;top:0}table th.mujs-header-name{width:50%}@media screen and (max-width: 800px){table th.mujs-header-name{width:auto !important}}magicuserjs-a{display:inline-block}magicuserjs-a.magicuserjs-euser{padding-left:.5rem;padding-right:.5rem}@media screen and (max-width: 800px){.frame:not(.webext-page){display:grid}.frame:not(.webext-page) magicuserjs-btn{margin-left:25%;margin-right:25%}}.frame.sf magicuserjs-a{color:#e75531 !important}.frame.sf magicuserjs-btn{background-color:#ed3f14 !important;border-color:#ed3f14 !important}.frame:not(.sf) magicuserjs-a{color:#00b7ff !important}.frame:not(.sf) magicuserjs-btn{color:#fff;background-color:#2d8cf0;border-color:#2d8cf0}.magicuserjs-name{display:grid}.magicuserjs-name span{font-size:.8em !important}mujs-btn{font-style:normal;font-weight:400;font-variant:normal;text-transform:none;text-rendering:auto;border:1px solid #fff;font-size:16px;border-radius:4px;line-height:1;padding:6px 15px}mujs-btn svg{fill:#fff;width:14px;height:14px}magicuserjs-btn{font-size:14px;border-radius:4px;font-style:normal;padding:7px 15%;font-weight:400;font-variant:normal;line-height:normal;display:block}input[type*=number],input[type*=text]{border:rgba(0,0,0,0);outline:none !important}magicuserjs-a,magicuserjs-btn,.mujs-pointer,.magicuserjs-cfg mujs-section *:not(input[type*=text],input[type*=number]),.mainbtn,.mainframe,mujs-btn{cursor:pointer !important}th,.magicuserjs-cfg *:not(input[type*=text],input[type*=number]){-webkit-user-select:none !important;-moz-user-select:none !important;-ms-user-select:none !important;user-select:none !important}mujs-btn,input,.magicuserjs-homepage{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content}`; (() => { const win = window, /** * Object is Null * @param {Object} obj - Object * @returns {boolean} Returns if statement true or false */ isNull = obj => (Object.is(obj,null) || Object.is(obj,undefined)), /** * Object is Blank * @param {(Object|Object[]|string)} obj - Array, object or string * @returns {boolean} Returns if statement true or false */ isBlank = obj => typeof obj === 'string' && Object.is(obj.trim(),'') || typeof obj === 'object' && Object.is(Object.keys(obj).length,0), /** * Object is Empty * @param {(Object|Object[]|string)} obj - Array, object or string * @returns {boolean} Returns if statement true or false */ isEmpty = obj => isNull(obj) || isBlank(obj); class Timeout { constructor() { this.ids = []; } set = (delay, reason) => { return new Promise((resolve, reject) => { const id = setTimeout(() => { isNull(reason) ? resolve() : reject(reason); this.clear(id); }, delay); this.ids.push(id); }); }; clear = (...ids) => { this.ids = this.ids.filter(id => { if (ids.includes(id)) { clearTimeout(id); return false; }; return true; }); }; }; class MUError extends Error { /** * @param {string} fnName - (Optional) Function name * @param {...string} params - Extra error parameters */ constructor(fnName = 'AFError',...params) { super(...params); if (Error.captureStackTrace) { Error.captureStackTrace(this, MUError) } else { this.stack = (new Error).stack }; this.fn = `[${fnName}]`; const dt = new Date(Date.now()); this.date = `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}]`; this.name = this.constructor.name; }; }; let langs = { en: { daily: 'Daily Installs', close: 'Close', filterA: 'Filter', max: 'Maximize', min: 'Minimize', search: 'Search', searcher: 'Title | Description | Author...', install: 'Install', issue: 'New Issue', version: 'Version', updated: 'Last Updated', legacy: 'Legacy', total: 'Total Installs', rating: 'Ratings', good: 'Good', ok: 'Ok', bad: 'Bad', created: 'Created', redirect: 'Greasy Fork for adults', filter: 'Filter out other languages', dtime: 'Display Timeout', save: 'Save', }, es: { daily: 'Instalaciones diarias', close: 'Ya no se muestra', filterA: 'Filtro', max: 'Maximizar', min: 'Minimizar', search: 'Busque en', searcher: 'Título | Descripción | Autor...', install: 'Instalar', issue: 'Nueva edición', version: 'Versión', updated: 'Última actualización', legacy: 'Legado', total: 'Total de instalaciones', rating: 'Clasificaciones', good: 'Bueno', ok: 'Ok', bad: 'Malo', created: 'Creado', redirect: 'Greasy Fork para adultos', filter: 'Filtrar otros idiomas', dtime: 'Mostrar el tiempo de espera', save: 'Guardar', }, ru: { daily: 'Ежедневные установки', close: 'Больше не показывать', filterA: 'Фильтр', max: 'Максимизировать', min: 'Минимизировать', search: 'Поиск', searcher: 'Название | Описание | Автор...', install: 'Установите', issue: 'Новый выпуск', version: 'Версия', updated: 'Последнее обновление', legacy: 'Наследие', total: 'Всего установок', rating: 'Рейтинги', good: 'Хорошо', ok: 'Хорошо', bad: 'Плохо', created: 'Создано', redirect: 'Greasy Fork для взрослых', filter: 'Отфильтровать другие языки', dtime: 'Тайм-аут отображения', save: 'Сохранить', }, ja: { daily: 'デイリーインストール', close: '表示されなくなりました', filterA: 'フィルター', max: '最大化', min: 'ミニマム', search: '検索', searcher: 'タイトル|説明|著者...', install: 'インストール', issue: '新刊のご案内', version: 'バージョン', updated: '最終更新日', legacy: 'レガシー', total: '総インストール数', rating: 'レーティング', good: 'グッド', ok: '良い', bad: '悪い', created: '作成', redirect: '大人のGreasyfork', filter: '他の言語をフィルタリングする', dtime: '表示タイムアウト', save: '拯救', }, fr: { daily: 'Installations quotidiennes', close: 'Ne plus montrer', filterA: 'Filtre', max: 'Maximiser', min: 'Minimiser', search: 'Recherche', searcher: 'Titre | Description | Auteur...', install: 'Installer', issue: 'Nouveau numéro', version: 'Version', updated: 'Dernière mise à jour', legacy: 'Héritage', total: 'Total des installations', rating: 'Notations', good: 'Bon', ok: 'Ok', bad: 'Mauvais', created: 'Créé', redirect: 'Greasy Fork pour les adultes', filter: 'Filtrer les autres langues', // eslint-disable-next-line quotes dtime: `Délai d'affichage`, save: 'Sauvez', }, zh: { daily: '日常安装', close: '不再显示', filterA: '过滤器', max: '最大化', min: '最小化', search: '搜索', searcher: '标题|描述|作者...', install: '安装', issue: '新问题', version: '版本', updated: '最后更新', legacy: '遗产', total: '总安装量', rating: '评级', good: '好的', ok: '好的', bad: '不好', created: '创建', redirect: '大人的Greasyfork', filter: '过滤掉其他语言', dtime: '显示超时', save: '拯救', }, }, alang = [], clang = navigator.language.split('-')[0] ?? 'en', lang = langs[clang], isGM = typeof GM !== 'undefined', defcfg = { cache: true, autoexpand: false, filterlang: false, sleazyredirect: false, time: 10000, blacklist: [ { enabled: true, regex: true, flags: '', name: 'Blacklist 1', url: '(gov|cart|checkout|login|join|signin|signup|sign-up|password|reset|password_reset)', }, { enabled: true, regex: true, flags: '', name: 'Blacklist 2', url: '(pay|bank|money|localhost|authorize|checkout|bill|wallet|router)', }, { enabled: true, regex: false, flags: '', name: 'Blacklist 3', url: 'https://home.bluesnap.com', }, { enabled: true, regex: false, flags: '', name: 'Blacklist 4', url: [ 'zalo.me', 'skrill.com' ], }, ], engines: [ { enabled: true, name: 'greasyfork', url: 'https://greasyfork.org', }, { enabled: true, name: 'sleazyfork', url: 'https://sleazyfork.org', }, { enabled: false, name: 'openuserjs', url: 'https://openuserjs.org/?q=', }, { enabled: false, name: 'github', url: 'https://github.com/search?l=JavaScript&o=desc&q="==UserScript=="+', }, { enabled: false, name: 'gist', url: 'https://gist.github.com/search?l=JavaScript&o=desc&q="==UserScript=="+', }, ] }, cfg = {}, urls = [], sitegfcount = 0, sitesfcount = 0, MU = { /** * Get Value * @param {string} key - Key to get the value of * @param {Object} def - Fallback default value of key * @returns {Object} Value or default value of key * @link https://violentmonkey.github.io/api/gm/#gm_getvalue * @link https://developer.mozilla.org/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API */ async getValue(key,def = {}) { try { return await new Promise((resolve) => { const params = JSON.stringify(def ?? {}); if (isGM) { resolve(JSON.parse(GM_getValue(key, params))); } else { resolve(localStorage.getItem(`MUJS${key}`) ? JSON.parse(localStorage.getItem(`MUJS${key}`)) : def); }; }); } catch (ex) { err(ex); return def; } }, /** * Get info of script * @returns {Object} Script info * @link https://violentmonkey.github.io/api/gm/#gm_info */ info() { return isGM ? GM_info : { script: { updateURL: '', version: 'Bookmarklet' } } }, /** * Open a new window * @param {string} url - URL of webpage to open * @param {object} params - GM parameters * @returns {object} GM_openInTab object with Window object as a fallback * @link https://violentmonkey.github.io/api/gm/#gm_openintab * @link https://developer.mozilla.org/docs/Web/API/Window/open */ openInTab(url, params = { active: true, insert: true, }, features) { if(!isGM && isBlank(params)) { params = '_blank'; }; if(features) { return win.open(url, params, features); }; return isGM ? GM_openInTab(url, params) : win.open(url, params); }, /** * Set value * @param {string} key - Key to set the value of * @param {Object} v - Value of key * @returns {Promise} Saves key to either GM managed storage or webpages localstorage * @link https://violentmonkey.github.io/api/gm/#gm_setvalue * @link https://developer.mozilla.org/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API */ setValue(key,v) { return new Promise((resolve) => { v = typeof v !== 'string' ? JSON.stringify(v ?? {}) : v; if(isGM && cfg.cache) { resolve( GM_setValue(key,v) ); } else { resolve( win.localStorage.setItem(`MUJS${key}`,v) ); }; }); }, /** * Fetch a URL with fetch API as fallback * * When GM is supported, makes a request like XMLHttpRequest, with some special capabilities, not restricted by same-origin policy * @param {string} url - The URL to fetch * @param {string} method - Fetch method * @param {string} responseType - Response type * @param {Object} extras - Fetch parameters * @param {boolean} forcefetch - Force use fetch API * @returns {*} Fetch results * @link https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest * @link https://developer.mozilla.org/docs/Web/API/Fetch_API */ fetchURL(url,method = 'GET',responseType = 'json',extras = {},forcefetch) { return new Promise((resolve, reject) => { if(isGM && !forcefetch) { GM_xmlhttpRequest({ method: method, url, responseType, ...extras, onerror: e => reject(e), onload: (r) => { if(r.status !== 200) reject(`${r.status} ${url}`); if(responseType.match(/basic/gi)) resolve(r); resolve(r.response); }, }); } else { fetch(url, { method: method, ...extras, }).then((response) => { if(!response.ok) reject(response); if(responseType.match(/json/gi)) { resolve(response.json()); } else if(responseType.match(/text/gi)) { resolve(response.text()); } else if(responseType.match(/blob/gi)) { resolve(response.blob()); }; resolve(response); }).catch(handleError); }; }); }, }; const doc = document, /** * preventDefault + stopPropagation * @param {Object} e - Selected Element */ halt = (e) => { e.preventDefault(); e.stopPropagation(); }, /** * setTimeout w/ Promise * @param {number} ms - Timeout in milliseconds (ms) * @returns {Promise} Promise object */ delay = ms => new Promise(resolve => setTimeout(resolve, ms)), /** * Add Event Listener * @param {Object} root - Selected Element * @param {string} event - root Event Listener * @param {Function} callback - Callback function * @param {Object} [options={}] - (Optional) Options * @returns {Object} Returns selected Element */ ael = (root, event, callback, options = {}) => { try { let isMobile = () => { let a = navigator.userAgent || win.opera; return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4)); }; root = (root || doc || doc.documentElement); if(isMobile()) { if(event === 'click') { event = 'mouseup'; root.addEventListener('touchstart', callback); root.addEventListener('touchend', callback); }; }; if(event === 'fclick') {event = 'click'}; return root.addEventListener(event, callback, {...options}); } catch(ex) { handleError(ex); }; }, /** * Prefix for document.querySelectorAll() * @param {Object} element - Elements for query selection * @param {Object} [root=document] - Root selector Element * @returns {Object} Returns root.querySelectorAll(element) */ qsA = (element, root) => { root = root ?? doc ?? doc.body; return root.querySelectorAll(element); }, /** * Prefix for document.querySelector() * @param {Object} element - Element for query selection * @param {Object} [root=document] - Root selector Element * @returns {Object} Returns root.querySelector(element) */ qs = (element, root) => { root = root ?? doc ?? doc.body; return root.querySelector(element); }, /** * Prefix for document.querySelector() w/ Promise * @param {Object} element - Element for query selection * @param {Object} [root=document] - Root selector Element * @returns {Object} Returns root.querySelector(element) */ query = async (element, root) => { root = root ?? doc ?? doc.body; while(isNull(root.querySelector(element))) { await new Promise(resolve=>requestAnimationFrame(resolve)) }; return root.querySelector(element); }, /** * Create/Make Element * @param {Object} element - Element to create * @param {string} cname - (Optional) Element class name * @param {Object} [attrs={}] - (Optional) Element attributes * @returns {Object} Returns created Element */ make = (element, cname, attrs = {}) => { let el; try { el = doc.createElement(element); if(!isEmpty(cname)) { el.className = cname; }; if(!isEmpty(attrs)) { for (const key in attrs) { el[key] = attrs[key]; }; }; return el; } catch(ex) {handleError(ex)} }, iconSVG = { cfg: ' ', close: '', filter: ' ', fsClose: ' ', fsOpen: ' ', fullscreen: '', gf: '', gh: '', hide: ' ', install: '', issue: '', nav: ' ', plus: ' ', search: ' ', }, container = make('main-userjs','mujs-primary'), ifram = make('iframe','mujs-iframe', { src: 'about:blank', style: 'position: fixed; bottom: 1rem; right: 1rem; height: 525px; width: 90%; margin-left: 1rem; margin-right: 1rem; z-index: 100000000000000020 !important;' }); function main() { const injCon = container.attachShadow instanceof Function ? container.shadowRoot : ifram.contentDocument.body; let unsaved = false, isBlacklisted = false, seen = new Set(), switchRows = true, thisHost = location.hostname.split('.').splice(-2).join('.'); const save = () => { try { MU.setValue('Config', cfg); unsaved = false; log('Saved:',cfg); } catch(e) {err(e)}; }, timeout = new Timeout(), timeoutFrame = async () => { if(typeof cfg.time === 'number' && !isNaN(cfg.time)) { timeout.clear(...timeout.ids); await timeout.set(isBlacklisted ? cfg.time/2 : cfg.time); container.remove(); ifram.remove(); return timeout.clear(...timeout.ids); } }, sh = elem => injCon.querySelector(elem), shA = elem => injCon.querySelectorAll(elem), table = make('table'), tabbody = make('tbody'), tabhead = make('thead'), makeTHead = (rows = []) => { let tr = make('tr'); for(let r of rows) { let tparent = make('th', r.class ?? '', r); tr.append(tparent); }; tabhead.append(tr); table.append(tabhead, tabbody); }, showError = (msg) => { err(msg); let txt = make('mujs-row','error', { innerHTML: msg }); for(let u of urls) { let dwnbtn = make('a','magicuserjs-urls', { href: u, target: '_blank', rel: 'noopener', innerHTML: u }); txt.append(dwnbtn); }; if(sh('.magicuserjs-body')) { sh('.magicuserjs-body').prepend(txt); }; }, sortRowBy = (cellIndex) => { const rows = Array.from(tabbody.rows); rows.sort((tr1, tr2) => { const t1cell = tr1.cells[cellIndex], t2cell = tr2.cells[cellIndex], tr1Text = (t1cell.firstElementChild ?? t1cell).textContent, tr2Text = (t2cell.firstElementChild ?? t2cell).textContent, t1pDate = Date.parse(tr1Text), t2pDate = Date.parse(tr2Text); if(!Number.isNaN(t1pDate) && !Number.isNaN(t2pDate)) { return new Date(t1pDate) - new Date(t2pDate); }; if(Number(tr1Text) && Number(tr2Text)) { return tr1Text - tr2Text; }; return tr1Text.localeCompare(tr2Text); }); if(switchRows) { rows.reverse() }; switchRows = !switchRows; tabbody.append(...rows); }, createjs = (ujs, issleazy) => { let eframe = make('td', 'install-btn'), uframe = make('td','magicuserjs-uframe'), fdaily = make('td','magicuserjs-list', { innerHTML: ujs.daily_installs, }), fupdated = make('td','magicuserjs-list', { innerHTML: new Intl.DateTimeFormat(navigator.language).format(new Date(ujs.code_updated_at)), }), fname = make('td','magicuserjs-name'), ftitle = make('magicuserjs-a','magicuserjs-homepage', { title: ujs.name, innerHTML: ujs.name, onclick: (e) => { halt(e); MU.openInTab(ujs.url); } }), fver = make('magic-userjs','magicuserjs-list', { innerHTML: `${lang.version}: ${ujs.version}`, }), fcreated = make('magic-userjs','magicuserjs-list', { innerHTML: `${lang.created}: ${new Intl.DateTimeFormat(navigator.language).format(new Date(ujs.created_at))}`, }), fmore = make('mujs-column','magicuserjs-list hidden', { style: 'margin-top: 3px;', }), ftotal = make('magic-userjs','magicuserjs-list', { innerHTML: `${lang.total}: ${ujs.total_installs}`, }), fratings = make('magic-userjs','magicuserjs-list', { title: lang.rating, innerHTML: `${lang.rating}:`, }), fgood = make('magic-userjs','magicuserjs-list magicuserjs-ratings', { title: lang.good, innerHTML: ujs.good_ratings, style: 'border-color: rgb(51, 155, 51); background-color: #339b331a; color: #339b33;', }), fok = make('magic-userjs','magicuserjs-list magicuserjs-ratings', { title: lang.ok, innerHTML: ujs.ok_ratings, style: 'border-color: rgb(155, 155, 0); background-color: #9b9b001a; color: #9b9b00;', }), fbad = make('magic-userjs','magicuserjs-list magicuserjs-ratings', { title: lang.bad, innerHTML: ujs.bad_ratings, style: 'border-color: red; background-color: #9b33331a; color: red;', }), fdesc = make('magic-userjs','magicuserjs-list', { style: 'cursor: pointer; margin-top: 3px;', title: ujs.description, innerHTML: ujs.description, onclick: (e) => { halt(e); if(fmore.classList.contains('hidden')) { fmore.classList.remove('hidden'); } else { fmore.classList.add('hidden'); } }, }), fdwn = make('magicuserjs-btn','install', { title: `${lang.install} { ${ujs.name} }`, innerHTML: `${iconSVG.install} ${lang.install}`, onclick: (e) => { halt(e); MU.openInTab(ujs.code_url); }, }); for(let u of ujs.users) { let user = make('magicuserjs-a','magicuserjs-euser', { innerHTML: u.name, onclick: (e) => { halt(e); MU.openInTab(u.url); }, }); uframe.append(user); }; eframe.append(fdwn); fmore.append(ftotal,fratings,fgood,fok,fbad,fver,fcreated); fname.append(ftitle,fdesc,fmore); let tr = make('tr', `frame ${issleazy ? 'sf' : ''}`); for(let e of [fname,uframe,fdaily,fupdated,eframe]) { tr.append(e); }; tabbody.append(tr); }; if(!isEmpty(navigator.languages)) { for(let nlang of navigator.languages) { let lg = nlang.split('-')[0]; if(alang.indexOf(lg) === -1) { alang.push(lg); }; }; }; try { if(/greasyfork\.org/.test(doc.location.hostname) && cfg.sleazyredirect) { let otherSite = /greasyfork\.org/.test(document.location.hostname) ? 'sleazyfork' : 'greasyfork'; qs('span.sign-in-link') ? /scripts\/\d+/.test(document.location.href) ? !qs('#script-info') && (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) ? location.href = location.href.replace(/\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, '//$1' + otherSite + '.org') : false : false : false; }; let rebuild = false, siteujs = [], main = make('magic-userjs','main hidden'), usercss = make('style', 'primary-stylesheet', {innerHTML: main_css,}), tbody = make('magic-userjs','magicuserjs-body'), header = make('magic-userjs','magicuserjs-header'), cfgpage = make('mujs-row','magicuserjs-cfg hidden'), makerow = (desc,type,nm,attrs = {}) => { let sec = make('mujs-section','', { style: !isGM && nm === 'cache' ? 'display: none;' : '' }), lb = make('label'), divDesc = make('magic-userjs','', { innerHTML: desc, }), inp = make('input','', { type: type, id: nm, name: nm, ...attrs }); if(type === 'checkbox') { let inlab = make('magic-userjs','magicuserjs-inlab'), la = make('label','', { onclick: () => inp.click() }); inlab.append(inp,la); lb.append(divDesc,inlab); if(nm.match(/((greasy|sleazy)fork|openuserjs|gi(thub|st))/gi)) { for(let i of cfg.engines) { if(i.name === nm) { inp.checked = i.enabled; ael(inp,'change', (e) => { unsaved = true; i.enabled = e.target.checked; rebuild = true; }); }; }; } else { inp.checked = cfg[nm]; if(nm.match(/(autoexpand|sleazyredirect)/gi)) { ael(inp,'change', (e) => { unsaved = true; cfg[nm] = e.target.checked; }); } else { ael(inp,'change', (e) => { unsaved = true; cfg[nm] = e.target.checked; rebuild = true; }); }; }; } else { lb.append(divDesc,inp); }; sec.append(lb); cfgpage.append(sec); return inp; }, countframe = make('mujs-column'), gfcountframe = make('magic-userjs', 'counterframe'), sfcountframe = make('magic-userjs', 'counterframe'), gfcounter = make('count-frame','count', { title: 'https://greasyfork.org + https://sleazyfork.org', style: 'background: #00b7ff;' }), sfcounter = make('count-frame','count', { title: 'https://openuserjs.org', style: 'background: #ed3f14;' }), buildlist = async (host) => { try { if(isEmpty(host)) { host = thisHost; }; const template = { bad_ratings: 0, good_ratings: 0, ok_ratings: 0, daily_installs: 0, total_installs: 0, name: 'Not found', description: 'Not found', version: '0.0.0', url: 'about:blank', code_url: 'about:blank', created_at: Date.now(), code_updated_at: Date.now(), users: [ { name: '', url: '', } ] }; let sites = [], custom = [], engines = cfg.engines.filter(e => e.enabled); for(let i of engines) { if(i.url.match(/fork.org/gi)) { if(cfg.filterlang) { if(alang.length > 1) { for(let a of alang) { urls.push(`${i.url}/${a}/scripts/by-site/${host}.json`); sites.push(MU.fetchURL(`${i.url}/${a}/scripts/by-site/${host}.json?page=1`),); }; continue; }; urls.push(`${i.url}/${clang}/scripts/by-site/${host}.json`); sites.push(MU.fetchURL(`${i.url}/${clang}/scripts/by-site/${host}.json?page=1`),); continue; }; urls.push(`${i.url}/scripts/by-site/${host}.json`); sites.push(MU.fetchURL(`${i.url}/scripts/by-site/${host}.json`),); } else if(i.url.match(/(openuserjs.org|github.com)/gi)) { urls.push(`${i.url}${host}`); custom.push(MU.fetchURL(`${i.url}${host}`,'GET','text'),); }; }; info('Fetching data',host); if(!isBlank(sites)) { let hideData = []; let data = await Promise.all(sites).catch((e) => {throw new MUError('Data',e)}), joinData = [...new Set([...data[0], ...data[1]])], filterDeleted = joinData.filter(ujs => !ujs.deleted), filterLang = cfg.filterlang ? filterDeleted.filter((d) => { let dlocal = d.locale.split('-')[0] ?? d.locale; if(alang.length > 1) { for(let a of alang) { if(dlocal.includes(a)) { return true; }; }; } else if(dlocal.includes(clang)) { return true; }; hideData.push(d); return false; }) : filterDeleted, finalList = filterLang; if(!isBlank(hideData)) { let hds = []; for(let h of hideData) { let txt = await MU.fetchURL(h.code_url,'GET','text'); let headers = txt.match(/\/\/\s@[\w][\s\S]+/gi) || []; if(headers.length > 0) { let regName = new RegExp(`// @name:${clang}\\s+.+`,'gi'), findName = headers[0].match(regName) || []; if(isEmpty(findName)) { continue; }; let cReg = new RegExp(`// @name:${clang}\\s+`,'gi'), cutName = findName[0].replace(cReg, ''); Object.assign(h, { name: cutName }); let regDesc = new RegExp(`// @description:${clang}\\s+.+`,'gi'), findDesc = headers[0].match(regDesc) || []; if(isEmpty(findDesc)) { continue; }; let dReg = new RegExp(`// @description:${clang}\\s+`,'gi'), cutDesc = findDesc[0].replace(dReg, ''); Object.assign(h, { description: cutDesc }); hds.push(h); }; }; finalList = [...new Set([...hds, ...filterLang])]; }; for(let ujs of finalList) { siteujs.push( { url: ujs, sleazy: false, }, ); sitegfcount++; }; for(let ujs of siteujs) { createjs(ujs.url,ujs.sleazy); }; } else { showError('Error occured while loading UserJS for this webpage') }; gfcounter.innerHTML = sitegfcount; mainbtn.innerHTML = sitesfcount + sitegfcount; if(!isBlank(custom)) { let customRecords = []; let c = await Promise.all(custom).catch((e) => {throw new MUError('Custom',e)}), parser = new DOMParser(), htmlDocument = parser.parseFromString(c,'text/html'), selected = htmlDocument.documentElement; if(qs('.col-sm-8 .tr-link',selected)) { for(let i of qsA('.col-sm-8 .tr-link',selected)) { await query('.script-version',i); let fixurl = qs('.tr-link-a',i).href.replace(new RegExp(doc.location.origin, 'gi'),'https://openuserjs.org'), layout = { name: qs('.tr-link-a',i).textContent, description: qs('p',i).textContent, version: qs('.script-version',i).textContent, url: fixurl, code_url: `${fixurl.replace(new RegExp('/scripts', 'gi'),'/install')}.user.js`, total_installs: qs('td:nth-child(2) p',i).textContent, created_at: qs('td:nth-child(4) time',i).getAttribute('datetime'), code_updated_at: qs('td:nth-child(4) time',i).getAttribute('datetime'), users: [ { name: qs('.inline-block a',i).textContent, url: qs('.inline-block a',i).href, } ] }; for(const key in template) { if(!Object.hasOwn(layout, key)) { layout[key] = template[key]; }; }; createjs(layout, true); customRecords.push(layout); sitesfcount++; sfcounter.innerHTML = sitesfcount; }; }; if(qs('.repo-list-item',selected)) { for(let r of qsA('.repo-list-item',selected)) { let layout = {}, fixurl = qs('a',r).href.replace(new RegExp(doc.location.origin, 'gi'),'https://github.com'); layout = Object.assign(layout, { name: qs('a',r).textContent, description: qs('p.mb-1',r).textContent.trim(), url: fixurl, code_url: fixurl, code_updated_at: qs('relative-time.no-wrap',r).getAttribute('datetime'), total_installs: qs('a.Link--muted:nth-child(1)',r) ? qs('a.Link--muted:nth-child(1)',r).textContent : 0, users: [{ name: qs('a',r).href.match(/\/[\w\d-]+\//gi)[0].replaceAll('/',''), url: `https://github.com${qs('a',r).href.match(/\/[\w\d-]+\//gi)}`, }] }); for (const key in template) { if(!Object.hasOwn(layout, key)) { layout[key] = template[key]; }; }; createjs(layout, true); customRecords.push(layout); sitesfcount++; sfcounter.innerHTML = sitesfcount; }; }; if(qs('div.gist-snippet',selected)) { for(let g of qsA('div.gist-snippet',selected)) { if(qs('span > a:nth-child(2)',g).textContent.includes('.user.js')) { let layout = {}, fixurl = qs('span > a:nth-child(2)',g).href.replace(new RegExp(doc.location.origin, 'gi'),'https://gist.github.com'); layout = Object.assign(layout, { url: fixurl, code_url: `${fixurl}/raw/${qs('span > a:nth-child(2)',g).textContent}`, created_at: qs('time-ago.no-wrap',g).getAttribute('datetime'), users: [{ name: qs('span > a[data-hovercard-type]',g).textContent, url: qs('span > a[data-hovercard-type]',g).href.replace(new RegExp(doc.location.origin, 'gi'),'https://gist.github.com'), }] }); for(let i of qsA('.file-box table tr .blob-code',g)) { let txt = i.textContent, headers = txt.match(/\/\/\s@[\w][\s\S]+/gi) || []; if(headers.length > 0) { let crop = headers[0].split(/\/\/\s@(name|description|author|version)\s+/gi); if(headers[0].includes('@name') && !headers[0].includes('@namespace')) { layout = Object.assign(layout, { name: crop[2].trim(), }); }; if(headers[0].includes('@description')) { layout = Object.assign(layout, { description: crop[2].trim(), }); }; if(headers[0].includes('@version')) { layout = Object.assign(layout, { version: crop[2].trim(), }); }; } }; for (const key in template) { if(!Object.hasOwn(layout, key)) { layout[key] = template[key]; }; }; createjs(layout, true); customRecords.push(layout); sitesfcount++; sfcounter.innerHTML = sitesfcount; }; }; }; seen.add({ host: host, data: siteujs, custom: customRecords, gfcount: sitegfcount, sfcount: sitesfcount, }); sfcounter.innerHTML = sitesfcount; mainbtn.innerHTML = sitesfcount + sitegfcount; } else { seen.add({ host: host, data: siteujs, gfcount: sitegfcount, sfcount: sitesfcount, }); }; if(isBlank(sites) && isBlank(custom)) showError('No available UserJS for this webpage'); staticRows = Array.from(tabbody.rows); sortRowBy(2); } catch(ex) { showError(ex); }; }, preBuild = (site) => { let bhref = site ?? win.top.document.location.href, blacklist = cfg.blacklist.filter(b => b.enabled); siteujs = []; urls = []; sitegfcount = 0; sitesfcount = 0; tabbody.innerHTML = ''; if(sh('.error')) { sh('.error').remove(); }; gfcounter.innerHTML = sitegfcount; sfcounter.innerHTML = sitesfcount; mainbtn.innerHTML = sitegfcount; for(let b of blacklist) { if(b.regex) { let reg = new RegExp(b.url,b.flags), testurl = reg.test(bhref); if(!testurl) continue; isBlacklisted = true; }; if(!Array.isArray(b.url)) { if(!bhref.includes(b.url)) continue; isBlacklisted = true; }; for(let c of b.url) { if(!bhref.includes(c)) continue; isBlacklisted = true; }; }; if(isBlacklisted) { urls.push(bhref); showError('Blacklisted'); return timeoutFrame(); }; if(isEmpty(site)) { site = thisHost; }; if(seen.size > 0) { for(let s of seen) { if(Object.is(s.host,site)) { if(s.data) { for(let ujs of s.data) { createjs(ujs.url, ujs.sleazy); }; }; if(s.custom) { for(let ujs of s.custom) { createjs(ujs, true); }; }; gfcounter.innerHTML = s.gfcount; sfcounter.innerHTML = s.sfcount; mainbtn.innerHTML = s.sfcount + s.gfcount; return; } }; }; return buildlist(site); }, //#region Make Config makecfg = () => { makerow('Sync with GM','checkbox','cache'); makerow('Auto Fullscreen','checkbox','autoexpand', { onchange: (e) => { if(e.target.checked) { btnfullscreen.classList.add('expanded'); main.classList.add('expanded'); btnfullscreen.innerHTML = fcclose; } else { btnfullscreen.classList.remove('expanded'); main.classList.remove('expanded'); btnfullscreen.innerHTML = fcopen; }; }, }); makerow(lang.redirect,'checkbox','sleazyredirect'); makerow(lang.filter,'checkbox','filterlang'); makerow('Greasy Fork','checkbox','greasyfork'); makerow('Sleazy Fork','checkbox','sleazyfork'); makerow('Open UserJS','checkbox','openuserjs'); makerow('GitHub','checkbox','github'); makerow('Gist (GitHub)','checkbox','gist'); let rtime = makerow(`${lang.dtime} (ms)`,'number','time', { defaultValue: 10000, value: cfg.time, min: 0, step: 500, onbeforeinput: (e) => { if(e.target.validity.badInput) { e.target.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;'); } else { e.target.setAttribute('style',''); } }, oninput: (e) => { unsaved = true; let t = e.target; if(t.validity.badInput || t.validity.rangeUnderflow && t.value !== '-1') { t.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;'); } else { t.setAttribute('style',''); cfg.time = isEmpty(t.value) ? cfg.time : parseFloat(t.value); } } }); let isvalid = true, txta = make('textarea','tarea', { name: 'blacklist', id: 'blacklist', rows: '10', autocomplete: false, spellcheck: false, wrap: 'soft', value: JSON.stringify(cfg.blacklist, null, ' '), oninput: (e) => { try { cfg.blacklist = JSON.parse(e.target.value); if(!isvalid) { isvalid = true; e.target.setAttribute('style',''); }; } catch(ex) { isvalid = false; err(ex); }; }, }), cbtn = make('magic-userjs', 'b', { style: 'display: flex' }), savebtn = make('mujs-btn', 'save', { style: 'margin: auto;', innerHTML: lang.save, onclick: (e) => { halt(e); if(rtime.validity.badInput || rtime.validity.rangeUnderflow && rtime.value !== '-1') { return rtime.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;'); }; if(!isvalid) { return txta.setAttribute('style','border-radius: 8px; border-width: 2px !important; border-style: solid; border-color: red !important;'); }; save(); if(rebuild) { seen.clear(); rebuild = false; preBuild(); }; if(/greasyfork\.org/.test(doc.location.hostname) && cfg.sleazyredirect) { let otherSite = /greasyfork\.org/.test(document.location.hostname) ? 'sleazyfork' : 'greasyfork'; qs('span.sign-in-link') ? /scripts\/\d+/.test(document.location.href) ? !qs('#script-info') && (otherSite == 'greasyfork' || qs('div.width-constraint>section>p>a')) ? location.href = location.href.replace(/\/\/([^.]+\.)?(greasyfork|sleazyfork)\.org/, '//$1' + otherSite + '.org') : false : false : false; }; }, }), resetbtn = make('mujs-btn', 'reset', { style: 'margin: auto;', innerHTML: 'Reset', onclick: (e) => { halt(e); MU.setValue('Config'); unsaved = true; cfg = defcfg; txta.value = JSON.stringify(cfg.blacklist, null, ' '); for(let i of cfg.engines) { if(sh(`#${i.name}`)) { sh(`#${i.name}`).checked = i.enabled; }; }; for(let i of shA('.magicuserjs-inlab input[type="checkbox"]')) { if(!i.name.match(/((greasy|sleazy)fork|openuserjs|gi(thub|st))/gi)) { i.checked = cfg[i.name]; }; }; }, }); cbtn.append(savebtn,resetbtn); cfgpage.append(txta,cbtn); }, //#endregion fcopen = iconSVG.fsOpen, fcclose = iconSVG.fsClose, btnHide = make('mujs-btn','hide-list', { title: lang.min, innerHTML: iconSVG.hide, onclick: (e) => { halt(e); main.classList.add('hidden'); mainframe.classList.remove('hidden'); timeoutFrame(); } }), btnfullscreen = make('mujs-btn','fullscreen', { title: lang.max, innerHTML: iconSVG.fullscreen, onclick: (e) => { halt(e); if(btnfullscreen.classList.contains('expanded')) { btnfullscreen.classList.remove('expanded'); main.classList.remove('expanded'); btnfullscreen.innerHTML = fcopen; return; }; btnfullscreen.classList.add('expanded'); main.classList.add('expanded'); btnfullscreen.innerHTML = fcclose; } }), mainframe = make('magic-userjs','mainframe', { onclick: (e) => { e.preventDefault(); timeout.clear(...timeout.ids); main.classList.remove('hidden'); mainframe.classList.add('hidden'); if(cfg.autoexpand) { btnfullscreen.classList.add('expanded'); main.classList.add('expanded'); btnfullscreen.innerHTML = fcclose; }; } }), mainbtn = make('count-frame','mainbtn', { innerHTML: '0', }), fsearch = make('mujs-btn','hidden'), ssearch = make('mujs-btn','hidden'), filterList = make('input','searcher', { style: 'width: 170px;', autocomplete: 'off', spellcheck: false, type: 'text', placeholder: lang.searcher, oninput: (e) => { e.preventDefault(); let v = e.target.value; if(!isEmpty(v)) { let reg = new RegExp(v,'gi'); for(let ujs of shA('.frame')) { let m = ujs.children[0], n = ujs.children[1], final = m.textContent.match(reg) || n.textContent.match(reg) || []; if(final.length === 0) { ujs.classList.add('hidden'); } else { ujs.classList.remove('hidden'); }; }; } else { for(let ujs of shA('.frame')) { ujs.classList.remove('hidden') }; }; }, }), filterBtn = make('mujs-btn','filter', { title: lang.filterA, innerHTML: iconSVG.filter, onclick: (e) => { e.preventDefault(); fsearch.classList.toggle('hidden'); } }), siteSearcher = make('input','searcher', { style: 'width: 100px;', autocomplete: 'off', spellcheck: false, type: 'text', placeholder: thisHost, onchange: (e) => { e.preventDefault(); preBuild(e.target.value); }, }), siteSearchbtn = make('mujs-btn','search', { title: lang.search, innerHTML: iconSVG.search, onclick: (e) => { e.preventDefault(); ssearch.classList.toggle('hidden'); } }), closebtn = make('mujs-btn','close', { title: lang.close, innerHTML: iconSVG.close, onclick: async (e) => { halt(e); container.remove(); ifram.remove(); } }), btnframe = make('mujs-column'), btnHandles = make('mujs-column', 'btn-handles'), btncfg = make('mujs-btn','settings', { title: 'Settings', innerHTML: iconSVG.cfg, onclick: (e) => { e.preventDefault(); if(sh('.saveerror')) { sh('.saveerror').remove(); }; if(unsaved) { let txt = make('mujs-row','saveerror', { innerHTML: 'Unsaved changes' }); tbody.prepend(txt); delay(10000).then(() => txt.remove()); }; if(cfgpage.classList.contains('hidden')) { cfgpage.classList.remove('hidden'); tbody.classList.add('hidden'); main.classList.add('auto-height'); if(ifram) { ifram.setAttribute('style','height: 100%;'); }; } else { cfgpage.classList.add('hidden'); tbody.classList.remove('hidden'); main.classList.remove('auto-height'); if(ifram) { ifram.setAttribute('style',''); }; }; rebuild = false; }, }), btnhome = make('mujs-btn','github hidden', { title: `GitHub (v${MU.info().script.version.includes('.') || MU.info().script.version.includes('Book') ? MU.info().script.version : MU.info().script.version.slice(0,5)})`, innerHTML: iconSVG.gh, onclick: (e) => { halt(e); MU.openInTab('https://github.com/magicoflolis/Userscript-Plus'); } }), btnissue = make('mujs-btn','issue hidden', { title: lang.issue, innerHTML: iconSVG.issue, onclick: (e) => { e.preventDefault(); MU.openInTab('https://github.com/magicoflolis/Userscript-Plus/issues/new'); } }), btngreasy = make('mujs-btn','greasy hidden', { title: 'Greasy Fork', innerHTML: iconSVG.gf, onclick: (e) => { e.preventDefault(); MU.openInTab('https://greasyfork.org/scripts/421603'); } }), btnnav = make('mujs-btn','nav', { title: 'Navigation', innerHTML: iconSVG.nav, onclick: (e) => { halt(e); if(btngreasy.classList.contains('hidden')) { btnissue.classList.remove('hidden'); btnhome.classList.remove('hidden'); btngreasy.classList.remove('hidden'); } else { btnissue.classList.add('hidden'); btnhome.classList.add('hidden'); btngreasy.classList.add('hidden'); }; } }); gfcountframe.append(gfcounter); sfcountframe.append(sfcounter); countframe.append(gfcountframe,sfcountframe); fsearch.append(filterList); ssearch.append(siteSearcher); btnHandles.append(btnHide,btnfullscreen,closebtn); btnframe.append(fsearch,filterBtn,ssearch,siteSearchbtn,btncfg,btnissue,btnhome,btngreasy,btnnav,btnHandles); header.append(countframe,btnframe); tbody.append(table); makeTHead([ { class: 'mujs-header-name', textContent: 'Name' }, { textContent: 'Created by', }, { textContent: lang.daily, }, { textContent: lang.updated, }, { textContent: lang.install, }, ]); for (const th of tabhead.rows[0].cells) { if(th.textContent === lang.install) continue; th.classList.add('mujs-pointer'); ael(th, 'click', () => { sortRowBy(th.cellIndex); }); }; main.append(header,tbody,cfgpage); mainframe.append(mainbtn); injCon.append(usercss,mainframe,main); makecfg(); preBuild(); timeoutFrame(); ael(win,'beforeunload', () => { container.remove(); ifram.remove(); }); } catch(ex) {handleError(ex)} }; function containerInject() { try { info('Injecting Container...'); if(container.attachShadow instanceof Function) { doc.body.append(container); container.attachShadow({mode: 'open'}); return main(); }; ael(ifram, 'load', () => { ifram.contentDocument.documentElement.classList.add('mujs-iframe'); ifram.contentDocument.body.classList.add('mujs-iframe'); main(); }); doc.body.append(ifram); } catch(ex) {handleError(ex)} }; async function stateChange(event) { const evt = event.target ?? doc; if(Object.is(evt.readyState,'complete')) { containerInject(); }; }; async function setupConfig() { try { cfg = await MU.getValue('Config',defcfg); for (const key in defcfg) { if(!Object.hasOwn(cfg, key)) { cfg[key] = defcfg[key]; } else if (key === 'lang') { for (const keyl in defcfg[key]) { if(!Object.hasOwn(cfg[key], keyl)) { cfg[key][keyl] = defcfg[key][keyl]; }; }; } else if (key === 'engines') { for (const key2 in defcfg[key]) { if(!Object.hasOwn(cfg[key], key2)) { cfg[key][key2] = defcfg[key][key2]; }; }; } else if (key === 'blacklist') { for (const key3 in defcfg[key]) { if(!Object.hasOwn(cfg[key], key3)) { cfg[key][key3] = defcfg[key][key3]; }; }; } }; dbg('Config:',cfg); if(Object.is(doc.readyState,'complete')) { containerInject(); } else { ael(doc,'readystatechange',stateChange); }; } catch(ex) { handleError(ex) }; }; //#region Console function dbg(...msg) { const dt = new Date(Date.now()); return console.log('[%cAF%c] %cDBG', 'color: rgb(29, 155, 240);', '', 'color: rgb(255, 212, 0);', `[${dt.getHours()}:${('0' + dt.getMinutes()).slice(-2)}]`, ...msg); }; function err(...msg) { return console.error('[%cUserJS%c] %cERROR', 'color: rgb(29, 155, 240);', '', 'color: rgb(249, 24, 128);', ...msg); }; /** * Displays error messages in webpage * @param {Object|string} error - Error Object or message */ // TODO expand upon function function handleError(error) { const emsg = error.fn ? `${error.fn} ERROR: ${error.message}` : error; return err(emsg); }; function info(...msg) { return console.info('[%cUserJS%c] %cINF', 'color: rgb(29, 155, 240);', '', 'color: rgb(0, 186, 124);', ...msg); }; function log(...msg) { return console.log('[%cUserJS%c] %cLOG', 'color: rgb(29, 155, 240);', '', 'color: rgb(219, 160, 73);', ...msg); }; //#endregion if(!win.frameElement) { setupConfig(); }; })();