Merge branch 'master' into librariesio

This commit is contained in:
Mauro Pompilio 2016-08-12 10:48:47 +01:00
commit a51c440310
25 changed files with 2780 additions and 990 deletions

3
.gitignore vendored
View File

@ -3,8 +3,9 @@
/analytics.json
/coverage.html
/redis
/secret.json
/ServerScript
/secret.json
/.github-user-tokens.json
# Installed npm modules
node_modules

View File

@ -1,7 +1,9 @@
before_install: sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++
before_install:
- sudo apt-get update -myqq
- sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++
language: node_js
node_js:
- 0.10
- 6
branches:
only:

View File

@ -14,7 +14,19 @@ shields.io.
Note that the root gets redirected to <http://shields.io>.
For testing purposes, you can go to `http://localhost/try.html`.
You should modify that file. The "real" root, `http://localhost/index.html`,
gets generated from the `try.html` file.
gets generated from the `try.html` file with a `make website`.
## Ground rules
- The left-hand side of a badge should not advertize. It should be a noun
describing succinctly the meaning of the right-hand-side data.
- New query parameters (such as `?label=` or `?style=`) should apply to any
requested badge. They must be registered in the cache (see `LruCache` in
`server.js`).
- The format of new badges should be of the form
`/VENDOR/SUBVENDOR-BADGE-SPECIFIC/PARAMETERS.format`. For instance,
`https://img.shields.io/gitter/room/nwjs/nw.js.svg`. The vendor is gitter, the
badge is for rooms, the parameter is nwjs/nw.js, and the format is svg.
## Implementations

View File

@ -1,3 +1,3 @@
FROM node:0.12.7-onbuild
FROM node:0.12-onbuild
ENV INFOSITE http://shields.io
EXPOSE 80

View File

@ -6,9 +6,9 @@
[![build status](http://img.shields.io/travis/badges/gh-badges.svg)](https://travis-ci.org/badges/gh-badges)
Make your own badges [here][badges]!
Make your own badges [here][badges]! (Quick guide: `https://img.shields.io/badge/left-right-f39f37.svg`.)
[badges]: <http://img.shields.io>
[badges]: <http://shields.io/#your-badge>
# Install the API
@ -71,10 +71,13 @@ The format is the following:
/* Textual information shown, in order. */
"text": [ "build", "passed" ],
"format": "svg", // Also supports "json".
"colorscheme": "green"
"colorscheme": "green",
/* … Or… */
"colorA": "#555",
"colorB": "#4c1"
"colorB": "#4c1",
/* See template/ for a list of available templates.
Each offers a different visual design. */
"template": "flat"
}
```
@ -132,7 +135,7 @@ heroku open
You can build and run the server locally using Docker. First build an image:
```console
$ build -t shields ./
$ docker build -t shields ./
Sending build context to Docker daemon 3.923 MB
Step 0 : FROM node:0.12.7-onbuild
@ -153,6 +156,15 @@ http://[::1]:80/try.html
Assuming Docker is running locally, you should be able to get to the application at http://localhost:8080/try.html. If you run Docker in a virtual machine (such as boot2docker or Docker Machine) then you will need to replace `localhost` with the actual IP address of that virtual machine.
# Main Server Sysadmin
- DNS round-robin between https://vps197850.ovh.net/try.html and https://vps244529.ovh.net/try.html.
- Self-signed TLS certificates, but `img.shields.io` is behind CloudFlare, which provides signed certificates.
- Using node v0.12.7 because later versions, combined with node-canvas, give inaccurate badge measurements.
- Using forever (the node monitor) to automatically restart the server when it crashes.
See https://github.com/badges/ServerScript for helper admin scripts.
# Links
See <https://github.com/h5bp/lazyweb-requests/issues/150> for a story of the

View File

@ -4,17 +4,31 @@ favicon:
node gh-badge.js '' '' '#bada55' .png > favicon.png
website:
cat try.html | sed "s,\(<img src='\)/,\1https://img.shields.io/," \
| sed "s,<span id='imgUrlPrefix'>,&https://img.shields.io," \
| sed "s,var origin = '';,var origin = 'https://img.shields.io';," \
| sed "s,<style>,<!-- WARNING: THIS FILE WAS GENERATED FROM try.html -->\n<style>," > index.html
cat try.html | sed "s,\(<img src='\)\(/[^'\?]\+\)',\1https://img.shields.io\2?maxAge=2592000'," \
| sed "s,\(<img src='\)\(/[^'\?]\+\?[^']\+\)',\1https://img.shields.io\2\&maxAge=2592000'," \
| sed "s,<span id='imgUrlPrefix'>,&https://img.shields.io," \
| sed "s,var origin = '';,var origin = 'https://img.shields.io';," \
| sed "s,<style>,<!-- WARNING: THIS FILE WAS GENERATED FROM try.html -->\n<style>," > index.html
deploy:
deploy: deploy-s0 deploy-s1 deploy-gh-pages
deploy-s0:
git add -f Verdana.ttf
git add -f secret.json
git commit -m'MUST NOT BE ON GITHUB'
git push -f s0 HEAD:master
git reset HEAD~1
git checkout master
deploy-s1:
git add -f Verdana.ttf
git add -f secret.json
git commit -m'MUST NOT BE ON GITHUB'
git push -f s1 HEAD:master
git reset HEAD~1
git checkout master
deploy-gh-pages:
(git checkout -B gh-pages && \
git merge master && \
git push -f origin gh-pages:gh-pages) || git checkout master
@ -45,4 +59,4 @@ redis:
test:
npm test
.PHONY: all favicon website deploy deploy-heroku setup redis test
.PHONY: all favicon website deploy deploy-s0 deploy-s1 deploy-gh-pages deploy-heroku setup redis test

View File

@ -4,7 +4,7 @@
</p>
<p align="center">
<a href="https://www.gratipay.com/Shields/">
<img src="https://img.shields.io/gratipay/shields.svg"
<img src="https://img.shields.io/gratipay/team/shields.svg"
alt="Gratipay">
</a>
<a href="https://npmjs.org/package/gh-badges">
@ -23,6 +23,10 @@
* **[SPECIFICATION](spec/SPECIFICATION.md)** spec for the visual design of Shields badges.
* **[LICENSE](LICENSE.md)** public domain dedication.
Make your own badges [here][badges]! (Quick guide: `https://img.shields.io/badge/left-right-f39f37.svg`.)
[badges]: <http://shields.io/#your-badge>
## Solving the problem
Many GitHub repositories sport badges for things like:
<table>
@ -67,6 +71,7 @@ What kind of metadata can you convey using badges?
## Services using the Shields standard
* [Badger](https://github.com/badges/badgerbadgerbadger)
* [badges2svg](https://github.com/bfontaine/badges2svg)
* [Codacy](https://www.codacy.com)
* [Code Climate](https://codeclimate.com/changelog/510d4fde56b102523a0004bf)
* [Coveralls](https://coveralls.io/)
* [Forkability](http://basicallydan.github.io/forkability/)

View File

@ -1 +0,0 @@
require('ass'); require('../gh-badge.js');

View File

@ -2,18 +2,7 @@ var fs = require('fs');
var path = require('path');
var SVGO = require('svgo');
var dot = require('dot');
// Initialize what will be used for automatic text measurement.
var Canvas = require('canvas');
var canvasElement = new Canvas(0, 0); // Width and height are irrelevant.
var canvasContext = canvasElement.getContext('2d');
var CanvasFont = Canvas.Font;
try {
var opensans = new CanvasFont('Verdana',
path.join(__dirname, 'Verdana.ttf'));
canvasContext.addFont(opensans);
} catch(e) {}
canvasContext.font = '11px Verdana, "DejaVu Sans"';
var measureTextWidth = require('./measure-text');
// cache templates.
var templates = {};
@ -74,10 +63,15 @@ function makeImage(data, cb) {
if (data.text[0].length === 0) {
data.logoPadding = 0;
}
var textWidth1 = (measureTextWidth(data.text[0])|0);
var textWidth2 = (measureTextWidth(data.text[1])|0);
// Increase chances of pixel grid alignment.
if (textWidth1 % 2 === 0) { textWidth1++; }
if (textWidth2 % 2 === 0) { textWidth2++; }
data.widths = [
(canvasContext.measureText(data.text[0]).width|0) + 10
+ data.logoWidth + data.logoPadding,
(canvasContext.measureText(data.text[1]).width|0) + 10,
textWidth1 + 10 + data.logoWidth + data.logoPadding,
textWidth2 + 10,
];
if (data.links === undefined) {
data.links = ['', ''];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 332 B

File diff suppressed because it is too large Load Diff

253
lib/github-auth.js Normal file
View File

@ -0,0 +1,253 @@
var querystring = require('querystring');
var request = require('request');
var autosave = require('json-autosave');
var serverSecrets;
try {
// Everything that cannot be checked in but is useful server-side
// is stored in this JSON data.
serverSecrets = require('../secret.json');
} catch(e) {}
var githubUserTokens;
var githubUserTokensFile = '.github-user-tokens.json';
autosave(githubUserTokensFile, {data:[]}).then(function(f) {
githubUserTokens = f;
for (var i = 0; i < githubUserTokens.data.length; i++) {
addGithubToken(githubUserTokens.data[i]);
}
}).catch(function(e) { console.error('Could not create ' + githubUserTokensFile); });
function setRoutes(server) {
server.route(/^\/github-auth$/, function(data, match, end, ask) {
if (!(serverSecrets && serverSecrets.gh_client_id)) {
return end('This server is missing GitHub client secrets.');
}
var query = querystring.stringify({
client_id: serverSecrets.gh_client_id,
redirect_uri: 'https://img.shields.io/github-auth/done',
});
ask.res.statusCode = 302; // Found.
ask.res.setHeader('Location', 'https://github.com/login/oauth/authorize?' + query);
end('');
});
server.route(/^\/github-auth\/done$/, function(data, match, end, ask) {
if (!(serverSecrets && serverSecrets.gh_client_id && serverSecrets.gh_client_secret)) {
return end('This server is missing GitHub client secrets.');
}
if (!data.code) {
return end('GitHub OAuth authentication failed to provide a code.');
}
var options = {
url: 'https://github.com/login/oauth/access_token',
headers: {
'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
'User-Agent': 'Shields.io',
},
form: querystring.stringify({
client_id: serverSecrets.gh_client_id,
client_secret: serverSecrets.gh_client_secret,
code: data.code,
}),
method: 'POST',
};
request(options, function(err, res, body) {
if (err != null) { return end('The connection to GitHub failed.'); }
try {
var content = querystring.parse(body);
} catch(e) { return end('The GitHub OAuth token could not be parsed.'); }
var token = content.access_token;
if (!token) {
return end('The GitHub OAuth process did not return a user token.');
}
console.log('GitHub OAuth: ' + token);
ask.res.setHeader('Content-Type', 'text/html');
end('<p>Shields.io has received your app-specific GitHub user token. ' +
'You can revoke it by going to ' +
'<a href="https://github.com/settings/applications">GitHub</a>.</p>' +
'<p>Until you do, you have now increased the rate limit for GitHub ' +
'requests going through Shields.io. GitHub-related badges are ' +
'therefore more robust.</p>' +
'<p>Thanks for contributing to a smoother experience for ' +
'everyone!</p>' +
'<p><a href="/">Back to the website</a></p>');
sendTokenToAllServers(token)
.catch(function(e) {
console.error('GitHub user token transmission failed:', e);
});
});
});
server.route(/^\/github-auth\/add-token$/, function(data, match, end, ask) {
console.log('GitHub add token called with', JSON.stringify(data));
if (data.shieldsSecret !== serverSecrets.shieldsSecret) {
// An unknown entity tries to connect. Let the connection linger for a minute.
return setTimeout(function() { end('Invalid secret.'); }, 60000);
}
addGithubToken(data.token);
end('Thanks!');
});
};
function sendTokenToAllServers(token) {
var ips = serverSecrets.shieldsIps;
return Promise.all(ips.map(function(ip) {
return new Promise(function(resolve, reject) {
var options = {
url: 'https://' + ip + '/github-auth/add-token',
method: 'POST',
form: {
shieldsSecret: serverSecrets.shieldsSecret,
token: token,
},
// We target servers by IP, and we use HTTPS. Assuming that
// 1. Internet routers aren't hacked, and
// 2. We don't unknowingly lose our IP to someone else,
// we're not leaking people's and our information.
// (If we did, it would have no impact, as we only ask for a token,
// no GitHub scope. The malicious entity would only be able to use
// our rate limit pool.)
// FIXME: use letsencrypt.
strictSSL: false,
};
request(options, function(err, res, body) {
if (err != null) { return reject('Posting the GitHub user token failed: ' + err.stack); }
resolve();
});
});
}));
}
// Track rate limit requests remaining.
// Ideally, we would want priority queues here.
var reqRemaining = new Map(); // From token to requests remaining.
var reqReset = new Map(); // From token to timestamp.
// token: client token as a string.
// reqs: number of requests remaining.
// reset: timestamp when the number of remaining requests is reset.
function setReqRemaining(token, reqs, reset) {
reqRemaining.set(token, reqs);
reqReset.set(token, reset);
}
function rmReqRemaining(token) {
reqRemaining.delete(token);
reqReset.delete(token);
}
function utcEpochSeconds() {
return ((Date.now() / 1000) >>> 0);
}
var userTokenRateLimit = 12500;
// Return false if the token cannot reasonably be expected to perform
// a GitHub request.
function isTokenUsable(token, now) {
var reqs = reqRemaining.get(token);
var reset = reqReset.get(token);
// We don't want to empty more than 3/4 of a user's rate limit.
var hasRemainingReqs = reqs > (userTokenRateLimit / 4);
var isBeyondRateLimitReset = reset < now;
return hasRemainingReqs || isBeyondRateLimitReset;
}
// Return a list of tokens (as strings) which can be used for a GitHub request,
// with a reasonable chance that the request will succeed.
function usableTokens() {
var now = utcEpochSeconds();
return githubUserTokens.data.filter(function(token) {
return isTokenUsable(token, now);
});
}
// Retrieve a user token if there is one for which we believe there are requests
// remaining. Return undefined if we could not find one.
function getReqRemainingToken() {
// Go through the user tokens.
// Among usable ones, use the one with the highest number of remaining
// requests.
var tokens = usableTokens();
var highestReq = -1;
var highestToken;
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var reqs = reqRemaining.get(token);
if (reqs > highestReq) {
highestReq = reqs;
highestToken = token;
}
}
return highestToken;
}
function addGithubToken(token) {
// A reset date of 0 has to be in the past.
setReqRemaining(token, userTokenRateLimit, 0);
// Insert it only if it is not registered yet.
if (githubUserTokens.data.indexOf(token) === -1) {
githubUserTokens.data.push(token);
}
}
function rmGithubToken(token) {
rmReqRemaining(token);
// Remove it only if it is in there.
var idx = githubUserTokens.data.indexOf(token);
if (idx >= 0) {
githubUserTokens.data.splice(idx, 1);
}
}
// Personal tokens allow access to GitHub private repositories.
// You can manage your personal GitHub token at
// <https://github.com/settings/tokens>.
if (serverSecrets && serverSecrets.gh_token) {
addGithubToken(serverSecrets.gh_token);
}
// Act like request(), but tweak headers and query to avoid hitting a rate
// limit.
function githubRequest(request, url, query, cb) {
query = query || {};
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
var headers = {
'User-Agent': 'Shields.io',
'Accept': 'application/vnd.github.v3+json',
};
var githubToken = getReqRemainingToken();
if (githubToken != null) {
// Typically, GitHub user tokens grants us 12500 req/hour.
headers['Authorization'] = 'token ' + githubToken;
} else if (serverSecrets && serverSecrets.gh_client_id) {
// Using our OAuth App secret grants us 5000 req/hour
// instead of the standard 60 req/hour.
query.client_id = serverSecrets.gh_client_id;
query.client_secret = serverSecrets.gh_client_secret;
}
var qs = querystring.stringify(query);
if (qs) { url += '?' + qs; }
request(url, {headers: headers}, function(err, res, buffer) {
if (githubToken != null) {
if (res.statusCode === 401) { // Unauthorized.
rmGithubToken(githubToken);
} else {
var remaining = +res.headers['x-ratelimit-remaining'];
// reset is in UTC epoch seconds.
var reset = +res.headers['x-ratelimit-reset'];
setReqRemaining(githubToken, remaining, reset);
if (remaining === 0) { return; } // Hope for the best in the cache.
}
}
cb(err, res, buffer);
});
}
exports.setRoutes = setRoutes;
exports.request = githubRequest;

1
logo/bithound.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="-391.8 393.6 14 15.4"><style>.st0{fill:none}.st1{fill:#FFF}</style><path class="st0" d="M-1295 274H1v1296h-1296z"/><path class="st1" d="M-377.8 400.7c-.1-.6-.2-1.2-.4-1.8-.5-1.6-1.6-3.3-2.8-4.5-.1 0-.3-.2-.4-.1-.2.1.2.9.2 1.1.3.9.5 1.8.3 2.8-.3 1.2-1.2 2.1-2.5 2.2-.7.1-1.4 0-2.1.1-1 .1-2.1.5-2.8 1.3-.1.1-.1.2-.2.3-.3 0-.5-.1-.6-.1h-.1c-.8-.4-1.3-1.4-1.7-2.2-.2-.4-.3-.7-.4-1.1 0-.2 0-.4-.1-.5v-.4c-.1-.2-.3 0-.3.2 0 .3-.1.6-.1.8 0 .5.2 1 .3 1.4.3.7.6 1.3 1.1 1.8l.6.6c.2.2.4.3.6.5.2.3.2.6.2.9 0 .8-.1 1.5.2 2.3.2.7.4 1.4.8 1.9.2.3.5.6.7.8.2.1.5.2.3-.4-.4-1.2-.5-2.4.2-3.5.6-.9 1.6-1.7 2.8-1.6 1.7.2 2.9 1.8 2.8 3.5 0 .5 0 .9-.2 1.3-.1.2-.1.4-.1.6.1.3.4.2.6 0 .4-.6.9-1.1 1.2-1.7 1.5-2.1 2.2-4.3 1.9-6.5z"/><path class="st1" d="M-387.5 396c0 .1.1.2.1.3v.1c.1.3.4.4.6.6.1.1.4.2.6.2.2.1.7 0 .9 0 .5 0 1-.2 1.4 0-2.3 2.7-.1 3.3 1.6 2 1.9-1.5.1-5 .1-5s0-.3-.8-.4c-.1 0-.1-.1-.2-.1-.6-.3-1.3.2-1.8.7-.2.1-.3.3-.5.4-.2.2-.5.3-.7.4-.3.1-.6.3-.9.3h-.2c-.1 0-.2 0-.2.1 0 0-.1.1-.1.2-.1 0 0 .1.1.2z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

19
measure-text.js Normal file
View File

@ -0,0 +1,19 @@
'use strict';
var path = require('path');
var fs = require('fs');
var PDFDocument = require('pdfkit');
var doc = (new PDFDocument({size:'A4', layout:'landscape'}));
try {
doc = doc.font(path.join(__dirname, 'Verdana.ttf'));
} catch (ex) {
doc = doc.font('Helvetica-Bold')
console.warn('Could not load font file "Verdana.ttf", text widths will therefore be approximate', ex);
}
doc = doc.fontSize(11);
module.exports = measure;
function measure(str) {
return doc.widthOfString(str);
}

View File

@ -1,6 +1,6 @@
{
"name": "gh-badges",
"version": "1.1.2",
"version": "1.2.2",
"description": "GitHub badges implemented in SVG.",
"keywords": ["GitHub", "badge", "SVG", "image"],
"homepage": "http://shields.io",
@ -18,35 +18,30 @@
"dependencies": {
"dot": "~1.0.3",
"svgo": "~0.5.1",
"canvas": "~1.1.2",
"phantomjs": "~1.9.2-6",
"es6-promise": "~2.1.0",
"pdfkit": "~0.7.1",
"phantomjs-prebuilt": "~2.1.7",
"request": "~2.55.0",
"redis": "~1.0.0",
"camp": "~15.5.5",
"camp": "~16.2.2",
"semver": "~4.3.3",
"bower": "~1.4.1",
"promise": "~7.0.0"
},
"devDependencies": {
"ass": "~0.0.6",
"should": "~3.0.0",
"mocha": "~1.14.0"
"chrome-web-store-item-property": "^1.1.2",
"json-autosave": "~1.1.1"
},
"scripts": {
"test": "mocha -R spec test.js"
"test": "node test/test.js"
},
"bin": { "badge": "./gh-badge.js" },
"files": [
"badge.js",
"README.md",
"gh-badge.js",
"measure-text.js",
"templates",
"svg-to-img.js",
"colorscheme.json",
"phantomjs-svg2png.js",
"lru-cache.js",
"logo"
],
"engines": { "node": "0.10.x" }
]
}

1687
server.js

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
var nodeUrl = require('url');
var request = require('request');
var Promise = require('promise');
var serverSecrets;
try {
// Everything that cannot be checked in but is useful server-side
@ -185,11 +184,14 @@ var githubLicense = function(user, repo) {
// Key phrases for common licenses
var licensePhrases = {
'Apache 1.1': 'apache (software)? license,? (version)? 1\\.1',
'Apache 2': 'apache (software)? license,? (version)? 2',
'Apache 1.1': 'apache (software )?license,? (version)? 1\\.1',
'Apache 2': 'apache (software )?license,? (version)? 2',
'Original BSD': 'all advertising materials mentioning features or use of this software must display the following acknowledgement',
'New BSD': 'may be used to endorse or promote products derived from this software without specific prior written permission',
'BSD': 'redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met',
'AGPLv1': 'affero general public license,? version 1',
'AGPLv3': 'affero general public license,? version 3',
'AGPL': 'affero general public license',
'GPLv2': 'gnu general public license,? version 2',
'GPLv3': 'gnu general public license,? version 3',
'GPL': 'gnu general public license',
@ -205,9 +207,6 @@ var licensePhrases = {
'Eclipse': 'eclipse public license',
'Artistic': 'artistic license',
'zlib': 'the origin of this software must not be misrepresented',
'AGPLv1': 'affero general public license,? version 1',
'AGPLv3': 'affero general public license,? version 3',
'AGPL': 'affero general public license',
'ISC': 'permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted',
'CC0': 'cc0',
'Unlicense': 'this is free and unencumbered software released into the public domain',

View File

@ -1,7 +1,7 @@
var fs = require('fs');
var os = require('os');
var path = require('path');
var phantom = require('phantomjs');
var phantom = require('phantomjs-prebuilt');
var LruCache = require('./lru-cache.js');
var childProcess = require('child_process');
var phantomScript = path.join(__dirname, 'phantomjs-svg2png.js');

View File

@ -1,10 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="{{=it.widths[0]+it.widths[1]}}" height="20">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=it.widths[0]+it.widths[1]}}" height="20">
<g shape-rendering="crispEdges">
<rect width="{{=it.widths[0]}}" height="20" fill="{{=it.escapeXml(it.colorA||"#555")}}"/>
<rect x="{{=it.widths[0]}}" width="{{=it.widths[1]}}" height="20" fill="{{=it.escapeXml(it.colorB||"#4c1")}}"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="{{=it.widths[0]/2+1}}" y="14">{{=it.escapeXml(it.text[0])}}</text>
{{?it.logo}}
<image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
{{?}}
<text x="{{=(it.widths[0]+it.logoWidth+it.logoPadding)/2}}" y="14">{{=it.escapeXml(it.text[0])}}</text>
<text x="{{=it.widths[0]+it.widths[1]/2-1}}" y="14">{{=it.escapeXml(it.text[1])}}</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 826 B

View File

@ -14,9 +14,11 @@
</linearGradient>
<g stroke="#d5d5d5">
<rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="{{=it.widths[0]}}" height="19" rx="2"/>
{{?(it.text[1] && it.text[1].length)}}
<rect y="0.5" x="{{=it.widths[0]+6.5}}" width="{{=it.widths[1]}}" height="19" rx="2" fill="#fafafa"/>
<rect x="{{=it.widths[0]+6}}" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
<path d="M{{=it.widths[0]+6.5}} 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
{{?}}
</g>
{{?it.logo}}
<image x="5" y="3" width="{{=it.logoWidth}}" height="14" xlink:href="{{=it.logo}}"/>
@ -24,10 +26,12 @@
<g fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" font-weight="700" font-size="11px" line-height="14px">
<text x="{{=(it.widths[0]+it.logoWidth+it.logoPadding)/2}}" y="15" fill="#fff">{{=it.escapeXml(it.capitalize(it.text[0]))}}</text>
<text x="{{=(it.widths[0]+it.logoWidth+it.logoPadding)/2}}" y="14">{{=it.escapeXml(it.capitalize(it.text[0]))}}</text>
{{?(it.text[1] && it.text[1].length)}}
<text x="{{=it.widths[0]+it.widths[1]/2+6}}" y="15" fill="#fff">{{=it.escapeXml(it.text[1])}}</text>
<a xlink:href="{{=it.links[1]}}">
<text id="rlink" x="{{=it.widths[0]+it.widths[1]/2+6}}" y="14">{{=it.escapeXml(it.text[1])}}</text>
</a>
{{?}}
</g>
<a xlink:href="{{=it.links[0]}}">
<rect id="llink" stroke="#d5d5d5" fill="url(#a)" x="0.5" y="0.5" width="{{=it.widths[0]}}" height="19" rx="2"/>

151
test.js
View File

@ -1,151 +0,0 @@
var ass = require('ass').enable();
var should = require('should');
var http = require('http');
var cproc = require('child_process');
var fs = require('fs');
describe('the CLI', function() {
it('should provide a help message', function(done) {
var child = cproc.spawn('node', ['ass-stubs/cli-test.js']);
var buffer = '';
child.stdout.on('data', function(chunk) {
buffer += ''+chunk;
});
child.stdout.on('end', function() {
buffer.should.startWith('Usage');
done();
});
});
it('should produce default badges', function(done) {
var child = cproc.spawn('node',
['ass-stubs/cli-test.js', 'cactus', 'grown']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
buffer.should.startWith('<svg');
buffer.should.containEql('cactus');
buffer.should.containEql('grown');
done();
});
});
it('should produce colorschemed badges', function(done) {
child = cproc.spawn('node',
['ass-stubs/cli-test.js', 'cactus', 'grown', ':green']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
buffer.should.startWith('<svg');
done();
});
});
it('should produce right-color badges', function(done) {
child = cproc.spawn('node',
['ass-stubs/cli-test.js', 'cactus', 'grown', '#abcdef']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
buffer.should.containEql('#abcdef');
done();
});
});
it('should produce PNG badges', function(done) {
child = cproc.spawn('node',
['ass-stubs/cli-test.js', 'cactus', 'grown', '.png']);
child.stdout.on('data', function(chunk) {
// Check the PNG magic number.
chunk[0].should.equal(0x89);
chunk[1].should.equal(0x50);
chunk[2].should.equal(0x4e);
chunk[3].should.equal(0x47);
chunk[4].should.equal(0x0d);
chunk[5].should.equal(0x0a);
chunk[6].should.equal(0x1a);
chunk[7].should.equal(0x0a);
done();
});
});
});
describe('the server', function() {
var port = '1111';
var url = 'http://127.0.0.1:' + port + '/';
var server;
// Start running the server.
it('should start', function(done) {
server = cproc.spawn('node', ['ass-stubs/server-test.js', port]);
var isDone = false;
server.stdout.on('data', function(data) {
if (data.toString().indexOf('ready') >= 0 && !isDone) { done(); isDone = true; }
});
server.stderr.on('data', function(data) { console.log(''+data); });
});
it('should produce colorscheme badges', function(done) {
http.get(url + ':fruit-apple-green.svg',
function(res) {
var buffer = '';
res.on('data', function(chunk) { buffer += ''+chunk; });
res.on('end', function() {
buffer.should.startWith('<svg');
buffer.should.containEql('fruit');
buffer.should.containEql('apple');
done();
});
});
});
it('should produce colorscheme PNG badges', function(done) {
http.get(url + ':fruit-apple-green.png',
function(res) {
res.on('data', function(chunk) {
// Check the PNG magic number.
chunk[0].should.equal(0x89);
chunk[1].should.equal(0x50);
chunk[2].should.equal(0x4e);
chunk[3].should.equal(0x47);
chunk[4].should.equal(0x0d);
chunk[5].should.equal(0x0a);
chunk[6].should.equal(0x1a);
chunk[7].should.equal(0x0a);
done();
});
});
});
it('should shut down', function(done) {
server.kill();
server.on('exit', function() { done(); });
});
});
describe('the ass wrap-up', function() {
it('should write coverage', function(done) {
ass.report('html', function(err, r) {
fs.writeFileSync('coverage.html', r);
done();
});
});
it('should write the coverage image', function(done) {
ass.report('json', function(err, r) {
if (!!err) { return done(err); }
var badge = require('./badge.js');
var score = +r.percent;
var badgeData = {text:['coverage', score + '%']};
if (score < 70) {
badgeData.colorscheme = 'red';
} else if (score < 80) {
badgeData.colorscheme = 'yellow';
} else if (score < 90) {
badgeData.colorscheme = 'yellowgreen';
} else if (score < 100) {
badgeData.colorscheme = 'green';
} else {
badgeData.colorscheme = 'brightgreen';
}
badge(badgeData, function writeBadge(svg) {
fs.writeFileSync('./coverage.svg', svg);
done();
});
});
});
});

1
test/cli-test.js Normal file
View File

@ -0,0 +1 @@
require('../gh-badge.js');

View File

@ -1,5 +1,3 @@
// We don't require ass because we can't test a majority of the server reliably.
//require('ass');
require('../server.js');
console.log('ready');
process.on('SIGTERM', function() { process.exit(0); });

145
test/test.js Normal file
View File

@ -0,0 +1,145 @@
var assertion = require('assert');
var http = require('http');
var cproc = require('child_process');
var fs = require('fs');
function test(target, tests) {
var wrappedTests = tests.map(function(test) {
return function() {
var desc = test[0];
var f = test[1];
return new Promise(function(resolve, reject) {
var assert = function(pred, msg) { assert.ok(pred, msg); };
['ok', 'equal', 'deepEqual', 'strictEqual', 'deepStrictEqual',
'notEqual', 'notDeepEqual', 'notStrictEqual', 'notDeepStrictEqual',
'fail', 'doesNotThrow', 'throws',
].forEach(function(k) {
assert[k] = function(...args) {
try {
assertion[k].apply(null, args);
} catch(e) { reject(e); }
};
});
f(resolve, assert);
}).catch(function(e) {
console.error('Failed:', target + ' ' + desc + '\n', e.stack);
});
};
});
var prom = wrappedTests[0]();
for (var i = 1; i < wrappedTests.length; i++) {
prom = prom.then(wrappedTests[i]);
}
return prom;
}
// Test parameters
var port = '1111';
var url = 'http://127.0.0.1:' + port + '/';
var server;
test('The CLI', [
['should provide a help message', function(done, assert) {
var child = cproc.spawn('node', ['test/cli-test.js']);
var buffer = '';
child.stdout.on('data', function(chunk) {
buffer += ''+chunk;
});
child.stdout.on('end', function() {
assert(buffer.startsWith('Usage'));
done();
});
}],
['should produce default badges', function(done, assert) {
var child = cproc.spawn('node',
['test/cli-test.js', 'cactus', 'grown']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
assert(buffer.startsWith('<svg'), '<svg');
assert(buffer.includes('cactus'), 'cactus');
assert(buffer.includes('grown'), 'grown');
done();
});
}],
['should produce colorschemed badges', function(done, assert) {
child = cproc.spawn('node',
['test/cli-test.js', 'cactus', 'grown', ':green']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
assert(buffer.startsWith('<svg'), '<svg');
done();
});
}],
['should produce right-color badges', function(done, assert) {
child = cproc.spawn('node',
['test/cli-test.js', 'cactus', 'grown', '#abcdef']);
child.stdout.on('data', function(chunk) {
var buffer = ''+chunk;
assert(buffer.includes('#abcdef'), '#abcdef')
done();
});
}],
['should produce PNG badges', function(done, assert) {
child = cproc.spawn('node',
['test/cli-test.js', 'cactus', 'grown', '.png']);
child.stdout.on('data', function(chunk) {
// Check the PNG magic number.
assert.equal(chunk[0], 0x89);
assert.equal(chunk[1], 0x50);
assert.equal(chunk[2], 0x4e);
assert.equal(chunk[3], 0x47);
assert.equal(chunk[4], 0x0d);
assert.equal(chunk[5], 0x0a);
assert.equal(chunk[6], 0x1a);
assert.equal(chunk[7], 0x0a);
done();
});
}],
])
.then(function() {
test('The server', [
// Start running the server.
['should start', function(done, assert) {
server = cproc.spawn('node', ['test/server-test.js', port]);
var isDone = false;
server.stdout.on('data', function(data) {
if (data.toString().indexOf('ready') >= 0 && !isDone) { done(); isDone = true; }
});
server.stderr.on('data', function(data) { console.log(''+data); });
}],
['should produce colorscheme badges', function(done, assert) {
http.get(url + ':fruit-apple-green.svg',
function(res) {
var buffer = '';
res.on('data', function(chunk) { buffer += ''+chunk; });
res.on('end', function() {
assert(buffer.startsWith('<svg'), '<svg');
assert(buffer.includes('fruit'), 'fruit');
assert(buffer.includes('apple'), 'apple');
done();
});
});
}],
['should produce colorscheme PNG badges', function(done, assert) {
http.get(url + ':fruit-apple-green.png',
function(res) {
res.on('data', function(chunk) {
// Check the PNG magic number.
assert.equal(chunk[0], 0x89);
assert.equal(chunk[1], 0x50);
assert.equal(chunk[2], 0x4e);
assert.equal(chunk[3], 0x47);
assert.equal(chunk[4], 0x0d);
assert.equal(chunk[5], 0x0a);
assert.equal(chunk[6], 0x1a);
assert.equal(chunk[7], 0x0a);
done();
});
});
}],
['should shut down', function(done, assert) {
server.kill();
server.on('exit', function() { done(); });
}],
])});

526
try.html
View File

@ -37,12 +37,16 @@ hr.spacing { border: 0; display: block; height: 3mm; }
border: 0; background-color: rgba(50,50,55,0.7);
opacity: 0; transition: opacity 0.5s; }
#copyForm { background: #fafaff; position: fixed; width: 100%;
top: 50%; left: 0; transform: translate(0, -80%);
top: 17%; left: 0;
overflow: auto;
max-height: 56%;
border-top: 15px solid #eaeaff;
border-bottom: 15px solid #eaeaff;
}
#suggestButton { display: none; }
table.badge > tbody > tr > td > img { cursor: pointer; }
table.badge > tbody > tr > th,
table.badge > tbody > tr > td > img,
table.badge > tbody > tr > td > code { cursor: pointer; }
</style>
<main id='main'>
@ -104,6 +108,14 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/codeship/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/master.svg' alt=''/></td>
<td><code>https://img.shields.io/codeship/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/master.svg</code></td>
</tr>
<tr><th> Magnum CI: </th>
<td><img src='/magnumci/ci/96ffb83fa700f069024921b0702e76ff.svg' alt=''/></td>
<td><code>https://img.shields.io/magnumci/ci/96ffb83fa700f069024921b0702e76ff.svg</code></td>
</tr>
<tr><th> Magnum CI: </th>
<td><img src='/magnumci/ci/96ffb83fa700f069024921b0702e76ff/new-meta.svg' alt=''/></td>
<td><code>https://img.shields.io/magnumci/ci/96ffb83fa700f069024921b0702e76ff/new-meta.svg</code></td>
</tr>
<tr><th> CircleCI: </th>
<td><img src='/circleci/project/BrightFlair/PHP.Gt.svg' alt=''/></td>
<td><code>https://img.shields.io/circleci/project/BrightFlair/PHP.Gt.svg</code></td>
@ -116,13 +128,17 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/circleci/project/BrightFlair/PHP.Gt/master.svg' alt=''/></td>
<td><code>https://img.shields.io/circleci/token/YOURTOKEN/project/BrightFlair/PHP.Gt/master.svg</code></td>
</tr>
<tr><th data-doc='visualStudioTeamServices'> Visual Studio Team services: </th>
<td><img src='/vso/build/larsbrinkhoff/953a34b9-5966-4923-a48a-c41874cfb5f5/1.svg' alt=''/></td>
<td><code>https://img.shields.io/vso/build/larsbrinkhoff/953a34b9-5966-4923-a48a-c41874cfb5f5/1.svg</code></td>
</tr>
<tr><th> Shippable: </th>
<td><img src='/shippable/54d119db5ab6cc13528ab183.svg' alt=''/></td>
<td><code>https://img.shields.io/shippable/54d119db5ab6cc13528ab183.svg</code></td>
<td><img src='/shippable/5444c5ecb904a4b21567b0ff.svg' alt=''/></td>
<td><code>https://img.shields.io/shippable/5444c5ecb904a4b21567b0ff.svg</code></td>
</tr>
<tr><th> Shippable branch: </th>
<td><img src='/shippable/54d119db5ab6cc13528ab183/master.svg' alt=''/></td>
<td><code>https://img.shields.io/shippable/54d119db5ab6cc13528ab183/master.svg</code></td>
<td><img src='/shippable/5444c5ecb904a4b21567b0ff/master.svg' alt=''/></td>
<td><code>https://img.shields.io/shippable/5444c5ecb904a4b21567b0ff/master.svg</code></td>
</tr>
<tr><th> Snap CI branch: </th>
<td><img src='/snap-ci/ThoughtWorksStudios/eb_deployer/master.svg' alt=''/></td>
@ -136,6 +152,10 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/jenkins/t/https/jenkins.qa.ubuntu.com/precise-desktop-amd64_default.svg' alt=''/></td>
<td><code>https://img.shields.io/jenkins/t/https/jenkins.qa.ubuntu.com/precise-desktop-amd64_default.svg</code></td>
</tr>
<tr><th> Jenkins coverage: </th>
<td><img src='/jenkins/c/https/jenkins.qa.ubuntu.com/address-book-service-utopic-i386-ci.svg' alt=''/></td>
<td><code>https://img.shields.io/jenkins/c/https/jenkins.qa.ubuntu.com/address-book-service-utopic-i386-ci.svg</code></td>
</tr>
<tr><th> Coveralls: </th>
<td><img src='/coveralls/jekyll/jekyll.svg' alt=''/></td>
<td><code>https://img.shields.io/coveralls/jekyll/jekyll.svg</code></td>
@ -211,19 +231,19 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
</tbody></table>
<h3 id="downloads"> Downloads </h3>
<table class='badge'><tbody>
<tr><th data-keywords='github'> Github All Releases: </th>
<tr><th data-keywords='github' data-doc='githubDoc'> Github All Releases: </th>
<td><img src='/github/downloads/atom/atom/total.svg' alt=''/></td>
<td><code>https://img.shields.io/github/downloads/atom/atom/total.svg</code></td>
</tr>
<tr><th data-keywords='github'> Github Releases: </th>
<tr><th data-keywords='github' data-doc='githubDoc'> Github Releases: </th>
<td><img src='/github/downloads/atom/atom/latest/total.svg' alt=''/></td>
<td><code>https://img.shields.io/github/downloads/atom/atom/latest/total.svg</code></td>
</tr>
<tr><th data-keywords='github'> Github Releases (by Release): </th>
<tr><th data-keywords='github' data-doc='githubDoc'> Github Releases (by Release): </th>
<td><img src='/github/downloads/atom/atom/v0.190.0/total.svg' alt=''/></td>
<td><code>https://img.shields.io/github/downloads/atom/atom/v0.190.0/total.svg</code></td>
</tr>
<tr><th data-keywords='github'> Github Releases (by Asset): </th>
<tr><th data-keywords='github' data-doc='githubDoc'> Github Releases (by Asset): </th>
<td><img src='/github/downloads/atom/atom/latest/atom-amd64.deb.svg' alt=''/></td>
<td><code>https://img.shields.io/github/downloads/atom/atom/latest/atom-amd64.deb.svg</code></td>
</tr>
@ -251,14 +271,6 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/gem/dt/rails.svg' alt=''/></td>
<td><code>https://img.shields.io/gem/dt/rails.svg</code></td>
</tr>
<tr><th> NuGet: </th>
<td><img src='/nuget/dt/Microsoft.AspNet.Mvc.svg' alt=''/></td>
<td><code>https://img.shields.io/nuget/dt/Microsoft.AspNet.Mvc.svg</code></td>
</tr>
<tr><th> MyGet: </th>
<td><img src='/myget/yolodev/dt/FSharpSupport.svg' alt=''/></td>
<td><code>https://img.shields.io/myget/yolodev/dt/FSharpSupport.svg</code></td>
</tr>
<tr><th> Chocolatey: </th>
<td><img src='/chocolatey/dt/scriptcs.svg' alt=''/></td>
<td><code>https://img.shields.io/chocolatey/dt/scriptcs.svg</code></td>
@ -327,6 +339,10 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/sourceforge/dt/sevenzip.svg' alt=''/></td>
<td><code>https://img.shields.io/sourceforge/dt/sevenzip.svg</code></td>
</tr>
<tr><th> SourceForge: </th>
<td><img src='/sourceforge/dt/arianne/stendhal.svg' alt=''/></td>
<td><code>https://img.shields.io/sourceforge/dt/arianne/stendhal.svg</code></td>
</tr>
<tr><th data-keywords='atom'> apm: </th>
<td><img src='/apm/dm/vim-mode.svg' alt=''/></td>
<td><code>https://img.shields.io/apm/dm/vim-mode.svg</code></td>
@ -351,6 +367,42 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/dub/dt/vibe-d/0.7.23.svg' alt=''/></td>
<td><code>https://img.shields.io/dub/dt/vibe-d/0.7.23.svg</code></td>
</tr>
<tr><th data-keywords='sublime'> Package Control: </th>
<td><img src='/packagecontrol/dm/Package%20Control.svg' alt=''/></td>
<td><code>https://img.shields.io/packagecontrol/dm/Package%20Control.svg</code></td>
</tr>
<tr><th data-keywords='sublime'> Package Control: </th>
<td><img src='/packagecontrol/dw/Package%20Control.svg' alt=''/></td>
<td><code>https://img.shields.io/packagecontrol/dw/Package%20Control.svg</code></td>
</tr>
<tr><th data-keywords='sublime'> Package Control: </th>
<td><img src='/packagecontrol/dd/Package%20Control.svg' alt=''/></td>
<td><code>https://img.shields.io/packagecontrol/dd/Package%20Control.svg</code></td>
</tr>
<tr><th data-keywords='sublime'> Package Control: </th>
<td><img src='/packagecontrol/dt/Package%20Control.svg' alt=''/></td>
<td><code>https://img.shields.io/packagecontrol/dt/Package%20Control.svg</code></td>
</tr>
<tr><th data-keywords='chrome'> Chrome Web Store: </th>
<td><img src='/chrome-web-store/d/nimelepbpejjlbmoobocpfnjhihnpked.svg' alt=''/></td>
<td><code>https://img.shields.io/chrome-web-store/d/nimelepbpejjlbmoobocpfnjhihnpked.svg</code></td>
</tr>
<tr><th data-keywords='website' data-doc='websiteDoc'> Website: </th>
<td><img src='/website-up-down-green-red/http/shields.io.svg' alt=''/></td>
<td><code>https://img.shields.io/website-up-down-green-red/http/shields.io.svg</code></td>
</tr>
<tr><th data-keywords='cocoapods'> CocoaPods: </th>
<td><img src='/cocoapods/dt/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/dt/AFNetworking.svg</code></td>
</tr>
<tr><th data-keywords='cocoapods'> CocoaPods: </th>
<td><img src='/cocoapods/dm/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/dm/AFNetworking.svg</code></td>
</tr>
<tr><th data-keywords='cocoapods'> CocoaPods: </th>
<td><img src='/cocoapods/dw/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/dw/AFNetworking.svg</code></td>
</tr>
</tbody></table>
<h3 id="version"> Version </h3>
<table class='badge'><tbody>
@ -358,6 +410,10 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/npm/v/npm.svg' alt=''/></td>
<td><code>https://img.shields.io/npm/v/npm.svg</code></td>
</tr>
<tr><th data-keywords='node'> npm (scoped): </th>
<td><img src='/npm/v/@cycle/core.svg' alt=''/></td>
<td><code>https://img.shields.io/npm/v/@cycle/core.svg</code></td>
</tr>
<tr><th> node: </th>
<td><img src='/node/v/gh-badges.svg' alt=''/></td>
<td><code>https://img.shields.io/node/v/gh-badges.svg</code></td>
@ -410,15 +466,15 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/hexpm/v/plug.svg' alt=''/></td>
<td><code>https://img.shields.io/hexpm/v/plug.svg</code></td>
</tr>
<tr><th> GitHub tag: </th>
<tr><th data-doc='githubDoc'> GitHub tag: </th>
<td><img src='/github/tag/strongloop/express.svg' alt=''/></td>
<td><code>https://img.shields.io/github/tag/strongloop/express.svg</code></td>
</tr>
<tr><th> GitHub release: </th>
<tr><th data-doc='githubDoc'> GitHub release: </th>
<td><img src='/github/release/qubyte/rubidium.svg' alt=''/></td>
<td><code>https://img.shields.io/github/release/qubyte/rubidium.svg</code></td>
</tr>
<tr><th> GitHub commits: </th>
<tr><th data-doc='githubDoc'> GitHub commits: </th>
<td><img src='/github/commits-since/SubtitleEdit/subtitleedit/3.4.7.svg' alt=''/></td>
<td><code>https://img.shields.io/github/commits-since/SubtitleEdit/subtitleedit/3.4.7.svg</code></td>
</tr>
@ -431,16 +487,20 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><code>https://img.shields.io/nuget/v/Nuget.Core.svg</code></td>
</tr>
<tr><th> NuGet Pre Release: </th>
<td><img src='/nuget/vpre/Nuget.Core.svg' alt=''/></td>
<td><code>https://img.shields.io/nuget/vpre/Nuget.Core.svg</code></td>
<td><img src='/nuget/vpre/Microsoft.AspNet.Mvc.svg' alt=''/></td>
<td><code>https://img.shields.io/nuget/vpre/Microsoft.AspNet.Mvc.svg</code></td>
</tr>
<tr><th> MyGet: </th>
<td><img src='/myget/yolodev/v/FSharpSupport.svg' alt=''/></td>
<td><code>https://img.shields.io/myget/yolodev/v/FSharpSupport.svg</code></td>
<td><img src='/myget/mongodb/v/MongoDB.Driver.Core.svg' alt=''/></td>
<td><code>https://img.shields.io/myget/mongodb/v/MongoDB.Driver.Core.svg</code></td>
</tr>
<tr><th> MyGet Pre Release: </th>
<td><img src='/myget/yolodev/vpre/FSharpSupport.svg' alt=''/></td>
<td><code>https://img.shields.io/myget/yolodev/vpre/FSharpSupport.svg</code></td>
<td><img src='/myget/yolodev/vpre/YoloDev.Dnx.FSharp.svg' alt=''/></td>
<td><code>https://img.shields.io/myget/yolodev/vpre/YoloDev.Dnx.FSharp.svg</code></td>
</tr>
<tr><th> MyGet tenant: </th>
<td><img src='/dotnet.myget/dotnet-coreclr/v/Microsoft.DotNet.CoreCLR.svg' alt=''/></td>
<td><code>https://img.shields.io/dotnet.myget/dotnet-coreclr/v/Microsoft.DotNet.CoreCLR.svg</code></td>
</tr>
<tr><th> Chocolatey: </th>
<td><img src='/chocolatey/v/git.svg' alt=''/></td>
@ -478,23 +538,35 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/dub/v/vibe-d.svg' alt=''/></td>
<td><code>https://img.shields.io/dub/v/vibe-d.svg</code></td>
</tr>
<tr><th data-keywords='aur'> AUR: </th>
<td><img src='/aur/version/yaourt.svg' alt=''/></td>
<td><code>https://img.shields.io/aur/version/yaourt.svg</code></td>
</tr>
<tr><th data-keywords='chrome'> Chrome Web Store: </th>
<td><img src='/chrome-web-store/v/nimelepbpejjlbmoobocpfnjhihnpked.svg' alt=''/></td>
<td><code>https://img.shields.io/chrome-web-store/v/nimelepbpejjlbmoobocpfnjhihnpked.svg</code></td>
</tr>
<tr><th> homebrew: </th>
<td><img src='/homebrew/v/cake.svg' alt=''/></td>
<td><code>https://img.shields.io/homebrew/v/cake.svg</code></td>
</tr>
</tbody></table>
<h3 id="social"> Social </h3>
<table class='badge'><tbody>
<tr><th> GitHub forks: </th>
<tr><th data-doc='githubDoc'> GitHub forks: </th>
<td><img src='/github/forks/badges/shields.svg?style=social&label=Fork' alt=''/></td>
<td><code>https://img.shields.io/github/forks/badges/shields.svg?style=social&amp;label=Fork</code></td>
</tr>
<tr><th> GitHub stars: </th>
<tr><th data-doc='githubDoc'> GitHub stars: </th>
<td><img src='/github/stars/badges/shields.svg?style=social&label=Star' alt=''/></td>
<td><code>https://img.shields.io/github/stars/badges/shields.svg?style=social&amp;label=Star</code></td>
</tr>
<tr><th> GitHub watchers: </th>
<tr><th data-doc='githubDoc'> GitHub watchers: </th>
<td><img src='/github/watchers/badges/shields.svg?style=social&label=Watch' alt=''/></td>
<td><code>https://img.shields.io/github/watchers/badges/shields.svg?style=social&amp;label=Watch</code></td>
</tr>
<tr><th> GitHub followers: </th>
<tr><th data-doc='githubDoc'> GitHub followers: </th>
<td><img src='/github/followers/espadrine.svg?style=social&label=Follow' alt=''/></td>
<td><code>https://img.shields.io/github/followers/espadrine.svg?style=social&amp;label=Follow</code></td>
</tr>
@ -502,18 +574,30 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/twitter/url/http/shields.io.svg?style=social' alt=''/></td>
<td><code>https://img.shields.io/twitter/url/http/shields.io.svg?style=social</code></td>
</tr>
<tr><th> Twitter Follow: </th>
<td><img src='/twitter/follow/shields_io.svg?style=social&label=Follow' alt=''/></td>
<td><code>https://img.shields.io/twitter/follow/shields_io.svg?style=social&amp;label=Follow</code></td>
</tr>
</tbody></table>
<h3 id="miscellaneous"> Miscellaneous </h3>
<table class='badge'><tbody>
<tr><th> Gratipay: </th>
<td><img src='/gratipay/JSFiddle.svg' alt=''/></td>
<td><code>https://img.shields.io/gratipay/JSFiddle.svg</code></td>
<tr><th> Gratipay User: </th>
<td><img src='/gratipay/user/dougwilson.svg' alt=''/></td>
<td><code>https://img.shields.io/gratipay/user/dougwilson.svg</code></td>
</tr>
<tr><th> Gratipay Team: </th>
<td><img src='/gratipay/team/shields.svg' alt=''/></td>
<td><code>https://img.shields.io/gratipay/team/shields.svg</code></td>
</tr>
<tr><th> Bountysource: </th>
<td><img src='/bountysource/team/mozilla-core/activity.svg' alt=''/></td>
<td><code>https://img.shields.io/bountysource/team/mozilla-core/activity.svg</code></td>
</tr>
<tr><th> Beerpay: </th>
<td><img src='/beerpay/hashdog/scrapfy-chrome-extension.svg' alt=''/></td>
<td><code>https://img.shields.io/beerpay/hashdog/scrapfy-chrome-extension.svg</code></td>
</tr>
<tr><th> Code Climate: </th>
<td><img src='/codeclimate/github/kabisaict/flow.svg' alt=''/></td>
<td><code>https://img.shields.io/codeclimate/github/kabisaict/flow.svg</code></td>
@ -522,6 +606,14 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/codeclimate/coverage/github/triAGENS/ashikawa-core.svg' alt=''/></td>
<td><code>https://img.shields.io/codeclimate/coverage/github/triAGENS/ashikawa-core.svg</code></td>
</tr>
<tr><th> Code Climate: </th>
<td><img src='/codeclimate/issues/github/me-and/mdf.svg' alt=''/></td>
<td><code>https://img.shields.io/codeclimate/issues/github/me-and/mdf.svg</code></td>
</tr>
<tr><th> bitHound: </th>
<td><img src='/bithound/code/github/rexxars/sse-channel.svg' alt=''/></td>
<td><code>https://img.shields.io/bithound/code/github/rexxars/sse-channel.svg</code></td>
</tr>
<tr><th> Gemnasium: </th>
<td><img src='/gemnasium/mathiasbynens/he.svg' alt=''/></td>
<td><code>https://img.shields.io/gemnasium/mathiasbynens/he.svg</code></td>
@ -602,25 +694,77 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/wheelmap/a/2323004600.svg' alt='' /></td>
<td><code>https://img.shields.io/wheelmap/a/2323004600.svg</code></td>
</tr>
<tr><th> GitHub issues: </th>
<tr><th data-keywords='GitHub issue' data-doc='githubDoc'> GitHub issues: </th>
<td><img src='/github/issues/badges/shields.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues/badges/shields.svg</code></td>
</tr>
<tr><th data-keywords='GitHub'></th>
<tr><th data-keywords='GitHub issue' data-doc='githubDoc'></th>
<td><img src='/github/issues-raw/badges/shields.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-raw/badges/shields.svg</code></td>
</tr>
<tr><th data-keywords='github license'> GitHub license: </th>
<tr><th data-keywords='GitHub issue' data-doc='githubDoc'> GitHub closed issues: </th>
<td><img src='/github/issues-closed/badges/shields.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-closed/badges/shields.svg</code></td>
</tr>
<tr><th data-keywords='GitHub issue' data-doc='githubDoc'></th>
<td><img src='/github/issues-closed-raw/badges/shields.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-closed-raw/badges/shields.svg</code></td>
</tr>
<tr><th data-keywords='GitHub issue label' data-doc='githubDoc'> label:</th>
<td><img src='/github/issues-raw/badges/shields/website.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-raw/badges/shields/website.svg</code></td>
</tr>
<tr><th data-keywords='GitHub pullrequest pr' data-doc='githubDoc'> GitHub pull requests: </th>
<td><img src='/github/issues-pr/cdnjs/cdnjs.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-pr/cdnjs/cdnjs.svg</code></td>
</tr>
<tr><th data-keywords='GitHub pullrequest pr' data-doc='githubDoc'></th>
<td><img src='/github/issues-pr-raw/cdnjs/cdnjs.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-pr-raw/cdnjs/cdnjs.svg</code></td>
</tr>
<tr><th data-keywords='GitHub pullrequest pr' data-doc='githubDoc'> GitHub closed pull requests: </th>
<td><img src='/github/issues-pr-closed/cdnjs/cdnjs.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-pr-closed/cdnjs/cdnjs.svg</code></td>
</tr>
<tr><th data-keywords='GitHub pullrequest pr' data-doc='githubDoc'></th>
<td><img src='/github/issues-pr-closed-raw/cdnjs/cdnjs.svg' alt=''/></td>
<td><code>https://img.shields.io/github/issues-pr-closed-raw/cdnjs/cdnjs.svg</code></td>
</tr>
<tr><th data-keywords='GitHub contributor' data-doc='githubDoc'> GitHub contributors: </th>
<td><img src='/github/contributors/cdnjs/cdnjs.svg' alt=''/></td>
<td><code>https://img.shields.io/github/contributors/cdnjs/cdnjs.svg</code></td>
</tr>
<tr><th data-keywords='GitHub license' data-doc='githubDoc'> license: </th>
<td><img src='/github/license/mashape/apistatus.svg' alt=''/></td>
<td><code>https://img.shields.io/github/license/mashape/apistatus.svg</code></td>
</tr>
<tr><th> Bitbucket issues: </th>
<td><img src='/bitbucket/issues/atlassian/python-bitbucket.svg' alt=''/></td>
<td><code>https://img.shields.io/bitbucket/issues/atlassian/python-bitbucket.svg</code></td>
</tr>
<tr><th data-keywords='Bitbucket'></th>
<td><img src='/bitbucket/issues-raw/atlassian/python-bitbucket.svg' alt=''/></td>
<td><code>https://img.shields.io/bitbucket/issues-raw/atlassian/python-bitbucket.svg</code></td>
</tr>
<tr><th> WordPress rating: </th>
<td><img src='/wordpress/plugin/r/akismet.svg' alt=''/></td>
<td><code>https://img.shields.io/wordpress/plugin/r/akismet.svg</code></td>
</tr>
<tr><th> Codacy: </th>
<td><img src='/codacy/e27821fb6289410b8f58338c7e0bc686.svg' alt=''/></td>
<td><code>https://img.shields.io/codacy/e27821fb6289410b8f58338c7e0bc686.svg</code></td>
<tr><th> Codacy grade: </th>
<td><img src='/codacy/grade/e27821fb6289410b8f58338c7e0bc686.svg' alt=''/></td>
<td><code>https://img.shields.io/codacy/grade/e27821fb6289410b8f58338c7e0bc686.svg</code></td>
</tr>
<tr><th> Codacy branch grade: </th>
<td><img src='/codacy/grade/e27821fb6289410b8f58338c7e0bc686/master.svg' alt=''/></td>
<td><code>https://img.shields.io/codacy/grade/e27821fb6289410b8f58338c7e0bc686/master.svg</code></td>
</tr>
<tr><th> Codacy coverage: </th>
<td><img src='/codacy/coverage/c44df2d9c89a4809896914fd1a40bedd.svg' alt=''/></td>
<td><code>https://img.shields.io/codacy/coverage/c44df2d9c89a4809896914fd1a40bedd.svg</code></td>
</tr>
<tr><th> Codacy branch coverage: </th>
<td><img src='/codacy/coverage/c44df2d9c89a4809896914fd1a40bedd/master.svg' alt=''/></td>
<td><code>https://img.shields.io/codacy/coverage/c44df2d9c89a4809896914fd1a40bedd/master.svg</code></td>
</tr>
<tr><th> Libscore: </th>
<td><img src='/libscore/s/jQuery.svg' alt=''/></td>
@ -670,6 +814,10 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/docker/pulls/mashape/kong.svg' alt=''/></td>
<td><code>https://img.shields.io/docker/pulls/mashape/kong.svg</code></td>
</tr>
<tr><th data-keywords='docker automated build'> Docker Automated build </th>
<td><img src='/docker/automated/jrottenberg/ffmpeg.svg' alt=''/></td>
<td><code>https://img.shields.io/docker/automated/jrottenberg/ffmpeg.svg</code></td>
</tr>
<tr><th data-keywords='imagelayers'> ImageLayers Size: </th>
<td><img src='/imagelayers/image-size/_/ubuntu/latest.svg' alt=''/></td>
<td><code>https://img.shields.io/imagelayers/image-size/_/ubuntu/latest.svg</code></td>
@ -682,6 +830,37 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/gitter/room/nwjs/nw.js.svg' alt=''/></td>
<td><code>https://img.shields.io/gitter/room/nwjs/nw.js.svg</code></td>
</tr>
<tr><th> JIRA issue: </th>
<td><img src='/jira/issue/https/issues.apache.org/jira/KAFKA-2896.svg' alt=''/></td>
<td><code>https://img.shields.io/jira/issue/https/issues.apache.org/jira/KAFKA-2896.svg</code></td>
</tr>
<tr><th> Maintenance: </th>
<td><img src='/maintenance/yes/2016.svg' alt=''/></td>
<td><code>https://img.shields.io/maintenance/yes/2016.svg</code></td>
<tr><th data-keywords='aur'> AUR: </th>
<td><img src='/aur/license/yaourt.svg' alt=''/></td>
<td><code>https://img.shields.io/aur/license/yaourt.svg</code></td>
</tr>
<tr><th> Waffle.io: </th>
<td><img src='/waffle/label/evancohen/smart-mirror/in%20progress.svg' alt=''/></td>
<td><code>https://img.shields.io/waffle/label/evancohen/smart-mirror/in%20progress.svg</code></td>
</tr>
<tr><th data-keywords='chrome'> Chrome Web Store: </th>
<td><img src='/chrome-web-store/price/nimelepbpejjlbmoobocpfnjhihnpked.svg' alt=''/></td>
<td><code>https://img.shields.io/chrome-web-store/price/nimelepbpejjlbmoobocpfnjhihnpked.svg</code></td>
</tr>
<tr><th data-keywords='chrome'> Chrome Web Store: </th>
<td><img src='/chrome-web-store/rating/nimelepbpejjlbmoobocpfnjhihnpked.svg' alt=''/></td>
<td><code>https://img.shields.io/chrome-web-store/rating/nimelepbpejjlbmoobocpfnjhihnpked.svg</code></td>
</tr>
<tr><th data-keywords='chrome'> Chrome Web Store: </th>
<td><img src='/chrome-web-store/rating-count/nimelepbpejjlbmoobocpfnjhihnpked.svg' alt=''/></td>
<td><code>https://img.shields.io/chrome-web-store/rating-count/nimelepbpejjlbmoobocpfnjhihnpked.svg</code></td>
</tr>
<tr><th data-keywords='aur'> AUR: </th>
<td><img src='/aur/votes/yaourt.svg' alt=''/></td>
<td><code>https://img.shields.io/aur/votes/yaourt.svg</code></td>
</tr>
</tbody></table>
<h3 id="miscellaneous"> Longer Miscellaneous </h3>
@ -702,6 +881,22 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/david/peer/webcomponents/generator-element.svg' alt=''/></td>
<td><code>https://img.shields.io/david/peer/webcomponents/generator-element.svg</code></td>
</tr>
<tr><th> bitHound: </th>
<td><img src='/bithound/dependencies/github/rexxars/sse-channel.svg' alt=''/></td>
<td><code>https://img.shields.io/bithound/dependencies/github/rexxars/sse-channel.svg</code></td>
</tr>
<tr><th> bitHound: </th>
<td><img src='/bithound/devDependencies/github/rexxars/sse-channel.svg' alt=''/></td>
<td><code>https://img.shields.io/bithound/devDependencies/github/rexxars/sse-channel.svg</code></td>
</tr>
<tr><th> CocoaPods: </th>
<td><img src='/cocoapods/at/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/at/AFNetworking.svg</code></td>
</tr>
<tr><th> CocoaPods: </th>
<td><img src='/cocoapods/aw/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/aw/AFNetworking.svg</code></td>
</tr>
<tr><th> CocoaPods: </th>
<td><img src='/cocoapods/p/AFNetworking.svg' alt='' /></td>
<td><code>https://img.shields.io/cocoapods/p/AFNetworking.svg</code></td>
@ -714,6 +909,29 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/ansible/role/3078.svg' alt=''/></td>
<td><code>https://img.shields.io/ansible/role/3078.svg</code></td>
</tr>
<tr><th> StackExchange: </th>
<td><img src='/stackexchange/tex/r/951.svg' alt=''/></td>
<td><code>https://img.shields.io/stackexchange/tex/r/951.svg</code></td>
</tr>
<tr><th> StackExchange: </th>
<td><img src='/stackexchange/stackoverflow/t/augeas.svg' alt=''/></td>
<td><code>https://img.shields.io/stackexchange/stackoverflow/t/augeas.svg</code></td>
</tr>
<tr><th> Issue Stats: </th>
<td><img src='/issuestats/i/github/strongloop/express.svg' alt=''/></td>
<td><code>https://img.shields.io/issuestats/i/github/strongloop/express.svg</code></td>
</tr>
<tr><th> (long form): </th>
<td>&nbsp;</td>
<td><code>https://img.shields.io/issuestats/i/long/github/strongloop/express.svg</code></td>
</tr>
<tr><th> Issue Stats: </th>
<td><img src='/issuestats/p/github/strongloop/express.svg' alt=''/></td>
<td><code>https://img.shields.io/issuestats/p/github/strongloop/express.svg</code></td>
<tr><th> (long form): </th>
<td>&nbsp;</td>
<td><code>https://img.shields.io/issuestats/p/long/github/strongloop/express.svg</code></td>
</tr>
<tr><th> Libraries.io for releases: </th>
<td><img src='https://img.shields.io/librariesio/release/hex/phoenix/1.0.3.svg' alt=''/></td>
<td><code>https://img.shields.io/librariesio/release/hex/phoenix/1.0.3.svg</code></td>
@ -810,6 +1028,8 @@ Here are a few other parameters you can use:
<tr><td><code>?link=http://abc.xyz&amp;link=http://shields.io</code></td>
<td>Specify what clicking on the left/right of a badge should do (esp. for
social badge style)</td></tr>
<tr><td><code>?maxAge=3600</code></td>
<td>Set the HTTP cache lifetime in secs</td></tr>
</tbody></table>
<p>
@ -824,11 +1044,9 @@ Tell your favorite badge service to use it! <br/>
And tell us, we might be able to bring it to you anyway!
</p>
<p>
<a href="https://twitter.com/Shields_io" class="twitter-follow-button"
data-show-count="false">Follow @Shields_io</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<a href='https://twitter.com/Shields_io'><img src='/twitter/follow/shields_io.svg?style=social&label=Follow' alt='Follow @shields_io'></a>
<a href='https://www.gratipay.com/Shields/'><img src='/gratipay/Shields.svg?style=social&label=Donate' alt='Donate to us!'/></a>
<iframe src="http://ghbtns.com/github-btn.html?user=badges&amp;repo=shields&amp;type=fork&amp;count=true"
<iframe src="https://ghbtns.com/github-btn.html?user=badges&amp;repo=shields&amp;type=fork&amp;count=true"
style="border:0; background-color:transparent"
width="95" height="20"></iframe>
</p>
@ -837,127 +1055,11 @@ And tell us, we might be able to bring it to you anyway!
is where the current server got started.
</p>
<h2 id="contributors"> Contributors </h2>
<a class='photo' href='https://github.com/espadrine'>
<img alt='espadrine' src='https://gravatar.com/avatar/8c3bee0764c781e1b0b8c2e53f0f11fe'>
</a>
<a class='photo' href='https://github.com/mathiasbynens'>
<img alt='mathiasbynens' src='https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125'>
</a>
<a class='photo' href='https://github.com/olivierlacan'>
<img alt='olivierlacan' src='https://gravatar.com/avatar/6e77509d7891c1d2230f3240a5652b6e'>
</a>
<a class='photo' href='https://github.com/nathany'>
<img alt='nathany' src='https://gravatar.com/avatar/7d0978b275a362d11283396a929aee65'>
</a>
<a class='photo' href='https://github.com/whit537'>
<img alt='whit537' src='https://gravatar.com/avatar/fb054b407a6461e417ee6b6ae084da37'>
</a>
<a class='photo' href='https://github.com/kura'>
<img alt='kura' src='https://gravatar.com/avatar/6db32988bd24b2f19231a7e88a74455a'>
</a>
<a class='photo' href='https://github.com/maxogden'>
<img alt='maxogden' src='https://avatars1.githubusercontent.com/u/39759?s=80'>
</a>
<a class='photo' href='https://github.com/seanlinsley'>
<img alt='seanlinsley' src='https://avatars.githubusercontent.com/u/688886?s=80'>
</a>
<a class='photo' href='https://github.com/alrra'>
<img alt='alrra' src='https://avatars.githubusercontent.com/u/1223565?s=80'>
</a>
<a class='photo' href='https://github.com/jublo'>
<img alt='jublo' src='https://avatars.githubusercontent.com/u/6348321?s=80'>
</a>
<a class='photo' href='https://github.com/g105b'>
<img alt='g105b' src='https://avatars2.githubusercontent.com/u/358014?s=80'>
</a>
<a class='photo' href='http://dan.cx/'>
<img alt='Daniel15' src='https://www.gravatar.com/avatar/872578c4e56897b913fd03ed88daef51?s=80'>
</a>
<br>
<a class='photo' href='https://github.com/sebmck'>
<img alt='sebmck' src='https://avatars.githubusercontent.com/u/853712?s=80'>
</a>
<a class='photo' href='https://github.com/avanderhoorn'>
<img alt='avanderhoorn' src='https://www.gravatar.com/avatar/6af12b64c2fe22af3b27848b8eedd992'>
</a>
<a class='photo' href='https://github.com/stefanjudis'>
<img alt='stefanjudis' src='https://gravatar.com/avatar/22725c2d3eb331146549bf0d5d3c050c'>
</a>
<a class='photo' href='https://github.com/jmalloc'>
<img alt='jmalloc' src='https://gravatar.com/avatar/7a6666c5798fb54d1d4ebde0600567f3'>
</a>
<a class='photo' href='https://github.com/ezzatron'>
<img alt='ezzatron' src='https://gravatar.com/avatar/95ce5a53e68fe5287fadde649da8c6c7'>
</a>
<a class='photo' href='https://github.com/brettcannon'>
<img alt='brettcannon' src='https://avatars.githubusercontent.com/u/54418?s=80'>
</a>
<a class='photo' href='https://github.com/CodeBlock'>
<img alt='CodeBlock' src='https://avatars.githubusercontent.com/u/43930?s=80'>
</a>
<a class='photo' href='https://github.com/Mikulas'>
<img alt='Mikulas' src='https://gravatar.com/avatar/daa06ea257d7820ff84735a55b931ec8'>
</a>
<a class='photo' href='https://github.com/segiddins'>
<img alt='segiddins' src='https://avatars.githubusercontent.com/u/1946610?s=80'>
</a>
<a class='photo' href='https://github.com/SonicHedgehog'>
<img alt='SonicHedgehog' src='https://avatars.githubusercontent.com/u/652793?s=80'>
</a>
<a class='photo' href='https://github.com/akashivskyy'>
<img alt='akashivskyy' src='https://avatars.githubusercontent.com/u/565231?s=80'>
</a>
<a class='photo' href='https://github.com/jbarrus'>
<img alt='akashivskyy' src='https://avatars2.githubusercontent.com/u/1044815?s=80'>
</a>
<br>
<a class='photo' href='https://github.com/rmasters'>
<img alt='rmasters' src='https://avatars.githubusercontent.com/u/34284?s=80'>
</a>
<a class='photo' href='https://github.com/cainus'>
<img alt='cainus' src='https://gravatar.com/avatar/c8475420ebca73833e55ccf57d8d7500'>
</a>
<a class='photo' href='https://github.com/jbowes'>
<img alt='jbowes' src='https://gravatar.com/avatar/363ada7cda10d5eae5eeb7704278fb51'>
</a>
<a class='photo' href='https://github.com/rafalchmiel'>
<img alt='rafalchmiel' src='https://gravatar.com/avatar/6dd56028986d5e6c478e649ec229092b'>
</a>
<a class='photo' href='https://github.com/fjcaetano'>
<img alt='fjcaetano' src='https://gravatar.com/avatar/3b339db885930633b86d73b97a2ca1c0'>
</a>
<a class='photo' href='https://github.com/hughsk'>
<img alt='hughsk' src='https://gravatar.com/avatar/133cd05eb39521d55fb7a08c787925e2'>
</a>
<a class='photo' href='https://github.com/qubyte'>
<img alt='qubyte' src='https://gravatar.com/avatar/2b8729c02bc2fb886b32ed23cb7b0a31'>
</a>
<a class='photo' href='https://github.com/khellang'>
<img alt='khellang' src='https://gravatar.com/avatar/b3616645c168b0b8a65e109a6b1cb010'>
</a>
<a class='photo' href='https://github.com/nitram509'>
<img alt='nitram509' src='https://www.gravatar.com/avatar/fa01e907fd400c42d481f431c4410954.png'>
</a>
<a class='photo' href='https://github.com/raphink'>
<img alt='raphink' src='https://avatars.githubusercontent.com/u/650430?s=80'>
</a>
<a class='photo' href='https://github.com/montanaflynn'>
<img alt='montanaflynn' src='https://avatars.githubusercontent.com/u/24260?s=80'>
</a>
<a class='photo' href='https://github.com/PeterDaveHello'>
<img alt='PeterDaveHello' src='https://avatars3.githubusercontent.com/u/3691490?s=80'>
</a>
<br>
<a class='photo' href='https://github.com/sagiegurari'>
<img alt='sagiegurari' src='https://avatars.githubusercontent.com/u/8112599?s=80'>
</a>
<p><small>:wq</small></p>
</main>
<dialog id='copyDialog'>
<form id='copyForm'>
<form id='copyForm' action='javascript:void 0'>
<p><img id='copyImg'>
</p><p><label> Link <input type='url' name='url'/></label>
</p><p><label> Image <input type='url' name='img'/></label>
@ -972,9 +1074,83 @@ is where the current server got started.
<input class=code id=copyMarkdown>
</p><p> reStructuredText
<input class=code id=copyreStructuredText>
</p><div id=copyDoc></div>
</form>
</dialog>
<div id=documentation style='display:none'>
<div id=visualStudioTeamServices>
<p>To obtain your own badge, you will first need to enable badges for your
project:
</p>
<img
src='https://cloud.githubusercontent.com/assets/6189336/11894616/be744ab4-a578-11e5-9e44-0c32a7836b3b.png'
alt='Go to your builds, click General, then check Badge enabled.'>
<p>Then, click “Show url…” to reveal the URL of the default badge. In that
URL, you will need to extract three pieces of information:
<code>TEAM_NAME</code>, <code>PROJECT_ID</code> and
<code>BUILD_DEFINITION_ID</code>.
</p>
<img
src='https://cloud.githubusercontent.com/assets/6189336/11629345/f4eb0d78-9cf7-11e5-8d83-ca9fd895fcea.png'
alt='TEAM_NAME is just after the https:// part, PROJECT_ID is after
definitions/, BUILD_DEFINITION_ID is after that.'>
<p>Your badge will then have the form
<code>https://img.shields.io/vso/build/TEAM_NAME/PROJECT_ID/BUILD_DEFINITION_ID</code>.
</p>
</div>
<div id="websiteDoc">
<p>The badge is of the form <code>https://img.shields.io/website[OPTIONS]/PROTOCOL/URLREST.svg</code>,
the simplest case being <code>https://img.shields.io/website/http/example.com.svg</code>.
More options are described below.
</p>
<p>The whole URL is obtained by concatenating the <code>PROTOCOL</code> (<code>http</code> or <code>https</code>, for example)
with the <code>URLREST</code> (separating them with <code>://</code>).
</p>
<p>
The existence of a specific path on the server can be checked by appending a path after the domain name, e.g.
<code>https://img.shields.io/website/http/www.website.com/path/to/page.html.svg</code>.
</p>
<p>The URLREST should be URLEncoded:<br>
<input type="text" id="websiteDocUrlField" placeholder="Paste your URL (without the protocol) here" /><br>
<button onclick="(function(el) { el.value = encodeURIComponent(el.value); })(document.getElementById('websiteDocUrlField'))">Encode</button>
<button onclick="(function(el) { el.value = decodeURIComponent(el.value); })(document.getElementById('websiteDocUrlField'))">Decode</button>
</p>
<p><code>[OPTIONS]</code> can be:
<ul>
<li>Nothing:
<code>…/website/…</code></li>
<li>Online and offline text:
<code>…/website-up-down/…</code></li>
<li>Online and offline text, then online and offline colors:
<code>…/website-up-down-green-orange/…</code></li>
</ul>
<table class=centered><tbody>
<tr><td> Dashes <code>--</code>
</td><td>
</td><td> <code>-</code> Dash
</td></tr>
<tr><td> Underscores <code>__</code>
</td><td>
</td><td> <code>_</code> Underscore <br/>
</td></tr>
<tr><td> <code>_</code> or Space <code>&nbsp;</code>
</td><td>
</td><td> <code>&nbsp;</code> Space
</td></tr>
</tbody></table>
</p>
</div>
<div id="githubDoc">
<p>If your GitHub badge errors, it might be because you hit GitHub's rate
limits.<br>
You can increase Shield.io's rate limit by
<a href="https://img.shields.io/github-auth">going to this page</a>
to add Shields as a GitHub application on your GitHub account.
</p>
</div>
</div>
<svg style='height:0'>
<filter id='gaussian-blur'>
<feGaussianBlur stdDeviation='0.7' />
@ -1082,14 +1258,6 @@ function selectNode(e) {
};
copyMarkdown.addEventListener('click', selectNode);
copyreStructuredText.addEventListener('click', selectNode);
function autoselectCode() {
var codes = document.querySelectorAll('table.badge code');
for (var i = 0; i < codes.length; i++) {
codes[i].addEventListener('click', selectNode);
}
}
document.addEventListener('DOMContentLoaded', autoselectCode);
// Markup copier dialog
//
@ -1098,10 +1266,14 @@ function markupDialogInit() {
var trs = document.querySelectorAll('table.badge tr');
for (var i = 0; i < trs.length; i++) {
var tr = trs[i];
var title = tr.querySelector('th');
var target = tr.querySelector('img');
if (target) {
target.addEventListener('click', makeMarkupDialogListener(tr));
}
var code = tr.querySelector('code');
// Markup dialog listener.
var mdl = makeMarkupDialogListener(tr);
if (title != null) { title.addEventListener('click', mdl); }
if (target != null) { target.addEventListener('click', mdl); }
if (code != null) { code.addEventListener('click', mdl); }
}
}
function makeMarkupDialogListener(tr) {
@ -1119,8 +1291,18 @@ function markupDialog(tr) {
var trimg = tr.querySelector('img').src;
var th = tr.firstElementChild;
var link = th.dataset.link? th.dataset.link: '';
// Remove the ?maxAge parameter from the query string.
trimg.replace(/[\?&]maxAge=\d+$|maxAge=\d+&/, '');
copyForm.img.value = trimg;
copyForm.url.value = link;
// Insert documentation.
var doc = th.dataset.doc? th.dataset.doc: '';
var docelt = document.getElementById(doc);
if (docelt != null) {
copyDoc.innerHTML = '<h4>Documentation</h4>' + docelt.innerHTML;
} else {
copyDoc.innerHTML = '';
}
// Set up the input listeners.
copyForm.url.removeEventListener('input', copyFormUrlEventListener);
copyForm.img.removeEventListener('input', copyFormUrlEventListener);