您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 GitHub 中查看用户历年的贡献图。
- // ==UserScript==
- // @name GreenWall: View all contribution graphs in GitHub ⬜🟩
- // @description View a graph of users' contributions over the years in GitHub.
- // @name:zh-CN GreenWall - 查看历年 GitHub 的贡献图 ⬜🟩
- // @description:zh-CN 在 GitHub 中查看用户历年的贡献图。
- // @version 1.2.0
- // @namespace https://green-wall.leoku.dev
- // @author LeoKu(https://leoku.dev)
- // @match https://github.com/*
- // @run-at document-start
- // @icon https://green-wall.leoku.dev/favicon.svg
- // @grant GM.xmlHttpRequest
- // @homepageURL https://github.com/Codennnn/Green-Wall
- // @license MIT
- // ==/UserScript==
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
- if (ar || !(i in from)) {
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
- ar[i] = from[i];
- }
- }
- return to.concat(ar || Array.prototype.slice.call(from));
- };
- function addHistoryEvent(type) {
- var originalMethod = window.history[type];
- return function () {
- var args = [];
- for (var _i = 0; _i < arguments.length; _i++) {
- args[_i] = arguments[_i];
- }
- originalMethod.apply(window.history, args);
- var ev = new Event(type);
- window.dispatchEvent(ev);
- };
- }
- window.history.replaceState = addHistoryEvent('replaceState');
- var handler = function () {
- var _a, _b, _c;
- var githubUserPageRegex = /^https:\/\/github\.com\/[a-zA-Z0-9-]+(?=\/?$)/;
- var isProfile = githubUserPageRegex.test(window.location.href);
- if (isProfile) {
- var ORIGIN_1 = 'https://green-wall.leoku.dev';
- var produceData_1 = function (_a) {
- var data = _a.data;
- var contributionCalendars = data.contributionCalendars.map(function (cur) {
- var rows = [[], [], [], [], [], [], []];
- var nullDay = { count: 0, date: '', level: 'Null' };
- cur.weeks.forEach(function (_a) {
- var days = _a.days;
- if (days.length !== 7) {
- var newDays = __spreadArray([], days, true);
- for (var i = 0; i <= 6; i++) {
- var theDay = newDays.at(i);
- var weekday = i;
- if (theDay && typeof theDay.weekday === 'number') {
- if (theDay.weekday === weekday) {
- rows[theDay.weekday].push(theDay);
- }
- else {
- newDays.splice(i, 0, nullDay);
- rows[i].push(nullDay);
- }
- }
- else {
- rows[i].push(nullDay);
- }
- }
- }
- else {
- days.forEach(function (day) {
- if (typeof day.weekday === 'number') {
- rows[day.weekday].push(day);
- }
- });
- }
- });
- var calendar = {
- total: cur.total,
- year: cur.year,
- rows: rows,
- };
- return calendar;
- });
- return {
- contributionCalendars: contributionCalendars,
- };
- };
- var isHalloween_1 = false;
- var createGraph_1 = function (params) {
- var year = params.year, total = params.total, rows = params.rows;
- var table = document.createElement('table');
- table.classList.add('ContributionCalendar-grid');
- table.style.borderSpacing = '3px';
- table.style.overflow = 'hidden';
- table.style.position = 'relative';
- var tbody = document.createElement('tbody');
- var tr = document.createElement('tr');
- tr.style.height = '10px';
- rows.forEach(function (row) {
- var clonedTr = tr.cloneNode();
- var htmlStr = '';
- row.forEach(function (col, idx) {
- var td = '<td></td>';
- if (col.level !== "Null" /* ContributionLevel.Null */) {
- var level = col.level === "NONE" /* ContributionLevel.NONE */
- ? 0
- : col.level === "FIRST_QUARTILE" /* ContributionLevel.FIRST_QUARTILE */
- ? 1
- : col.level === "SECOND_QUARTILE" /* ContributionLevel.SECOND_QUARTILE */
- ? 2
- : col.level === "THIRD_QUARTILE" /* ContributionLevel.THIRD_QUARTILE */
- ? 3
- : 4;
- td = "\n <td\n title=\"".concat(col.count === 0 ? 'No' : col.count, " contributions in ").concat(col.date, "\"\n tabindex=\"-1\"\n data-ix=\"").concat(idx, "\"\n style=\"width: 10px\"\n data-level=\"").concat(level, "\"\n class=\"ContributionCalendar-day\"\n data-date=\"").concat(col.level, "\"\n aria-selected=\"false\"\n role=\"gridcell\"\n ></td>\n ");
- }
- htmlStr += td;
- });
- if (clonedTr instanceof HTMLTableRowElement) {
- clonedTr.innerHTML = htmlStr;
- tbody.append(clonedTr);
- }
- });
- table.appendChild(tbody);
- var graphItem = document.createElement('div');
- var countText = document.createElement('div');
- countText.style.marginBottom = '5px';
- countText.textContent = "".concat(total, " contributions in ").concat(year);
- graphItem.append(countText, table);
- if (isHalloween_1) {
- graphItem.classList.add('ContributionCalendar');
- graphItem.setAttribute('data-holiday', 'halloween');
- }
- return { graphItem: graphItem };
- };
- var createDialog = function (params) {
- var username = params.username;
- var dialog = document.createElement('dialog');
- dialog.id = 'green-wall-dialog';
- dialog.classList.add('Overlay', 'Overlay-whenNarrow', 'Overlay--size-medium-portrait', 'Overlay--motion-scaleFadeOverlay', 'Overlay-whenNarrow', 'Overlay--size-medium-portrait', 'Overlay--motion-scaleFade');
- dialog.style.minWidth = '720px';
- dialog.style.maxHeight = 'calc(100vh - 50px)';
- dialog.addEventListener('close', function () {
- document.body.classList.remove('has-modal');
- });
- var mouseDownTarget;
- var mouseDownHandler = function (ev) {
- if (ev.target instanceof HTMLElement) {
- mouseDownTarget = ev.target;
- }
- };
- var mouseUpHandler = function (ev) {
- if (ev.target instanceof HTMLDialogElement &&
- ev.target === mouseDownTarget &&
- ev.target === dialog) {
- dialog.close();
- }
- };
- dialog.addEventListener('mousedown', mouseDownHandler);
- dialog.addEventListener('mouseup', mouseUpHandler);
- // ---
- var wrap = document.createElement('div');
- wrap.style.display = 'flex';
- wrap.style.flexDirection = 'column';
- wrap.style.overflow = 'hidden';
- // ---
- var dialogHeader = document.createElement('div');
- dialogHeader.classList.add('Overlay-header');
- var contentWrap = document.createElement('div');
- contentWrap.classList.add('Overlay-headerContentWrap');
- var titleWrap = document.createElement('div');
- titleWrap.classList.add('Overlay-titleWrap');
- var title = document.createElement('h1');
- title.classList.add('Overlay-title');
- title.textContent = "".concat(username, "'s GreenWall");
- var actionWrap = document.createElement('div');
- actionWrap.classList.add('Overlay-actionWrap');
- var actionButton = document.createElement('button');
- actionButton.classList.add('close-button', 'Overlay-closeButton');
- actionButton.setAttribute('type', 'button');
- actionButton.innerHTML = "\n <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-x\">\n <path d=\"M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z\"></path>\n </svg>\n ";
- actionButton.addEventListener('click', function (ev) {
- ev.stopPropagation();
- dialog.close();
- });
- // ---
- var dialogBody = document.createElement('div');
- dialogBody.classList.add('Overlay-body');
- dialogBody.style.overflowY = 'auto';
- var dialogContent = document.createElement('div');
- dialogContent.style.display = 'flex';
- dialogContent.style.flexDirection = 'column';
- dialogContent.style.rowGap = '10px';
- dialogContent.style.alignItems = 'center';
- dialogContent.style.padding = 'var(--stack-padding-normal, 1rem)';
- // ---
- var dialogFooter = document.createElement('div');
- dialogFooter.classList.add('Overlay-footer', 'Overlay-footer--alignEnd', 'Overlay-footer--divided');
- var openExtrnalBtn = document.createElement('button');
- var btnContent = document.createElement('span');
- btnContent.classList.add('Button-label');
- btnContent.textContent = 'Open in Green Wall';
- openExtrnalBtn.classList.add('Button', 'Button--primary', 'Button--medium');
- openExtrnalBtn.addEventListener('click', function () {
- window.open("".concat(ORIGIN_1, "/user/").concat(username), '_blank');
- });
- titleWrap.append(title);
- actionWrap.append(actionButton);
- contentWrap.append(titleWrap, actionWrap);
- openExtrnalBtn.append(btnContent);
- dialogHeader.append(contentWrap);
- dialogBody.append(dialogContent);
- dialogFooter.append(openExtrnalBtn);
- wrap.append(dialogHeader, dialogBody, dialogFooter);
- dialog.append(wrap);
- document.body.append(dialog);
- return { dialog: dialog, dialogContent: dialogContent };
- };
- var profileArea = document.querySelector('.Layout-sidebar .h-card .js-profile-editable-replace');
- var refNode = (_b = (_a = document.querySelector('.js-profile-editable-replace > .d-flex.flex-column')) === null || _a === void 0 ? void 0 : _a.nextSibling) === null || _b === void 0 ? void 0 : _b.nextSibling;
- if (profileArea instanceof HTMLElement && refNode instanceof HTMLElement) {
- var username_1 = (_c = document
- .querySelector('meta[name="octolytics-dimension-user_login"]')) === null || _c === void 0 ? void 0 : _c.getAttribute('content');
- if (username_1) {
- var exists = !!document.querySelector('#green-wall-block');
- if (!exists) {
- var block = document.createElement('div');
- block.setAttribute('id', 'green-wall-block');
- block.classList.add('border-top', 'color-border-muted', 'pt-3', 'mt-3', 'clearfix', 'hide-sm', 'hide-md');
- var title = document.createElement('h2');
- title.classList.add('h4', 'mb-2');
- title.textContent = 'Green Wall';
- var openBtn = document.createElement('button');
- openBtn.classList.add('btn');
- openBtn.textContent = ' ⬜🟩 View All Green';
- block.appendChild(title);
- block.appendChild(openBtn);
- profileArea.insertBefore(block, refNode);
- var _d = createDialog({ username: username_1 }), dialog_1 = _d.dialog, dialogContent_1 = _d.dialogContent;
- var hasLoaded_1 = false;
- var handleLoadError_1 = function () {
- dialogContent_1.innerHTML = '';
- var errorBlock = document.createElement('div');
- errorBlock.style.display = 'flex';
- errorBlock.style.flexDirection = 'column';
- errorBlock.style.alignItems = 'center';
- var tip = document.createElement('p');
- tip.textContent = 'The process of obtaining data has an exception.';
- var retryBtn = document.createElement('button');
- retryBtn.classList.add('btn');
- retryBtn.textContent = 'Retry';
- retryBtn.addEventListener('click', function () {
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
- handleLoadData_1();
- });
- errorBlock.append(tip, retryBtn);
- dialogContent_1.append(errorBlock);
- };
- var handleLoadData_1 = function () {
- var loading = "\n <svg aria-label=\"Loading\" style=\"box-sizing: content-box; color: var(--color-icon-primary);\" width=\"32\" height=\"32\" viewBox=\"0 0 16 16\" fill=\"none\" data-view-component=\"true\" class=\"anim-rotate\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" stroke=\"currentColor\" stroke-opacity=\"0.25\" stroke-width=\"2\" vector-effect=\"non-scaling-stroke\" fill=\"none\"></circle>\n <path d=\"M15 8a7.002 7.002 0 00-7-7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" vector-effect=\"non-scaling-stroke\"></path>\n </svg>\n ";
- dialogContent_1.innerHTML = loading;
- GM.xmlHttpRequest({
- method: 'GET',
- url: "".concat(ORIGIN_1, "/api/contribution/").concat(username_1),
- onload: function (response) {
- try {
- dialogContent_1.innerHTML = '';
- var data = JSON.parse(response.responseText);
- var xData = produceData_1(data);
- xData.contributionCalendars.forEach(function (calendar) {
- var graphItem = createGraph_1(calendar).graphItem;
- dialogContent_1.append(graphItem);
- });
- hasLoaded_1 = true;
- }
- catch (_a) {
- handleLoadError_1();
- }
- },
- onerror: function (err) {
- console.error('[Green Wall]: ', err);
- handleLoadError_1();
- },
- });
- };
- var handleDialogOpen_1 = function () {
- var _a;
- dialog_1.showModal();
- document.body.classList.add('has-modal');
- if (!hasLoaded_1) {
- isHalloween_1 =
- ((_a = document.querySelector('.ContributionCalendar')) === null || _a === void 0 ? void 0 : _a.getAttribute('data-holiday')) ===
- 'halloween';
- handleLoadData_1();
- }
- };
- openBtn.addEventListener('click', function () {
- handleDialogOpen_1();
- });
- }
- }
- }
- else {
- console.warn('[Green Wall]: Target node not found.');
- }
- }
- };
- window.addEventListener('replaceState', handler);