github-contribution-ratio (typescript)

查看Github项目的贡献比例

  1. // ==UserScript==
  2. // @name github-contribution-ratio (typescript)
  3. // @author Axetroy
  4. // @collaborator Axetroy
  5. // @description 查看Github项目的贡献比例
  6. // @version 0.1.0
  7. // @update 2017-04-06 21:28:02
  8. // @grant GM_xmlhttpRequest
  9. // @include https://github.com*
  10. // @connect *
  11. // @compatible chrome 完美运行
  12. // @compatible firefox 完美运行
  13. // @supportURL http://www.burningall.com
  14. // @run-at document-idle
  15. // @contributionURL troy450409405@gmail.com|alipay.com
  16. // @namespace https://greasyfork.org/zh-CN/users/3400-axetroy
  17. // @license The MIT License (MIT); http://opensource.org/licenses/MIT
  18. // ==/UserScript==
  19.  
  20. // Github源码:https://github.com/axetroy/github-contribution-ratio
  21.  
  22.  
  23. /******/ (function(modules) { // webpackBootstrap
  24. /******/ // The module cache
  25. /******/ var installedModules = {};
  26. /******/
  27. /******/ // The require function
  28. /******/ function __webpack_require__(moduleId) {
  29. /******/
  30. /******/ // Check if module is in cache
  31. /******/ if(installedModules[moduleId])
  32. /******/ return installedModules[moduleId].exports;
  33. /******/
  34. /******/ // Create a new module (and put it into the cache)
  35. /******/ var module = installedModules[moduleId] = {
  36. /******/ i: moduleId,
  37. /******/ l: false,
  38. /******/ exports: {}
  39. /******/ };
  40. /******/
  41. /******/ // Execute the module function
  42. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  43. /******/
  44. /******/ // Flag the module as loaded
  45. /******/ module.l = true;
  46. /******/
  47. /******/ // Return the exports of the module
  48. /******/ return module.exports;
  49. /******/ }
  50. /******/
  51. /******/
  52. /******/ // expose the modules object (__webpack_modules__)
  53. /******/ __webpack_require__.m = modules;
  54. /******/
  55. /******/ // expose the module cache
  56. /******/ __webpack_require__.c = installedModules;
  57. /******/
  58. /******/ // identity function for calling harmony imports with the correct context
  59. /******/ __webpack_require__.i = function(value) { return value; };
  60. /******/
  61. /******/ // define getter function for harmony exports
  62. /******/ __webpack_require__.d = function(exports, name, getter) {
  63. /******/ if(!__webpack_require__.o(exports, name)) {
  64. /******/ Object.defineProperty(exports, name, {
  65. /******/ configurable: false,
  66. /******/ enumerable: true,
  67. /******/ get: getter
  68. /******/ });
  69. /******/ }
  70. /******/ };
  71. /******/
  72. /******/ // getDefaultExport function for compatibility with non-harmony modules
  73. /******/ __webpack_require__.n = function(module) {
  74. /******/ var getter = module && module.__esModule ?
  75. /******/ function getDefault() { return module['default']; } :
  76. /******/ function getModuleExports() { return module; };
  77. /******/ __webpack_require__.d(getter, 'a', getter);
  78. /******/ return getter;
  79. /******/ };
  80. /******/
  81. /******/ // Object.prototype.hasOwnProperty.call
  82. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  83. /******/
  84. /******/ // __webpack_public_path__
  85. /******/ __webpack_require__.p = "";
  86. /******/
  87. /******/ // Load entry module and return exports
  88. /******/ return __webpack_require__(__webpack_require__.s = 2);
  89. /******/ })
  90. /************************************************************************/
  91. /******/ ([
  92. /* 0 */
  93. /***/ (function(module, exports, __webpack_require__) {
  94.  
  95. "use strict";
  96.  
  97. Object.defineProperty(exports, "__esModule", { value: true });
  98. var extend = __webpack_require__(1);
  99. var Http = (function () {
  100. function Http(options) {
  101. this.options = options;
  102. }
  103. Http.prototype.request = function (method, url, body, headers) {
  104. if (headers === void 0) { headers = { 'Cookie': document.cookie }; }
  105. var options = extend({}, this.options, { method: method, url: url, data: body, headers: headers });
  106. if (options.url.indexOf('http') !== 0) {
  107. options.url = (options.base || '') + url;
  108. }
  109. options.url += '?client_id=b8257841dd7ca5eef2aa&client_secret=4da33dd6fcb0a01d395945ad18613ecf9c12079e';
  110. return new Promise(function (resolve, reject) {
  111. options.synchronous = true; // async
  112. options.onreadystatechange = function (response) {
  113. if (response.readyState !== 4)
  114. return;
  115. response.status >= 200 && response.status < 400 ? resolve(response) : reject(response);
  116. };
  117. options.onerror = function (response) {
  118. console.error('http error');
  119. reject(response);
  120. };
  121. options.onabort = function (response) {
  122. console.error('http abort');
  123. reject(response);
  124. };
  125. options.ontimeout = function (response) {
  126. console.error('http timeout');
  127. reject(response);
  128. };
  129. GM_xmlhttpRequest(extend({}, options));
  130. });
  131. };
  132. Http.prototype.get = function (url, body, headers) {
  133. if (body === void 0) { body = null; }
  134. if (headers === void 0) { headers = {}; }
  135. return this.request('GET', url, body, headers);
  136. };
  137. Http.prototype.post = function (url, body, headers) {
  138. if (body === void 0) { body = null; }
  139. if (headers === void 0) { headers = {}; }
  140. return this.request('POST', url, body, headers);
  141. };
  142. return Http;
  143. }());
  144. var timeout = 5000;
  145. exports.timeout = timeout;
  146. var http = new Http({ timeout: timeout, base: 'https://api.github.com' });
  147. exports.http = http;
  148.  
  149.  
  150. /***/ }),
  151. /* 1 */
  152. /***/ (function(module, exports, __webpack_require__) {
  153.  
  154. "use strict";
  155.  
  156.  
  157. var hasOwn = Object.prototype.hasOwnProperty;
  158. var toStr = Object.prototype.toString;
  159.  
  160. var isArray = function isArray(arr) {
  161. if (typeof Array.isArray === 'function') {
  162. return Array.isArray(arr);
  163. }
  164.  
  165. return toStr.call(arr) === '[object Array]';
  166. };
  167.  
  168. var isPlainObject = function isPlainObject(obj) {
  169. if (!obj || toStr.call(obj) !== '[object Object]') {
  170. return false;
  171. }
  172.  
  173. var hasOwnConstructor = hasOwn.call(obj, 'constructor');
  174. var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
  175. // Not own constructor property must be Object
  176. if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
  177. return false;
  178. }
  179.  
  180. // Own properties are enumerated firstly, so to speed up,
  181. // if last one is own, then all properties are own.
  182. var key;
  183. for (key in obj) {/**/}
  184.  
  185. return typeof key === 'undefined' || hasOwn.call(obj, key);
  186. };
  187.  
  188. module.exports = function extend() {
  189. var options, name, src, copy, copyIsArray, clone,
  190. target = arguments[0],
  191. i = 1,
  192. length = arguments.length,
  193. deep = false;
  194.  
  195. // Handle a deep copy situation
  196. if (typeof target === 'boolean') {
  197. deep = target;
  198. target = arguments[1] || {};
  199. // skip the boolean and the target
  200. i = 2;
  201. } else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) {
  202. target = {};
  203. }
  204.  
  205. for (; i < length; ++i) {
  206. options = arguments[i];
  207. // Only deal with non-null/undefined values
  208. if (options != null) {
  209. // Extend the base object
  210. for (name in options) {
  211. src = target[name];
  212. copy = options[name];
  213.  
  214. // Prevent never-ending loop
  215. if (target !== copy) {
  216. // Recurse if we're merging plain objects or arrays
  217. if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
  218. if (copyIsArray) {
  219. copyIsArray = false;
  220. clone = src && isArray(src) ? src : [];
  221. } else {
  222. clone = src && isPlainObject(src) ? src : {};
  223. }
  224.  
  225. // Never move original objects, clone them
  226. target[name] = extend(deep, clone, copy);
  227.  
  228. // Don't bring in undefined values
  229. } else if (typeof copy !== 'undefined') {
  230. target[name] = copy;
  231. }
  232. }
  233. }
  234. }
  235. }
  236.  
  237. // Return the modified object
  238. return target;
  239. };
  240.  
  241.  
  242.  
  243. /***/ }),
  244. /* 2 */
  245. /***/ (function(module, exports, __webpack_require__) {
  246.  
  247. "use strict";
  248.  
  249. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  250. return new (P || (P = Promise))(function (resolve, reject) {
  251. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  252. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  253. function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
  254. step((generator = generator.apply(thisArg, _arguments || [])).next());
  255. });
  256. };
  257. var __generator = (this && this.__generator) || function (thisArg, body) {
  258. var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
  259. return { next: verb(0), "throw": verb(1), "return": verb(2) };
  260. function verb(n) { return function (v) { return step([n, v]); }; }
  261. function step(op) {
  262. if (f) throw new TypeError("Generator is already executing.");
  263. while (_) try {
  264. if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
  265. if (y = 0, t) op = [0, t.value];
  266. switch (op[0]) {
  267. case 0: case 1: t = op; break;
  268. case 4: _.label++; return { value: op[1], done: false };
  269. case 5: _.label++; y = op[1]; op = [0]; continue;
  270. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  271. default:
  272. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  273. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  274. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  275. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  276. if (t[2]) _.ops.pop();
  277. _.trys.pop(); continue;
  278. }
  279. op = body.call(thisArg, _);
  280. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  281. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  282. }
  283. };
  284. Object.defineProperty(exports, "__esModule", { value: true });
  285. var http_1 = __webpack_require__(0);
  286. function getUserName() {
  287. var matcher = location.href.match(/https:\/\/github\.com\/(\w+)\??/);
  288. return matcher[1];
  289. }
  290. function shouldRun() {
  291. return /https:\/\/github\.com\/\w+\?/.test(location.href) && location.search.indexOf('tab=repositories') >= 0;
  292. }
  293. function getRepositories() {
  294. return [].slice.call(document.querySelectorAll('.js-repo-list>li'))
  295. .filter(function (li) { return !li.getAttribute('contribute-ratio'); })
  296. .map(function (li) {
  297. var a = li.querySelector('h3>a');
  298. var match = a.pathname.match(/([^\/]+)\/([^\/]+)/);
  299. li.setAttribute('repository-owner', match[1]);
  300. li.setAttribute('repository-name', match[2]);
  301. return li;
  302. });
  303. }
  304. function main() {
  305. return __awaiter(this, void 0, void 0, function () {
  306. var userName, lis;
  307. return __generator(this, function (_a) {
  308. if (!shouldRun())
  309. return [2 /*return*/];
  310. userName = getUserName();
  311. console.info("Stating " + userName + "'s contribution...");
  312. lis = getRepositories();
  313. if (!lis.length)
  314. return [2 /*return*/];
  315. clearInterval(timer);
  316. timer = void 0;
  317. lis.forEach(function (li) {
  318. var owner = li.getAttribute('repository-owner');
  319. var name = li.getAttribute('repository-name');
  320. http_1.http.get("/repos/" + owner + "/" + name + "/stats/contributors")
  321. .then(function (res) {
  322. var raw = res.response;
  323. if (!raw)
  324. return;
  325. var response = JSON.parse(raw);
  326. response = Object.keys(response).length === 0 ? [] : response;
  327. var contributes = response.filter(function (v) { return v["author"]["login"] === userName; }).map(function (v) { return v; });
  328. var totalAdditions = 0;
  329. var totalDeletions = 0;
  330. var additions = 0;
  331. var deletions = 0;
  332. contributes.forEach(function (contribute) {
  333. contribute.weeks.forEach(function (week) { return (additions += week.a) && (deletions += week.d); });
  334. });
  335. response.forEach(function (contribute) {
  336. contribute.weeks.forEach(function (week) { return (totalAdditions += week.a) && (totalDeletions += week.d); });
  337. });
  338. var contributeRatio = (((additions + deletions) / (totalAdditions + totalDeletions)) * 100) + '';
  339. li.setAttribute('total-additions', totalAdditions);
  340. li.setAttribute('total-deletions', totalDeletions);
  341. li.setAttribute('additions', additions);
  342. li.setAttribute('deletions', deletions);
  343. li.setAttribute('contribute-ratio', parseInt(contributeRatio));
  344. var percent = parseInt(contributeRatio) > 0 ? parseInt(contributeRatio) : 1;
  345. var uncontribution = document.createElement('span');
  346. var contribution = document.createElement('span');
  347. var container = document.createElement('span');
  348. container.setAttribute('aria-label', "Contribution " + percent + "%");
  349. var width = 155;
  350. container.classList.add('d-inline-block');
  351. container.classList.add('tooltipped');
  352. container.classList.add('tooltipped-s');
  353. container.style.width = '155px';
  354. var contributionWidth = width * percent / 100;
  355. contribution.style.width = contributionWidth + 'px';
  356. contribution.style.borderBottom = '2px solid #009688';
  357. contribution.style.display = 'inline-block';
  358. uncontribution.style.width = width - contributionWidth + 'px';
  359. uncontribution.style.borderBottom = '2px solid #9E9E9E';
  360. uncontribution.style.display = 'inline-block';
  361. container.appendChild(contribution);
  362. container.appendChild(uncontribution);
  363. li.querySelector('.col-3.float-right.text-right').appendChild(container);
  364. })
  365. .catch(function (err) {
  366. console.error(err);
  367. });
  368. });
  369. return [2 /*return*/];
  370. });
  371. });
  372. }
  373. (function (history) {
  374. var pushState = history.pushState;
  375. history.pushState = function (state) {
  376. if (typeof history["onpushstate"] == "function") {
  377. history["onpushstate"]({ state: state });
  378. }
  379. setTimeout(function () {
  380. run();
  381. });
  382. return pushState.apply(history, arguments);
  383. };
  384. })(window.history);
  385. var timer;
  386. function run() {
  387. if (!shouldRun())
  388. return;
  389. timer = setInterval(function () {
  390. main();
  391. }, 1500);
  392. }
  393. run();
  394.  
  395.  
  396. /***/ })
  397. /******/ ]);