gh-proxy-buttons

add a button beside github link(releases,files and repository url), click to get alternative url according to previously specified proxy.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         gh-proxy-buttons
// @name:zh-CN   github加速按钮
// @namespace    https://github.com/du33169/gh-proxy-buttons
// @version      0.7
// @license      MPL-2.0
// @require      https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.6/clipboard.min.js
// @description  add a button beside github link(releases,files and repository url), click to get alternative url according to previously specified proxy.
// @description:zh-CN  为github中的特定链接(releases、文件、项目地址)添加一个悬浮按钮,提供代理后的加速链接
// @author       du33169
// @match        *://github.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==
(function() 
{
    'use strict'; 
	
//--------------------------代理设置-------------------------------      
//用于在线代理的workers地址                                                                                                            
const proxy_url= 'https://gh-proxy.du33169.workers.dev/';
/*      
	备用: 'https://gh.api.99988866.xyz/';   (来自gh-proxy项目作者)
	代理服务器地址可自行修改,末尾斜杠不可省略!
*/
//--------------------------其他设置------------------------------
//是否打开调试输出
const open_log=true;
//鼠标离开链接或按钮后消失前等待的时间
const fade_timeout=100;//ms

//--------------------------功能代码------------------------------
    function isRepoFile(ourTarget){
        return (ourTarget.tagName=='A'
                &&ourTarget.getAttribute('aria-label')!=null//利用&&短路特性防止没有属性的元素导致脚本终止
                &&ourTarget.classList.contains("Link--primary")
                &&ourTarget.getAttribute('aria-label').endsWith('(File)')
                &&ourTarget.closest('#js-repo-pjax-container')!=null
               );
    }
    function isReleaseAsset(ourTarget){
        return (ourTarget.tagName=='A'
                &&ourTarget.getAttribute('rel')!=null
                &&ourTarget.rel=="nofollow"
                //&&/github.com/.test(ourTarget.href)==true
                &&(ourTarget.closest('#repo-content-pjax-container')!=null)||(ourTarget.closest('.js-truncated-assets-fragment')!=null)
               );
    }
    function isDownloadZip(e){
        return (e.tagName=='A'
                &&e.classList.contains("kHKEGN")
                &&e.getAttribute('rel')!=null
                &&e.rel=="nofollow"
                &&e.getAttribute('role')!=null
                &&e.role=="menuitem"
               );
    }
    function isHttpCopyGit(e){
        return (
            e.tagName=='BUTTON'
            &&e.classList.contains('hUTZcL')
            &&e.previousElementSibling!=null
            &&e.previousElementSibling.tagName=='INPUT'
            &&e.previousElementSibling.value.startsWith('https')
        );
    }


    function getBtn(originLink,filename,copy=false)
    {
        var ghBtn=document.getElementById("gh-proxy-button");
        //existed
        if(!ghBtn){
            //not existed, create instance
            ghBtn=document.createElement('a');
            ghBtn.setAttribute('class','btn');
            ghBtn.id="gh-proxy-button";
            ghBtn.title="[gh-proxy-buttons] get proxy link";
            ghBtn.style.position="absolute";
            ghBtn.role="button";
            ghBtn.style.zIndex=9999;
            //ghBtn.style.top=0;//must be set for transition
            //ghBtn.style.left=0;
            //ghBtn.style.transition="transform 0.5s ease-in-out";

            ghBtn.addEventListener('mouseenter',function(){
                if(open_log)console.debug('[gh-proxy-buttons] onbtn');
                if(ghBtn.timerId !=undefined && ghBtn.timerId!=-1){
                    clearTimeout(ghBtn.timerId);
                    ghBtn.timerId=-1;
                }
            });
            ghBtn.addEventListener('mouseleave',function(){
                if(open_log)console.debug('[gh-proxy-buttons] mouseleave-btn');
                if(ghBtn.timerId !=undefined && ghBtn.timerId!=-1)return;
                ghBtn.timerId=setTimeout(function(){
                    ghBtn.style.visibility="hidden";
                     ghBtn.timerId=-1;
                     if(open_log)console.debug('[gh-proxy-buttons] timeout fade mouseleave-btn');
                },fade_timeout);
                 if(open_log)console.debug('[gh-proxy-buttons] btn leave timerid:',ghBtn.timerId);
            });
            document.documentElement.appendChild(ghBtn);
        }
        //now gh-proxy-button exists
        if(copy)//仓库地址input标签特殊处理,使用ClipboardJS实现点击复制
		{
            ghBtn.removeAttribute('target');
            ghBtn.removeAttribute('download');
            ghBtn.href="javascript:void(0)";
			ghBtn.innerText="🚀📄";
			ghBtn.clip=new ClipboardJS(ghBtn);
            ghBtn.clip.on('success',function(){
                ghBtn.innerText="🚀📄✔";
            });
			ghBtn.setAttribute('data-clipboard-text',proxy_url+originLink);

			//console.log('[gh-proxy-buttons] input url processed');
		}
		else{
            ghBtn.innerText="🚀";
            ghBtn.target="_blank";
            if(ghBtn.clip)ghBtn.clip.destroy();
            ghBtn.clip=undefined;
            ghBtn.download=filename;
            ghBtn.removeAttribute('data-clipboard-text');
            ghBtn.href=proxy_url+originLink;
        }

        return ghBtn;
    }

	console.log('[gh-proxy-buttons] processing...');
	function moveHere(e,originLink,copy=false)//用于注册mouseenter事件,e为当前元素
	{
		//创建按钮对象,github中使用.btn的class可以为<a>标签加上按钮外观
		var btn=getBtn(originLink,e.title,copy);
        if(open_log)console.debug("[gh-proxy-buttons]",btn);
        //e.parentElement.insertBefore(btn,e);
        if(btn.timerId !=undefined && btn.timerId!=-1){
            clearTimeout(btn.timerId);
            btn.timerId=-1;
        }
        const rect = e.getBoundingClientRect();
        const btnRect=btn.getBoundingClientRect();
        const x = rect.left + window.scrollX - btnRect.width;
        const y = rect.top + window.scrollY;

        console.log(`Element coordinates (relative to document): x: ${x}, y: ${y}`);

        btn.style.left=`${x}px`;
        btn.style.top =`${y}px`;
        if(btn.style.visibility=="visible"){
            //btn.style.transform = `translate(${event.x}px, ${event.y}px)`;
        }
        else{
            //btn.style.transform="";
            btn.style.visibility="visible";
        }

		if(open_log)console.debug(`[gh-proxy-buttons] mousein, move btn to ${event.x},${event.y}`);
        e.addEventListener('mouseleave',function(){
                if(open_log)console.debug('[gh-proxy-buttons] mouseleave-target');
            if(btn.timerId !=undefined && btn.timerId!=-1)return;
                btn.timerId=setTimeout(function(){
                   btn.style.visibility="hidden";
                   btn.timerId=-1;
                     if(open_log)console.debug('[gh-proxy-buttons] timeout fade mouseleave-target');
                },fade_timeout);
            if(open_log)console.debug('[gh-proxy-buttons] target leave timerid:',btn.timerId);
            });
	}

    function eventDelegation(e)
	{
    // e.target 是事件触发的元素
		if(e.target!=null)
		{
            var ourTarget=e.target;
			while(ourTarget!=e.currentTarget&&ourTarget.tagName!='A'&&ourTarget.tagName!='BUTTON')//releases页面触发元素为<a>内的span,需要上浮寻找
			{
				ourTarget=ourTarget.parentNode;
			}
            if(ourTarget==e.currentTarget)return;
            //if(open_log)console.debug('[gh-proxy-buttons]','found A',ourTarget);
			if(isRepoFile(ourTarget)||isReleaseAsset(ourTarget)||isDownloadZip(ourTarget))
			{
				console.log('[gh-proxy-buttons] ','found target',ourTarget);
				moveHere(ourTarget,ourTarget.href);
			}else if(isHttpCopyGit(ourTarget)){
                console.log('[gh-proxy-buttons] ','found target copy button',ourTarget);
				moveHere(ourTarget,ourTarget.previousElementSibling.value,true);
            }
		}
	}
    document.body.addEventListener("mouseover", eventDelegation);
})();