first commit

This commit is contained in:
s.golasch
2023-08-01 13:49:46 +02:00
commit 1fc239fd54
20238 changed files with 3112246 additions and 0 deletions

479
build/node_modules/favicons/helpers.js generated vendored Normal file
View File

@@ -0,0 +1,479 @@
/* eslint camelcase: 0, no-shadow: 0 */
const path = require('path'),
fs = require('fs'),
_ = require('underscore'),
color = require('tinycolor2'),
cheerio = require('cheerio'),
colors = require('colors'),
jsonxml = require('jsontoxml'),
sizeOf = require('image-size'),
async = require('async'),
mkdirp = require('mkdirp'),
Jimp = require('jimp'),
svg2png = require('svg2png'),
File = require('vinyl'),
Reflect = require('harmony-reflect'),
NRC = require('node-rest-client').Client,
PLATFORM_OPTIONS = require('./config/platform-options.json'),
ANDROID_BASE_SIZE = 36,
IOS_BASE_SIZE = 57,
IOS_STARTUP_BASE_SIZE = 320,
COAST_BASE_SIZE = 228,
FIREFOX_BASE_SIZE = 60;
(() => {
'use strict';
const xmlconfig = { prettyPrint: true, xmlHeader: true, indent: ' ' },
client = new NRC(),
HEX_MAX = 255,
NON_EXISTANT = -1,
ROTATE_DEGREES = 90,
HTTP_SUCCESS = 200;
client.setMaxListeners(0);
function helpers (options) {
function contains (array, element) {
return array.indexOf(element.toLowerCase()) > NON_EXISTANT;
}
function relative (directory) {
return path.join(options.path, directory).replace(/\\/g, '/');
}
function print (context, message) {
let newMessage = '';
if (options.logging && message) {
_.each(message.split(' '), (item) => {
newMessage += ` ${ ((/^\d+x\d+$/gm).test(item) ? colors.magenta(item) : item) }`;
});
console.log(`${ colors.green('[Favicons]') } ${ context.yellow }:${ newMessage }...`);
}
}
function readFile (filepath, callback) {
fs.readFile(filepath, callback);
}
function updateDocument (document, code, tags, next) {
const $ = cheerio.load(document, { decodeEntities: false }),
target = $('head').length > 0 ? $('head') : $.root(),
newCode = cheerio.load(code.join('\n'), { decodeEntities: false });
async.each(tags, (platform, callback) => {
async.forEachOf(platform, (tag, selector, cb) => {
if (options.replace) {
$(selector).remove();
} else if ($(selector).length) {
newCode(selector).remove();
}
return cb(null);
}, callback);
}, (error) => {
target.append(newCode.html());
return next(error, $.html().replace(/^\s*$[\n\r]{1,}/gm, ''));
});
}
function preparePlatformOptions (platform, options, baseOptions) {
if (typeof options !== 'object') {
options = {};
}
_.each(options, (value, key) => {
const platformOptionsRef = PLATFORM_OPTIONS[key];
if (typeof platformOptionsRef === 'undefined' || platformOptionsRef.platforms.indexOf(platform) === -1) {
return Reflect.deleteProperty(options, key);
}
});
_.each(PLATFORM_OPTIONS, ({ platforms, defaultTo }, key) => {
if (typeof options[key] === 'undefined' && platforms.indexOf(platform) !== -1) {
options[key] = defaultTo;
}
});
if (typeof options.background === 'boolean') {
if (platform === 'android' && !options.background) {
options.background = 'transparent';
} else {
options.background = baseOptions.background;
}
}
if (platform === 'android' && options.background !== 'transparent') {
options.disableTransparency = true;
}
return options;
}
return {
General: {
preparePlatformOptions,
background: (hex) => {
print('General:background', `Parsing colour ${ hex }`);
const rgba = color(hex).toRgb();
return Jimp.rgbaToInt(rgba.r, rgba.g, rgba.b, rgba.a * HEX_MAX);
},
source: (source, callback) => {
let sourceset = [];
print('General:source', `Source type is ${ typeof source }`);
if (!source || !source.length) {
return callback('No source provided');
} else if (Buffer.isBuffer(source)) {
sourceset = [{ size: sizeOf(source), file: source }];
return callback(null, sourceset);
} else if (Array.isArray(source)) {
async.each(source, (file, cb) =>
readFile(file, (error, buffer) => {
if (error) {
return cb(error);
}
sourceset.push({
size: sizeOf(buffer),
file: buffer
});
cb(null);
}),
(error) =>
callback(error || sourceset.length ? null : 'Favicons source is invalid', sourceset)
);
} else if (typeof source === 'string') {
readFile(source, (error, buffer) => {
if (error) {
return callback(error);
}
sourceset = [{ size: sizeOf(buffer), file: buffer }];
return callback(null, sourceset);
});
} else {
return callback('Invalid source type provided');
}
},
/* eslint no-underscore-dangle: 0 */
vinyl: (object, input) => {
const output = new File({
path: object.name,
contents: Buffer.isBuffer(object.contents) ? object.contents : new Buffer(object.contents)
});
// gulp-cache support
if (typeof input._cachedKey !== 'undefined') {
output._cachedKey = input._cachedKey;
}
return output;
}
},
HTML: {
parse: (html, callback) => {
print('HTML:parse', 'HTML found, parsing and modifying source');
const $ = cheerio.load(html),
link = $('*').is('link'),
attribute = link ? 'href' : 'content',
value = $('*').first().attr(attribute);
if (path.extname(value)) {
$('*').first().attr(attribute, relative(value));
} else if (value.slice(0, 1) === '#') {
$('*').first().attr(attribute, options.background);
} else if (html.indexOf('application-name') !== NON_EXISTANT || html.indexOf('apple-mobile-web-app-title') !== NON_EXISTANT) {
$('*').first().attr(attribute, options.appName);
}
return callback(null, $.html());
},
update: (document, code, tags, callback) => {
const encoding = { encoding: 'utf8' };
async.waterfall([
(cb) =>
mkdirp(path.dirname(document), cb),
(made, cb) =>
fs.readFile(document, encoding, (error, data) => cb(null, error ? null : data)),
(data, cb) =>
(data ? updateDocument(data, code, tags, cb) : cb(null, code.join('\n'))),
(html, cb) =>
fs.writeFile(document, html, options, cb)
], callback);
}
},
Files: {
create: (properties, name, platformOptions, callback) => {
print('Files:create', `Creating file: ${ name }`);
if (name === 'manifest.json') {
properties.name = options.appName;
properties.short_name = options.appName;
properties.description = options.appDescription;
properties.dir = options.dir;
properties.lang = options.lang;
properties.display = options.display;
properties.orientation = options.orientation;
properties.start_url = options.start_url;
properties.background_color = options.background;
properties.theme_color = options.theme_color;
_.map(properties.icons, (icon) => (icon.src = relative(icon.src)));
properties = JSON.stringify(properties, null, 2);
} else if (name === 'manifest.webapp') {
properties.version = options.version;
properties.name = options.appName;
properties.description = options.appDescription;
properties.developer.name = options.developerName;
properties.developer.url = options.developerURL;
properties.icons = _.mapObject(properties.icons, (property) => relative(property));
properties = JSON.stringify(properties, null, 2);
} else if (name === 'browserconfig.xml') {
_.map(properties[0].children[0].children[0].children, (property) => {
if (property.name === 'TileColor') {
property.text = platformOptions.background;
} else {
property.attrs.src = relative(property.attrs.src);
}
});
properties = jsonxml(properties, xmlconfig);
} else if (name === 'yandex-browser-manifest.json') {
properties.version = options.version;
properties.api_version = 1;
properties.layout.logo = relative(properties.layout.logo);
properties.layout.color = platformOptions.background;
properties = JSON.stringify(properties, null, 2);
} else if (/\.html$/.test(name)) {
properties = properties.join('\n');
}
return callback(null, { name, contents: properties });
}
},
Images: {
create: (properties, background, callback) => {
let jimp = null;
print('Image:create', `Creating empty ${ properties.width }x${ properties.height } canvas with ${ (properties.transparent ? 'transparent' : background) } background`);
jimp = new Jimp(properties.width, properties.height, properties.transparent ? 0x00000000 : background, (error, canvas) =>
callback(error, canvas, jimp));
},
read: (file, callback) => {
print('Image:read', `Reading file: ${ file.buffer }`);
return Jimp.read(file, callback);
},
nearest: (sourceset, properties, offset, callback) => {
print('Image:nearest', `Find nearest icon to ${ properties.width }x${ properties.height } with offset ${ offset }`);
const offsetSize = offset * 2,
width = properties.width - offsetSize,
height = properties.height - offsetSize,
sideSize = Math.max(width, height),
svgSource = _.find(sourceset, (source) => source.size.type === 'svg');
let nearestIcon = sourceset[0],
nearestSideSize = Math.max(nearestIcon.size.width, nearestIcon.size.height);
if (svgSource) {
print('Image:nearest', `SVG source will be saved as ${ width }x${ height }`);
svg2png(svgSource.file, { height, width })
.then((resizedBuffer) => callback(null, {
size: sizeOf(resizedBuffer),
file: resizedBuffer
}))
.catch(callback);
} else {
_.each(sourceset, (icon) => {
const max = Math.max(icon.size.width, icon.size.height);
if ((nearestSideSize > max || nearestSideSize < sideSize) && max >= sideSize) {
nearestIcon = icon;
nearestSideSize = max;
}
});
return callback(null, nearestIcon);
}
},
resize: (image, properties, offset, callback) => {
print('Images:resize', `Resizing image to contain in ${ properties.width }x${ properties.height } with offset ${ offset }`);
const offsetSize = offset * 2;
if (properties.rotate) {
print('Images:resize', `Rotating image by ${ROTATE_DEGREES}`);
image.rotate(ROTATE_DEGREES, false);
}
image.contain(properties.width - offsetSize, properties.height - offsetSize, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE);
return callback(null, image);
},
composite: (canvas, image, properties, offset, maximum, callback) => {
const circle = path.join(__dirname, 'mask.png'),
overlay = path.join(__dirname, 'overlay.png');
function compositeIcon () {
print('Images:composite', `Compositing favicon on ${ properties.width }x${ properties.height } canvas with offset ${ offset }`);
canvas.composite(image, offset, offset);
}
if (properties.mask) {
print('Images:composite', 'Masking composite image on circle');
async.parallel([
(cb) => Jimp.read(circle, cb),
(cb) => Jimp.read(overlay, cb)
], (error, images) => {
images[0].resize(maximum, Jimp.AUTO);
images[1].resize(maximum, Jimp.AUTO);
canvas.mask(images[0], 0, 0);
canvas.composite(images[1], 0, 0);
compositeIcon();
return callback(error, canvas);
});
} else {
compositeIcon();
return callback(null, canvas);
}
},
getBuffer: (canvas, callback) => {
print('Images:getBuffer', 'Collecting image buffer from canvas');
canvas.getBuffer(Jimp.MIME_PNG, callback);
}
},
RFG: {
configure: (sourceset, request, callback) => {
print('RFG:configure', 'Configuring RFG API request');
const svgSource = _.find(sourceset, (source) => source.size.type === 'svg');
options.background = `#${ color(options.background).toHex() }`;
request.master_picture.content = (svgSource || _.max(sourceset, ({ size: { width, height } }) => Math.max(width, height))).file.toString('base64');
request.files_location.path = options.path;
if (options.icons.android) {
const androidOptions = preparePlatformOptions('android', options.icons.android, options);
request.favicon_design.android_chrome.theme_color = options.background;
request.favicon_design.android_chrome.manifest.name = options.appName;
request.favicon_design.android_chrome.manifest.display = options.display;
request.favicon_design.android_chrome.manifest.orientation = options.orientation;
if (androidOptions.shadow) {
request.favicon_design.android_chrome.picture_aspect = 'shadow';
} else if (androidOptions.offset > 0 && androidOptions.background) {
request.favicon_design.android_chrome.picture_aspect = 'background_and_margin';
request.favicon_design.android_chrome.background_color = androidOptions.background;
request.favicon_design.android_chrome.margin = Math.round(ANDROID_BASE_SIZE / 100 * androidOptions.offset);
}
} else {
Reflect.deleteProperty(request.favicon_design, 'android_chrome');
}
if (options.icons.appleIcon) {
const appleIconOptions = preparePlatformOptions('appleIcon', options.icons.appleIcon, options);
request.favicon_design.ios.background_color = appleIconOptions.background;
request.favicon_design.ios.margin = Math.round(IOS_BASE_SIZE / 100 * appleIconOptions.offset);
} else {
Reflect.deleteProperty(request.favicon_design, 'ios');
}
if (options.icons.appleIcon && options.icons.appleStartup) {
const appleStartupOptions = preparePlatformOptions('appleStartup', options.icons.appleStartup, options);
request.favicon_design.ios.startup_image.background_color = appleStartupOptions.background;
request.favicon_design.ios.startup_image.margin = Math.round(IOS_STARTUP_BASE_SIZE / 100 * appleStartupOptions.offset);
} else if (request.favicon_design.ios) {
Reflect.deleteProperty(request.favicon_design.ios, 'startup_image');
}
if (options.icons.coast) {
const coastOptions = preparePlatformOptions('coast', options.icons.coast, options);
request.favicon_design.coast.background_color = coastOptions.background;
request.favicon_design.coast.margin = Math.round(COAST_BASE_SIZE / 100 * coastOptions.offset);
} else {
Reflect.deleteProperty(request.favicon_design, 'coast');
}
if (!options.icons.favicons) {
Reflect.deleteProperty(request.favicon_design, 'desktop_browser');
}
if (options.icons.firefox) {
const firefoxOptions = preparePlatformOptions('firefox', options.icons.firefox, options);
request.favicon_design.firefox_app.background_color = firefoxOptions.background;
request.favicon_design.firefox_app.margin = Math.round(FIREFOX_BASE_SIZE / 100 * firefoxOptions.offset);
request.favicon_design.firefox_app.manifest.app_name = options.appName;
request.favicon_design.firefox_app.manifest.app_description = options.appDescription;
request.favicon_design.firefox_app.manifest.developer_name = options.developerName;
request.favicon_design.firefox_app.manifest.developer_url = options.developerURL;
} else {
Reflect.deleteProperty(request.favicon_design, 'firefox_app');
}
if (options.icons.windows) {
const windowsOptions = preparePlatformOptions('windows', options.icons.windows, options);
request.favicon_design.windows.background_color = windowsOptions.background;
} else {
Reflect.deleteProperty(request.favicon_design, 'windows');
}
if (options.icons.yandex) {
const yandexOptions = preparePlatformOptions('yandex', options.icons.yandex, options);
request.favicon_design.yandex_browser.background_color = yandexOptions.background;
request.favicon_design.yandex_browser.manifest.version = options.version;
} else {
Reflect.deleteProperty(request.favicon_design, 'yandex_browser');
}
return callback(null, request);
},
request: (request, callback) => {
print('RFG:request', 'Posting a request to the RFG API');
client.post('http://realfavicongenerator.net/api/favicon', {
data: { favicon_generation: request },
headers: { 'Content-Type': 'application/json' }
}, (data, response) => {
const result = data.favicon_generation_result;
return result && response.statusCode === HTTP_SUCCESS ? callback(null, {
files: result.favicon.files_urls,
html: result.favicon.html_code
}) : callback(result.result.error_message);
});
},
fetch: (address, callback) => {
const name = path.basename(address),
image = contains(['.png', '.jpg', '.bmp', '.ico', '.svg'], path.extname(name));
print('RFG:fetch', `Fetching ${ image ? 'image' : 'file' } from RFG: ${ address }`);
client.get(address, (buffer, response) => {
const success = buffer && response.statusCode === HTTP_SUCCESS;
return success ? callback(null, {
file: image ? null : { name, contents: buffer },
image: image ? { name, contents: buffer } : null
}) : callback(`Could not fetch URL: ${ address }`);
});
}
}
};
}
module.exports = helpers;
})();