您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
If you have a script manager and you want to download some script without installing it, this script will help
当前为
// ==UserScript== // @name GreasyFork: download script button // @description If you have a script manager and you want to download some script without installing it, this script will help // @author Konf // @version 2.0.0 // @namespace https://greasyforks.org/users/424058 // @icon https://greasyforks.org/vite/assets/blacklogo96-e0c2c761.png // @match https://greasyforks.org/*/scripts/* // @match https://sleazyfork.org/*/scripts/* // @compatible Chrome // @compatible Opera // @compatible Firefox // @run-at document-end // @grant GM_addStyle // @noframes // ==/UserScript== /* jshint esversion: 8 */ (function() { 'use strict'; const i18n = { download: 'download', downloadWithoutInstalling: 'downloadWithoutInstalling', failedToDownload: 'failedToDownload', }; const translate = (function() { const userLang = location.pathname.split('/')[1]; const strings = { 'en': { [i18n.download]: 'Download ⇩', [i18n.downloadWithoutInstalling]: 'Download without installing', [i18n.failedToDownload]: 'Failed to download the script. There is might be more info in the browser console' }, 'ru': { [i18n.download]: 'Скачать ⇩', [i18n.downloadWithoutInstalling]: 'Скачать не устанавливая', [i18n.failedToDownload]: 'Не удалось скачать скрипт. Больше информации может быть в консоли браузера' }, 'zh-CN': { [i18n.download]: '下载 ⇩', [i18n.downloadWithoutInstalling]: '下载此脚本', [i18n.failedToDownload]: '无法下载此脚本' }, }; return id => (strings[userLang] || strings.en)[id] || strings.en[id]; }()); const installBtn = document.querySelector('a.install-link'); const installArea = document.querySelector('div#install-area'); const installHelpLink = document.querySelector('a.install-help-link'); const suggestions = document.querySelector('div#script-feedback-suggestion'); const libraryRequire = document.querySelector('div#script-content > p > code'); // if a script is detected if (installBtn && installArea && installHelpLink) { mountScriptDownloadButton(installBtn, installArea, installHelpLink); } else if (suggestions && libraryRequire) { // or maybe a library mountLibraryDownloadButton(suggestions, libraryRequire); } function mountScriptDownloadButton( installBtn, installArea, installHelpLink, ) { if (!installBtn.href) throw new Error('script href is not found'); // https://img.icons8.com/pastel-glyph/64/ffffff/download.png // array to fold the string in a code editor const downloadIconBase64 = [ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaX', 'HeAAAABmJLR0QA/wD/AP+gvaeTAAABgUlEQVR4nO3ZTU6DUAAE4HnEk+jWG3TrHV', 'wY3XoEt23cGleamtRtTbyPS3sCV0bXjptHRAIEsM/hZ76kCZRHGaZAGwDMzMzMbJ', '6CasMkMwBncXYbQvhSZZEgecEf56ocmWrDAA4L00eqEMoCBsEFqAOouQB1ADUXoA', '6g5gLUAdRcgDqAmgtQB1BzAeoAakkLIHlN8pPkDcnWd59IBpK3cd1VyoxJkfwo3P', 'V5KJZAcllYtiy8H+LY3HvKjKlPgU1h+hLAuulIiMvWcWzVZ4xL/Dbv+Nsjyax8BM', 'Sx96Wxm3jzdLwaSliVCpjezucqzmuSfKuZJkvXi0moORKqTOebL2tRwnR3PtdQwv', 'R3PldRgmznlc8GA4DTOPscQqAqy6x1+X8+6Ke5yfNxIE9z6/TN1+XCM4inuQ165Z', 'vHz04DF6AOoOYC1AHUXIA6gNpBz/UWJK/2muTvFn1W6lvASXyNXpdTYJcsxf69th', '3Y5QjYAiCA485x/tcLgCd1CDMzMzMbum8+xtkWw6QCvwAAAABJRU5ErkJggg==', ].join(''); GM_addStyle([` .GF-DSB__script-download-button { position: relative; padding: 0.5em 1.38em; cursor: pointer; border: none; background: #0F750F; transition: box-shadow 0.2s; } .GF-DSB__script-download-button:hover, .GF-DSB__script-download-button:focus { box-shadow: 0 8px 16px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%); } .GF-DSB__script-download-icon { position: absolute; } .GF-DSB__script-download-icon--download { width: 30px; height: 30px; top: 4px; left: 7px; } .GF-DSB__script-download-icon--loading, .GF-DSB__script-download-icon--loading:after { border-radius: 50%; width: 16px; height: 16px; } .GF-DSB__script-download-icon--loading { top: 8px; left: 11px; border-top: 3px solid rgba(255, 255, 255, 0.2); border-right: 3px solid rgba(255, 255, 255, 0.2); border-bottom: 3px solid rgba(255, 255, 255, 0.2); border-left: 3px solid #ffffff; transform: translateZ(0); object-position: -99999px; animation: GF-DSB__script-download-loading-icon 1.1s infinite linear; } @keyframes GF-DSB__script-download-loading-icon { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `][0]); const b = document.createElement('a'); const bIcon = document.createElement('img'); b.href = '#'; b.title = translate(i18n.downloadWithoutInstalling); b.draggable = false; b.className = 'GF-DSB__script-download-button'; bIcon.src = downloadIconBase64; bIcon.draggable = false; bIcon.className = 'GF-DSB__script-download-icon GF-DSB__script-download-icon--download'; installHelpLink.style.position = 'relative'; // shadows bugfix b.appendChild(bIcon); installArea.insertBefore(b, installHelpLink); b.addEventListener('click', clicksHandler); b.addEventListener('auxclick', e => e.button === 1 && clicksHandler(e)); // against doubleclicks let isFetchingAllowed = true; async function clicksHandler(ev) { ev.preventDefault(); setTimeout(() => b === document.activeElement && b.blur(), 250); if (isFetchingAllowed === false) return; isFetchingAllowed = false; bIcon.className = 'GF-DSB__script-download-icon GF-DSB__script-download-icon--loading'; try { await downloadScript({ href: installBtn.href, name: installBtn.dataset.scriptName, }); } catch (e) { console.error(e); alert(`${translate(i18n.failedToDownload)}: \n${e}`); } finally { setTimeout(() => { isFetchingAllowed = true; bIcon.className = 'GF-DSB__script-download-icon GF-DSB__script-download-icon--download'; }, 300); } } } function mountLibraryDownloadButton(suggestions, libraryRequire) { const [ libraryHref, libraryName, ] = libraryRequire.innerText.match( /\/\/ @require (https:\/\/.+\/scripts\/\d+.+\/code\/(.*)\.js\?version=\d+)/ ).slice(1); // this probably is completely useless but whatever if (!libraryHref) throw new Error('library href is not found'); GM_addStyle([` .GF-DSB__library-download-button { transition: box-shadow 0.2s; } .GF-DSB__library-download-button--loading { animation: GF-DSB__loading-text 1s infinite linear; } @keyframes GF-DSB__loading-text { 50% { opacity: 0.4; } } `][0]); const b = document.createElement('a'); b.href = '#'; b.draggable = false; b.innerText = translate(i18n.download); b.className = 'GF-DSB__library-download-button'; suggestions.appendChild(b); b.addEventListener('click', clicksHandler); b.addEventListener('auxclick', e => e.button === 1 && clicksHandler(e)); // against doubleclicks let isFetchingAllowed = true; async function clicksHandler(ev) { ev.preventDefault(); setTimeout(() => b === document.activeElement && b.blur(), 250); if (isFetchingAllowed === false) return; isFetchingAllowed = false; b.className = 'GF-DSB__library-download-button GF-DSB__library-download-button--loading'; try { await downloadScript({ href: libraryHref, name: libraryName, isLibrary: true, }); } catch (e) { console.error(e); alert(`${translate(i18n.failedToDownload)}: \n${e}`); } finally { setTimeout(() => { isFetchingAllowed = true; b.className = 'GF-DSB__library-download-button'; }, 300); } } } // utils -------------------------------------------------------------------- async function downloadScript({ href, name, isLibrary = false, } = {}) { if (!href) throw new Error('href is missing'); const blob = await (await fetch(href)).blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${name || Date.now()}${!isLibrary ? '.user' : ''}.js`; document.body.appendChild(a); // is needed due to firefox bug a.click(); a.remove(); window.URL.revokeObjectURL(url); } }());