github加速按钮

为github中的特定链接(releases、文件、项目地址)添加一个悬浮按钮,提供代理后的加速链接

  1. // ==UserScript==
  2. // @name gh-proxy-buttons
  3. // @name:zh-CN github加速按钮
  4. // @namespace https://github.com/du33169/gh-proxy-buttons
  5. // @version 0.7
  6. // @license MPL-2.0
  7. // @require https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.6/clipboard.min.js
  8. // @description add a button beside github link(releases,files and repository url), click to get alternative url according to previously specified proxy.
  9. // @description:zh-CN 为github中的特定链接(releases、文件、项目地址)添加一个悬浮按钮,提供代理后的加速链接
  10. // @author du33169
  11. // @match *://github.com/*
  12. // @grant none
  13. // @run-at document-end
  14. // ==/UserScript==
  15. (function()
  16. {
  17. 'use strict';
  18. //--------------------------代理设置-------------------------------
  19. //用于在线代理的workers地址
  20. const proxy_url= 'https://gh-proxy.du33169.workers.dev/';
  21. /*
  22. 备用: 'https://gh.api.99988866.xyz/'; (来自gh-proxy项目作者)
  23. 代理服务器地址可自行修改,末尾斜杠不可省略!
  24. */
  25. //--------------------------其他设置------------------------------
  26. //是否打开调试输出
  27. const open_log=true;
  28. //鼠标离开链接或按钮后消失前等待的时间
  29. const fade_timeout=100;//ms
  30.  
  31. //--------------------------功能代码------------------------------
  32. function isRepoFile(ourTarget){
  33. return (ourTarget.tagName=='A'
  34. &&ourTarget.getAttribute('aria-label')!=null//利用&&短路特性防止没有属性的元素导致脚本终止
  35. &&ourTarget.classList.contains("Link--primary")
  36. &&ourTarget.getAttribute('aria-label').endsWith('(File)')
  37. &&ourTarget.closest('#js-repo-pjax-container')!=null
  38. );
  39. }
  40. function isReleaseAsset(ourTarget){
  41. return (ourTarget.tagName=='A'
  42. &&ourTarget.getAttribute('rel')!=null
  43. &&ourTarget.rel=="nofollow"
  44. //&&/github.com/.test(ourTarget.href)==true
  45. &&(ourTarget.closest('#repo-content-pjax-container')!=null)||(ourTarget.closest('.js-truncated-assets-fragment')!=null)
  46. );
  47. }
  48. function isDownloadZip(e){
  49. return (e.tagName=='A'
  50. &&e.classList.contains("kHKEGN")
  51. &&e.getAttribute('rel')!=null
  52. &&e.rel=="nofollow"
  53. &&e.getAttribute('role')!=null
  54. &&e.role=="menuitem"
  55. );
  56. }
  57. function isHttpCopyGit(e){
  58. return (
  59. e.tagName=='BUTTON'
  60. &&e.classList.contains('hUTZcL')
  61. &&e.previousElementSibling!=null
  62. &&e.previousElementSibling.tagName=='INPUT'
  63. &&e.previousElementSibling.value.startsWith('https')
  64. );
  65. }
  66.  
  67.  
  68. function getBtn(originLink,filename,copy=false)
  69. {
  70. var ghBtn=document.getElementById("gh-proxy-button");
  71. //existed
  72. if(!ghBtn){
  73. //not existed, create instance
  74. ghBtn=document.createElement('a');
  75. ghBtn.setAttribute('class','btn');
  76. ghBtn.id="gh-proxy-button";
  77. ghBtn.title="[gh-proxy-buttons] get proxy link";
  78. ghBtn.style.position="absolute";
  79. ghBtn.role="button";
  80. ghBtn.style.zIndex=9999;
  81. //ghBtn.style.top=0;//must be set for transition
  82. //ghBtn.style.left=0;
  83. //ghBtn.style.transition="transform 0.5s ease-in-out";
  84.  
  85. ghBtn.addEventListener('mouseenter',function(){
  86. if(open_log)console.debug('[gh-proxy-buttons] onbtn');
  87. if(ghBtn.timerId !=undefined && ghBtn.timerId!=-1){
  88. clearTimeout(ghBtn.timerId);
  89. ghBtn.timerId=-1;
  90. }
  91. });
  92. ghBtn.addEventListener('mouseleave',function(){
  93. if(open_log)console.debug('[gh-proxy-buttons] mouseleave-btn');
  94. if(ghBtn.timerId !=undefined && ghBtn.timerId!=-1)return;
  95. ghBtn.timerId=setTimeout(function(){
  96. ghBtn.style.visibility="hidden";
  97. ghBtn.timerId=-1;
  98. if(open_log)console.debug('[gh-proxy-buttons] timeout fade mouseleave-btn');
  99. },fade_timeout);
  100. if(open_log)console.debug('[gh-proxy-buttons] btn leave timerid:',ghBtn.timerId);
  101. });
  102. document.documentElement.appendChild(ghBtn);
  103. }
  104. //now gh-proxy-button exists
  105. if(copy)//仓库地址input标签特殊处理,使用ClipboardJS实现点击复制
  106. {
  107. ghBtn.removeAttribute('target');
  108. ghBtn.removeAttribute('download');
  109. ghBtn.href="javascript:void(0)";
  110. ghBtn.innerText="🚀📄";
  111. ghBtn.clip=new ClipboardJS(ghBtn);
  112. ghBtn.clip.on('success',function(){
  113. ghBtn.innerText="🚀📄✔";
  114. });
  115. ghBtn.setAttribute('data-clipboard-text',proxy_url+originLink);
  116.  
  117. //console.log('[gh-proxy-buttons] input url processed');
  118. }
  119. else{
  120. ghBtn.innerText="🚀";
  121. ghBtn.target="_blank";
  122. if(ghBtn.clip)ghBtn.clip.destroy();
  123. ghBtn.clip=undefined;
  124. ghBtn.download=filename;
  125. ghBtn.removeAttribute('data-clipboard-text');
  126. ghBtn.href=proxy_url+originLink;
  127. }
  128.  
  129. return ghBtn;
  130. }
  131.  
  132. console.log('[gh-proxy-buttons] processing...');
  133. function moveHere(e,originLink,copy=false)//用于注册(不可用)mouseenter事件,e为当前元素
  134. {
  135. //创建按钮对象,github中使用.btn的class可以为<a>标签加上按钮外观
  136. var btn=getBtn(originLink,e.title,copy);
  137. if(open_log)console.debug("[gh-proxy-buttons]",btn);
  138. //e.parentElement.insertBefore(btn,e);
  139. if(btn.timerId !=undefined && btn.timerId!=-1){
  140. clearTimeout(btn.timerId);
  141. btn.timerId=-1;
  142. }
  143. const rect = e.getBoundingClientRect();
  144. const btnRect=btn.getBoundingClientRect();
  145. const x = rect.left + window.scrollX - btnRect.width;
  146. const y = rect.top + window.scrollY;
  147.  
  148. console.log(`Element coordinates (relative to document): x: ${x}, y: ${y}`);
  149.  
  150. btn.style.left=`${x}px`;
  151. btn.style.top =`${y}px`;
  152. if(btn.style.visibility=="visible"){
  153. //btn.style.transform = `translate(${event.x}px, ${event.y}px)`;
  154. }
  155. else{
  156. //btn.style.transform="";
  157. btn.style.visibility="visible";
  158. }
  159.  
  160. if(open_log)console.debug(`[gh-proxy-buttons] mousein, move btn to ${event.x},${event.y}`);
  161. e.addEventListener('mouseleave',function(){
  162. if(open_log)console.debug('[gh-proxy-buttons] mouseleave-target');
  163. if(btn.timerId !=undefined && btn.timerId!=-1)return;
  164. btn.timerId=setTimeout(function(){
  165. btn.style.visibility="hidden";
  166. btn.timerId=-1;
  167. if(open_log)console.debug('[gh-proxy-buttons] timeout fade mouseleave-target');
  168. },fade_timeout);
  169. if(open_log)console.debug('[gh-proxy-buttons] target leave timerid:',btn.timerId);
  170. });
  171. }
  172.  
  173. function eventDelegation(e)
  174. {
  175. // e.target 是事件触发的元素
  176. if(e.target!=null)
  177. {
  178. var ourTarget=e.target;
  179. while(ourTarget!=e.currentTarget&&ourTarget.tagName!='A'&&ourTarget.tagName!='BUTTON')//releases页面触发元素为<a>内的span,需要上浮寻找
  180. {
  181. ourTarget=ourTarget.parentNode;
  182. }
  183. if(ourTarget==e.currentTarget)return;
  184. //if(open_log)console.debug('[gh-proxy-buttons]','found A',ourTarget);
  185. if(isRepoFile(ourTarget)||isReleaseAsset(ourTarget)||isDownloadZip(ourTarget))
  186. {
  187. console.log('[gh-proxy-buttons] ','found target',ourTarget);
  188. moveHere(ourTarget,ourTarget.href);
  189. }else if(isHttpCopyGit(ourTarget)){
  190. console.log('[gh-proxy-buttons] ','found target copy button',ourTarget);
  191. moveHere(ourTarget,ourTarget.previousElementSibling.value,true);
  192. }
  193. }
  194. }
  195. document.body.addEventListener("mouseover", eventDelegation);
  196. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址