├── 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 |
34 |
35 |
36 |
37 | Repository |
38 | Language |
39 | Stars |
40 | Forks |
41 | Issues |
42 | Created |
43 | Updated |
44 | Description |
45 |
46 |
47 |
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 |
--------------------------------------------------------------------------------