查看Github项目的贡#比例
// ==UserScript== // @name github-contribution-ratio (typescript) // @author Axetroy // @collaborator Axetroy // @description 查看Github项目的贡#比例 // @version 0.1.0 // @update 2017-04-06 21:28:02 // @grant GM_xmlhttpRequest // @include https://github.com* // @connect * // @compatible chrome 完美运行 // @compatible firefox 完美运行 // @supportURL http://www.burningall.com // @run-at document-idle // @contributionURL [email protected]|alipay.com // @namespace https://greasyfork.org/zh-CN/users/3400-axetroy // @license The MIT License (MIT); http://opensource.org/licenses/MIT // ==/UserScript== // Github源码:https://github.com/axetroy/github-contribution-ratio /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 2); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var extend = __webpack_require__(1); var Http = (function () { function Http(options) { this.options = options; } Http.prototype.request = function (method, url, body, headers) { if (headers === void 0) { headers = { 'Cookie': document.cookie }; } var options = extend({}, this.options, { method: method, url: url, data: body, headers: headers }); if (options.url.indexOf('http') !== 0) { options.url = (options.base || '') + url; } options.url += '?client_id=b8257841dd7ca5eef2aa&client_secret=4da33dd6fcb0a01d395945ad18613ecf9c12079e'; return new Promise(function (resolve, reject) { options.synchronous = true; // async options.onreadystatechange = function (response) { if (response.readyState !== 4) return; response.status >= 200 && response.status < 400 ? resolve(response) : reject(response); }; options.onerror = function (response) { console.error('http error'); reject(response); }; options.onabort = function (response) { console.error('http abort'); reject(response); }; options.ontimeout = function (response) { console.error('http timeout'); reject(response); }; GM_xmlhttpRequest(extend({}, options)); }); }; Http.prototype.get = function (url, body, headers) { if (body === void 0) { body = null; } if (headers === void 0) { headers = {}; } return this.request('GET', url, body, headers); }; Http.prototype.post = function (url, body, headers) { if (body === void 0) { body = null; } if (headers === void 0) { headers = {}; } return this.request('POST', url, body, headers); }; return Http; }()); var timeout = 5000; exports.timeout = timeout; var http = new Http({ timeout: timeout, base: 'https://api.github.com' }); exports.http = http; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var hasOwn = Object.prototype.hasOwnProperty; var toStr = Object.prototype.toString; var isArray = function isArray(arr) { if (typeof Array.isArray === 'function') { return Array.isArray(arr); } return toStr.call(arr) === '[object Array]'; }; var isPlainObject = function isPlainObject(obj) { if (!obj || toStr.call(obj) !== '[object Object]') { return false; } var hasOwnConstructor = hasOwn.call(obj, 'constructor'); var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); // Not own constructor property must be Object if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for (key in obj) {/**/} return typeof key === 'undefined' || hasOwn.call(obj, key); }; module.exports = function extend() { var options, name, src, copy, copyIsArray, clone, target = arguments[0], i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if (typeof target === 'boolean') { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) { target = {}; } for (; i < length; ++i) { options = arguments[i]; // Only deal with non-null/undefined values if (options != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target !== copy) { // Recurse if we're merging plain objects or arrays if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = extend(deep, clone, copy); // Don't bring in undefined values } else if (typeof copy !== 'undefined') { target[name] = copy; } } } } } // Return the modified object return target; }; /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(r###lt) { r###lt.done ? resolve(r###lt.value) : new P(function (resolve) { resolve(r###lt.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; return { next: verb(0), "throw": verb(1), "return": verb(2) }; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var http_1 = __webpack_require__(0); function getUserName() { var matcher = location.href.match(/https:\/\/github\.com\/(\w+)\??/); return matcher[1]; } function shouldRun() { return /https:\/\/github\.com\/\w+\?/.test(location.href) && location.search.indexOf('tab=repositories') >= 0; } function getRepositories() { return [].slice.call(document.querySelectorAll('.js-repo-list>li')) .filter(function (li) { return !li.getAttribute('contribute-ratio'); }) .map(function (li) { var a = li.querySelector('h3>a'); var match = a.pathname.match(/([^\/]+)\/([^\/]+)/); li.setAttribute('repository-owner', match[1]); li.setAttribute('repository-name', match[2]); return li; }); } function main() { return __awaiter(this, void 0, void 0, function () { var userName, lis; return __generator(this, function (_a) { if (!shouldRun()) return [2 /*return*/]; userName = getUserName(); console.info("Stating " + userName + "'s contribution..."); lis = getRepositories(); if (!lis.length) return [2 /*return*/]; clearInterval(timer); timer = void 0; lis.forEach(function (li) { var owner = li.getAttribute('repository-owner'); var name = li.getAttribute('repository-name'); http_1.http.get("/repos/" + owner + "/" + name + "/stats/contributors") .then(function (res) { var raw = res.response; if (!raw) return; var response = JSON.parse(raw); response = Object.keys(response).length === 0 ? [] : response; var contributes = response.filter(function (v) { return v["author"]["login"] === userName; }).map(function (v) { return v; }); var totalAdditions = 0; var totalDeletions = 0; var additions = 0; var deletions = 0; contributes.forEach(function (contribute) { contribute.weeks.forEach(function (week) { return (additions += week.a) && (deletions += week.d); }); }); response.forEach(function (contribute) { contribute.weeks.forEach(function (week) { return (totalAdditions += week.a) && (totalDeletions += week.d); }); }); var contributeRatio = (((additions + deletions) / (totalAdditions + totalDeletions)) * 100) + ''; li.setAttribute('total-additions', totalAdditions); li.setAttribute('total-deletions', totalDeletions); li.setAttribute('additions', additions); li.setAttribute('deletions', deletions); li.setAttribute('contribute-ratio', parseInt(contributeRatio)); var percent = parseInt(contributeRatio) > 0 ? parseInt(contributeRatio) : 1; var uncontribution = document.createElement('span'); var contribution = document.createElement('span'); var container = document.createElement('span'); container.setAttribute('aria-label', "Contribution " + percent + "%"); var width = 155; container.classList.add('d-inline-block'); container.classList.add('tooltipped'); container.classList.add('tooltipped-s'); container.style.width = '155px'; var contributionWidth = width * percent / 100; contribution.style.width = contributionWidth + 'px'; contribution.style.borderBottom = '2px solid #009688'; contribution.style.display = 'inline-block'; uncontribution.style.width = width - contributionWidth + 'px'; uncontribution.style.borderBottom = '2px solid #9E9E9E'; uncontribution.style.display = 'inline-block'; container.appendChild(contribution); container.appendChild(uncontribution); li.querySelector('.col-3.float-right.text-right').appendChild(container); }) .catch(function (err) { console.error(err); }); }); return [2 /*return*/]; }); }); } (function (history) { var pushState = history.pushState; history.pushState = function (state) { if (typeof history["onpushstate"] == "function") { history["onpushstate"]({ state: state }); } setTimeout(function () { run(); }); return pushState.apply(history, arguments); }; })(window.history); var timer; function run() { if (!shouldRun()) return; timer = setInterval(function () { main(); }, 1500); } run(); /***/ }) /******/ ]);