├── .gitignore ├── Gulpfile.js ├── README.md ├── badge-gh.css ├── badge.js ├── dist ├── .gitkeep ├── Gulpfile.js ├── badge-gh.css └── badge.js ├── example.html ├── example_octo.png ├── example_octogeek.png └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | node_modules 3 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var uglify = require('gulp-uglify'); 3 | var minifyCss = require('gulp-minify-css'); 4 | 5 | gulp.task('css', function(){ 6 | return gulp.src('*.css') 7 | .pipe(minifyCss({compatibility: 'ie8'})) 8 | .pipe(gulp.dest('dist')); 9 | }); 10 | 11 | gulp.task('js', function(){ 12 | return gulp.src('*.js') 13 | .pipe(uglify()) 14 | .pipe(gulp.dest('dist')); //the destination folder 15 | }); 16 | 17 | gulp.task('default', ['js', 'css']); 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OctoBadge - Unofficial GitHub Badge 2 | 3 | ### Octo 4 | 5 | ![octo](https://raw.githubusercontent.com/calvinfroedge/octobadge-github-badge/master/example_octo.png) 6 | 7 | ``` 8 | 9 | ``` 10 | 11 | ### Octogeek 12 | 13 | ![octogeek](https://raw.githubusercontent.com/calvinfroedge/octobadge-github-badge/master/example_octogeek.png) 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | *"When it comes to hiring, I'll take a GitHub commit log over a resume any day." - John Resig, Creator of jQuery* 20 | 21 | Display a Cool Badge that Shows off Your GitHub Project Contributions! You can put this on your website, such as on your blog, to display some things about your GitHub profile: 22 | 23 | - Name 24 | - Location 25 | - Number of followers 26 | - Number of repos 27 | - Number of stars 28 | - Number of gists 29 | - Popular repositories and the number of stars 30 | - Languages you've written code in (by number of repos) 31 | 32 | ## Installing 33 | 34 | Just include the JS file before the closing `` tag, include the CSS in the `` section. 35 | 36 | You can grab these from jsDelivr: 37 | 38 | [JS](https://cdn.jsdelivr.net/octobadge.github/0.0.1/badge.min.js) 39 | ``` 40 | 41 | ``` 42 | 43 | [CSS](https://cdn.jsdelivr.net/octobadge.github/0.0.1/badge.min.css) 44 | ``` 45 | 46 | ``` 47 | 48 | ...Then use the badge HTML: 49 | 50 | ``` 51 | 52 | ``` 53 | 54 | ## Considerations 55 | 56 | The badge requires loading data from the GitHub API. It uses the browsing user's public API GitHub ratelimit, which is 60 requests per hour. The badge makes API requests for the following: 57 | 58 | - User profile data (name, # of followers, etc.) 59 | - Repo data (Needed to see how many stars a user has received) 60 | - Events data (for contribution graph). 61 | 62 | For most users, there will be 3 API requests needed to load all data, but some users with many contributions and repositories may require more to load all the data. 63 | 64 | To make sure users don't quickly use this up, the badge will cache the results of API requests for 1 day. To improve the badge, I think there should be a service that you OAuth with, which will cache your data from GitHub's API and allow it to be served from one request. This will avoid unnecessary requests and rate limits. If there is a lot of interest in this project, that will be my next step. 65 | 66 | ## What needs to be done? 67 | 68 | Testing! I've only tested in Chrome, Firefox and Safari on OSX so far, and the badge works in all of them. Automated functional tests would be nice. I don't think this project is a good candidate for unit testing (convince me otherwise?). 69 | 70 | It would also be cool to have other badges, so if you have a design idea, please reach out to me so I can see about getting it implemented. 71 | 72 | ## Developing 73 | 74 | I intentionally used vanilla JavaScript and CSS to make it simple for others to contribute. The build is just for minification. To run that, just run `npm install` and then run `gulp`. You should have node >= 0.10 75 | 76 | ## Future development 77 | 78 | If you want to get involved, feel free to create an issue. We may set up an IRC or Slack channel. 79 | 80 | Here are some future ideas: 81 | 82 | - Allow for hiring inquiries from the badge 83 | - Allow users to follow directly from badge (if they are logged into GitHub) 84 | - Display private contribution data as well as public contribution data in badge 85 | -------------------------------------------------------------------------------- /badge-gh.css: -------------------------------------------------------------------------------- 1 | .github-badge { 2 | cursor: pointer; 3 | color:#333; 4 | background:#efefef; 5 | border-radius: 1em; 6 | padding: 1em; 7 | display:inline-block; 8 | text-align:center; 9 | position:relative; 10 | font:13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; 11 | -moz-box-shadow: 10px 10px 5px #999; 12 | -webkit-box-shadow: 10px 10px 5px #999; 13 | box-shadow: 5px 5px 10px #999; 14 | box-sizing:border-box; 15 | } 16 | 17 | .github-badge .gb-summary, .github-badge .detail { 18 | float:left; 19 | } 20 | 21 | .github-badge .gb-summary { 22 | margin-right: 40px; 23 | } 24 | 25 | .github-badge .fa { 26 | width: 2em; 27 | } 28 | 29 | .github-badge .octicon { 30 | margin-left: 0.1em; 31 | width: 1.5em; 32 | } 33 | 34 | .github-badge .fa-map-marker { 35 | margin-left: 0.25em; 36 | width: 1.75em; 37 | } 38 | 39 | .github-badge .fa-star { 40 | margin-left: 0.2em; 41 | width: 1.8em; 42 | } 43 | 44 | 45 | .github-badge .fa-pencil { 46 | margin-left: 0.25em; 47 | width: 1.75em; 48 | } 49 | 50 | .github-badge img { 51 | text-align:center; 52 | } 53 | 54 | .github-badge img.user { 55 | border-radius: 50%; 56 | display: block; 57 | margin:0 auto 10px auto; 58 | } 59 | 60 | .github-badge img.octodex { 61 | display: block; 62 | position:absolute; 63 | bottom:3px; 64 | right:2px; 65 | } 66 | 67 | .github-badge h4 { 68 | font-size:1.2em; 69 | } 70 | 71 | .github-badge h4, p { 72 | text-align: left; 73 | display: block; 74 | vertical-align:top; 75 | margin:5px 0 5px 0; 76 | } 77 | 78 | .github-badge .detail section { 79 | border: 1px solid #d8d8d8; 80 | background:#fff; 81 | border-radius:3px; 82 | margin-bottom: 1em; 83 | max-width: 270px; 84 | } 85 | 86 | .github-badge .detail section h4 { 87 | border-radius:3px 3px 0 0; 88 | background:#f5f5f5; 89 | margin: 0; 90 | padding:0.5em 0.75em; 91 | } 92 | 93 | .github-badge .detail section p { 94 | padding:0.5em 0.5em 0.5em 0.75em; 95 | margin:0; 96 | border-top: 1px solid #d8d8d8; 97 | white-space: nowrap; 98 | } 99 | 100 | .github-badge .repos .fa-star { 101 | width: 1.25em; 102 | } 103 | 104 | .github-badge .repos span { 105 | margin-right: 1em; 106 | display: inline-block; 107 | width: 1.5em; 108 | } 109 | 110 | .github-badge.horizontal { 111 | 112 | } 113 | 114 | .powered { 115 | padding-top:20px; 116 | } 117 | 118 | .powered a { 119 | color:#ccc; 120 | font-family:'Helvetica'; 121 | font-size:9px; 122 | } 123 | 124 | 125 | 126 | .github-badge.octo { 127 | padding: 0.5em; 128 | -moz-box-shadow: 2px 2px 7px #999; 129 | -webkit-box-shadow: 2px 2px 7px #999; 130 | box-shadow: 2px 2px 7px #999; 131 | } 132 | 133 | .github-badge.octo .repos span { 134 | margin-right:0; 135 | } 136 | 137 | .github-badge.octo .user { 138 | float:left; 139 | margin-right:1.25em; 140 | } 141 | 142 | .github-badge.octo .gb-summary { 143 | margin-right: 20px; 144 | padding-top: 2px; 145 | } 146 | 147 | .github-badge.octo .gb-summary p { 148 | display: inline-block; 149 | padding-top: 10px; 150 | margin-right:1em; 151 | font-size:0.9em; 152 | } 153 | 154 | .github-badge.octo .fa, .github-badge.octo .octicon { 155 | display:inline-block; 156 | height: 1.25em; 157 | padding: 0; 158 | margin: 0; 159 | font-size: 15px; 160 | width:1.5em !important; 161 | } 162 | 163 | .github-badge.octo .fa-star { 164 | } 165 | 166 | .github-badge.octo .octicon-repo { 167 | padding-left: 0.2em; 168 | } 169 | 170 | .github-badge.octo .octodex { 171 | right:6px; 172 | top:5px; 173 | } 174 | 175 | 176 | .github-badge.octo h4 { 177 | position:absolute; 178 | top:0px; 179 | left:72px; 180 | font-size:0.8em; 181 | font-weight:normal; 182 | } 183 | 184 | .github-badge .history { 185 | width:150px; 186 | left:72px; 187 | border:1px solid #ccc; 188 | clear:both; 189 | position:absolute; 190 | top:46px; 191 | } 192 | 193 | .github-badge .history i { 194 | height: 8px; 195 | width:8px; 196 | background:#fff; 197 | margin:1px; 198 | display:inline-block; 199 | } 200 | 201 | .github-badge .history i.few-commits { 202 | background:#d6e685; 203 | } 204 | 205 | .github-badge .history i.some-commits { 206 | background:#8cc665; 207 | } 208 | 209 | .github-badge .history i.more-commits { 210 | background:#44a340; 211 | } 212 | 213 | .github-badge .history i.many-commits { 214 | background:#1e6823; 215 | } 216 | 217 | .github-badge .detail section.languages p { 218 | white-space: normal; 219 | } 220 | -------------------------------------------------------------------------------- /badge.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var API_URL = 'https://api.github.com/'; 3 | 4 | /** 5 | * Load and cache JSONP data 6 | */ 7 | var loadJSONP = function ( url, callback ) { 8 | var setLocalData = false; 9 | 10 | /* 11 | * Setting a variable callback method since we're writing to window 12 | */ 13 | var callbackMethod = url.replace(/[^a-zA-Z0-9]/g, '_') 14 | /* 15 | * Send data to the callback function, caching data if needed 16 | */ 17 | window[callbackMethod] = function(data){ 18 | if(setLocalData){ 19 | //Cache for 1 day 20 | var d = new Date(); 21 | d.setDate(d.getDate()+1); 22 | 23 | try { 24 | localStorage.setItem(url, JSON.stringify({expiry: d.getTime(), value: data})); 25 | } catch(e){ 26 | console.log("Could not add item to local storage...clearing localStorage...", e); 27 | localStorage.clear(); 28 | } 29 | } 30 | 31 | window[callback](data); 32 | } 33 | 34 | /* 35 | * Make the JSONP request 36 | */ 37 | function requestDataFromRemote(){ 38 | // Create script with url and callback (if specified) 39 | var ref = window.document.getElementsByTagName( 'script' )[ 0 ]; 40 | var script = window.document.createElement( 'script' ); 41 | script.src = url + (url.indexOf( '?' ) + 1 ? '&' : '?') + 'callback='+callbackMethod 42 | 43 | // Insert script tag into the DOM (append to ) 44 | ref.parentNode.insertBefore( script, ref ); 45 | 46 | // After the script is loaded (and executed), remove it 47 | script.onload = function () { 48 | this.remove(); 49 | }; 50 | } 51 | 52 | var stored = localStorage.getItem(url); 53 | if(stored){ 54 | var now = (new Date()).getTime(); 55 | var json = JSON.parse(stored); 56 | 57 | if(json.expiry > now){ 58 | window[callbackMethod](json.value); 59 | } else { 60 | localStorage.removeItem(url); 61 | setLocalData = true; 62 | requestDataFromRemote(); 63 | } 64 | } else { 65 | setLocalData = true; 66 | requestDataFromRemote(); 67 | } 68 | }; 69 | 70 | /* 71 | * Shorthand to grab an element 72 | */ 73 | var el = function(s){ 74 | return document.createElement(s); 75 | } 76 | 77 | /* 78 | * Set basic info from the widget 79 | */ 80 | function widgetInit(widget, data){ 81 | widget.className = 'github-badge'; 82 | data.orientation = widget.getAttribute('orientation') || 'vertical'; 83 | if(data.orientation) widget.className += ' '+data.orientation; 84 | data.badge = widget.getAttribute('badge') || 'octo'; 85 | widget.className += ' '+data.badge; 86 | data.username = widget.getAttribute('user'); 87 | } 88 | 89 | /* 90 | * Build the widget summary column 91 | */ 92 | function widgetSummaryColumnElements(widget, data, nodes){ 93 | //Create summary column 94 | nodes.summary = el('div'); 95 | nodes.summary.className = 'gb-summary'; 96 | 97 | var img = el('img'); 98 | img.className = "user"; 99 | img.width = (data.badge == 'octo' ? "50" : "100"); 100 | nodes.img = img; 101 | nodes.summary.appendChild(nodes.img); 102 | 103 | var name = el('h4'); 104 | nodes.name = name; 105 | nodes.summary.appendChild(nodes.name); 106 | 107 | function pwContent(contentName, iconClass){ 108 | var containerName = contentName + 'Container'; 109 | var c = el('p'); 110 | c.className = contentName; 111 | var i = el('i'); 112 | i.className=iconClass; 113 | c.appendChild(i); 114 | nodes[contentName] = el('span'); 115 | c.appendChild(nodes[contentName]); 116 | nodes[containerName] = c; 117 | nodes.summary.appendChild(nodes[containerName]); 118 | } 119 | 120 | if(data.badge != 'octo') pwContent('location', 'fa fa-map-marker'); 121 | pwContent('followers', 'fa fa-users'); 122 | pwContent('repostotal', 'octicon octicon-repo'); 123 | pwContent('stars', 'fa fa-star'); 124 | if(data.badge != 'octo') pwContent('gists', 'fa fa-pencil'); 125 | 126 | widget.appendChild(nodes.summary); 127 | } 128 | 129 | /* 130 | * Create detail elements 131 | */ 132 | function widgetDetailElements(widget, data, nodes){ 133 | if(data.badge != 'octo'){ 134 | //Repos 135 | nodes.detail = el('div'); 136 | nodes.detail.className = "detail"; 137 | 138 | nodes.repos = { 139 | container: el('section'), 140 | title: el('h4'), 141 | listContainer: el('div') 142 | } 143 | 144 | nodes.repos.container.className = 'repos'; 145 | nodes.repos.title.innerHTML = 'Popular repositories'; 146 | 147 | var r_ = nodes.repos; 148 | var rc = r_.container; 149 | rc.appendChild(r_.title); 150 | rc.appendChild(r_.listContainer); 151 | nodes.detail.appendChild(rc); 152 | 153 | //Languages 154 | nodes.languages = { 155 | container: el('section'), 156 | title: el('h4'), 157 | listContainer: el('div') 158 | } 159 | 160 | nodes.languages.container.className = 'languages'; 161 | nodes.languages.title.innerHTML = 'Codes in'; 162 | nodes.languages.container.appendChild(nodes.languages.title); 163 | nodes.languages.container.appendChild(nodes.languages.listContainer); 164 | 165 | nodes.detail.appendChild(nodes.languages.container); 166 | 167 | widget.appendChild(nodes.detail); 168 | } 169 | } 170 | 171 | /* 172 | * Add octodex to the widget 173 | */ 174 | function widgetOctodex(widget){ 175 | var octodex = el('img'); 176 | octodex.className = 'octodex'; 177 | octodex.width = '30'; 178 | octodex.src = 'data:image/png;base64,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'; 179 | widget.appendChild(octodex); 180 | } 181 | 182 | /* 183 | * Load the Widget's Basic Data 184 | */ 185 | function widgetDataBasic(widget, widget_data, nodes, callback){ 186 | // Function to run on success 187 | window.gh_badge_basicData = function(res) { 188 | var meta = res.meta; 189 | var data = res.data; 190 | 191 | widget.addEventListener('click', function(){ 192 | var a = el('a'); 193 | a.target = "_blank"; 194 | a.href = data.html_url; 195 | a.click(); 196 | }); 197 | 198 | if(nodes.img) nodes.img.src = data.avatar_url; 199 | 200 | if(nodes.name) nodes.name.innerHTML = data.name; 201 | 202 | if(nodes.location) nodes.location.innerHTML = data.location; 203 | 204 | if(nodes.followers) nodes.followers.innerHTML = data.followers + (widget_data.badge != 'octo' ? ' followers' : ''); 205 | 206 | if(nodes.repostotal){ 207 | nodes.repostotal.innerHTML = data.public_repos + (widget_data.badge != 'octo' ? ' public repos' : ''); 208 | widget_data.repos_count = data.public_repos; 209 | } 210 | 211 | if(nodes.gists) nodes.gists.innerHTML = data.public_gists + ' gists'; 212 | //if(nodes.followers) nodes.followers.innerHTML = data.followers + ' followers'; 213 | 214 | var page = 0; 215 | callback(data); 216 | } 217 | 218 | // Run request 219 | loadJSONP( API_URL+'users/'+widget_data.username, 'gh_badge_basicData' ); 220 | } 221 | 222 | /* 223 | * Load the Widget's Repo Data 224 | */ 225 | function widgetDataRepos(widget, widget_data, nodes, basic_data){ 226 | //Number of requests to make 227 | var n = Math.ceil(basic_data.public_repos/30); 228 | //Counter to check completed requests 229 | var n_ = 0; 230 | 231 | var untilDone = setInterval(function(){ 232 | if(n_ == n){ 233 | clearInterval(untilDone); 234 | 235 | if(widget_data.badge != 'octo'){ 236 | var sorted = widget_data.repos.sort(function(a, b){ 237 | if(a.stargazers_count < b.stargazers_count) return 1; 238 | if(a.stargazers_count > b.stargazers_count) return -1; 239 | return 0; 240 | }); 241 | 242 | var t = sorted.slice(0, 3); 243 | t.map(function(repo){ 244 | var p = el('p'); 245 | var i = el('i'); 246 | i.className = 'fa fa-star'; 247 | p.appendChild(i); 248 | var s = el('span'); 249 | s.innerHTML = repo.stargazers_count; 250 | p.appendChild(s); 251 | p.innerHTML = p.innerHTML + repo.name + ' ('+repo.language+')'; 252 | nodes.repos.listContainer.appendChild(p); 253 | }); 254 | 255 | 256 | var langP = el('p'); 257 | var langArray = []; 258 | for(var key in widget_data.languages){ 259 | if(key != 'null'){ 260 | var obj = {name: key, value: widget_data.languages[key]}; 261 | langArray.push(obj); 262 | } 263 | } 264 | 265 | var langSorted = langArray.sort(function(a, b){ 266 | if(a.value < b.value) return 1; 267 | if(a.value > b.value) return -1; 268 | return 0; 269 | }); 270 | 271 | var langString = langSorted.map(function(lang){ 272 | return lang.name; 273 | }).join(', '); 274 | 275 | 276 | langP.innerHTML = langString; 277 | 278 | nodes.languages.listContainer.appendChild(langP); 279 | } 280 | } 281 | }, 1); 282 | 283 | window.gh_badge_repoData = function(res){ 284 | var meta = res.meta; 285 | var data = res.data; 286 | 287 | data.map(function(repo){ 288 | widget_data.stargazers = widget_data.stargazers + repo.stargazers_count; 289 | widget_data.repos.push(repo); 290 | if(widget_data.languages[repo.language]){ 291 | ++widget_data.languages[repo.language]; 292 | } else { 293 | widget_data.languages[repo.language] = 1; 294 | } 295 | }); 296 | 297 | if(nodes.stars) nodes.stars.innerHTML = widget_data.stargazers + (widget_data.badge != 'octo' ? ' stars' : ''); 298 | ++n_; 299 | } 300 | 301 | //n JSONP calls 302 | for(var i=1;i<(n+1);i++){ 303 | var url = API_URL+'users/'+widget_data.username+'/repos?page='+i; 304 | loadJSONP(url, 'gh_badge_repoData'); 305 | } 306 | } 307 | 308 | /* 309 | * Add the history elements 310 | */ 311 | function widgetHistoryElement(widget, widget_data, nodes){ 312 | if(widget_data.badge == 'octo'){ 313 | nodes.history = el('div'); 314 | nodes.history.className = 'history'; 315 | nodes.history.id = 'github-badge-history'; 316 | 317 | var day = new Date(); 318 | day.setHours(0,0,0,0); 319 | day.setDate(day.getDate()-29); 320 | var DAYS_OF_HISTORY = 30; 321 | 322 | function padDigits(number, digits) { 323 | return Array(Math.max(digits - String(number).length + 1, 0)).join(0) + number; 324 | } 325 | 326 | widget_data.history = {days: {}}; 327 | for(var i=0;i monthAgo; 369 | } 370 | 371 | function populateHistoryData(){ 372 | var daysArr = widget_data.history.days; 373 | 374 | for(key in widget_data.history.days){ 375 | var day = widget_data.history.days[key]; 376 | 377 | var dayClass = ''; 378 | 379 | if(day.value >= 18){ 380 | dayClass = 'many-commits'; 381 | } else if(day.value >= 12){ 382 | dayClass = 'more-commits'; 383 | } else if(day.value >= 6){ 384 | dayClass = 'some-commits'; 385 | } else if(day.value > 0){ 386 | dayClass = 'few-commits'; 387 | } 388 | 389 | day.el.className = dayClass; 390 | } 391 | } 392 | 393 | window.gh_badge_history = function(res){ 394 | var meta = res.meta; 395 | var data = res.data; 396 | var daysObj = widget_data.history.days; 397 | 398 | data.map(function(e){ 399 | if(e.type == 'PushEvent' || e.type == 'PullRequestEvent'){ 400 | var s = githubDate(e); 401 | if(daysObj[s] != undefined){ 402 | if(e.type == 'PushEvent'){ 403 | daysObj[s].value += parseInt(e.payload.size); 404 | } 405 | 406 | if(e.type == 'PullRequestEvent'){ 407 | daysObj[s].value += parseInt(e.payload.pull_request.commits); 408 | } 409 | } else { 410 | } 411 | } 412 | }); 413 | 414 | if(data.length && widget_data.history.pages < 10){ //Only 10 days of data can be requested 415 | var lastEvent = githubDate(data[data.length-1]); 416 | if(dateStringIn30Days(lastEvent)){ 417 | loadHistory(); 418 | } else { 419 | populateHistoryData(); 420 | } 421 | } else { 422 | populateHistoryData(); 423 | } 424 | } 425 | 426 | loadHistory(); 427 | } 428 | 429 | /* 430 | * Main Widget Class 431 | */ 432 | var Widget = function(widget){ 433 | var nodes = {}; 434 | 435 | var data = { 436 | stargazers: 0, 437 | repos: [], 438 | repos_count: 0, 439 | languages: {} 440 | }; 441 | 442 | //Create elements 443 | widgetInit(widget, data); 444 | widgetSummaryColumnElements(widget, data, nodes); 445 | widgetDetailElements(widget, data, nodes); 446 | widgetOctodex(widget); 447 | widgetHistoryElement(widget, data, nodes); 448 | 449 | //Load data 450 | widgetDataBasic(widget, data, nodes, function(basic_data){ //Load repo data after 451 | widgetDataRepos(widget, data, nodes, basic_data); 452 | }); 453 | widgetHistoryData(widget, data, nodes); 454 | } 455 | 456 | /* 457 | * Badge is Initialized Here 458 | */ 459 | var init = (function(){ 460 | var widgets = document.getElementsByTagName('github-badge'); 461 | if(widgets.length){ 462 | //Add external dependencies 463 | function addCSS(src){ 464 | var head = document.getElementsByTagName('head')[0]; 465 | var css = el('link'); 466 | css.type = 'text/css'; 467 | css.rel = 'stylesheet'; 468 | css.href = src; 469 | head.appendChild(css); 470 | } 471 | addCSS('//cdnjs.cloudflare.com/ajax/libs/octicons/2.2.2/octicons.min.css'); 472 | addCSS('//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'); 473 | 474 | for(var i=0;ie.stargazers_count?-1:0}),n=a.slice(0,3);n.map(function(a){var e=g("p"),n=g("i");n.className="fa fa-star",e.appendChild(n);var i=g("span");i.innerHTML=a.stargazers_count,e.appendChild(i),e.innerHTML=e.innerHTML+a.name+" ("+a.language+")",t.repos.listContainer.appendChild(e)});var s=g("p"),l=[];for(var c in e.languages)if("null"!=c){var u={name:c,value:e.languages[c]};l.push(u)}var d=l.sort(function(a,e){return a.valuee.value?-1:0}),m=d.map(function(a){return a.name}).join(", ");s.innerHTML=m,t.languages.listContainer.appendChild(s)}},1);window.gh_badge_repoData=function(a){var n=(a.meta,a.data);n.map(function(a){e.stargazers=e.stargazers+a.stargazers_count,e.repos.push(a),e.languages[a.language]?++e.languages[a.language]:e.languages[a.language]=1}),t.stars&&(t.stars.innerHTML=e.stargazers+("octo"!=e.badge?" stars":"")),++r};for(var s=1;i+1>s;s++){var u=l+"users/"+e.username+"/repos?page="+s;c(u,"gh_badge_repoData")}}function o(a,e,t){function n(a,e){return Array(Math.max(e-String(a).length+1,0)).join(0)+a}if("octo"==e.badge){t.history=g("div"),t.history.className="history",t.history.id="github-badge-history";var i=new Date;i.setHours(0,0,0,0),i.setDate(i.getDate()-29);var r=30;e.history={days:{}};for(var o=0;r>o;o++){var s=[i.getFullYear(),n(i.getMonth()+1,2),n(i.getDate(),2)].join("-"),l=g("i");e.history.days[s]={el:l,value:0},l.setAttribute("data-date",s),i.setDate(i.getDate()+1),t.history.appendChild(l)}a.appendChild(t.history)}}function s(a,e){function t(){c(l+"users/"+e.username+"/events/public?page="+ ++e.history.pages,"gh_badge_history")}function n(a){return a.created_at.split("T")[0]}function i(a){var e=new Date;e.setHours(0,0,0,0),e.setDate(e.getDate()-30);var t=e,n=new Date(a);return n.setDate(n.getDate()+1),n>t}function r(){e.history.days;for(key in e.history.days){var a=e.history.days[key],t="";a.value>=18?t="many-commits":a.value>=12?t="more-commits":a.value>=6?t="some-commits":a.value>0&&(t="few-commits"),a.el.className=t}}"octo"==e.badge&&(e.history.pages=0,window.gh_badge_history=function(a){var o=(a.meta,a.data),s=e.history.days;if(o.map(function(a){if("PushEvent"==a.type||"PullRequestEvent"==a.type){var e=n(a);void 0!=s[e]&&("PushEvent"==a.type&&(s[e].value+=parseInt(a.payload.size)),"PullRequestEvent"==a.type&&(s[e].value+=parseInt(a.payload.pull_request.commits)))}}),o.length&&e.history.pages<10){var l=n(o[o.length-1]);i(l)?t():r()}else r()},t())}{var l="https://api.github.com/",c=function(a,e){function t(){var e=window.document.getElementsByTagName("script")[0],t=window.document.createElement("script");t.src=a+(a.indexOf("?")+1?"&":"?")+"callback="+i,e.parentNode.insertBefore(t,e),t.onload=function(){this.remove()}}var n=!1,i=a.replace(/[^a-zA-Z0-9]/g,"_");window[i]=function(t){if(n){var i=new Date;i.setDate(i.getDate()+1);try{localStorage.setItem(a,JSON.stringify({expiry:i.getTime(),value:t}))}catch(r){console.log("Could not add item to local storage...clearing localStorage...",r),localStorage.clear()}}window[e](t)};var r=localStorage.getItem(a);if(r){var o=(new Date).getTime(),s=JSON.parse(r);s.expiry>o?window[i](s.value):(localStorage.removeItem(a),n=!0,t())}else n=!0,t()},g=function(a){return document.createElement(a)},u=function(l){var c={},g={stargazers:0,repos:[],repos_count:0,languages:{}};a(l,g),e(l,g,c),t(l,g,c),n(l),o(l,g,c),i(l,g,c,function(a){r(l,g,c,a)}),s(l,g,c)};!function(){function a(a){var e=document.getElementsByTagName("head")[0],t=g("link");t.type="text/css",t.rel="stylesheet",t.href=a,e.appendChild(t)}var e=document.getElementsByTagName("github-badge");if(e.length){a("//cdnjs.cloudflare.com/ajax/libs/octicons/2.2.2/octicons.min.css"),a("//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css");for(var t=0;t 2 | 3 | 4 | 5 | 6 | 7 |

Octo Badge

8 | 9 | 10 |

Octogeek Badge

11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example_octo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinfroedge/octobadge-github-badge/b049c07a2f7a00edccdda2f1b79e6629a2f6f8c8/example_octo.png -------------------------------------------------------------------------------- /example_octogeek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvinfroedge/octobadge-github-badge/b049c07a2f7a00edccdda2f1b79e6629a2f6f8c8/example_octogeek.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OctoBadge", 3 | "description": "A cool badge you can use to show off your github profile and contributions.", 4 | "version": "0.0.1", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/calvinfroedge/octobadge.git" 8 | }, 9 | "devDependencies": { 10 | "gulp" : "3.8.11", 11 | "gulp-minify-css" : "1.1.1", 12 | "gulp-uglify" : "1.0.2" 13 | } 14 | } 15 | --------------------------------------------------------------------------------