├── LICENSE ├── README.md ├── index.css ├── index.html ├── index.js └── lib ├── tablesort.date.js ├── tablesort.min.js └── tablesort.numeric.js /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2015, Mapbox 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose 6 | with or without fee is hereby granted, provided that the above copyright notice 7 | and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GitHub org browser 2 | 3 | A really simple tool for listing public repositories of a GitHub organization, with stats, sorting and filtering. 4 | 5 | http://mapbox.github.io/github-org-browser 6 | 7 | Note that it uses the GitHub JSON API under the hood, so you may hit rate limit if you browse through too many organizations 8 | in a short period of time. 9 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | table { 6 | border-collapse: collapse; 7 | border-spacing: 0; 8 | background: white; 9 | } 10 | table th, table td { 11 | text-align: left; 12 | padding: 5px 10px; 13 | border-right: 1px solid #eee; 14 | border-bottom: 1px solid #eee; 15 | } 16 | table th { 17 | border-bottom: none; 18 | border-color: #37A5C0; 19 | white-space: nowrap; 20 | padding-right: 5px; 21 | } 22 | table td:nth-child(2), 23 | table td:nth-child(6), 24 | table td:nth-child(7) { 25 | white-space: nowrap; 26 | } 27 | input[type=text] { 28 | border-color: #ccc; 29 | } 30 | input[type=text]:focus { 31 | border-color: #999; 32 | } 33 | .mr { 34 | margin-right: 20px; 35 | } 36 | .header { 37 | background: white; 38 | } 39 | .loaded .header { 40 | border: none; 41 | } 42 | .gh-buttons { 43 | margin-top: 4px; 44 | } 45 | body { 46 | background: #ddd; 47 | } 48 | th.sort-header { 49 | cursor: pointer; 50 | -webkit-touch-callout: none; 51 | -webkit-user-select: none; 52 | -khtml-user-select: none; 53 | -moz-user-select: none; 54 | -ms-user-select: none; 55 | user-select: none; 56 | } 57 | table th.sort-header:after { 58 | content: ''; 59 | display: inline-block; 60 | margin-left: 5px; 61 | margin-bottom: 2px; 62 | border-width: 0 4px 4px; 63 | border-style: solid; 64 | border-color: #404040 transparent; 65 | visibility: hidden; 66 | } 67 | table th.sort-header:hover:after { 68 | visibility: visible; 69 | } 70 | table th.sort-up:after, 71 | table th.sort-down:after, 72 | table th.sort-down:hover:after { 73 | visibility: visible; 74 | opacity: 0.4; 75 | } 76 | table th.sort-up:after { 77 | border-bottom: none; 78 | border-width: 4px 4px 0; 79 | } 80 | 81 | #repos, .filters { 82 | display: none; 83 | } 84 | .loaded #repos { 85 | display: table; 86 | } 87 | .loaded .filters { 88 | display: block; 89 | } 90 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mapbox Open Repositories 5 | 6 | 7 | 8 | 9 | 10 |
11 |

GitHub Org Browser

12 |
13 |
14 | 15 | 16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
RepositoryLanguageStarsForksIssuesCreatedUpdatedDescription
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var reposTable = document.getElementById('repos'); 4 | 5 | var tbody = document.createElement('tbody'); 6 | reposTable.appendChild(tbody); 7 | 8 | var tablesort; 9 | 10 | var numOrgs = 0; 11 | 12 | var searchInput = document.getElementById('search'); 13 | var forkCheckbox = document.getElementById('include-forks'); 14 | 15 | var update = debounce(function (e) { 16 | var rows = tbody.getElementsByTagName('tr'), 17 | value = searchInput.value; 18 | 19 | for (var i = 0, len = rows.length; i < len; i++) { 20 | rows[i].style.display = 21 | (!rows[i].dataset.fork || forkCheckbox.checked) && 22 | (!value || rows[i].textContent.toLowerCase().indexOf(value.toLowerCase())) >= 0 ? 'table-row' : 'none'; 23 | } 24 | }, 150); 25 | 26 | searchInput.oninput = update; 27 | forkCheckbox.onclick = update; 28 | 29 | var orgInput = document.getElementById('org'), 30 | orgForm = document.getElementById('org-form'); 31 | 32 | orgForm.onsubmit = loadFromInput; 33 | 34 | function loadFromInput() { 35 | numOrgs = orgInput.value.split(',').length; 36 | orgInput.value.split(',').forEach(function(org) { 37 | loadOrganization(org.trim()); 38 | }); 39 | return false; 40 | } 41 | 42 | function loadOrganization(org) { 43 | tbody.innerHTML = ''; 44 | document.body.className = 'loading'; 45 | getRepos('https://api.github.com/orgs/' + org + '/repos?type=public&per_page=100'); 46 | } 47 | 48 | function getRepos(url) { 49 | var xhr = new XMLHttpRequest(); 50 | xhr.onload = onResponse; 51 | xhr.open('get', url, true); 52 | xhr.send(); 53 | xhr.responseType = 'json'; 54 | } 55 | 56 | function onResponse(e) { 57 | 58 | var xhr = e.target; 59 | if (xhr.response.message === 'Not Found') { 60 | document.body.className = ''; 61 | return; 62 | } 63 | 64 | addRepos(xhr.response); 65 | 66 | var links = getLinks(xhr.getResponseHeader('Link')); 67 | if (links && links.next) getRepos(links.next); 68 | 69 | document.body.className = 'loaded'; 70 | } 71 | 72 | function getLinks(header) { 73 | if (!header) return null; 74 | 75 | var parts = header.split(','), 76 | links = {}; 77 | 78 | for (var i = 0; i < parts.length; i++) { 79 | var section = parts[i].split(';'), 80 | url = section[0].match(/<(.*)>/)[1], 81 | name = section[1].match(/rel="(.*)"/)[1]; 82 | links[name] = url; 83 | } 84 | return links; 85 | } 86 | 87 | function addRepos(repos) { 88 | 89 | for (var i = 0; i < repos.length; i++) { 90 | var repo = repos[i]; 91 | var repoName = numOrgs > 1 ? repo.full_name : repo.name 92 | 93 | var createdDate = formatDate(repo.created_at); 94 | var pushedDate = formatDate(repo.pushed_at); 95 | 96 | addRow(repoName, repo.html_url, repo.language, repo.stargazers_count, repo.forks_count, 97 | repo.open_issues_count, createdDate, pushedDate, repo.description, repo); 98 | } 99 | 100 | if (!tablesort) tablesort = new Tablesort(reposTable, {descending: true}); 101 | else tablesort.refresh(); 102 | } 103 | 104 | function formatDate(str) { 105 | return new Date(str).toDateString().substr(4); 106 | } 107 | 108 | function addRow(name, link, language, stars, forks, issues, created, pushed, description, repo) { 109 | var tr = document.createElement('tr'); 110 | if (repo.fork) tr.dataset.fork = true; 111 | 112 | // Create link to repository via the DOM 113 | var td = document.createElement('td'); 114 | var a = document.createElement('a'); 115 | a.setAttribute('href', link) 116 | a.setAttribute('target', '_blank') 117 | name = document.createTextNode(name); 118 | a.appendChild(name); 119 | td.appendChild(a); 120 | tr.appendChild(td); 121 | 122 | // create the other TDs in the table 123 | createCell(language, tr); 124 | createCell(stars, tr); 125 | createCell(forks, tr); 126 | createCell(issues, tr); 127 | createCell(created, tr); 128 | createCell(pushed, tr); 129 | createCell(description, tr); 130 | 131 | tbody.appendChild(tr); 132 | } 133 | 134 | function createCell(content, row) { 135 | 136 | var td = document.createElement('td'); 137 | content = document.createTextNode(content); 138 | td.appendChild(content); 139 | row.appendChild(td); 140 | 141 | } 142 | 143 | function debounce(fn, wait) { 144 | var timeout; 145 | 146 | return function() { 147 | var context = this, 148 | args = arguments; 149 | 150 | var later = function() { 151 | timeout = null; 152 | fn.apply(context, args); 153 | }; 154 | 155 | clearTimeout(timeout); 156 | timeout = setTimeout(later, wait); 157 | }; 158 | } 159 | -------------------------------------------------------------------------------- /lib/tablesort.date.js: -------------------------------------------------------------------------------- 1 | // Basic dates in dd/mm/yy or dd-mm-yy format. 2 | // Years can be 4 digits. Days and Months can be 1 or 2 digits. 3 | (function(){ 4 | var parseDate = function(date) { 5 | date = date.replace(/\-/g, '/'); 6 | date = date.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, '$1/$2/$3'); // format before getTime 7 | 8 | return new Date(date).getTime(); 9 | }; 10 | 11 | Tablesort.extend('date', function(item) { 12 | return ( 13 | item.search(/(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\.?\,?\s*/i) !== -1 || 14 | item.search(/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/) !== -1 || 15 | item.search(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i) !== -1 16 | ) && !isNaN(parseDate(item)); 17 | }, function(a, b) { 18 | a = a.toLowerCase(); 19 | b = b.toLowerCase(); 20 | 21 | return parseDate(b) - parseDate(a); 22 | }); 23 | }()); 24 | -------------------------------------------------------------------------------- /lib/tablesort.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * tablesort v3.0.2 (2015-02-25) 3 | * http://tristen.ca/tablesort/demo/ 4 | * Copyright (c) 2015 ; Licensed MIT 5 | */!function(){function a(a,b){if(!a||"TABLE"!==a.tagName)throw new Error("Element must be a table");this.init(a,b||{})}var b=[],c=function(a){var b;return window.CustomEvent&&"function"==typeof window.CustomEvent?b=new CustomEvent(a):(b=document.createEvent("CustomEvent"),b.initCustomEvent(a,!1,!1,void 0)),b},d=function(a){return a.getAttribute("data-sort")||a.textContent||a.innerText||""},e=function(a,b){return a=a.toLowerCase(),b=b.toLowerCase(),a===b?0:b>a?1:-1},f=function(a,b){return function(c,d){var e=a(c.td,d.td);return 0===e?b?d.index-c.index:c.index-d.index:e}};a.extend=function(a,c,d){if("function"!=typeof c||"function"!=typeof d)throw new Error("Pattern and sort must be a function");b.push({name:a,pattern:c,sort:d})},a.prototype={init:function(a,b){var c,d,e,f,g=this;if(g.table=a,g.thead=!1,g.options=b,a.rows&&a.rows.length>0&&(a.tHead&&a.tHead.rows.length>0?(c=a.tHead.rows[a.tHead.rows.length-1],g.thead=!0):c=a.rows[0]),c){var h=function(){g.current&&g.current!==this&&(g.current.classList.remove("sort-up"),g.current.classList.remove("sort-down")),g.current=this,g.sortTable(this)};for(e=0;e0&&l.push(k),m++;if(!l)return}for(m=0;mm;m++)r[m]?(k=r[m],t++):k=q[m-t].tr,h.table.tBodies[0].appendChild(k);h.table.dispatchEvent(c("afterSort"))}},refresh:function(){void 0!==this.current&&this.sortTable(this.current,!0)}},"undefined"!=typeof module&&module.exports?module.exports=a:window.Tablesort=a}(); -------------------------------------------------------------------------------- /lib/tablesort.numeric.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var cleanNumber = function(i) { 3 | return i.replace(/[^\-?0-9.]/g, ''); 4 | }, 5 | 6 | compareNumber = function(a, b) { 7 | a = parseFloat(a); 8 | b = parseFloat(b); 9 | 10 | a = isNaN(a) ? 0 : a; 11 | b = isNaN(b) ? 0 : b; 12 | 13 | return a - b; 14 | }; 15 | 16 | Tablesort.extend('number', function(item) { 17 | return item.match(/^-?[£\x24Û¢´€]?\d+\s*([,\.]\d{0,2})/) || // Prefixed currency 18 | item.match(/^-?\d+\s*([,\.]\d{0,2})?[£\x24Û¢´€]/) || // Suffixed currency 19 | item.match(/^-?(\d)*-?([,\.]){0,1}-?(\d)+([E,e][\-+][\d]+)?%?$/); // Number 20 | }, function(a, b) { 21 | a = cleanNumber(a); 22 | b = cleanNumber(b); 23 | 24 | return compareNumber(b, a); 25 | }); 26 | }()); 27 | --------------------------------------------------------------------------------