// ==UserScript== // @name GitHub Show Repo Issues // @namespace github-show-repo-issues // @description Show repo issues count on the repository tab & organization page (https://github.com/:user) // @version 2.2.0 // @include https://github.com/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @run-at document-idle // @author Rob Garrison >> http://github.com/Mottie // @downloadURL none // ==/UserScript== /* global GM_addStyle, GM_xmlhttpRequest */ ( function() { 'use strict'; var busy = false, addIssues = function() { // look for repo tab or user/organization page if ( document.querySelectorAll( '.tabnav-tab.selected, .repo-list' ).length && // and not already applied !document.querySelectorAll( '.repo-list-stat-item.issues' ).length ) { // set busy flag to ignore mutation observer firing while adding new content busy = true; // Does not include forks & only includes the first 10 repos, or first 20 on the // organization page - these are the repos showing the participation graphs var user, len, url, items = document.querySelectorAll( '.repo-list-item' ), // bug icon icon = '', // issue count = get all repos from user => api v3 - https://api.github.com/users/:user/repos, // then look for "open_issues_count" in the named repos // previsouly used https://api.github.com/repos/:user/:repo/issues?state=open (first 30 issues only) api = 'https://api.github.com/users'; items = Array.prototype.filter.call( items, function( item ) { var cl = item.classList; return cl.contains( 'public' ) && !cl.contains( 'fork' ); }); len = items.length; // expecting fork link to look like this: // // 1 // url = len ? items[ 0 ].querySelector( 'a.repo-list-stat-item[aria-label="Forks"]' ).getAttribute( 'href' ) : ''; user = ( url || '' ).match( /^\/[^/]+/ ); if ( user && user.length ) { // add bug image background GM_addStyle([ '.repo-list-stats a.issues svg { position: relative; top: 2px; fill: #888; }', '.repo-list-stats a.issues:hover svg { fill: #4078C0; }' ].join('')); GM_xmlhttpRequest({ method : 'GET', url : api + user[ 0 ] + '/repos', onload : function( response ) { var itemIndex, repoIndex, repoLen, repo, link, data = JSON.parse( response.responseText || 'null' ); if ( data ) { repoLen = data.length; for ( itemIndex = 0; itemIndex < len; itemIndex++ ) { link = items[ itemIndex ].querySelector( 'a.repo-list-stat-item[aria-label="Forks"]' ); repo = ( link.getAttribute( 'href' ) || '' ).replace( '/network', '' ).slice( 1 ); for ( repoIndex = 0; repoIndex < repoLen; repoIndex++ ) { if ( repo === data[ repoIndex ].full_name ) { link.insertAdjacentHTML( 'afterend', '' + icon + ' ' + data[ repoIndex ].open_issues_count + '' ); } } } } busy = false; } }); } else { busy = false; } } else { busy = false; } }, containers = '#js-repo-pjax-container, #js-pjax-container, .js-contribution-activity', targets = document.querySelectorAll( containers ); Array.prototype.forEach.call(targets, function(target) { new MutationObserver( function( mutations ) { mutations.forEach( function( mutation ) { // preform checks before addIssues to minimize function calls if ( !( busy || document.querySelectorAll( '.repo-list-stat-item.issues' ).length ) && document.querySelectorAll( '.tabnav-tab.selected, .repo-list' ).length && mutation.target === target ) { addIssues(); } }); }).observe( target, { childList: true, subtree: true }); }); addIssues(); })();