// ==UserScript== // @name GitHub Remark // @namespace https://greasyfork.org/zh-CN/scripts/443857-github-remark // @version 0.1.0 // @description GitHub remark // @author Dorad // @license MIT License // @match https://github.com/* // @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.slim.min.js // @grant GM_downlaod // @grant GM_xmlhttpRequest // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC5klEQVRYR+2Xu49NURTGjZEomdC7iNZby8XUjKg9ghoRrQRRegStx4yaQSdB0CIepUe4/ANoROn7TfaSdfbZj3vGTCFxki/3zN5r7f2t95mRBfnnvbaWC/eEy8Lbgmxqa6kWdwnHhI3CAeFWLDiSObSn9Q/CIrd/U+8nhCXC+oBY/Y0WwDrhorDaCVzX+5FhCfQl+KSjxTXxxxIY/2cIEIKPwmjNrA77nUJADF9GOdDhrqToN62SjAO/m0tCKmDN396Y0G/lQYrAfilOzcPlduSGUCkzf6cI3Nb63nkkcEFnn7TzUwReaHNzRIC43Re+J4it0NqXDOF9Wl8V7TXCkCLwSwqLnZI1oNTlwzjqmoQOO8EqAZ+An50F5MbpgFZLdRdQQTeCVw4Frz3X75YgUyXwSII7g7D1757+9q15LBMO1B4K1vGYA1cEn9hVAt5l26X8NDqAS5KDJZD2IbTL+tqz1l5NQs92Qook326BqWiPrbulP681AkcledWkU0nIGMXdjGJj69dIxpWFEHgPmqe8UegOSgTYY45jMZdRRvySXHuEu0Lp2wCyWPlD4DuCx0q7NQ9yrRil8wLz/7VAUs62DE9J92w4Z0d8TokAJLYJlBRZT3uerFgfDJ75wYvHBRL5UiDRMiImANuF4SLf3SByUKCPEwq+enIeoTMS52cC4SKUuU7ZmgUcTufrBeapWCNDeS3z5rp3q/3MdnO5VAVfJZqK/TCjupHpJSa5HLDEeSVl2imewLX0A+JJmOwZhD1/VqPWZ0PA131KnxnxKXiIJHsgMMAoMwYPBpwbJgalKiDW00I8TjmXHOAhRJ4AHZKkmxMCZsBWvZD9PBAmBDkCkKHnzykB78l+uOA/gTvyBIka54CFoDFyS8lYa8Wxrk06yoySjAlQ//xDQ+fbJFTnRxcCWMVwmhTOCO+EnwKXQowvaapgbZCjTCFRfLoQwOVmEZOSzy7mPU2KPQhCwFq0l8+S+A28A6wh+zYOCgAAAABJRU5ErkJggg== // ==/UserScript== const key = 'GithubRemark-cache'; const defaultRemark = 'unset'; /** * API for cache data. */ function updateRemark(userToken, username, remark) { let cache = JSON.parse(localStorage.getItem(key)); if(!cache){ cache=[]; } const matchedItemIdx = cache.findIndex(e => e.username==username && e.userToken==userToken) if(matchedItemIdx>-1){ // 已存在 cache[matchedItemIdx].remark = remark; cache[matchedItemIdx].updatedAt = new Date().getTime() / 1000 }else{ const item={ userToken:userToken, username:username, remark:remark, updatedAt: new Date().getTime() / 1000 } cache.push(item); } console.log(cache); localStorage.setItem(key, JSON.stringify(cache)); // showRemarks(userToken); } function getRemark(userToken, username, callback) { let cache = JSON.parse(localStorage.getItem(key)); if(!cache){ callback(defaultRemark); return } const item = cache.find(e => e.username==username && e.userToken==userToken) if(item){ callback(item.remark); }else{ callback(defaultRemark); } } /** * * page functions */ function getGithubLoginUsername() { var doc = document.querySelector("head > meta[name*='login']"); return doc == null ? null : doc.content; } function hasLoginFrame() { var loginBtn = document.querySelector('div.HeaderMenu a[href*=login]'); return loginBtn != null; } function getMasterOfPage(url) { var master = /github.com\/([^\/|^\?]+)/.exec(url); if (master !== null) master = master[1]; return master; } function getCurrentTab() { var homepage = /github.com\/$/.exec(location.href); if (homepage !== null) return 'homepage'; var tab = /[\?|\&]tab=([^\&]+)/.exec(location.href); if (tab !== null) tab = tab[1]; if(/https:\/\/github.com\/orgs\/([\S\s]+)\/people/.exec(location.href)) tab = 'orgs-people'; if(/https:\/\/github.com\/orgs\/([\S\s]+)\/members/.exec(location.href)) tab = 'orgs-members'; return tab; } function insertAfter(newEl, targetEl) { var parentEl = targetEl.parentNode; if (parentEl.lastChild == targetEl) { parentEl.appendChild(newEl); } else { parentEl.insertBefore(newEl, targetEl.nextSibling); } } function generateRemarkSpan(className, userToken, username, remark){ var span = document.createElement('span'); span.className = className; span.textContent = '('+remark+')'; span.title = '('+remark+')'; span.addEventListener('dblclick', function (event) { console.log(event); const newRemark = changeRemarks(userToken, username, remark); if(newRemark!==remark){ span.replaceWith(generateRemarkSpan( className,userToken, username,newRemark )); } }, false); return span; } function clearRemarkOfCurrentNode(div){ if (!!div.querySelector('span.github-remarks')) div.removeChild(div.querySelector('span.github-remarks')); } /** * * Show remark functions, adapted for each page */ function showRemarkInHomepage(userToken) { var news = document.querySelector("#dashboard > div.news"); var userCount = document.querySelectorAll("div.flex-items-baseline > div > a[data-hovercard-type=user]").length; var observer = new MutationObserver(function (mutations, self) { var users = document.querySelectorAll("div.flex-items-baseline > div > a[data-hovercard-type=user]"); if (userCount != users.length) { userCount = users.length users.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); var username = getMasterOfPage(element.href); getRemark(userToken, username, function (remark) { var remarkEl = generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark); insertAfter(remarkEl, element); }); }, this); } }); observer.observe(news, { childList: true, subtree: true }); } function showRemarkInLeftPannel(userToken) { var vcard = document.querySelector('h1.vcard-names');//author in home page if (!!vcard) { if (vcard.childElementCount > 2) vcard.removeChild(vcard.querySelector('span.github-remarks')); var username = getMasterOfPage(location.href); getRemark(userToken, username, function (remark) { vcard.appendChild(generateRemarkSpan('vcard-username d-block github-remarks', userToken, username, remark)); }); } } function showRemarkInStarsTab(userToken) { var stars = document.querySelectorAll('div > h3 > a');//in star page if (stars !== null) { stars.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); if (!!element.querySelector('span.text-normal')) { var text = element.querySelector('span.text-normal').textContent; var username = text.substring(0, text.indexOf(' /')); getRemark(userToken, username, function (remark) { insertAfter(generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark), element); }); } }, this); } } function showRemarkInFollowersTab(userToken) { var followers = document.querySelectorAll('div.d-table > div:nth-child(2) > a');//in followers/following page if (!!followers) { followers.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); var username = element.querySelector('span:last-child').textContent; getRemark(userToken, username, function (remark) { insertAfter(generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark), element); }); }, this); } } function showRemarkInRepoStargazersPage(userToken) { var stargazers = document.querySelectorAll('div > h3 > span'); if (!!stargazers) { stargazers.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); var a = element.querySelector('a'); var username = getMasterOfPage(a.href); getRemark(userToken, username, function (remark) { var remarkEl = generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark) insertAfter(remarkEl, a); //如果username太长,截断显示,为remark留点位置 if (a.offsetWidth > element.clientWidth * 4 / 5) { a.style.width = element.clientWidth * 4 / 5 + 'px'; a.className += 'css-truncate-target'; remarkEl.style.width = element.clientWidth * 1 / 5 + 'px'; remarkEl.className += 'css-truncate-target'; } }); }, this); } } function showRemarkInRepoDetailPage(userToken) { var author = document.querySelector('span.author > a');//in a repo page if (!!author) { var username = getMasterOfPage(location.href); getRemark(userToken, username, function (remark) { author.textContent = username + '(' + remark + ')'; }); } var repoDetail = /\/(stargazers|watchers)(\/you_know)?$/.exec(location.href); if (repoDetail !== null) { switch (repoDetail[1]) { case 'watchers': case 'stargazers': showRemarkInRepoStargazersPage(userToken); break; } } } function showRemarkInOrgPeople(userToken){ var users = document.querySelectorAll('a[data-hovercard-type=user][id]'); if(!!users){ users.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); var username = getMasterOfPage(element.href); if(element.href.indexOf('orgs')>-1){ username = /https:\/\/github.com\/orgs\/([\S\s]+)\/people\/([\s\S]+)$/.exec(element.href)[2]; } getRemark(userToken, username, function (remark) { insertAfter(generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark), element); }); }, this) } } function showRemarkInOrgMembers(userToken){ var users = document.querySelectorAll('ul.member-listing > li > div > a[data-hovercard-type=user]'); if(!!users){ users.forEach(function (element) { clearRemarkOfCurrentNode(element.parentNode); var username = /https:\/\/github.com\/orgs\/([\S\s]+)\/people\/([\s\S]+)$/.exec(element.href)[2]; getRemark(userToken, username, function (remark) { insertAfter(generateRemarkSpan('link-gray pl-1 github-remarks', userToken, username, remark), element); }); }, this) } } function changeRemarks(userToken, username, oldValue) { var newValue = window.prompt("请输入新备注 (Please input new remark):", oldValue); if (newValue !== null && newValue !== oldValue) { updateRemark(userToken, username, newValue); return newValue; } return oldValue; } function showRemarks(userToken) { showRemarkInLeftPannel(userToken); var tab = getCurrentTab(); switch (tab) { case 'homepage': showRemarkInHomepage(userToken); break; case 'repositories': break; case 'stars': showRemarkInStarsTab(userToken); break; case 'following': case 'followers': showRemarkInFollowersTab(userToken); break; case 'orgs-members': showRemarkInOrgMembers(userToken); break; case 'orgs-people': showRemarkInOrgPeople(userToken); break; default: showRemarkInRepoDetailPage(userToken); break; } console.log(tab,'Show remarks') } (function () { console.log('GithubRemark-Dorad'); var username = getGithubLoginUsername(); if (username !== null && username != '') { showRemarks(username); } else if (hasLoginFrame()) { alert('你还未登陆github,请先登录你的github账户!\r\nYou have not log in to Github!\r\nPlease log in first.'); } }());