Last active
April 20, 2023 03:56
-
-
Save mbtolou/7d917068108091e1682707f1c479b9a0 to your computer and use it in GitHub Desktop.
GitHub Stars-Fork Count
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name GitHub Stars-Fork Count | |
// @version 1.110 | |
// @description A userscript that add stars to github link | |
// @license MIT | |
// @author Mohamad Tolou | |
// @namespace https://github.com/mbtolou | |
// @include https://github.com/* | |
// @include https://stackoverflow.com/* | |
// @run-at document-end | |
// @grant GM_addStyle | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// @grant GM_registerMenuCommand | |
// @grant GM.xmlHttpRequest | |
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js | |
// @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=666427 | |
// @icon https://github.githubassets.com/pinned-octocat.svg | |
// @updateURL https://gist.github.com/mbtolou/7d917068108091e1682707f1c479b9a0#file-github_stars-user-js | |
// @downloadURL https://gist.github.com/mbtolou/7d917068108091e1682707f1c479b9a0#file-github_stars-user-js | |
// ==/UserScript== | |
let busy = false; | |
const badgesAdded = []; | |
const reservedUsername = [ | |
"topics", | |
"search", | |
"ghost", | |
"pulls", | |
"issues", | |
"marketplace", | |
"explore", | |
"discover", | |
"notifications", | |
"new", | |
"organizations", | |
"settings", | |
"site", | |
"about", | |
"contact", | |
"pricing", | |
"apps", | |
"features", | |
"password_reset", | |
"trending" | |
]; | |
const allBadgeClasses = [ | |
"added-stars-badge", | |
"added-last-commit-badge", | |
"added-followers-badge", | |
]; | |
class URLParseResult { | |
constructor({ user, repo }) { | |
this.user = user; | |
this.repo = repo; | |
} | |
equals(other) { | |
return other.repo === this.repo && other.user === this.user; | |
} | |
} | |
URLParseResult.EMPTY = new URLParseResult({}); | |
function parseURL(v) { | |
const match = v.match(/^https?:\/\/github.com\/([^/]*?)(?:\/([^/]*?))?(?:\.git)?(?:[#?].*)?(?:$|\/)/); | |
if (!match ) { | |
return URLParseResult.EMPTY; | |
} | |
if (reservedUsername.includes(match[1])) { | |
return URLParseResult.EMPTY; | |
} | |
return new URLParseResult({ | |
user: match[1], | |
repo: match[2], | |
}); | |
} | |
async function appendBadge(el, className, url) { | |
if (el.classList.contains(className)) { | |
return; | |
} | |
return new Promise((resolve, reject) => { | |
GM.xmlHttpRequest({ | |
method: "GET", | |
url: url, | |
onload: resp => { | |
if (resp.status === 200) { | |
if (!el.classList.contains(className)) { | |
const img = document.createElement("img"); | |
img.src = `data:image/svg+xml;base64,${btoa(resp.response)}`; | |
const containerClassNames = [ | |
"natescarlet-gmail-com", | |
"badge-container", | |
]; | |
const selector = containerClassNames.map(i => "." + i).join(""); | |
/** @type {HTMLElement} */ | |
const container = el.querySelector(selector) || document.createElement("span"); | |
container.style="vertical-align: middle;"; | |
el.appendChild(container); | |
container.classList.add(...containerClassNames); | |
container.append(img); | |
img.style.order = allBadgeClasses.indexOf(className).toString(); | |
container.style.display = "inline-flex; "; | |
el.classList.add(className); | |
} | |
resolve(); | |
} | |
reject(`${resp.status}: ${url}`); | |
}, | |
onerror: reject, | |
}); | |
}); | |
} | |
async function appendStarsBadge(el) { | |
const { repo, user } = parseURL(el.href); | |
if (!(user && repo)) { | |
return; | |
} | |
await appendBadge(el, "added-stars-badge", `https://img.shields.io/github/stars/${user}/${repo}.svg?style=social`); | |
} | |
async function appendLastCommitBadge(el) { | |
const { repo, user } = parseURL(el.href); | |
if (!(user && repo)) { | |
return; | |
} | |
await appendBadge(el, "added-last-commit-badge", `https://img.shields.io/github/last-commit/${user}/${repo}.svg`); | |
} | |
async function appendFollowersBadge(el) { | |
const { user } = parseURL(el.href); | |
if (!user) { | |
return; | |
} | |
await appendBadge(el, "added-followers-badge", `https://img.shields.io/github/followers/${user}.svg?style=social`); | |
} | |
function getAbsoluteLink(a) { | |
let currentUrl = document.URL; | |
var url = a.getAttribute('href'); | |
// console.log(url); | |
if (!currentUrl.toLocaleLowerCase().startsWith("https://github.com")) { | |
return (a.getAttribute('href') || '').toLowerCase().trim(); | |
} | |
else { | |
if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) { | |
url = "https://github.com" + url; | |
} | |
// console.log(url); | |
const match = a.href.match(/^\s*https:\/\/github.com\/([^/#]+)\/([^/#]+)(?:[\/#].*)?$/i); | |
if (match) { | |
return url; | |
} | |
else return ''; | |
// url.replace('http://', '').replace('https://', '').replace('www.', '').split(/[/?#]/)[0]; | |
} | |
} | |
(async function () { | |
async function processStars(tempFormat) { | |
if (busy) { | |
return; | |
} | |
busy = true; | |
let selector = "a"; | |
if ($(selector)) { | |
let indx = 0; | |
const els = $$(selector), | |
len = els.length; | |
// loop with delay to allow user interaction | |
const loop = () => { | |
let currentUrl = document.location.href.toLowerCase() | |
let el, time, node, formatted, | |
// max number of DOM insertions per loop | |
max = 0; | |
while (max < 20 && indx < len) { | |
if (indx >= len) { | |
return; | |
} | |
el = els[indx]; | |
let absLink = getAbsoluteLink(el); | |
//console.log(absLink); | |
const match = absLink.match(/^\s*https:\/\/github.com\/([^/#]+)\/([^/#]+)(?:[\/#].*)?$/i); | |
if (match) { | |
const userName = match[1]; | |
const repoName = match[2]; | |
const url = `https://github.com/${userName.toLowerCase()}/${repoName.toLowerCase()}`; | |
if (currentUrl.startsWith(url)) { | |
// console.log("Do not replace badges on the repo page itself"); | |
indx++; | |
continue; | |
} | |
// Exclude Github's own pages | |
if (reservedUsername.includes(userName)) { | |
// console.log("Exclude Github's own pages"); | |
indx++; | |
continue; | |
} | |
// Only add each badge once | |
if (badgesAdded.some(badge => badge.url === url)) { | |
// console.log("Only add each badge once"); | |
indx++; | |
continue; | |
} | |
badgesAdded.push({ url, userName, repoName, el: el }); | |
try { | |
Promise.all([ | |
appendStarsBadge(el), | |
appendLastCommitBadge(el), | |
appendFollowersBadge(el), | |
]); | |
} | |
catch (err) { | |
console.error(err); | |
} | |
} | |
indx++; | |
} | |
if (indx < len) { | |
setTimeout(() => { | |
loop(); | |
}, 200); | |
} | |
}; | |
loop(); | |
} | |
busy = false; | |
} | |
function $(str, el) { | |
return (el || document).querySelector(str); | |
} | |
function $$(str, el) { | |
return Array.from((el || document).querySelectorAll(str)); | |
} | |
function on(el, name, handler) { | |
$(el).addEventListener(name, handler); | |
} | |
function init() { | |
//addPanel(); | |
//moment.locale(locale); | |
processStars(); | |
} | |
// repo file list needs additional time to render | |
document.addEventListener("ghmo:container", () => { | |
setTimeout(() => { | |
processStars(); | |
}, 100); | |
}); | |
document.addEventListener("ghmo:preview", processStars); | |
//console.log("start started"); | |
init(); | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment