Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split various utilities into standalone files #952

Merged
merged 3 commits into from
Apr 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions lib/color-formatters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Commonly-used functions for determining the colour to use for a badge,
* including colours based off download count, version number, etc.
*/
'use strict';

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you make any changes in these implementations?

function versionFormatter(version) {
var first = version[0];
if (first === 'v') {
first = version[1];
} else if (/^[0-9]/.test(version)) {
version = 'v' + version;
}
if (first === '0' || (version.indexOf('-') !== -1)) {
return { version: version, color: 'orange' };
} else {
return { version: version, color: 'blue' };
}
}
exports.version = versionFormatter;

function downloadCount(downloads) {
return floorCount(downloads, 10, 100, 1000);
}
exports.downloadCount = downloadCount;

function coveragePercentage(percentage) {
return floorCount(percentage, 80, 90, 100);
}
exports.coveragePercentage = coveragePercentage;

function floorCount(value, yellow, yellowgreen, green) {
if (value === 0) {
return 'red';
} else if (value < yellow) {
return 'yellow';
} else if (value < yellowgreen) {
return 'yellowgreen';
} else if (value < green) {
return 'green';
} else {
return 'brightgreen';
}
}
exports.floorCount = floorCount;
181 changes: 181 additions & 0 deletions lib/php-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* Utilities relating to PHP version numbers. This compares version numbers
* using the algorithm followed by Composer (see
* https://getcomposer.org/doc/04-schema.md#version).
*/
'use strict';

const {listCompare} = require('./version.js');
Copy link
Member

@paulmelnikow paulmelnikow Apr 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can omit the .js suffix in these requires. There are a few in server.js too.


// Return a negative value if v1 < v2,
// zero if v1 = v2,
// a positive value otherwise.
//
// See https://getcomposer.org/doc/04-schema.md#version
// and https://github.com/badges/shields/issues/319#issuecomment-74411045
function compare(v1, v2) {
// Omit the starting `v`.
var rawv1 = omitv(v1);
var rawv2 = omitv(v2);
try {
var v1data = numberedVersionData(rawv1);
var v2data = numberedVersionData(rawv2);
} catch(e) {
return asciiVersionCompare(rawv1, rawv2);
}

// Compare the numbered part (eg, 1.0.0 < 2.0.0).
var numbersCompare = listCompare(v1data.numbers, v2data.numbers);
if (numbersCompare !== 0) {
return numbersCompare;
}

// Compare the modifiers (eg, alpha < beta).
if (v1data.modifier < v2data.modifier) {
return -1;
} else if (v1data.modifier > v2data.modifier) {
return 1;
}

// Compare the modifier counts (eg, alpha1 < alpha3).
if (v1data.modifierCount < v2data.modifierCount) {
return -1;
} else if (v1data.modifierCount > v2data.modifierCount) {
return 1;
}

return 0;
}
exports.compare = compare;

function latest(versions) {
var latest = versions[0];
for (var i = 1; i < versions.length; i++) {
if (compare(latest, versions[i]) < 0) {
latest = versions[i];
}
}
return latest;
}
exports.latest = latest;

// Whether a version is stable.
function isStable(version) {
var rawVersion = omitv(version);
try {
var versionData = numberedVersionData(rawVersion);
} catch(e) {
return false;
}
// normal or patch
return (versionData.modifier === 3) || (versionData.modifier === 4);
}
exports.isStable = isStable;

// === Private helper functions ===

// Remove the starting v in a string.
function omitv(version) {
if (version.charCodeAt(0) === 118) { // v
return version.slice(1);
} else {
return version;
}
}

// Return a negative value if v1 < v2,
// zero if v1 = v2, a positive value otherwise.
function asciiVersionCompare(v1, v2) {
if (v1 < v2) {
return -1;
} else if (v1 > v2) {
return 1;
} else {
return 0;
}
}

// Take a version without the starting v.
// eg, '1.0.x-beta'
// Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
function numberedVersionData(version) {
// A version has a numbered part and a modifier part
// (eg, 1.0.0-patch, 2.0.x-dev).
var parts = version.split('-');
var numbered = parts[0];

// Aliases that get caught here.
if (numbered === 'dev') {
return {
numbers: parts[1],
modifier: 5,
modifierCount: 1,
};
}

var modifierLevel = 3;
var modifierLevelCount = 0;

if (parts.length > 1) {
var modifier = parts[parts.length - 1];
var firstLetter = modifier.charCodeAt(0);
var modifierLevelCountString;

// Modifiers: alpha < beta < RC < normal < patch < dev
if (firstLetter === 97) { // a
modifierLevel = 0;
if (/^alpha/.test(modifier)) {
modifierLevelCountString = + (modifier.slice(5));
} else {
modifierLevelCountString = + (modifier.slice(1));
}
} else if (firstLetter === 98) { // b
modifierLevel = 1;
if (/^beta/.test(modifier)) {
modifierLevelCountString = + (modifier.slice(4));
} else {
modifierLevelCountString = + (modifier.slice(1));
}
} else if (firstLetter === 82) { // R
modifierLevel = 2;
modifierLevelCountString = + (modifier.slice(2));
} else if (firstLetter === 112) { // p
modifierLevel = 4;
if (/^patch/.test(modifier)) {
modifierLevelCountString = + (modifier.slice(5));
} else {
modifierLevelCountString = + (modifier.slice(1));
}
} else if (firstLetter === 100) { // d
modifierLevel = 5;
if (/^dev/.test(modifier)) {
modifierLevelCountString = + (modifier.slice(3));
} else {
modifierLevelCountString = + (modifier.slice(1));
}
}

// If we got the empty string, it defaults to a modifier count of 1.
if (!modifierLevelCountString) {
modifierLevelCount = 1;
} else {
modifierLevelCount = + modifierLevelCountString;
}
}

// Try to convert to a list of numbers.
var toNum = function(s) {
var n = +s;
if (n !== n) { // If n is NaN…
n = 0xffffffff;
}
return n;
};
var numberList = numbered.split('.').map(toNum);

return {
numbers: numberList,
modifier: modifierLevel,
modifierCount: modifierLevelCount,
};
}
47 changes: 47 additions & 0 deletions lib/text-formatters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Commonly-used functions for formatting text in badge labels. Includes
* ordinal numbers, currency codes, star ratings, etc.
*/
'use strict';

function starRating(rating) {
var stars = '';
while (stars.length < rating) { stars += '★'; }
while (stars.length < 5) { stars += '☆'; }
return stars;
}
exports.starRating = starRating;

// Convert ISO 4217 code to unicode string.
function currencyFromCode(code) {
return ({
CNY: '¥',
EUR: '€',
GBP: '₤',
USD: '$',
})[code] || code;
}
exports.currencyFromCode = currencyFromCode;

function ordinalNumber(n) {
var s=["ᵗʰ","ˢᵗ","ⁿᵈ","ʳᵈ"], v=n%100;
return n+(s[(v-20)%10]||s[v]||s[0]);
}
exports.ordinalNumber = ordinalNumber;

// Given a number, string with appropriate unit in the metric system, SI.
// Note: numbers beyond the peta- cannot be represented as integers in JS.
var metricPrefix = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
var metricPower = metricPrefix
.map(function(a, i) { return Math.pow(1000, i + 1); });
function metric(n) {
for (var i = metricPrefix.length - 1; i >= 0; i--) {
var limit = metricPower[i];
if (n >= limit) {
n = Math.round(n / limit);
return ''+n + metricPrefix[i];
}
}
return ''+n;
}
exports.metric = metric;
80 changes: 80 additions & 0 deletions lib/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Utilities relating to generating badges relating to version numbers. Includes
* comparing versions to determine the latest, and determining the color to use
* for the badge based on whether the version is a stable release.
*
* For utilities specific to PHP version ranges, see php-version.js.
*/
'use strict';

const semver = require('semver');

// Given a list of versions (as strings), return the latest version.
// Return undefined if no version could be found.
function latest(versions) {
var version = '';
var origVersions = versions;
versions = versions.filter(function(version) {
return (/^v?[0-9]/).test(version);
});
try {
version = semver.maxSatisfying(versions, '');
} catch(e) {
version = latestDottedVersion(versions);
}
if (version === undefined) {
origVersions = origVersions.sort();
version = origVersions[origVersions.length - 1];
}
return version;
}
exports.latest = latest;

function listCompare(a, b) {
var alen = a.length, blen = b.length;
for (var i = 0; i < alen; i++) {
if (a[i] < b[i]) {
return -1;
} else if (a[i] > b[i]) {
return 1;
}
}
return alen - blen;
}
exports.listCompare = listCompare;

// === Private helper functions ===

// Take a list of string versions.
// Return the latest, or undefined, if there are none.
function latestDottedVersion(versions) {
var len = versions.length;
if (len === 0) { return; }
var version = versions[0];
for (var i = 1; i < len; i++) {
if (compareDottedVersion(version, versions[i]) < 0) {
version = versions[i];
}
}
return version;
}

// Take string versions.
// -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
function compareDottedVersion(v1, v2) {
var parts1 = /([0-9\.]+)(.*)$/.exec(v1);
var parts2 = /([0-9\.]+)(.*)$/.exec(v2);
if (parts1 != null && parts2 != null) {
var numbers1 = parts1[1];
var numbers2 = parts2[1];
var distinguisher1 = parts1[2];
var distinguisher2 = parts2[2];
var numlist1 = numbers1.split('.').map(function(e) { return +e; });
var numlist2 = numbers2.split('.').map(function(e) { return +e; });
var cmp = listCompare(numlist1, numlist2);
if (cmp !== 0) { return cmp; }
else { return distinguisher1 < distinguisher2? -1:
distinguisher1 > distinguisher2? 1: 0; }
}
return v1 < v2? -1: v1 > v2? 1: 0;
}
Loading