* Validate input to BadgeFactory.create() (#3875) * validate input to create() * remove deprecated properties (#3881) * remove BadgeFactory class (#3884) * Template literal templates (#4459) - Remove use of the doT template library and move to generating SVG output using javascript template literals. - Drop SVGO and mostly manually implement the optimisations. - Add a bunch more tests Co-authored-by: Paul Melnikow <github@paulmelnikow.com> * drop raster support in package CLI (#4523) * drop raster support in package CLI * update docs * rename gh-badges package to badge-maker * rename gh-badges dir to badge-maker * update relative imports and other refs to in parent dir 'gh-badges' --> 'badge-maker' * update snyk service tests This change is only tangentially related We've used the shields repo as an example for these tests so moving files around in our repo has a knock-on effect on them * add missing type hints to dev style page * write the changelog/migration guide for v3 * use extension in README CLI example * update CLI help whoops - missed this in #4523 * bump version * update for self-hosting users * README updates * drop .format param from CLI, always output SVG * Change text[] to label and message, Remove JSON output - Change text[] to label and message - Fix message only badge - Remove JSON output format - Update the docs * update package-lock * rename 'template' to 'style' * handle invalid styles in coalesceBadge * ensure makeBadge is passed a string for template in coalesceBadge() issue #4925 * fix (logo/no label text/label color specified) case issue #4926 * add example of (logo/no label text/label color specified) to style debug page * update type defs * padding fix for FTB style Co-authored-by: Paul Melnikow <github@paulmelnikow.com>
152 lines
3.4 KiB
JavaScript
152 lines
3.4 KiB
JavaScript
'use strict'
|
|
|
|
const Joi = require('@hapi/joi')
|
|
const { toSvgColor } = require('../badge-maker/lib/color')
|
|
const coalesce = require('../core/base-service/coalesce')
|
|
const { svg2base64 } = require('./svg-helpers')
|
|
const logos = require('./load-logos')()
|
|
const simpleIcons = require('./load-simple-icons')()
|
|
|
|
// for backwards-compatibility with deleted logos
|
|
const logoAliases = {
|
|
azuredevops: 'azure-devops',
|
|
eclipse: 'eclipse-ide',
|
|
'gitter-white': 'gitter',
|
|
scrutinizer: 'scrutinizer-ci',
|
|
stackoverflow: 'stack-overflow',
|
|
tfs: 'azure-devops',
|
|
}
|
|
|
|
function prependPrefix(s, prefix) {
|
|
if (s === undefined) {
|
|
return undefined
|
|
}
|
|
|
|
s = `${s}`
|
|
|
|
if (s.startsWith(prefix)) {
|
|
return s
|
|
} else {
|
|
return prefix + s
|
|
}
|
|
}
|
|
|
|
function isDataUrl(s) {
|
|
try {
|
|
Joi.assert(s, Joi.string().dataUri())
|
|
return true
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// +'s are replaced with spaces when used in query params, this returns them
|
|
// to +'s, then removes remaining whitespace.
|
|
// https://github.com/badges/shields/pull/1546
|
|
function decodeDataUrlFromQueryParam(value) {
|
|
if (typeof value !== 'string') {
|
|
return undefined
|
|
}
|
|
const maybeDataUrl = prependPrefix(value, 'data:')
|
|
.replace(/ /g, '+')
|
|
.replace(/\s/g, '')
|
|
return isDataUrl(maybeDataUrl) ? maybeDataUrl : undefined
|
|
}
|
|
|
|
function getShieldsIcon({ name, color }) {
|
|
if (!(name in logos)) {
|
|
return undefined
|
|
}
|
|
|
|
const { svg, base64, isMonochrome } = logos[name]
|
|
const svgColor = toSvgColor(color)
|
|
if (svgColor && isMonochrome) {
|
|
return svg2base64(svg.replace(/fill="(.+?)"/g, `fill="${svgColor}"`))
|
|
} else {
|
|
return base64
|
|
}
|
|
}
|
|
|
|
function brightness({ r, g, b }) {
|
|
return +((r * 299 + g * 587 + b * 114) / 255000).toFixed(2)
|
|
}
|
|
|
|
const hexColorRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
|
|
|
|
function hexToRgb(hex) {
|
|
const result = hexColorRegex.exec(hex)
|
|
return {
|
|
r: parseInt(result[1], 16),
|
|
g: parseInt(result[2], 16),
|
|
b: parseInt(result[3], 16),
|
|
}
|
|
}
|
|
|
|
function getSimpleIconStyle({ icon, style }) {
|
|
const { hex } = icon
|
|
if (style !== 'social' && brightness(hexToRgb(hex)) <= 0.4) {
|
|
return 'light'
|
|
}
|
|
if (style === 'social' && brightness(hexToRgb(hex)) >= 0.6) {
|
|
return 'dark'
|
|
}
|
|
return 'default'
|
|
}
|
|
|
|
function getSimpleIcon({ name, color, style }) {
|
|
const key = name.replace(/ /g, '-')
|
|
|
|
if (!(key in simpleIcons)) {
|
|
return undefined
|
|
}
|
|
|
|
const svgColor = toSvgColor(color)
|
|
if (svgColor) {
|
|
return svg2base64(
|
|
simpleIcons[key].svg.replace('<svg', `<svg fill="${svgColor}"`)
|
|
)
|
|
} else {
|
|
const iconStyle = getSimpleIconStyle({ icon: simpleIcons[key], style })
|
|
return simpleIcons[key].base64[iconStyle]
|
|
}
|
|
}
|
|
|
|
function prepareNamedLogo({ name, color, style }) {
|
|
if (typeof name !== 'string') {
|
|
return undefined
|
|
}
|
|
|
|
name = name.toLowerCase()
|
|
|
|
if (name in logoAliases) {
|
|
name = logoAliases[name]
|
|
}
|
|
|
|
return (
|
|
getShieldsIcon({ name, color }) || getSimpleIcon({ name, color, style })
|
|
)
|
|
}
|
|
|
|
function makeLogo(defaultNamedLogo, overrides) {
|
|
const maybeDataUrl = decodeDataUrlFromQueryParam(overrides.logo)
|
|
if (maybeDataUrl) {
|
|
return maybeDataUrl
|
|
} else {
|
|
return prepareNamedLogo({
|
|
name: coalesce(overrides.logo, defaultNamedLogo),
|
|
color: overrides.logoColor,
|
|
style: overrides.style,
|
|
})
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
prependPrefix,
|
|
isDataUrl,
|
|
decodeDataUrlFromQueryParam,
|
|
prepareNamedLogo,
|
|
getShieldsIcon,
|
|
getSimpleIcon,
|
|
makeLogo,
|
|
}
|