github-contribution-ratio (typescript)

查看Github项目的贡献比例

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              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(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.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();


/***/ })
/******/ ]);