Merge branch 'master' into feature/circleci-token

Conflicts:
	try.html
This commit is contained in:
Chris Reeves 2015-06-08 09:54:19 +01:00
commit f46f6f301b
7 changed files with 250 additions and 16 deletions

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="110" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="110" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#dfb317" d="M63 0h47v20H63z"/><path fill="url(#b)" d="M0 0h110v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="32.5" y="15" fill="#010101" fill-opacity=".3">coverage</text><text x="32.5" y="14">coverage</text><text x="85.5" y="15" fill="#010101" fill-opacity=".3">76.5%</text><text x="85.5" y="14">76.5%</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="110" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="110" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#dfb317" d="M63 0h47v20H63z"/><path fill="url(#b)" d="M0 0h110v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text><text x="31.5" y="14">coverage</text><text x="85.5" y="15" fill="#010101" fill-opacity=".3">76.8%</text><text x="85.5" y="14">76.8%</text></g></svg>

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

View File

@ -74,8 +74,8 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><code>https://img.shields.io/travis/joyent/node/v0.6.svg</code></td>
</tr>
<tr><th> Wercker: </th>
<td><img src='https://img.shields.io/wercker/ci/54330318b4ce963d50020750.svg' alt=''/></td>
<td><code>https://img.shields.io/wercker/ci/54330318b4ce963d50020750.svg</code></td>
<td><img src='https://img.shields.io/wercker/ci/wercker/docs.svg' alt=''/></td>
<td><code>https://img.shields.io/wercker/ci/wercker/docs.svg</code></td>
</tr>
<tr><th> TeamCity CodeBetter: </th>
<td><img src='https://img.shields.io/teamcity/codebetter/bt428.svg' alt=''/></td>
@ -471,6 +471,10 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='https://img.shields.io/david/dev/strongloop/express.svg' alt=''/></td>
<td><code>https://img.shields.io/david/dev/strongloop/express.svg</code></td>
</tr>
<tr><th> David: </th>
<td><img src='https://img.shields.io/david/optional/elnounch/byebye.svg' alt=''/></td>
<td><code>https://img.shields.io/david/optional/elnounch/byebye.svg</code></td>
</tr>
<tr><th> David: </th>
<td><img src='https://img.shields.io/david/peer/webcomponents/generator-element.svg' alt=''/></td>
<td><code>https://img.shields.io/david/peer/webcomponents/generator-element.svg</code></td>
@ -676,8 +680,8 @@ The following styles are available (flat is the default as of Feb 1st 2015):
<td><code>https://img.shields.io/badge/style-flat-green.svg?style=flat</code></td>
</tr>
<tr>
<td><img src='https://img.shields.io/badge/style-flat--squared-green.svg?style=flat-square' alt=''/></td>
<td><code>https://img.shields.io/badge/style-flat--squared-green.svg?style=flat-square</code></td>
<td><img src='https://img.shields.io/badge/style-flat--square-green.svg?style=flat-square' alt=''/></td>
<td><code>https://img.shields.io/badge/style-flat--square-green.svg?style=flat-square</code></td>
</tr>
</tbody></table>
@ -700,7 +704,7 @@ And tell us, we might be able to bring it to you anyway!
<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://www.gittip.com/Shields/'><img src='https://img.shields.io/gittip/shields.svg?style=flat' alt='Tip us!'/></a>
<a href='https://www.gratipay.com/Shields/'><img src='https://img.shields.io/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"
style="border:0; background-color:transparent"
width="95" height="20"></iframe>

20
load-logos.js Normal file
View File

@ -0,0 +1,20 @@
var fs = require('fs');
var path = require('path');
var loadLogos = function() {
var logos = {};
var logoFiles = fs.readdirSync(path.join(__dirname, 'logo'));
logoFiles.forEach(function(filename) {
if (filename[0] === '.') { return; }
// filename is eg, github.svg
var svg = fs.readFileSync(
path.join(__dirname, 'logo', filename)).toString();
// eg, github
var name = filename.slice(0, -('.svg'.length));
logos[name] = 'data:image/svg+xml;base64,' + Buffer(svg).toString('base64');
});
return logos;
};
module.exports = loadLogos;

3
logo/github.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="12 12 40 40">
<path fill="#333333" d="M32,13.4c-10.5,0-19,8.5-19,19c0,8.4,5.5,15.5,13,18c1,0.2,1.3-0.4,1.3-0.9c0-0.5,0-1.7,0-3.2 c-5.3,1.1-6.4-2.6-6.4-2.6C20,41.6,18.8,41,18.8,41c-1.7-1.2,0.1-1.1,0.1-1.1c1.9,0.1,2.9,2,2.9,2c1.7,2.9,4.5,2.1,5.5,1.6 c0.2-1.2,0.7-2.1,1.2-2.6c-4.2-0.5-8.7-2.1-8.7-9.4c0-2.1,0.7-3.7,2-5.1c-0.2-0.5-0.8-2.4,0.2-5c0,0,1.6-0.5,5.2,2 c1.5-0.4,3.1-0.7,4.8-0.7c1.6,0,3.3,0.2,4.7,0.7c3.6-2.4,5.2-2,5.2-2c1,2.6,0.4,4.6,0.2,5c1.2,1.3,2,3,2,5.1c0,7.3-4.5,8.9-8.7,9.4 c0.7,0.6,1.3,1.7,1.3,3.5c0,2.6,0,4.6,0,5.2c0,0.5,0.4,1.1,1.3,0.9c7.5-2.6,13-9.7,13-18.1C51,21.9,42.5,13.4,32,13.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 683 B

3
logo/gratipay.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="16 16 96 96">
<path fill="#663300" d="M63.998,16C37.49,16,16,37.489,16,63.998C16,90.509,37.49,112,63.998,112 c26.514,0,48-21.491,48-48.002C111.998,37.489,90.513,16,63.998,16z M86.169,59.827L64.33,89.394L42.492,59.827 c-1.848-2.173-3.692-9.738,2.65-13.919c5.402-3.561,10.533-0.864,13.222,2.261c1.003,1.166,2.722,2.539,5.966,2.539 c3.245,0,4.964-1.373,5.967-2.539c2.688-3.125,7.819-5.822,13.222-2.261C89.861,50.089,88.016,57.653,86.169,59.827z"/>
</svg>

After

Width:  |  Height:  |  Size: 524 B

192
server.js
View File

@ -12,6 +12,7 @@ var fs = require('fs');
var LruCache = require('./lru-cache.js');
var badge = require('./badge.js');
var svg2img = require('./svg-to-img.js');
var loadLogos = require('./load-logos.js');
var querystring = require('querystring');
var serverSecrets;
try {
@ -23,6 +24,7 @@ var semver = require('semver');
var serverStartTime = new Date((new Date()).toGMTString());
var validTemplates = ['default', 'plastic', 'flat', 'flat-square', 'social'];
var logos = loadLogos();
// Analytics
@ -176,7 +178,8 @@ function cache(f) {
}
var cacheIndex = match[0] + '?label=' + data.label + '&style=' + data.style
+ '&logo=' + data.logo + '&logoWidth=' + data.logoWidth;
+ '&logo=' + data.logo + '&logoWidth=' + data.logoWidth
+ '&link=' + data.link;
// Should we return the data right away?
var cached = requestCache.get(cacheIndex);
var cachedVersionSent = false;
@ -222,6 +225,8 @@ function cache(f) {
} else {
options = uri;
}
options.headers = options.headers || {};
options.headers['User-Agent'] = options.headers['User-Agent'] || 'Shields.io';
return request(options, function(err, res, json) {
if (res != null && res.headers != null) {
var cacheControl = res.headers['cache-control'];
@ -309,8 +314,43 @@ cache(function(data, match, sendBadge, request) {
});
}));
// Shippable integration
camp.route(/^\/shippable?\/([^\/]+)(?:\/(.+))?\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var project = match[1]; // eg, 54d119db5ab6cc13528ab183
var branch = match[2];
var format = match[3];
var url = 'https://api.shippable.com/projects/' + project + '/badge';
if (branch != null) {
url += '?branchName=' + branch;
}
var badgeData = getBadgeData('build', data);
fetchFromSvg(request, url, function(err, res) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
try {
badgeData.text[1] = res;
if (res === 'shippable') {
badgeData.colorscheme = 'brightgreen';
} else if (res === 'unshippable') {
badgeData.colorscheme = 'red';
} else {
badgeData.text[1] = res;
}
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}));
// Wercker integration
camp.route(/^\/wercker\/ci\/(.+)\.(svg|png|gif|jpg|json)$/,
camp.route(/^\/wercker\/ci\/([a-fA-F0-9]+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var projectId = match[1]; // eg, `54330318b4ce963d50020750`
var format = match[2];
@ -349,6 +389,47 @@ cache(function(data, match, sendBadge, request) {
});
}));
// Wercker V3 integration
camp.route(/^\/wercker\/ci\/(.+)\/(.+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var owner = match[1];
var application = match[2];
var format = match[3];
var options = {
method: 'GET',
json: true,
uri: 'https://app.wercker.com/api/v3/applications/' + owner + '/' + application + '/builds?limit=1'
};
var badgeData = getBadgeData('build', data);
request(options, function(err, res, json) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
try {
var build = json[0];
if (build.status === 'finished') {
if (build.result === 'passed') {
badgeData.colorscheme = 'brightgreen';
badgeData.text[1] = build.result;
} else {
badgeData.colorscheme = 'red';
badgeData.text[1] = build.result;
}
} else {
badgeData.text[1] = build.status;
}
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}));
// Rust download and version integration
camp.route(/^\/crates\/(d|v|dv|l)\/([A-Za-z0-9_-]+)(?:\/([0-9.]+))?\.(svg|png|gif|jpg|json)$/,
cache(function (data, match, sendBadge, request) {
@ -647,6 +728,9 @@ cache(function(data, match, sendBadge, request) {
var format = match[3];
var apiUrl = 'https://www.gratipay.com/' + user + '/public.json';
var badgeData = getBadgeData('tips', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.gratipay;
}
request(apiUrl, function dealWithData(err, res, buffer) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
@ -1389,16 +1473,87 @@ cache(function(data, match, sendBadge, request) {
badgeData.text[1] = vdata.version;
badgeData.colorscheme = vdata.color;
sendBadge(format, badgeData);
} else if (info == 'l') {
} else if (info === 'l') {
var license = data.info.license;
badgeData.text[0] = 'license';
if (license == null || license == 'UNKNOWN') {
if (license === null || license === 'UNKNOWN') {
badgeData.text[1] = 'Unknown';
} else {
badgeData.text[1] = license;
badgeData.colorscheme = 'blue';
}
sendBadge(format, badgeData);
} else if (info === 'wheel') {
var releases = data.releases[data.info.version];
var hasWheel = false;
for (var i = 0; i < releases.length; i++) {
if (releases[i].packagetype === 'wheel' ||
releases[i].packagetype === 'bdist_wheel') {
hasWheel = true;
break;
}
}
badgeData.text[0] = 'wheel';
badgeData.text[1] = hasWheel ? 'yes' : 'no';
badgeData.colorscheme = hasWheel ? 'brightgreen' : 'red';
sendBadge(format, badgeData);
} else if (info === 'pyversions') {
var versions = [];
var pattern = /^Programming Language \:\: Python \:\: (\d\.\d)$/;
for (var i = 0; i < data.info.classifiers.length; i++) {
var matched = pattern.exec(data.info.classifiers[i]);
if (matched && matched[1]) {
versions.push(matched[1]);
}
}
if (!versions.length) {
versions.push('not found');
}
badgeData.text[0] = 'python';
badgeData.text[1] = versions.sort().join(', ');
badgeData.colorscheme = 'blue';
sendBadge(format, badgeData);
} else if (info === 'implementation') {
var implementations = [];
var pattern = /^Programming Language \:\: Python \:\: Implementation \:\: (\S+)$/;
for (var i = 0; i < data.info.classifiers.length; i++) {
var matched = pattern.exec(data.info.classifiers[i]);
if (matched && matched[1]) {
implementations.push(matched[1].toLowerCase());
}
}
if (!implementations.length) {
implementations.push('cpython'); // assume CPython
}
badgeData.text[0] = 'implementation';
badgeData.text[1] = implementations.sort().join(', ');
badgeData.colorscheme = 'blue';
sendBadge(format, badgeData);
} else if (info === 'status') {
var pattern = /^Development Status \:\: ([1-7]) - (\S+)$/;
var statusColors = {
'1': 'red', '2': 'red', '3': 'red', '4': 'yellow',
'5': 'brightgreen', '6': 'brightgreen', '7': 'red'};
var statusCode = '1', statusText = 'unknown';
for (var i = 0; i < data.info.classifiers.length; i++) {
var matched = pattern.exec(data.info.classifiers[i]);
if (matched && matched[1] && matched[2]) {
statusCode = matched[1];
statusText = matched[2].toLowerCase().replace('-', '--');
if (statusText === 'production/stable') {
statusText = 'stable';
}
break;
}
}
badgeData.text[0] = 'status';
badgeData.text[1] = statusText;
badgeData.colorscheme = statusColors[statusCode];
sendBadge(format, badgeData);
} else {
// That request is incorrect.
badgeData.text[1] = 'request unknown';
sendBadge(format, badgeData);
}
} catch(e) {
badgeData.text[1] = 'invalid';
@ -2109,6 +2264,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('tag', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2149,6 +2307,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('release', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2192,6 +2353,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('downloads', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2239,6 +2403,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('issues', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2277,6 +2444,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('forks', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2316,6 +2486,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('stars', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2352,6 +2525,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('followers', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@ -2383,6 +2559,9 @@ cache(function(data, match, sendBadge, request) {
var format = match[3];
var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo;
var badgeData = getBadgeData('license', data);
if (badgeData.template === 'social') {
badgeData.logo = badgeData.logo || logos.github;
}
// Using our OAuth App secret grants us 5000 req/hour
// instead of the standard 60 req/hour.
if (serverSecrets) {
@ -3705,7 +3884,7 @@ function getLabel(label, data) {
return data.label || label;
}
// data (URL query) can include `label`, `style`, `logo`, `logoWidth`.
// data (URL query) can include `label`, `style`, `logo`, `logoWidth`, `link`.
function getBadgeData(defaultLabel, data) {
var label = getLabel(defaultLabel, data);
var template = data.style || 'default';
@ -3722,7 +3901,8 @@ function getBadgeData(defaultLabel, data) {
colorscheme: 'lightgrey',
template: template,
logo: data.logo,
logoWidth: +data.logoWidth
logoWidth: +data.logoWidth,
links: data.link,
};
}

View File

@ -73,8 +73,8 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><code>https://img.shields.io/travis/joyent/node/v0.6.svg</code></td>
</tr>
<tr><th> Wercker: </th>
<td><img src='/wercker/ci/54330318b4ce963d50020750.svg' alt=''/></td>
<td><code>https://img.shields.io/wercker/ci/54330318b4ce963d50020750.svg</code></td>
<td><img src='/wercker/ci/wercker/docs.svg' alt=''/></td>
<td><code>https://img.shields.io/wercker/ci/wercker/docs.svg</code></td>
</tr>
<tr><th> TeamCity CodeBetter: </th>
<td><img src='/teamcity/codebetter/bt428.svg' alt=''/></td>
@ -116,7 +116,15 @@ 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> Jenkins: </th>
<tr><th> Shippable: </th>
<td><img src='/shippable/54d119db5ab6cc13528ab183.svg' alt=''/></td>
<td><code>https://img.shields.io/shippable/54d119db5ab6cc13528ab183.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>
</tr>
<th> Jenkins: </th>
<td><img src='/jenkins/s/https/jenkins.qa.ubuntu.com/precise-desktop-amd64_default.svg' alt=''/></td>
<td><code>https://img.shields.io/jenkins/s/https/jenkins.qa.ubuntu.com/precise-desktop-amd64_default.svg</code></td>
</tr>
@ -514,6 +522,22 @@ Pixel-perfect &nbsp; Retina-ready &nbsp; Fast &nbsp; Consistent &nbsp; Hackable
<td><img src='/pypi/l/Django.svg' alt=''/></td>
<td><code>https://img.shields.io/pypi/l/Django.svg</code></td>
</tr>
<tr><th data-keywords='python'> PyPI: </th>
<td><img src='/pypi/wheel/Django.svg' alt=''/></td>
<td><code>https://img.shields.io/pypi/wheel/Django.svg</code></td>
</tr>
<tr><th data-keywords='python'> PyPI: </th>
<td><img src='/pypi/pyversions/Django.svg' alt=''/></td>
<td><code>https://img.shields.io/pypi/pyversions/Django.svg</code></td>
</tr>
<tr><th data-keywords='python'> PyPI: </th>
<td><img src='/pypi/implementation/Django.svg' alt=''/></td>
<td><code>https://img.shields.io/pypi/implementation/Django.svg</code></td>
</tr>
<tr><th data-keywords='python'> PyPI: </th>
<td><img src='/pypi/status/Django.svg' alt=''/></td>
<td><code>https://img.shields.io/pypi/status/Django.svg</code></td>
</tr>
<tr><th> Hex.pm: </th>
<td><img src='/hexpm/l/plug.svg' alt=''/></td>
<td><code>https://img.shields.io/hexpm/l/plug.svg</code></td>
@ -707,7 +731,7 @@ And tell us, we might be able to bring it to you anyway!
<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://www.gittip.com/Shields/'><img src='/gittip/shields.svg?style=flat' alt='Tip us!'/></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"
style="border:0; background-color:transparent"
width="95" height="20"></iframe>