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-01-29. 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. // @namespace https://greatest.deepsurf.us/users/424058
  5. // @version 1.0.0
  6. // @author Konf
  7. // @match https://greatest.deepsurf.us
  8. // @include /^http(s|):\/\/greatest.deepsurf.us\/[a-zA-Z_-]*\/scripts\/\d.*$/
  9. // @run-at document-end
  10. // @resource iconDownload https://img.icons8.com/pastel-glyph/64/ffffff/download.png
  11. // @compatible Chrome
  12. // @compatible Opera
  13. // @compatible Firefox
  14. // @grant GM_getResourceUrl
  15. // @grant GM.getResourceUrl
  16. // @noframes
  17. // ==/UserScript==
  18.  
  19. /* jshint esversion: 6 */
  20. /* eslint-disable no-multi-spaces */
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25. const userLang = location.pathname.split('/')[1];
  26. const langStringsList = {
  27. en: {
  28. Dl: 'Download without installing',
  29. unableDl: 'Unable to download the script'
  30. },
  31. ru: {
  32. Dl: 'Скачать не устанавливая',
  33. unableDl: 'Не удалось скачать скрипт'
  34. }
  35. };
  36. const langStrings = langStringsList[userLang] || langStringsList.en;
  37.  
  38. (GM.getResourceUrl || GM_getResourceUrl)('iconDownload')
  39. .then(downloadIconUrl => {
  40. addCSS(`
  41. .tm-dsb-downloadBtn {
  42. width: 43px;
  43. height: 38px;
  44. position: relative;
  45. padding: 0;
  46. vertical-align: bottom;
  47. cursor: pointer;
  48. border: none;
  49. outline: none;
  50. background: #0F750F;
  51. transition: box-shadow 0.2s;
  52. }
  53.  
  54. .tm-dsb-downloadBtn:hover,
  55. .tm-dsb-downloadBtn:focus {
  56. box-shadow: 0 8px 16px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
  57. }
  58.  
  59.  
  60. .tm-dsb-downloadBtn__icon {
  61. position: absolute;
  62. }
  63.  
  64. .tm-dsb-downloadBtn__icon--download {
  65. width: 35px;
  66. height: 35px;
  67. top: 1px;
  68. left: 4px;
  69. background: url(${downloadIconUrl});
  70. background-size: contain;
  71. }
  72.  
  73. .tm-dsb-downloadBtn__icon--loading,
  74. .tm-dsb-downloadBtn__icon--loading:after {
  75. border-radius: 50%;
  76. width: 16px;
  77. height: 16px;
  78. }
  79.  
  80. .tm-dsb-downloadBtn__icon--loading {
  81. top: 6px;
  82. left: 8px;
  83. text-indent: -9999em;
  84. border-top: 5px solid rgba(255, 255, 255, 0.2);
  85. border-right: 5px solid rgba(255, 255, 255, 0.2);
  86. border-bottom: 5px solid rgba(255, 255, 255, 0.2);
  87. border-left: 5px solid #ffffff;
  88. -webkit-transform: translateZ(0);
  89. -ms-transform: translateZ(0);
  90. transform: translateZ(0);
  91. -webkit-animation: loading 1.1s infinite linear;
  92. animation: loading 1.1s infinite linear;
  93. }
  94.  
  95. @-webkit-keyframes loading {
  96. 0% {
  97. -webkit-transform: rotate(0deg);
  98. transform: rotate(0deg);
  99. }
  100. 100% {
  101. -webkit-transform: rotate(360deg);
  102. transform: rotate(360deg);
  103. }
  104. }
  105.  
  106. @keyframes loading {
  107. 0% {
  108. -webkit-transform: rotate(0deg);
  109. transform: rotate(0deg);
  110. }
  111. 100% {
  112. -webkit-transform: rotate(360deg);
  113. transform: rotate(360deg);
  114. }
  115. }
  116. `);
  117. });
  118.  
  119. const b = document.createElement('button'); // downloadBtn
  120. const bIcon = document.createElement('div');
  121. const bParent = document.body.querySelector('div#install-area');
  122. const bNeighbour = document.body.querySelector('a.install-help-link');
  123. const installBtn = document.body.querySelector('a.install-link');
  124.  
  125. b.title = langStrings.Dl;
  126. b.className = 'tm-dsb-downloadBtn';
  127. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--download';
  128. bNeighbour.style.position = 'relative'; // shadow bugfix
  129. bParent.insertBefore(b, bNeighbour);
  130. b.appendChild(bIcon);
  131.  
  132. let fetchingDelay = false;
  133.  
  134. b.addEventListener('click', () => {
  135. setTimeout(() => {
  136. if (b === document.activeElement) document.activeElement.blur();
  137. }, 750);
  138.  
  139. if (fetchingDelay) return;
  140. fetchingDelay = true;
  141. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--loading';
  142.  
  143. fetch(installBtn.href)
  144. .then(res => res.blob())
  145. .then(blob => {
  146. const url = window.URL.createObjectURL(blob);
  147. const a = document.createElement('a');
  148.  
  149. a.href = url;
  150. a.download = `${installBtn.dataset.scriptName}.user.js`;
  151. document.body.appendChild(a); // needed due to firefox bug
  152. a.click();
  153. a.remove();
  154.  
  155. setTimeout(closeFetchUX, 300);
  156. window.URL.revokeObjectURL(url);
  157. })
  158. .catch(e => {
  159. setTimeout(closeFetchUX, 300);
  160. alert(`${langStrings.unableDl}: \n${e}`);
  161. });
  162. });
  163.  
  164.  
  165. // utils -------------------------------------------------------------
  166.  
  167. function closeFetchUX() {
  168. fetchingDelay = false;
  169. bIcon.className = 'tm-dsb-downloadBtn__icon tm-dsb-downloadBtn__icon--download';
  170. }
  171.  
  172. function addCSS(cssCode) {
  173. const styleEl = document.createElement("style");
  174. styleEl.type = "text/css";
  175.  
  176. if (styleEl.styleSheet) {
  177. styleEl.styleSheet.cssText = cssCode;
  178. } else {
  179. styleEl.appendChild(document.createTextNode(cssCode));
  180. }
  181.  
  182. document.getElementsByTagName("head")[0].appendChild(styleEl);
  183.  
  184. return styleEl;
  185. }
  186.  
  187. // --------------------------------------------------------------------
  188.  
  189. })();