GreasyFork: download script button

If you have a script manager and you want to download some script without installing it, this script will help

As of 2021-12-24. See the latest version.

  1. // ==UserScript==
  2. // @name GreasyFork: download script button
  3. // @description If you have a script manager and you want to download some script without installing it, this script will help
  4. // @author Konf
  5. // @version 1.2.1
  6. // @namespace https://greatest.deepsurf.us/users/424058
  7. // @icon https://www.google.com/s2/favicons?domain=greatest.deepsurf.us&sz=32
  8. // @include /^http(s|):\/\/(sleaz|greas)yfork\.org\/[a-zA-Z_-]*\/scripts\/\d.*$/
  9. // @compatible Chrome
  10. // @compatible Opera
  11. // @compatible Firefox
  12. // @run-at document-end
  13. // @grant GM_addStyle
  14. // @noframes
  15. // ==/UserScript==
  16.  
  17. /* jshint esversion: 6 */
  18. /* eslint-disable no-multi-spaces */
  19.  
  20. (function() {
  21. 'use strict';
  22.  
  23. const langStrings = (function() {
  24. const userLang = location.pathname.split('/')[1];
  25. const langStringsList = {
  26. 'en': {
  27. Dl: 'Download without installing',
  28. failedDl: 'Failed to download the script. ' +
  29. 'More info might be in console'
  30. },
  31. 'zh-CN': {
  32. Dl: '下载此脚本',
  33. failedDl: '无法下载此脚本'
  34. },
  35. 'ru': {
  36. Dl: 'Скачать не устанавливая',
  37. failedDl: 'Не удалось скачать скрипт. ' +
  38. 'Больше информации может быть в консоли'
  39. }
  40. };
  41.  
  42. return function(id) {
  43. const userStrings = langStringsList[userLang];
  44. const enStrings = langStringsList.en;
  45.  
  46. return (userStrings || enStrings)[id] || enStrings[id];
  47. }
  48. }());
  49.  
  50. // https://img.icons8.com/pastel-glyph/64/ffffff/download.png
  51. // array because tampermonkey can't roll up a string in the code editor
  52. const downloadIcon = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE',
  53. 'AAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAABgUlEQVR4nO3ZTU6DUAAE4HnE',
  54. 'k+jWG3TrHVwY3XoEt23cGleamtRtTbyPS3sCV0bXjptHRAIEsM/hZ76kCZRHGaZAGwDMzM',
  55. 'zMbJ6CasMkMwBncXYbQvhSZZEgecEf56ocmWrDAA4L00eqEMoCBsEFqAOouQB1ADUXoA6g',
  56. '5gLUAdRcgDqAmgtQB1BzAeoAakkLIHlN8pPkDcnWd59IBpK3cd1VyoxJkfwo3PV5KJZAcl',
  57. 'lYtiy8H+LY3HvKjKlPgU1h+hLAuulIiMvWcWzVZ4xL/Dbv+Nsjyax8BMSx96Wxm3jzdLwa',
  58. 'SliVCpjezucqzmuSfKuZJkvXi0moORKqTOebL2tRwnR3PtdQwvR3PldRgmznlc8GA4DTOP',
  59. 'scQqAqy6x1+X8+6Ke5yfNxIE9z6/TN1+XCM4inuQ165ZvHz04DF6AOoOYC1AHUXIA6gNpB',
  60. 'z/UWJK/2muTvFn1W6lvASXyNXpdTYJcsxf69th3Y5QjYAiCA485x/tcLgCd1CDMzMzMbum',
  61. '8+xtkWw6QCvwAAAABJRU5ErkJggg=='].join('');
  62.  
  63. const b = document.createElement('button'); // downloadBtn
  64. const bIcon = document.createElement('div');
  65. const bParent = document.body.querySelector('div#install-area');
  66. const bNeighbour = document.body.querySelector('a.install-help-link');
  67. const installBtn = document.body.querySelector('a.install-link');
  68.  
  69. if (!bParent || !bNeighbour || !installBtn) return;
  70.  
  71. GM_addStyle([`
  72. .tm-dsb-downloadBtn {
  73. width: 43px;
  74. height: 38px;
  75. position: relative;
  76. padding: 0;
  77. vertical-align: bottom;
  78. cursor: pointer;
  79. border: none;
  80. outline: none;
  81. background: #0F750F;
  82. transition: box-shadow 0.2s;
  83. }
  84.  
  85. .tm-dsb-downloadBtn:hover,
  86. .tm-dsb-downloadBtn:focus {
  87. box-shadow: 0 8px 16px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
  88. }
  89.  
  90.  
  91. .tm-dsb-downloadBtn__icon {
  92. position: absolute;
  93. }
  94.  
  95. .tm-dsb-downloadBtn__icon--download {
  96. width: 35px;
  97. height: 35px;
  98. top: 1px;
  99. left: 4px;
  100. background: url(${downloadIcon});
  101. background-size: contain;
  102. }
  103.  
  104. .tm-dsb-downloadBtn__icon--loading,
  105. .tm-dsb-downloadBtn__icon--loading:after {
  106. border-radius: 50%;
  107. width: 16px;
  108. height: 16px;
  109. }
  110.  
  111. .tm-dsb-downloadBtn__icon--loading {
  112. top: 6px;
  113. left: 8px;
  114. border-top: 5px solid rgba(255, 255, 255, 0.2);
  115. border-right: 5px solid rgba(255, 255, 255, 0.2);
  116. border-bottom: 5px solid rgba(255, 255, 255, 0.2);
  117. border-left: 5px solid #ffffff;
  118. transform: translateZ(0);
  119. animation: loading 1.1s infinite linear;
  120. }
  121.  
  122. @keyframes loading {
  123. 0% {
  124. transform: rotate(0deg);
  125. }
  126. 100% {
  127. transform: rotate(360deg);
  128. }
  129. }
  130. `][0]);
  131.  
  132. b.title = langStrings('Dl');
  133. b.className = 'tm-dsb-downloadBtn';
  134. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--download';
  135. bNeighbour.style.position = 'relative'; // shadow bugfix
  136. bParent.insertBefore(b, bNeighbour);
  137. b.appendChild(bIcon);
  138.  
  139. let fetchingDelay = false;
  140.  
  141. b.addEventListener('click', () => {
  142. setTimeout(() => {
  143. if (b === document.activeElement) b.blur();
  144. }, 250);
  145.  
  146. if (fetchingDelay) return;
  147. fetchingDelay = true;
  148. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--loading';
  149.  
  150. fetch(installBtn.href)
  151. .then(res => res.blob())
  152. .then(blob => {
  153. const url = window.URL.createObjectURL(blob);
  154. const a = document.createElement('a');
  155.  
  156. a.href = url;
  157. a.download = `${installBtn.dataset.scriptName || Date.now()}.user.js`;
  158. document.body.appendChild(a); // needed due to firefox bug
  159. a.click();
  160. a.remove();
  161.  
  162. setTimeout(closeFetchUX, 300);
  163. window.URL.revokeObjectURL(url);
  164. })
  165. .catch(e => {
  166. setTimeout(closeFetchUX, 300);
  167. alert(`${langStrings('failedDl')}: \n${e}`);
  168. });
  169. });
  170.  
  171.  
  172. // utils -------------------------------------------------------------
  173.  
  174. function closeFetchUX() {
  175. fetchingDelay = false;
  176. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--download';
  177. }
  178.  
  179. // --------------------------------------------------------------------
  180.  
  181. })();