mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-02 22:22:19 +00:00
0.2.0 - Mid migration
This commit is contained in:
parent
139e6a915e
commit
7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions
222
web/node_modules/svgo/lib/css-tools.js
generated
vendored
Normal file
222
web/node_modules/svgo/lib/css-tools.js
generated
vendored
Normal file
|
@ -0,0 +1,222 @@
|
|||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
List = csstree.List,
|
||||
stable = require('stable'),
|
||||
specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
|
||||
|
||||
/**
|
||||
* Flatten a CSS AST to a selectors list.
|
||||
*
|
||||
* @param {Object} cssAst css-tree AST to flatten
|
||||
* @return {Array} selectors
|
||||
*/
|
||||
function flattenToSelectors(cssAst) {
|
||||
var selectors = [];
|
||||
|
||||
csstree.walk(cssAst, {visit: 'Rule', enter: function(node) {
|
||||
if (node.type !== 'Rule') {
|
||||
return;
|
||||
}
|
||||
|
||||
var atrule = this.atrule;
|
||||
var rule = node;
|
||||
|
||||
node.prelude.children.each(function(selectorNode, selectorItem) {
|
||||
var selector = {
|
||||
item: selectorItem,
|
||||
atrule: atrule,
|
||||
rule: rule,
|
||||
pseudos: []
|
||||
};
|
||||
|
||||
selectorNode.children.each(function(selectorChildNode, selectorChildItem, selectorChildList) {
|
||||
if (selectorChildNode.type === 'PseudoClassSelector' ||
|
||||
selectorChildNode.type === 'PseudoElementSelector') {
|
||||
selector.pseudos.push({
|
||||
item: selectorChildItem,
|
||||
list: selectorChildList
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
selectors.push(selector);
|
||||
});
|
||||
}});
|
||||
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by Media Query.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} useMqs Array with strings of media queries that should pass (<name> <expression>)
|
||||
* @return {Array} Filtered selectors that match the passed media queries
|
||||
*/
|
||||
function filterByMqs(selectors, useMqs) {
|
||||
return selectors.filter(function(selector) {
|
||||
if (selector.atrule === null) {
|
||||
return ~useMqs.indexOf('');
|
||||
}
|
||||
|
||||
var mqName = selector.atrule.name;
|
||||
var mqStr = mqName;
|
||||
if (selector.atrule.expression &&
|
||||
selector.atrule.expression.children.first().type === 'MediaQueryList') {
|
||||
var mqExpr = csstree.generate(selector.atrule.expression);
|
||||
mqStr = [mqName, mqExpr].join(' ');
|
||||
}
|
||||
|
||||
return ~useMqs.indexOf(mqStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by the pseudo-elements and/or -classes they contain.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} usePseudos Array with strings of single or sequence of pseudo-elements and/or -classes that should pass
|
||||
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
|
||||
*/
|
||||
function filterByPseudos(selectors, usePseudos) {
|
||||
return selectors.filter(function(selector) {
|
||||
var pseudoSelectorsStr = csstree.generate({
|
||||
type: 'Selector',
|
||||
children: new List().fromArray(selector.pseudos.map(function(pseudo) {
|
||||
return pseudo.item.data;
|
||||
}))
|
||||
});
|
||||
return ~usePseudos.indexOf(pseudoSelectorsStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove pseudo-elements and/or -classes from the selectors for proper matching.
|
||||
*
|
||||
* @param {Array} selectors to clean
|
||||
* @return {Array} Selectors without pseudo-elements and/or -classes
|
||||
*/
|
||||
function cleanPseudos(selectors) {
|
||||
selectors.forEach(function(selector) {
|
||||
selector.pseudos.forEach(function(pseudo) {
|
||||
pseudo.list.remove(pseudo.item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
*
|
||||
* @param {Array} aSpecificity Specificity of selector A
|
||||
* @param {Array} bSpecificity Specificity of selector B
|
||||
* @return {Number} Score of selector specificity A compared to selector specificity B
|
||||
*/
|
||||
function compareSpecificity(aSpecificity, bSpecificity) {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
if (aSpecificity[i] < bSpecificity[i]) {
|
||||
return -1;
|
||||
} else if (aSpecificity[i] > bSpecificity[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare two simple selectors.
|
||||
*
|
||||
* @param {Object} aSimpleSelectorNode Simple selector A
|
||||
* @param {Object} bSimpleSelectorNode Simple selector B
|
||||
* @return {Number} Score of selector A compared to selector B
|
||||
*/
|
||||
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
|
||||
var aSpecificity = specificity(aSimpleSelectorNode),
|
||||
bSpecificity = specificity(bSimpleSelectorNode);
|
||||
return compareSpecificity(aSpecificity, bSpecificity);
|
||||
}
|
||||
|
||||
function _bySelectorSpecificity(selectorA, selectorB) {
|
||||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort selectors stably by their specificity.
|
||||
*
|
||||
* @param {Array} selectors to be sorted
|
||||
* @return {Array} Stable sorted selectors
|
||||
*/
|
||||
function sortSelectors(selectors) {
|
||||
return stable(selectors, _bySelectorSpecificity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
|
||||
*
|
||||
* @param {Object} declaration css-tree style declaration
|
||||
* @return {Object} CSSStyleDeclaration property
|
||||
*/
|
||||
function csstreeToStyleDeclaration(declaration) {
|
||||
var propertyName = declaration.property,
|
||||
propertyValue = csstree.generate(declaration.value),
|
||||
propertyPriority = (declaration.important ? 'important' : '');
|
||||
return {
|
||||
name: propertyName,
|
||||
value: propertyValue,
|
||||
priority: propertyPriority
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} element style element
|
||||
* @return {String|Array} CSS string or empty array if no styles are set
|
||||
*/
|
||||
function getCssStr(elem) {
|
||||
return elem.content[0].text || elem.content[0].cdata || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} element style element
|
||||
* @param {String} CSS string to be set
|
||||
* @return {Object} reference to field with CSS
|
||||
*/
|
||||
function setCssStr(elem, css) {
|
||||
// in case of cdata field
|
||||
if(elem.content[0].cdata) {
|
||||
elem.content[0].cdata = css;
|
||||
return elem.content[0].cdata;
|
||||
}
|
||||
|
||||
// in case of text field + if nothing was set yet
|
||||
elem.content[0].text = css;
|
||||
return elem.content[0].text;
|
||||
}
|
||||
|
||||
|
||||
module.exports.flattenToSelectors = flattenToSelectors;
|
||||
|
||||
module.exports.filterByMqs = filterByMqs;
|
||||
module.exports.filterByPseudos = filterByPseudos;
|
||||
module.exports.cleanPseudos = cleanPseudos;
|
||||
|
||||
module.exports.compareSpecificity = compareSpecificity;
|
||||
module.exports.compareSimpleSelectorNode = compareSimpleSelectorNode;
|
||||
|
||||
module.exports.sortSelectors = sortSelectors;
|
||||
|
||||
module.exports.csstreeToStyleDeclaration = csstreeToStyleDeclaration;
|
||||
|
||||
module.exports.getCssStr = getCssStr;
|
||||
module.exports.setCssStr = setCssStr;
|
90
web/node_modules/svgo/lib/svgo.js
generated
vendored
Executable file
90
web/node_modules/svgo/lib/svgo.js
generated
vendored
Executable file
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* SVGO is a Nodejs-based tool for optimizing SVG vector graphics files.
|
||||
*
|
||||
* @see https://github.com/svg/svgo
|
||||
*
|
||||
* @author Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)
|
||||
* @copyright © 2012 Kir Belevich
|
||||
* @license MIT https://raw.githubusercontent.com/svg/svgo/master/LICENSE
|
||||
*/
|
||||
|
||||
var CONFIG = require('./svgo/config.js'),
|
||||
SVG2JS = require('./svgo/svg2js.js'),
|
||||
PLUGINS = require('./svgo/plugins.js'),
|
||||
JSAPI = require('./svgo/jsAPI.js'),
|
||||
encodeSVGDatauri = require('./svgo/tools.js').encodeSVGDatauri,
|
||||
JS2SVG = require('./svgo/js2svg.js');
|
||||
|
||||
var SVGO = function(config) {
|
||||
this.config = CONFIG(config);
|
||||
};
|
||||
|
||||
SVGO.prototype.optimize = function(svgstr, info) {
|
||||
info = info || {};
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.config.error) {
|
||||
reject(this.config.error);
|
||||
return;
|
||||
}
|
||||
|
||||
var config = this.config,
|
||||
maxPassCount = config.multipass ? 10 : 1,
|
||||
counter = 0,
|
||||
prevResultSize = Number.POSITIVE_INFINITY,
|
||||
optimizeOnceCallback = (svgjs) => {
|
||||
if (svgjs.error) {
|
||||
reject(svgjs.error);
|
||||
return;
|
||||
}
|
||||
|
||||
info.multipassCount = counter;
|
||||
if (++counter < maxPassCount && svgjs.data.length < prevResultSize) {
|
||||
prevResultSize = svgjs.data.length;
|
||||
this._optimizeOnce(svgjs.data, info, optimizeOnceCallback);
|
||||
} else {
|
||||
if (config.datauri) {
|
||||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
|
||||
}
|
||||
if (info && info.path) {
|
||||
svgjs.path = info.path;
|
||||
}
|
||||
resolve(svgjs);
|
||||
}
|
||||
};
|
||||
|
||||
this._optimizeOnce(svgstr, info, optimizeOnceCallback);
|
||||
});
|
||||
};
|
||||
|
||||
SVGO.prototype._optimizeOnce = function(svgstr, info, callback) {
|
||||
var config = this.config;
|
||||
|
||||
SVG2JS(svgstr, function(svgjs) {
|
||||
if (svgjs.error) {
|
||||
callback(svgjs);
|
||||
return;
|
||||
}
|
||||
|
||||
svgjs = PLUGINS(svgjs, info, config.plugins);
|
||||
|
||||
callback(JS2SVG(svgjs, config.js2svg));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* The factory that creates a content item with the helper methods.
|
||||
*
|
||||
* @param {Object} data which passed to jsAPI constructor
|
||||
* @returns {JSAPI} content item
|
||||
*/
|
||||
SVGO.prototype.createContentItem = function(data) {
|
||||
return new JSAPI(data);
|
||||
};
|
||||
|
||||
SVGO.Config = CONFIG;
|
||||
|
||||
module.exports = SVGO;
|
||||
// Offer ES module interop compatibility.
|
||||
module.exports.default = SVGO;
|
579
web/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
579
web/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
|
@ -0,0 +1,579 @@
|
|||
/* jshint quotmark: false */
|
||||
'use strict';
|
||||
|
||||
var FS = require('fs'),
|
||||
PATH = require('path'),
|
||||
chalk = require('chalk'),
|
||||
mkdirp = require('mkdirp'),
|
||||
promisify = require('util.promisify'),
|
||||
readdir = promisify(FS.readdir),
|
||||
readFile = promisify(FS.readFile),
|
||||
writeFile = promisify(FS.writeFile),
|
||||
SVGO = require('../svgo.js'),
|
||||
YAML = require('js-yaml'),
|
||||
PKG = require('../../package.json'),
|
||||
encodeSVGDatauri = require('./tools.js').encodeSVGDatauri,
|
||||
decodeSVGDatauri = require('./tools.js').decodeSVGDatauri,
|
||||
checkIsDir = require('./tools.js').checkIsDir,
|
||||
regSVGFile = /\.svg$/,
|
||||
noop = () => {},
|
||||
svgo;
|
||||
|
||||
/**
|
||||
* Command-Option-Argument.
|
||||
*
|
||||
* @see https://github.com/veged/coa
|
||||
*/
|
||||
module.exports = require('coa').Cmd()
|
||||
.helpful()
|
||||
.name(PKG.name)
|
||||
.title(PKG.description)
|
||||
.opt()
|
||||
.name('version').title('Version')
|
||||
.short('v').long('version')
|
||||
.only()
|
||||
.flag()
|
||||
.act(function() {
|
||||
// output the version to stdout instead of stderr if returned
|
||||
process.stdout.write(PKG.version + '\n');
|
||||
// coa will run `.toString` on the returned value and send it to stderr
|
||||
return '';
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('input').title('Input file, "-" for STDIN')
|
||||
.short('i').long('input')
|
||||
.arr()
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--input' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('string').title('Input SVG data string')
|
||||
.short('s').long('string')
|
||||
.end()
|
||||
.opt()
|
||||
.name('folder').title('Input folder, optimize and rewrite all *.svg files')
|
||||
.short('f').long('folder')
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--folder' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('output').title('Output file or folder (by default the same as the input), "-" for STDOUT')
|
||||
.short('o').long('output')
|
||||
.arr()
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--output' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('precision').title('Set number of digits in the fractional part, overrides plugins params')
|
||||
.short('p').long('precision')
|
||||
.val(function(val) {
|
||||
return !isNaN(val) ? val : this.reject("Option '--precision' must be an integer number");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('config').title('Config file or JSON string to extend or replace default')
|
||||
.long('config')
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--config' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('disable').title('Disable plugin by name, "--disable={PLUGIN1,PLUGIN2}" for multiple plugins (*nix)')
|
||||
.long('disable')
|
||||
.arr()
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--disable' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('enable').title('Enable plugin by name, "--enable={PLUGIN3,PLUGIN4}" for multiple plugins (*nix)')
|
||||
.long('enable')
|
||||
.arr()
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--enable' must have a value.");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('datauri').title('Output as Data URI string (base64, URI encoded or unencoded)')
|
||||
.long('datauri')
|
||||
.val(function(val) {
|
||||
return val || this.reject("Option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('multipass').title('Pass over SVGs multiple times to ensure all optimizations are applied')
|
||||
.long('multipass')
|
||||
.flag()
|
||||
.end()
|
||||
.opt()
|
||||
.name('pretty').title('Make SVG pretty printed')
|
||||
.long('pretty')
|
||||
.flag()
|
||||
.end()
|
||||
.opt()
|
||||
.name('indent').title('Indent number when pretty printing SVGs')
|
||||
.long('indent')
|
||||
.val(function(val) {
|
||||
return !isNaN(val) ? val : this.reject("Option '--indent' must be an integer number");
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('recursive').title('Use with \'-f\'. Optimizes *.svg files in folders recursively.')
|
||||
.short('r').long('recursive')
|
||||
.flag()
|
||||
.end()
|
||||
.opt()
|
||||
.name('quiet').title('Only output error messages, not regular status messages')
|
||||
.short('q').long('quiet')
|
||||
.flag()
|
||||
.end()
|
||||
.opt()
|
||||
.name('show-plugins').title('Show available plugins and exit')
|
||||
.long('show-plugins')
|
||||
.flag()
|
||||
.end()
|
||||
.arg()
|
||||
.name('input').title('Alias to --input')
|
||||
.arr()
|
||||
.end()
|
||||
.act(function(opts, args) {
|
||||
var input = opts.input || args.input,
|
||||
output = opts.output,
|
||||
config = {};
|
||||
|
||||
// --show-plugins
|
||||
if (opts['show-plugins']) {
|
||||
showAvailablePlugins();
|
||||
return;
|
||||
}
|
||||
|
||||
// w/o anything
|
||||
if (
|
||||
(!input || input[0] === '-') &&
|
||||
!opts.string &&
|
||||
!opts.stdin &&
|
||||
!opts.folder &&
|
||||
process.stdin.isTTY === true
|
||||
) return this.usage();
|
||||
|
||||
if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
|
||||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
|
||||
return printErrorAndExit(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
|
||||
}
|
||||
}
|
||||
|
||||
// --config
|
||||
if (opts.config) {
|
||||
// string
|
||||
if (opts.config.charAt(0) === '{') {
|
||||
try {
|
||||
config = JSON.parse(opts.config);
|
||||
} catch (e) {
|
||||
return printErrorAndExit(`Error: Couldn't parse config JSON.\n${String(e)}`);
|
||||
}
|
||||
// external file
|
||||
} else {
|
||||
var configPath = PATH.resolve(opts.config),
|
||||
configData;
|
||||
try {
|
||||
// require() adds some weird output on YML files
|
||||
configData = FS.readFileSync(configPath, 'utf8');
|
||||
config = JSON.parse(configData);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return printErrorAndExit(`Error: couldn't find config file '${opts.config}'.`);
|
||||
} else if (err.code === 'EISDIR') {
|
||||
return printErrorAndExit(`Error: directory '${opts.config}' is not a config file.`);
|
||||
}
|
||||
config = YAML.safeLoad(configData);
|
||||
config.__DIR = PATH.dirname(configPath); // will use it to resolve custom plugins defined via path
|
||||
|
||||
if (!config || Array.isArray(config)) {
|
||||
return printErrorAndExit(`Error: invalid config file '${opts.config}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --quiet
|
||||
if (opts.quiet) {
|
||||
config.quiet = opts.quiet;
|
||||
}
|
||||
|
||||
// --recursive
|
||||
if (opts.recursive) {
|
||||
config.recursive = opts.recursive;
|
||||
}
|
||||
|
||||
// --precision
|
||||
if (opts.precision) {
|
||||
var precision = Math.min(Math.max(0, parseInt(opts.precision)), 20);
|
||||
if (!isNaN(precision)) {
|
||||
config.floatPrecision = precision;
|
||||
}
|
||||
}
|
||||
|
||||
// --disable
|
||||
if (opts.disable) {
|
||||
changePluginsState(opts.disable, false, config);
|
||||
}
|
||||
|
||||
// --enable
|
||||
if (opts.enable) {
|
||||
changePluginsState(opts.enable, true, config);
|
||||
}
|
||||
|
||||
// --multipass
|
||||
if (opts.multipass) {
|
||||
config.multipass = true;
|
||||
}
|
||||
|
||||
// --pretty
|
||||
if (opts.pretty) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.pretty = true;
|
||||
var indent;
|
||||
if (opts.indent && !isNaN(indent = parseInt(opts.indent))) {
|
||||
config.js2svg.indent = indent;
|
||||
}
|
||||
}
|
||||
|
||||
svgo = new SVGO(config);
|
||||
|
||||
// --output
|
||||
if (output) {
|
||||
if (input && input[0] != '-') {
|
||||
if (output.length == 1 && checkIsDir(output[0])) {
|
||||
var dir = output[0];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
output[i] = checkIsDir(input[i]) ? input[i] : PATH.resolve(dir, PATH.basename(input[i]));
|
||||
}
|
||||
} else if (output.length < input.length) {
|
||||
output = output.concat(input.slice(output.length));
|
||||
}
|
||||
}
|
||||
} else if (input) {
|
||||
output = input;
|
||||
} else if (opts.string) {
|
||||
output = '-';
|
||||
}
|
||||
|
||||
if (opts.datauri) {
|
||||
config.datauri = opts.datauri;
|
||||
}
|
||||
|
||||
// --folder
|
||||
if (opts.folder) {
|
||||
var ouputFolder = output && output[0] || opts.folder;
|
||||
return optimizeFolder(config, opts.folder, ouputFolder).then(noop, printErrorAndExit);
|
||||
}
|
||||
|
||||
// --input
|
||||
if (input) {
|
||||
// STDIN
|
||||
if (input[0] === '-') {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = '',
|
||||
file = output[0];
|
||||
|
||||
process.stdin
|
||||
.on('data', chunk => data += chunk)
|
||||
.once('end', () => processSVGData(config, {input: 'string'}, data, file).then(resolve, reject));
|
||||
});
|
||||
// file
|
||||
} else {
|
||||
return Promise.all(input.map((file, n) => optimizeFile(config, file, output[n])))
|
||||
.then(noop, printErrorAndExit);
|
||||
}
|
||||
|
||||
// --string
|
||||
} else if (opts.string) {
|
||||
var data = decodeSVGDatauri(opts.string);
|
||||
|
||||
return processSVGData(config, {input: 'string'}, data, output[0]);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Change plugins state by names array.
|
||||
*
|
||||
* @param {Array} names plugins names
|
||||
* @param {Boolean} state active state
|
||||
* @param {Object} config original config
|
||||
* @return {Object} changed config
|
||||
*/
|
||||
function changePluginsState(names, state, config) {
|
||||
names.forEach(flattenPluginsCbk);
|
||||
|
||||
// extend config
|
||||
if (config.plugins) {
|
||||
for (var name of names) {
|
||||
var matched = false,
|
||||
key;
|
||||
|
||||
for (var plugin of config.plugins) {
|
||||
// get plugin name
|
||||
if (typeof plugin === 'object') {
|
||||
key = Object.keys(plugin)[0];
|
||||
} else {
|
||||
key = plugin;
|
||||
}
|
||||
|
||||
// if there is such a plugin name
|
||||
if (key === name) {
|
||||
// don't replace plugin's params with true
|
||||
if (typeof plugin[key] !== 'object' || !state) {
|
||||
plugin[key] = state;
|
||||
}
|
||||
// mark it as matched
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if not matched and current config is not full
|
||||
if (!matched && !config.full) {
|
||||
// push new plugin Object
|
||||
config.plugins.push({ [name]: state });
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
// just push
|
||||
} else {
|
||||
config.plugins = names.map(name => ({ [name]: state }));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten an array of plugins by invoking this callback on each element
|
||||
* whose value may be a comma separated list of plugins.
|
||||
*
|
||||
* @param {String} name Plugin name
|
||||
* @param {Number} index Plugin index
|
||||
* @param {Array} names Plugins being traversed
|
||||
*/
|
||||
function flattenPluginsCbk(name, index, names)
|
||||
{
|
||||
var split = name.split(',');
|
||||
|
||||
if(split.length > 1) {
|
||||
names[index] = split.shift();
|
||||
names.push.apply(names, split);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG files in a directory.
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
*/
|
||||
function optimizeFolder(config, dir, output) {
|
||||
if (!config.quiet) {
|
||||
console.log(`Processing directory '${dir}':\n`);
|
||||
}
|
||||
return readdir(dir).then(files => processDirectory(config, dir, files, output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process given files, take only SVG.
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
*/
|
||||
function processDirectory(config, dir, files, output) {
|
||||
// take only *.svg files, recursively if necessary
|
||||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
|
||||
return svgFilesDescriptions.length ?
|
||||
Promise.all(svgFilesDescriptions.map(fileDescription => optimizeFile(config, fileDescription.inputPath, fileDescription.outputPath))) :
|
||||
Promise.reject(new Error(`No SVG files have been found in '${dir}' directory.`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get svg files descriptions
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Array}
|
||||
*/
|
||||
function getFilesDescriptions(config, dir, files, output) {
|
||||
const filesInThisFolder = files
|
||||
.filter(name => regSVGFile.test(name))
|
||||
.map(name => ({
|
||||
inputPath: PATH.resolve(dir, name),
|
||||
outputPath: PATH.resolve(output, name),
|
||||
}));
|
||||
|
||||
return config.recursive ?
|
||||
[].concat(
|
||||
filesInThisFolder,
|
||||
files
|
||||
.filter(name => checkIsDir(PATH.resolve(dir, name)))
|
||||
.map(subFolderName => {
|
||||
const subFolderPath = PATH.resolve(dir, subFolderName);
|
||||
const subFolderFiles = FS.readdirSync(subFolderPath);
|
||||
const subFolderOutput = PATH.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(config, subFolderPath, subFolderFiles, subFolderOutput);
|
||||
})
|
||||
.reduce((a, b) => [].concat(a, b), [])
|
||||
) :
|
||||
filesInThisFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SVG file and pass to processing.
|
||||
* @param {Object} config options
|
||||
* @param {string} file
|
||||
* @param {string} output
|
||||
* @return {Promise}
|
||||
*/
|
||||
function optimizeFile(config, file, output) {
|
||||
return readFile(file, 'utf8').then(
|
||||
data => processSVGData(config, {input: 'file', path: file}, data, output, file),
|
||||
error => checkOptimizeFileError(config, file, output, error)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG data.
|
||||
* @param {Object} config options
|
||||
* @param {string} data SVG content to optimize
|
||||
* @param {string} output where to write optimized file
|
||||
* @param {string} [input] input file name (being used if output is a directory)
|
||||
* @return {Promise}
|
||||
*/
|
||||
function processSVGData(config, info, data, output, input) {
|
||||
var startTime = Date.now(),
|
||||
prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
|
||||
return svgo.optimize(data, info).then(function(result) {
|
||||
if (config.datauri) {
|
||||
result.data = encodeSVGDatauri(result.data, config.datauri);
|
||||
}
|
||||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
||||
processingTime = Date.now() - startTime;
|
||||
|
||||
return writeOutput(input, output, result.data).then(function() {
|
||||
if (!config.quiet && output != '-') {
|
||||
if (input) {
|
||||
console.log(`\n${PATH.basename(input)}:`);
|
||||
}
|
||||
printTimeInfo(processingTime);
|
||||
printProfitInfo(prevFileSize, resultFileSize);
|
||||
}
|
||||
},
|
||||
error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Write result of an optimization.
|
||||
* @param {string} input
|
||||
* @param {string} output output file name. '-' for stdout
|
||||
* @param {string} data data to write
|
||||
* @return {Promise}
|
||||
*/
|
||||
function writeOutput(input, output, data) {
|
||||
if (output == '-') {
|
||||
console.log(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
mkdirp.sync(PATH.dirname(output));
|
||||
|
||||
return writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write a time taken by optimization.
|
||||
* @param {number} time time in milliseconds.
|
||||
*/
|
||||
function printTimeInfo(time) {
|
||||
console.log(`Done in ${time} ms!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write optimizing information in human readable format.
|
||||
* @param {number} inBytes size before optimization.
|
||||
* @param {number} outBytes size after optimization.
|
||||
*/
|
||||
function printProfitInfo(inBytes, outBytes) {
|
||||
var profitPercents = 100 - outBytes * 100 / inBytes;
|
||||
|
||||
console.log(
|
||||
(Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
|
||||
(profitPercents < 0 ? ' + ' : ' - ') +
|
||||
chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' +
|
||||
(Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors, if it's a dir optimize the dir.
|
||||
* @param {Object} config
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
*/
|
||||
function checkOptimizeFileError(config, input, output, error) {
|
||||
if (error.code == 'EISDIR') {
|
||||
return optimizeFolder(config, input, output);
|
||||
} else if (error.code == 'ENOENT') {
|
||||
return Promise.reject(new Error(`Error: no such file or directory '${error.path}'.`));
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for saving file error. If the output is a dir, then write file there.
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {string} data
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
*/
|
||||
function checkWriteFileError(input, output, data, error) {
|
||||
if (error.code == 'EISDIR' && input) {
|
||||
return writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8');
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list of available plugins with short description.
|
||||
*/
|
||||
function showAvailablePlugins() {
|
||||
console.log('Currently available plugins:');
|
||||
|
||||
// Flatten an array of plugins grouped per type, sort and write output
|
||||
var list = [].concat.apply([], new SVGO().config.plugins)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(plugin => ` [ ${chalk.green(plugin.name)} ] ${plugin.description}`)
|
||||
.join('\n');
|
||||
console.log(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an error and exit.
|
||||
* @param {Error} error
|
||||
* @return {Promise} a promise for running tests
|
||||
*/
|
||||
function printErrorAndExit(error) {
|
||||
console.error(chalk.red(error));
|
||||
process.exit(1);
|
||||
return Promise.reject(error); // for tests
|
||||
}
|
250
web/node_modules/svgo/lib/svgo/config.js
generated
vendored
Normal file
250
web/node_modules/svgo/lib/svgo/config.js
generated
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
'use strict';
|
||||
|
||||
var FS = require('fs');
|
||||
var PATH = require('path');
|
||||
var yaml = require('js-yaml');
|
||||
|
||||
/**
|
||||
* Read and/or extend/replace default config file,
|
||||
* prepare and optimize plugins array.
|
||||
*
|
||||
* @param {Object} [config] input config
|
||||
* @return {Object} output config
|
||||
*/
|
||||
module.exports = function(config) {
|
||||
|
||||
var defaults;
|
||||
config = typeof config == 'object' && config || {};
|
||||
|
||||
if (config.plugins && !Array.isArray(config.plugins)) {
|
||||
return { error: 'Error: Invalid plugins list. Provided \'plugins\' in config should be an array.' };
|
||||
}
|
||||
|
||||
if (config.full) {
|
||||
defaults = config;
|
||||
|
||||
if (Array.isArray(defaults.plugins)) {
|
||||
defaults.plugins = preparePluginsArray(config, defaults.plugins);
|
||||
}
|
||||
} else {
|
||||
defaults = Object.assign({}, yaml.safeLoad(FS.readFileSync(__dirname + '/../../.svgo.yml', 'utf8')));
|
||||
defaults.plugins = preparePluginsArray(config, defaults.plugins || []);
|
||||
defaults = extendConfig(defaults, config);
|
||||
}
|
||||
|
||||
if ('floatPrecision' in config && Array.isArray(defaults.plugins)) {
|
||||
defaults.plugins.forEach(function(plugin) {
|
||||
if (plugin.params && ('floatPrecision' in plugin.params)) {
|
||||
// Don't touch default plugin params
|
||||
plugin.params = Object.assign({}, plugin.params, { floatPrecision: config.floatPrecision });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ('datauri' in config) {
|
||||
defaults.datauri = config.datauri;
|
||||
}
|
||||
|
||||
if (Array.isArray(defaults.plugins)) {
|
||||
defaults.plugins = optimizePluginsArray(defaults.plugins);
|
||||
}
|
||||
|
||||
return defaults;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Require() all plugins in array.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Array} plugins input plugins array
|
||||
* @return {Array} input plugins array of arrays
|
||||
*/
|
||||
function preparePluginsArray(config, plugins) {
|
||||
|
||||
var plugin,
|
||||
key;
|
||||
|
||||
return plugins.map(function(item) {
|
||||
|
||||
// {}
|
||||
if (typeof item === 'object') {
|
||||
|
||||
key = Object.keys(item)[0];
|
||||
|
||||
// custom
|
||||
if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
|
||||
plugin = setupCustomPlugin(key, item[key]);
|
||||
|
||||
} else {
|
||||
|
||||
plugin = setPluginActiveState(
|
||||
loadPlugin(config, key, item[key].path),
|
||||
item,
|
||||
key
|
||||
);
|
||||
plugin.name = key;
|
||||
}
|
||||
|
||||
// name
|
||||
} else {
|
||||
|
||||
plugin = loadPlugin(config, item);
|
||||
plugin.name = item;
|
||||
if (typeof plugin.params === 'object') {
|
||||
plugin.params = Object.assign({}, plugin.params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return plugin;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend plugins with the custom config object.
|
||||
*
|
||||
* @param {Array} plugins input plugins
|
||||
* @param {Object} config config
|
||||
* @return {Array} output plugins
|
||||
*/
|
||||
function extendConfig(defaults, config) {
|
||||
|
||||
var key;
|
||||
|
||||
// plugins
|
||||
if (config.plugins) {
|
||||
|
||||
config.plugins.forEach(function(item) {
|
||||
|
||||
// {}
|
||||
if (typeof item === 'object') {
|
||||
|
||||
key = Object.keys(item)[0];
|
||||
|
||||
if (item[key] == null) {
|
||||
console.error(`Error: '${key}' plugin is misconfigured! Have you padded its content in YML properly?\n`);
|
||||
}
|
||||
|
||||
// custom
|
||||
if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
|
||||
defaults.plugins.push(setupCustomPlugin(key, item[key]));
|
||||
|
||||
// plugin defined via path
|
||||
} else if (typeof item[key] === 'object' && item[key].path) {
|
||||
defaults.plugins.push(setPluginActiveState(loadPlugin(config, undefined, item[key].path), item, key));
|
||||
|
||||
} else {
|
||||
defaults.plugins.forEach(function(plugin) {
|
||||
|
||||
if (plugin.name === key) {
|
||||
plugin = setPluginActiveState(plugin, item, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
defaults.multipass = config.multipass;
|
||||
|
||||
// svg2js
|
||||
if (config.svg2js) {
|
||||
defaults.svg2js = config.svg2js;
|
||||
}
|
||||
|
||||
// js2svg
|
||||
if (config.js2svg) {
|
||||
defaults.js2svg = config.js2svg;
|
||||
}
|
||||
|
||||
return defaults;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and enable a custom plugin
|
||||
*
|
||||
* @param {String} plugin name
|
||||
* @param {Object} custom plugin
|
||||
* @return {Array} enabled plugin
|
||||
*/
|
||||
function setupCustomPlugin(name, plugin) {
|
||||
plugin.active = true;
|
||||
plugin.params = Object.assign({}, plugin.params || {});
|
||||
plugin.name = name;
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to group sequential elements of plugins array.
|
||||
*
|
||||
* @param {Object} plugins input plugins
|
||||
* @return {Array} output plugins
|
||||
*/
|
||||
function optimizePluginsArray(plugins) {
|
||||
|
||||
var prev;
|
||||
|
||||
return plugins.reduce(function(plugins, item) {
|
||||
if (prev && item.type == prev[0].type) {
|
||||
prev.push(item);
|
||||
} else {
|
||||
plugins.push(prev = [item]);
|
||||
}
|
||||
return plugins;
|
||||
}, []);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets plugin to active or inactive state.
|
||||
*
|
||||
* @param {Object} plugin
|
||||
* @param {Object} item
|
||||
* @param {Object} key
|
||||
* @return {Object} plugin
|
||||
*/
|
||||
function setPluginActiveState(plugin, item, key) {
|
||||
// name: {}
|
||||
if (typeof item[key] === 'object') {
|
||||
plugin.params = Object.assign({}, plugin.params || {}, item[key]);
|
||||
plugin.active = true;
|
||||
|
||||
// name: false
|
||||
} else if (item[key] === false) {
|
||||
plugin.active = false;
|
||||
|
||||
// name: true
|
||||
} else if (item[key] === true) {
|
||||
plugin.active = true;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads default plugin using name or custom plugin defined via path in config.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Object} name
|
||||
* @param {Object} path
|
||||
* @return {Object} plugin
|
||||
*/
|
||||
function loadPlugin(config, name, path) {
|
||||
var plugin;
|
||||
|
||||
if (!path) {
|
||||
plugin = require('../../plugins/' + name);
|
||||
} else {
|
||||
plugin = require(PATH.resolve(config.__DIR, path));
|
||||
}
|
||||
|
||||
return Object.assign({}, plugin);
|
||||
}
|
138
web/node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
Normal file
138
web/node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
'use strict';
|
||||
|
||||
var values = require('object.values');
|
||||
if (!Object.values) {
|
||||
values.shim();
|
||||
}
|
||||
|
||||
|
||||
var CSSClassList = function(node) {
|
||||
this.parentNode = node;
|
||||
this.classNames = new Set();
|
||||
this.classAttr = null;
|
||||
//this.classValue = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a deep clone of this object.
|
||||
*
|
||||
* @param parentNode the parentNode to assign to the cloned result
|
||||
*/
|
||||
CSSClassList.prototype.clone = function(parentNode) {
|
||||
var node = this;
|
||||
var nodeData = {};
|
||||
|
||||
Object.keys(node).forEach(function(key) {
|
||||
if (key !== 'parentNode') {
|
||||
nodeData[key] = node[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Deep-clone node data.
|
||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||
|
||||
var clone = new CSSClassList(parentNode);
|
||||
Object.assign(clone, nodeData);
|
||||
return clone;
|
||||
};
|
||||
|
||||
CSSClassList.prototype.hasClass = function() {
|
||||
this.classAttr = { // empty class attr
|
||||
'name': 'class',
|
||||
'value': null
|
||||
};
|
||||
|
||||
this.addClassHandler();
|
||||
};
|
||||
|
||||
|
||||
// attr.class
|
||||
|
||||
CSSClassList.prototype.addClassHandler = function() {
|
||||
|
||||
Object.defineProperty(this.parentNode.attrs, 'class', {
|
||||
get: this.getClassAttr.bind(this),
|
||||
set: this.setClassAttr.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
this.addClassValueHandler();
|
||||
};
|
||||
|
||||
// attr.class.value
|
||||
|
||||
CSSClassList.prototype.addClassValueHandler = function() {
|
||||
|
||||
Object.defineProperty(this.classAttr, 'value', {
|
||||
get: this.getClassValue.bind(this),
|
||||
set: this.setClassValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
CSSClassList.prototype.getClassAttr = function() {
|
||||
return this.classAttr;
|
||||
};
|
||||
|
||||
CSSClassList.prototype.setClassAttr = function(newClassAttr) {
|
||||
this.setClassValue(newClassAttr.value); // must before applying value handler!
|
||||
|
||||
this.classAttr = newClassAttr;
|
||||
this.addClassValueHandler();
|
||||
};
|
||||
|
||||
CSSClassList.prototype.getClassValue = function() {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames.join(' ');
|
||||
};
|
||||
|
||||
CSSClassList.prototype.setClassValue = function(newValue) {
|
||||
if(typeof newValue === 'undefined') {
|
||||
this.classNames.clear();
|
||||
return;
|
||||
}
|
||||
var arrClassNames = newValue.split(' ');
|
||||
this.classNames = new Set(arrClassNames);
|
||||
};
|
||||
|
||||
|
||||
CSSClassList.prototype.add = function(/* variadic */) {
|
||||
this.hasClass();
|
||||
Object.values(arguments).forEach(this._addSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._addSingle = function(className) {
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
|
||||
CSSClassList.prototype.remove = function(/* variadic */) {
|
||||
this.hasClass();
|
||||
Object.values(arguments).forEach(this._removeSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._removeSingle = function(className) {
|
||||
this.classNames.delete(className);
|
||||
};
|
||||
|
||||
|
||||
CSSClassList.prototype.item = function(index) {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames[index];
|
||||
};
|
||||
|
||||
CSSClassList.prototype.toggle = function(className, force) {
|
||||
if(this.contains(className) || force === false) {
|
||||
this.classNames.delete(className);
|
||||
}
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.contains = function(className) {
|
||||
return this.classNames.has(className);
|
||||
};
|
||||
|
||||
|
||||
module.exports = CSSClassList;
|
53
web/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
53
web/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
var baseCssAdapter = require('css-select-base-adapter');
|
||||
|
||||
/**
|
||||
* DOMUtils API for SVGO AST (used by css-select)
|
||||
*/
|
||||
var svgoCssSelectAdapterMin = {
|
||||
|
||||
// is the node a tag?
|
||||
// isTag: ( node:Node ) => isTag:Boolean
|
||||
isTag: function(node) {
|
||||
return node.isElem();
|
||||
},
|
||||
|
||||
// get the parent of the node
|
||||
// getParent: ( node:Node ) => parentNode:Node
|
||||
// returns null when no parent exists
|
||||
getParent: function(node) {
|
||||
return node.parentNode || null;
|
||||
},
|
||||
|
||||
// get the node's children
|
||||
// getChildren: ( node:Node ) => children:[Node]
|
||||
getChildren: function(node) {
|
||||
return node.content || [];
|
||||
},
|
||||
|
||||
// get the name of the tag
|
||||
// getName: ( elem:ElementNode ) => tagName:String
|
||||
getName: function(elemAst) {
|
||||
return elemAst.elem;
|
||||
},
|
||||
|
||||
// get the text content of the node, and its children if it has any
|
||||
// getText: ( node:Node ) => text:String
|
||||
// returns empty string when there is no text
|
||||
getText: function(node) {
|
||||
return node.content[0].text || node.content[0].cdata || '';
|
||||
},
|
||||
|
||||
// get the attribute value
|
||||
// getAttributeValue: ( elem:ElementNode, name:String ) => value:String
|
||||
// returns null when attribute doesn't exist
|
||||
getAttributeValue: function(elem, name) {
|
||||
return elem.hasAttr(name) ? elem.attr(name).value : null;
|
||||
}
|
||||
};
|
||||
|
||||
// use base adapter for default implementation
|
||||
var svgoCssSelectAdapter = baseCssAdapter(svgoCssSelectAdapterMin);
|
||||
|
||||
module.exports = svgoCssSelectAdapter;
|
285
web/node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
Normal file
285
web/node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
Normal file
|
@ -0,0 +1,285 @@
|
|||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
csstools = require('../css-tools');
|
||||
|
||||
|
||||
var CSSStyleDeclaration = function(node) {
|
||||
this.parentNode = node;
|
||||
|
||||
this.properties = new Map();
|
||||
this.hasSynced = false;
|
||||
|
||||
this.styleAttr = null;
|
||||
this.styleValue = null;
|
||||
|
||||
this.parseError = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a deep clone of this object.
|
||||
*
|
||||
* @param parentNode the parentNode to assign to the cloned result
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.clone = function(parentNode) {
|
||||
var node = this;
|
||||
var nodeData = {};
|
||||
|
||||
Object.keys(node).forEach(function(key) {
|
||||
if (key !== 'parentNode') {
|
||||
nodeData[key] = node[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Deep-clone node data.
|
||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||
|
||||
var clone = new CSSStyleDeclaration(parentNode);
|
||||
Object.assign(clone, nodeData);
|
||||
return clone;
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.hasStyle = function() {
|
||||
this.addStyleHandler();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// attr.style
|
||||
|
||||
CSSStyleDeclaration.prototype.addStyleHandler = function() {
|
||||
|
||||
this.styleAttr = { // empty style attr
|
||||
'name': 'style',
|
||||
'value': null
|
||||
};
|
||||
|
||||
Object.defineProperty(this.parentNode.attrs, 'style', {
|
||||
get: this.getStyleAttr.bind(this),
|
||||
set: this.setStyleAttr.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
this.addStyleValueHandler();
|
||||
};
|
||||
|
||||
// attr.style.value
|
||||
|
||||
CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
|
||||
|
||||
Object.defineProperty(this.styleAttr, 'value', {
|
||||
get: this.getStyleValue.bind(this),
|
||||
set: this.setStyleValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.getStyleAttr = function() {
|
||||
return this.styleAttr;
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
|
||||
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
|
||||
|
||||
this.styleAttr = newStyleAttr;
|
||||
this.addStyleValueHandler();
|
||||
this.hasSynced = false; // raw css changed
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.getStyleValue = function() {
|
||||
return this.getCssText();
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
|
||||
this.properties.clear(); // reset all existing properties
|
||||
this.styleValue = newValue;
|
||||
this.hasSynced = false; // raw css changed
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
CSSStyleDeclaration.prototype._loadCssText = function() {
|
||||
if (this.hasSynced) {
|
||||
return;
|
||||
}
|
||||
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
||||
|
||||
if (!this.styleValue || this.styleValue.length === 0) {
|
||||
return;
|
||||
}
|
||||
var inlineCssStr = this.styleValue;
|
||||
|
||||
var declarations = {};
|
||||
try {
|
||||
declarations = csstree.parse(inlineCssStr, {
|
||||
context: 'declarationList',
|
||||
parseValue: false
|
||||
});
|
||||
} catch (parseError) {
|
||||
this.parseError = parseError;
|
||||
return;
|
||||
}
|
||||
this.parseError = false;
|
||||
|
||||
var self = this;
|
||||
declarations.children.each(function(declaration) {
|
||||
try {
|
||||
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
|
||||
self.setProperty(styleDeclaration.name, styleDeclaration.value, styleDeclaration.priority);
|
||||
} catch(styleError) {
|
||||
if(styleError.message !== 'Unknown node type: undefined') {
|
||||
self.parseError = styleError;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// only reads from properties
|
||||
|
||||
/**
|
||||
* Get the textual representation of the declaration block (equivalent to .cssText attribute).
|
||||
*
|
||||
* @return {String} Textual representation of the declaration block (empty string for no properties)
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getCssText = function() {
|
||||
var properties = this.getProperties();
|
||||
|
||||
if (this.parseError) {
|
||||
// in case of a parse error, pass through original styles
|
||||
return this.styleValue;
|
||||
}
|
||||
|
||||
var cssText = [];
|
||||
properties.forEach(function(property, propertyName) {
|
||||
var strImportant = property.priority === 'important' ? '!important' : '';
|
||||
cssText.push(propertyName.trim() + ':' + property.value.trim() + strImportant);
|
||||
});
|
||||
return cssText.join(';');
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._handleParseError = function() {
|
||||
if (this.parseError) {
|
||||
console.warn('Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr(\'style\').value. Error details: ' + this.parseError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
|
||||
if(typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = properties.get(propertyName.trim());
|
||||
return property;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the optional priority, "important".
|
||||
*
|
||||
* @param {String} propertyName representing the property name to be checked.
|
||||
* @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.priority : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the property value given a property name.
|
||||
*
|
||||
* @param {String} propertyName representing the property name to be checked.
|
||||
* @return {String} value containing the value of the property. If not set, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.value : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a property name.
|
||||
*
|
||||
* @param {Number} index of the node to be fetched. The index is zero-based.
|
||||
* @return {String} propertyName that is the name of the CSS property at the specified index.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.item = function(index) {
|
||||
if(typeof index === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
return Array.from(properties.keys())[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all properties of the node.
|
||||
*
|
||||
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getProperties = function() {
|
||||
this._loadCssText();
|
||||
return this.properties;
|
||||
};
|
||||
|
||||
|
||||
// writes to properties
|
||||
|
||||
/**
|
||||
* Remove a property from the CSS declaration block.
|
||||
*
|
||||
* @param {String} propertyName representing the property name to be removed.
|
||||
* @return {String} oldValue equal to the value of the CSS property before it was removed.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
|
||||
if(typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
this.hasStyle();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var oldValue = this.getPropertyValue(propertyName);
|
||||
properties.delete(propertyName.trim());
|
||||
return oldValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify an existing CSS property or creates a new CSS property in the declaration block.
|
||||
*
|
||||
* @param {String} propertyName representing the CSS property name to be modified.
|
||||
* @param {String} [value] containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter.
|
||||
* @param {String} [priority] allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
|
||||
* @return {undefined}
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priority) {
|
||||
if(typeof propertyName === 'undefined') {
|
||||
throw Error('propertyName argument required, but only not present.');
|
||||
}
|
||||
|
||||
this.hasStyle();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = {
|
||||
value: value.trim(),
|
||||
priority: priority.trim()
|
||||
};
|
||||
properties.set(propertyName.trim(), property);
|
||||
|
||||
return property;
|
||||
};
|
||||
|
||||
|
||||
module.exports = CSSStyleDeclaration;
|
348
web/node_modules/svgo/lib/svgo/js2svg.js
generated
vendored
Normal file
348
web/node_modules/svgo/lib/svgo/js2svg.js
generated
vendored
Normal file
|
@ -0,0 +1,348 @@
|
|||
'use strict';
|
||||
|
||||
var EOL = require('os').EOL,
|
||||
textElem = require('../../plugins/_collections.js').elemsGroups.textContent.concat('title');
|
||||
|
||||
var defaults = {
|
||||
doctypeStart: '<!DOCTYPE',
|
||||
doctypeEnd: '>',
|
||||
procInstStart: '<?',
|
||||
procInstEnd: '?>',
|
||||
tagOpenStart: '<',
|
||||
tagOpenEnd: '>',
|
||||
tagCloseStart: '</',
|
||||
tagCloseEnd: '>',
|
||||
tagShortStart: '<',
|
||||
tagShortEnd: '/>',
|
||||
attrStart: '="',
|
||||
attrEnd: '"',
|
||||
commentStart: '<!--',
|
||||
commentEnd: '-->',
|
||||
cdataStart: '<![CDATA[',
|
||||
cdataEnd: ']]>',
|
||||
textStart: '',
|
||||
textEnd: '',
|
||||
indent: 4,
|
||||
regEntities: /[&'"<>]/g,
|
||||
regValEntities: /[&"<>]/g,
|
||||
encodeEntity: encodeEntity,
|
||||
pretty: false,
|
||||
useShortTags: true
|
||||
};
|
||||
|
||||
var entities = {
|
||||
'&': '&',
|
||||
'\'': ''',
|
||||
'"': '"',
|
||||
'>': '>',
|
||||
'<': '<',
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert SVG-as-JS object to SVG (XML) string.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} config config
|
||||
*
|
||||
* @return {Object} output data
|
||||
*/
|
||||
module.exports = function(data, config) {
|
||||
|
||||
return new JS2SVG(config).convert(data);
|
||||
|
||||
};
|
||||
|
||||
function JS2SVG(config) {
|
||||
|
||||
if (config) {
|
||||
this.config = Object.assign({}, defaults, config);
|
||||
} else {
|
||||
this.config = Object.assign({}, defaults);
|
||||
}
|
||||
|
||||
var indent = this.config.indent;
|
||||
if (typeof indent == 'number' && !isNaN(indent)) {
|
||||
this.config.indent = (indent < 0) ? '\t' : ' '.repeat(indent);
|
||||
} else if (typeof indent != 'string') {
|
||||
this.config.indent = ' ';
|
||||
}
|
||||
|
||||
if (this.config.pretty) {
|
||||
this.config.doctypeEnd += EOL;
|
||||
this.config.procInstEnd += EOL;
|
||||
this.config.commentEnd += EOL;
|
||||
this.config.cdataEnd += EOL;
|
||||
this.config.tagShortEnd += EOL;
|
||||
this.config.tagOpenEnd += EOL;
|
||||
this.config.tagCloseEnd += EOL;
|
||||
this.config.textEnd += EOL;
|
||||
}
|
||||
|
||||
this.indentLevel = 0;
|
||||
this.textContext = null;
|
||||
|
||||
}
|
||||
|
||||
function encodeEntity(char) {
|
||||
return entities[char];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start conversion.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.convert = function(data) {
|
||||
|
||||
var svg = '';
|
||||
|
||||
if (data.content) {
|
||||
|
||||
this.indentLevel++;
|
||||
|
||||
data.content.forEach(function(item) {
|
||||
|
||||
if (item.elem) {
|
||||
svg += this.createElem(item);
|
||||
} else if (item.text) {
|
||||
svg += this.createText(item.text);
|
||||
} else if (item.doctype) {
|
||||
svg += this.createDoctype(item.doctype);
|
||||
} else if (item.processinginstruction) {
|
||||
svg += this.createProcInst(item.processinginstruction);
|
||||
} else if (item.comment) {
|
||||
svg += this.createComment(item.comment);
|
||||
} else if (item.cdata) {
|
||||
svg += this.createCDATA(item.cdata);
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
}
|
||||
|
||||
this.indentLevel--;
|
||||
|
||||
return {
|
||||
data: svg,
|
||||
info: {
|
||||
width: this.width,
|
||||
height: this.height
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create indent string in accordance with the current node level.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createIndent = function() {
|
||||
|
||||
var indent = '';
|
||||
|
||||
if (this.config.pretty && !this.textContext) {
|
||||
indent = this.config.indent.repeat(this.indentLevel - 1);
|
||||
}
|
||||
|
||||
return indent;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create doctype tag.
|
||||
*
|
||||
* @param {String} doctype doctype body string
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createDoctype = function(doctype) {
|
||||
|
||||
return this.config.doctypeStart +
|
||||
doctype +
|
||||
this.config.doctypeEnd;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create XML Processing Instruction tag.
|
||||
*
|
||||
* @param {Object} instruction instruction object
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createProcInst = function(instruction) {
|
||||
|
||||
return this.config.procInstStart +
|
||||
instruction.name +
|
||||
' ' +
|
||||
instruction.body +
|
||||
this.config.procInstEnd;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create comment tag.
|
||||
*
|
||||
* @param {String} comment comment body
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createComment = function(comment) {
|
||||
|
||||
return this.config.commentStart +
|
||||
comment +
|
||||
this.config.commentEnd;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create CDATA section.
|
||||
*
|
||||
* @param {String} cdata CDATA body
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createCDATA = function(cdata) {
|
||||
|
||||
return this.createIndent() +
|
||||
this.config.cdataStart +
|
||||
cdata +
|
||||
this.config.cdataEnd;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create element tag.
|
||||
*
|
||||
* @param {Object} data element object
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createElem = function(data) {
|
||||
|
||||
// beautiful injection for obtaining SVG information :)
|
||||
if (
|
||||
data.isElem('svg') &&
|
||||
data.hasAttr('width') &&
|
||||
data.hasAttr('height')
|
||||
) {
|
||||
this.width = data.attr('width').value;
|
||||
this.height = data.attr('height').value;
|
||||
}
|
||||
|
||||
// empty element and short tag
|
||||
if (data.isEmpty()) {
|
||||
if (this.config.useShortTags) {
|
||||
return this.createIndent() +
|
||||
this.config.tagShortStart +
|
||||
data.elem +
|
||||
this.createAttrs(data) +
|
||||
this.config.tagShortEnd;
|
||||
} else {
|
||||
return this.createIndent() +
|
||||
this.config.tagShortStart +
|
||||
data.elem +
|
||||
this.createAttrs(data) +
|
||||
this.config.tagOpenEnd +
|
||||
this.config.tagCloseStart +
|
||||
data.elem +
|
||||
this.config.tagCloseEnd;
|
||||
}
|
||||
// non-empty element
|
||||
} else {
|
||||
var tagOpenStart = this.config.tagOpenStart,
|
||||
tagOpenEnd = this.config.tagOpenEnd,
|
||||
tagCloseStart = this.config.tagCloseStart,
|
||||
tagCloseEnd = this.config.tagCloseEnd,
|
||||
openIndent = this.createIndent(),
|
||||
textIndent = '',
|
||||
processedData = '',
|
||||
dataEnd = '';
|
||||
|
||||
if (this.textContext) {
|
||||
tagOpenStart = defaults.tagOpenStart;
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
tagCloseEnd = defaults.tagCloseEnd;
|
||||
openIndent = '';
|
||||
} else if (data.isElem(textElem)) {
|
||||
if (this.config.pretty) {
|
||||
textIndent += openIndent + this.config.indent;
|
||||
}
|
||||
this.textContext = data;
|
||||
}
|
||||
|
||||
processedData += this.convert(data).data;
|
||||
|
||||
if (this.textContext == data) {
|
||||
this.textContext = null;
|
||||
if (this.config.pretty) dataEnd = EOL;
|
||||
}
|
||||
|
||||
return openIndent +
|
||||
tagOpenStart +
|
||||
data.elem +
|
||||
this.createAttrs(data) +
|
||||
tagOpenEnd +
|
||||
textIndent +
|
||||
processedData +
|
||||
dataEnd +
|
||||
this.createIndent() +
|
||||
tagCloseStart +
|
||||
data.elem +
|
||||
tagCloseEnd;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create element attributes.
|
||||
*
|
||||
* @param {Object} elem attributes object
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createAttrs = function(elem) {
|
||||
|
||||
var attrs = '';
|
||||
|
||||
elem.eachAttr(function(attr) {
|
||||
|
||||
if (attr.value !== undefined) {
|
||||
attrs += ' ' +
|
||||
attr.name +
|
||||
this.config.attrStart +
|
||||
String(attr.value).replace(this.config.regValEntities, this.config.encodeEntity) +
|
||||
this.config.attrEnd;
|
||||
}
|
||||
else {
|
||||
attrs += ' ' +
|
||||
attr.name;
|
||||
}
|
||||
|
||||
|
||||
}, this);
|
||||
|
||||
return attrs;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create text node.
|
||||
*
|
||||
* @param {String} text text
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
JS2SVG.prototype.createText = function(text) {
|
||||
|
||||
return this.createIndent() +
|
||||
this.config.textStart +
|
||||
text.replace(this.config.regEntities, this.config.encodeEntity) +
|
||||
(this.textContext ? '' : this.config.textEnd);
|
||||
|
||||
};
|
372
web/node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
Normal file
372
web/node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
Normal file
|
@ -0,0 +1,372 @@
|
|||
'use strict';
|
||||
|
||||
var cssSelect = require('css-select');
|
||||
|
||||
var svgoCssSelectAdapter = require('./css-select-adapter');
|
||||
var cssSelectOpts = {
|
||||
xmlMode: true,
|
||||
adapter: svgoCssSelectAdapter
|
||||
};
|
||||
|
||||
var JSAPI = module.exports = function(data, parentNode) {
|
||||
Object.assign(this, data);
|
||||
if (parentNode) {
|
||||
Object.defineProperty(this, 'parentNode', {
|
||||
writable: true,
|
||||
value: parentNode
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a deep clone of this node.
|
||||
*
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.clone = function() {
|
||||
var node = this;
|
||||
var nodeData = {};
|
||||
|
||||
Object.keys(node).forEach(function(key) {
|
||||
if (key !== 'class' && key !== 'style' && key !== 'content') {
|
||||
nodeData[key] = node[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Deep-clone node data.
|
||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||
|
||||
// parentNode gets set to a proper object by the parent clone,
|
||||
// but it needs to be true/false now to do the right thing
|
||||
// in the constructor.
|
||||
var clonedNode = new JSAPI(nodeData, !!node.parentNode);
|
||||
|
||||
if (node.class) {
|
||||
clonedNode.class = node.class.clone(clonedNode);
|
||||
}
|
||||
if (node.style) {
|
||||
clonedNode.style = node.style.clone(clonedNode);
|
||||
}
|
||||
if (node.content) {
|
||||
clonedNode.content = node.content.map(function(childNode) {
|
||||
var clonedChild = childNode.clone();
|
||||
clonedChild.parentNode = clonedNode;
|
||||
return clonedChild;
|
||||
});
|
||||
}
|
||||
|
||||
return clonedNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if item is an element
|
||||
* (any, with a specific name or in a names array).
|
||||
*
|
||||
* @param {String|Array} [param] element name or names arrays
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isElem = function(param) {
|
||||
|
||||
if (!param) return !!this.elem;
|
||||
|
||||
if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
|
||||
|
||||
return !!this.elem && this.elem === param;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Renames an element
|
||||
*
|
||||
* @param {String} name new element name
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.renameElem = function(name) {
|
||||
|
||||
if (name && typeof name === 'string')
|
||||
this.elem = this.local = name;
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element is empty.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isEmpty = function() {
|
||||
|
||||
return !this.content || !this.content.length;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the closest ancestor of the current element.
|
||||
* @param elemName
|
||||
*
|
||||
* @return {?Object}
|
||||
*/
|
||||
JSAPI.prototype.closestElem = function(elemName) {
|
||||
var elem = this;
|
||||
|
||||
while ((elem = elem.parentNode) && !elem.isElem(elemName));
|
||||
|
||||
return elem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes content by removing elements and/or adding new elements.
|
||||
*
|
||||
* @param {Number} start Index at which to start changing the content.
|
||||
* @param {Number} n Number of elements to remove.
|
||||
* @param {Array|Object} [insertion] Elements to add to the content.
|
||||
* @return {Array} Removed elements.
|
||||
*/
|
||||
JSAPI.prototype.spliceContent = function(start, n, insertion) {
|
||||
|
||||
if (arguments.length < 2) return [];
|
||||
|
||||
if (!Array.isArray(insertion))
|
||||
insertion = Array.apply(null, arguments).slice(2);
|
||||
|
||||
insertion.forEach(function(inner) { inner.parentNode = this }, this);
|
||||
|
||||
return this.content.splice.apply(this.content, [start, n].concat(insertion));
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [name] attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttr = function(name, val) {
|
||||
|
||||
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||
|
||||
if (!arguments.length) return !!this.attrs;
|
||||
|
||||
if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString();
|
||||
|
||||
return !!this.attrs[name];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute by local name
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [localName] local attribute name
|
||||
* @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttrLocal = function(localName, val) {
|
||||
|
||||
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||
|
||||
if (!arguments.length) return !!this.attrs;
|
||||
|
||||
var callback;
|
||||
|
||||
switch (val != null && val.constructor && val.constructor.name) {
|
||||
case 'Number': // same as String
|
||||
case 'String': callback = stringValueTest; break;
|
||||
case 'RegExp': callback = regexpValueTest; break;
|
||||
case 'Function': callback = funcValueTest; break;
|
||||
default: callback = nameTest;
|
||||
}
|
||||
return this.someAttr(callback);
|
||||
|
||||
function nameTest(attr) {
|
||||
return attr.local === localName;
|
||||
}
|
||||
|
||||
function stringValueTest(attr) {
|
||||
return attr.local === localName && val == attr.value;
|
||||
}
|
||||
|
||||
function regexpValueTest(attr) {
|
||||
return attr.local === localName && val.test(attr.value);
|
||||
}
|
||||
|
||||
function funcValueTest(attr) {
|
||||
return attr.local === localName && val(attr.value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific attribute from an element
|
||||
* (by name or name + value).
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.attr = function(name, val) {
|
||||
|
||||
if (!this.hasAttr() || !arguments.length) return undefined;
|
||||
|
||||
if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined;
|
||||
|
||||
return this.attrs[name];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get computed attribute value from an element
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.computedAttr = function(name, val) {
|
||||
/* jshint eqnull: true */
|
||||
if (!arguments.length) return;
|
||||
|
||||
for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode);
|
||||
|
||||
if (val != null) {
|
||||
return elem ? elem.hasAttr(name, val) : false;
|
||||
} else if (elem && elem.hasAttr(name)) {
|
||||
return elem.attrs[name].value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a specific attribute.
|
||||
*
|
||||
* @param {String|Array} name attribute name
|
||||
* @param {String} [val] attribute value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.removeAttr = function(name, val, recursive) {
|
||||
|
||||
if (!arguments.length) return false;
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(this.removeAttr, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.hasAttr(name)) return false;
|
||||
|
||||
if (!recursive && val && this.attrs[name].value !== val) return false;
|
||||
|
||||
delete this.attrs[name];
|
||||
|
||||
if (!Object.keys(this.attrs).length) delete this.attrs;
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Add attribute.
|
||||
*
|
||||
* @param {Object} [attr={}] attribute object
|
||||
* @return {Object|Boolean} created attribute or false if no attr was passed in
|
||||
*/
|
||||
JSAPI.prototype.addAttr = function(attr) {
|
||||
attr = attr || {};
|
||||
|
||||
if (attr.name === undefined ||
|
||||
attr.prefix === undefined ||
|
||||
attr.local === undefined
|
||||
) return false;
|
||||
|
||||
this.attrs = this.attrs || {};
|
||||
this.attrs[attr.name] = attr;
|
||||
|
||||
if(attr.name === 'class') { // newly added class attribute
|
||||
this.class.hasClass();
|
||||
}
|
||||
|
||||
if(attr.name === 'style') { // newly added style attribute
|
||||
this.style.hasStyle();
|
||||
}
|
||||
|
||||
return this.attrs[attr.name];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates over all attributes.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.eachAttr = function(callback, context) {
|
||||
|
||||
if (!this.hasAttr()) return false;
|
||||
|
||||
for (var name in this.attrs) {
|
||||
callback.call(context, this.attrs[name]);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests whether some attribute passes the test.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.someAttr = function(callback, context) {
|
||||
|
||||
if (!this.hasAttr()) return false;
|
||||
|
||||
for (var name in this.attrs) {
|
||||
if (callback.call(context, this.attrs[name])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns matched elements.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no elements matched
|
||||
*/
|
||||
JSAPI.prototype.querySelectorAll = function(selectors) {
|
||||
|
||||
var matchedEls = cssSelect(selectors, this, cssSelectOpts);
|
||||
|
||||
return matchedEls.length > 0 ? matchedEls : null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns only the first matched element.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no element matched
|
||||
*/
|
||||
JSAPI.prototype.querySelector = function(selectors) {
|
||||
|
||||
return cssSelect.selectOne(selectors, this, cssSelectOpts);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if a selector matches a given element.
|
||||
*
|
||||
* @param {String} selector CSS selector string
|
||||
* @return {Boolean} true if element would be selected by selector string, false if it does not
|
||||
*/
|
||||
JSAPI.prototype.matches = function(selector) {
|
||||
|
||||
return cssSelect.is(this, selector, cssSelectOpts);
|
||||
|
||||
};
|
101
web/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
101
web/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Plugins engine.
|
||||
*
|
||||
* @module plugins
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} info extra information
|
||||
* @param {Object} plugins plugins object from config
|
||||
* @return {Object} output data
|
||||
*/
|
||||
module.exports = function(data, info, plugins) {
|
||||
|
||||
plugins.forEach(function(group) {
|
||||
|
||||
switch(group[0].type) {
|
||||
case 'perItem':
|
||||
data = perItem(data, info, group);
|
||||
break;
|
||||
case 'perItemReverse':
|
||||
data = perItem(data, info, group, true);
|
||||
break;
|
||||
case 'full':
|
||||
data = full(data, info, group);
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return data;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Direct or reverse per-item loop.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins list to process
|
||||
* @param {Boolean} [reverse] reverse pass?
|
||||
* @return {Object} output data
|
||||
*/
|
||||
function perItem(data, info, plugins, reverse) {
|
||||
|
||||
function monkeys(items) {
|
||||
|
||||
items.content = items.content.filter(function(item) {
|
||||
|
||||
// reverse pass
|
||||
if (reverse && item.content) {
|
||||
monkeys(item);
|
||||
}
|
||||
|
||||
// main filter
|
||||
var filter = true;
|
||||
|
||||
for (var i = 0; filter && i < plugins.length; i++) {
|
||||
var plugin = plugins[i];
|
||||
|
||||
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
|
||||
filter = false;
|
||||
}
|
||||
}
|
||||
|
||||
// direct pass
|
||||
if (!reverse && item.content) {
|
||||
monkeys(item);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
});
|
||||
|
||||
return items;
|
||||
|
||||
}
|
||||
|
||||
return monkeys(data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* "Full" plugins.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins list to process
|
||||
* @return {Object} output data
|
||||
*/
|
||||
function full(data, info, plugins) {
|
||||
|
||||
plugins.forEach(function(plugin) {
|
||||
if (plugin.active) {
|
||||
data = plugin.fn(data, plugin.params, info);
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
|
||||
}
|
200
web/node_modules/svgo/lib/svgo/svg2js.js
generated
vendored
Normal file
200
web/node_modules/svgo/lib/svgo/svg2js.js
generated
vendored
Normal file
|
@ -0,0 +1,200 @@
|
|||
'use strict';
|
||||
|
||||
var SAX = require('sax'),
|
||||
JSAPI = require('./jsAPI.js'),
|
||||
CSSClassList = require('./css-class-list'),
|
||||
CSSStyleDeclaration = require('./css-style-declaration'),
|
||||
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^\']+)'|"([^\"]+)")\s*>/g;
|
||||
|
||||
var config = {
|
||||
strict: true,
|
||||
trim: false,
|
||||
normalize: true,
|
||||
lowercase: true,
|
||||
xmlns: true,
|
||||
position: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert SVG (XML) string to SVG-as-JS object.
|
||||
*
|
||||
* @param {String} data input data
|
||||
* @param {Function} callback
|
||||
*/
|
||||
module.exports = function(data, callback) {
|
||||
|
||||
var sax = SAX.parser(config.strict, config),
|
||||
root = new JSAPI({ elem: '#document', content: [] }),
|
||||
current = root,
|
||||
stack = [root],
|
||||
textContext = null,
|
||||
parsingError = false;
|
||||
|
||||
function pushToContent(content) {
|
||||
|
||||
content = new JSAPI(content, current);
|
||||
|
||||
(current.content = current.content || []).push(content);
|
||||
|
||||
return content;
|
||||
|
||||
}
|
||||
|
||||
sax.ondoctype = function(doctype) {
|
||||
|
||||
pushToContent({
|
||||
doctype: doctype
|
||||
});
|
||||
|
||||
var subsetStart = doctype.indexOf('['),
|
||||
entityMatch;
|
||||
|
||||
if (subsetStart >= 0) {
|
||||
entityDeclaration.lastIndex = subsetStart;
|
||||
|
||||
while ((entityMatch = entityDeclaration.exec(data)) != null) {
|
||||
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sax.onprocessinginstruction = function(data) {
|
||||
|
||||
pushToContent({
|
||||
processinginstruction: data
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.oncomment = function(comment) {
|
||||
|
||||
pushToContent({
|
||||
comment: comment.trim()
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.oncdata = function(cdata) {
|
||||
|
||||
pushToContent({
|
||||
cdata: cdata
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.onopentag = function(data) {
|
||||
|
||||
var elem = {
|
||||
elem: data.name,
|
||||
prefix: data.prefix,
|
||||
local: data.local,
|
||||
attrs: {}
|
||||
};
|
||||
|
||||
elem.class = new CSSClassList(elem);
|
||||
elem.style = new CSSStyleDeclaration(elem);
|
||||
|
||||
if (Object.keys(data.attributes).length) {
|
||||
for (var name in data.attributes) {
|
||||
|
||||
if (name === 'class') { // has class attribute
|
||||
elem.class.hasClass();
|
||||
}
|
||||
|
||||
if (name === 'style') { // has style attribute
|
||||
elem.style.hasStyle();
|
||||
}
|
||||
|
||||
elem.attrs[name] = {
|
||||
name: name,
|
||||
value: data.attributes[name].value,
|
||||
prefix: data.attributes[name].prefix,
|
||||
local: data.attributes[name].local
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
elem = pushToContent(elem);
|
||||
current = elem;
|
||||
|
||||
// Save info about <text> tag to prevent trimming of meaningful whitespace
|
||||
if (data.name == 'text' && !data.prefix) {
|
||||
textContext = current;
|
||||
}
|
||||
|
||||
stack.push(elem);
|
||||
|
||||
};
|
||||
|
||||
sax.ontext = function(text) {
|
||||
|
||||
if (/\S/.test(text) || textContext) {
|
||||
|
||||
if (!textContext)
|
||||
text = text.trim();
|
||||
|
||||
pushToContent({
|
||||
text: text
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
sax.onclosetag = function() {
|
||||
|
||||
var last = stack.pop();
|
||||
|
||||
// Trim text inside <text> tag.
|
||||
if (last == textContext) {
|
||||
trim(textContext);
|
||||
textContext = null;
|
||||
}
|
||||
current = stack[stack.length - 1];
|
||||
|
||||
};
|
||||
|
||||
sax.onerror = function(e) {
|
||||
|
||||
e.message = 'Error in parsing SVG: ' + e.message;
|
||||
if (e.message.indexOf('Unexpected end') < 0) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
sax.onend = function() {
|
||||
|
||||
if (!this.error) {
|
||||
callback(root);
|
||||
} else {
|
||||
callback({ error: this.error.message });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
try {
|
||||
sax.write(data);
|
||||
} catch (e) {
|
||||
callback({ error: e.message });
|
||||
parsingError = true;
|
||||
}
|
||||
if (!parsingError) sax.close();
|
||||
|
||||
function trim(elem) {
|
||||
if (!elem.content) return elem;
|
||||
|
||||
var start = elem.content[0],
|
||||
end = elem.content[elem.content.length - 1];
|
||||
|
||||
while (start && start.content && !start.text) start = start.content[0];
|
||||
if (start && start.text) start.text = start.text.replace(/^\s+/, '');
|
||||
|
||||
while (end && end.content && !end.text) end = end.content[end.content.length - 1];
|
||||
if (end && end.text) end.text = end.text.replace(/\s+$/, '');
|
||||
|
||||
return elem;
|
||||
|
||||
}
|
||||
|
||||
};
|
155
web/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
155
web/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
|
@ -0,0 +1,155 @@
|
|||
'use strict';
|
||||
|
||||
var FS = require('fs');
|
||||
|
||||
/**
|
||||
* Encode plain SVG data string into Data URI string.
|
||||
*
|
||||
* @param {String} str input string
|
||||
* @param {String} type Data URI type
|
||||
* @return {String} output string
|
||||
*/
|
||||
exports.encodeSVGDatauri = function(str, type) {
|
||||
var prefix = 'data:image/svg+xml';
|
||||
if (!type || type === 'base64') {
|
||||
// base64
|
||||
prefix += ';base64,';
|
||||
if (Buffer.from) {
|
||||
str = prefix + Buffer.from(str).toString('base64');
|
||||
} else {
|
||||
str = prefix + new Buffer(str).toString('base64');
|
||||
}
|
||||
} else if (type === 'enc') {
|
||||
// URI encoded
|
||||
str = prefix + ',' + encodeURIComponent(str);
|
||||
} else if (type === 'unenc') {
|
||||
// unencoded
|
||||
str = prefix + ',' + str;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode SVG Data URI string into plain SVG string.
|
||||
*
|
||||
* @param {string} str input string
|
||||
* @return {String} output string
|
||||
*/
|
||||
exports.decodeSVGDatauri = function(str) {
|
||||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
var match = regexp.exec(str);
|
||||
|
||||
// plain string
|
||||
if (!match) return str;
|
||||
|
||||
var data = match[3];
|
||||
|
||||
if (match[2]) {
|
||||
// base64
|
||||
str = new Buffer(data, 'base64').toString('utf8');
|
||||
} else if (data.charAt(0) === '%') {
|
||||
// URI encoded
|
||||
str = decodeURIComponent(data);
|
||||
} else if (data.charAt(0) === '<') {
|
||||
// unencoded
|
||||
str = data;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
exports.intersectArrays = function(a, b) {
|
||||
return a.filter(function(n) {
|
||||
return b.indexOf(n) > -1;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a row of numbers to an optimized string view.
|
||||
*
|
||||
* @example
|
||||
* [0, -1, .5, .5] → "0-1 .5.5"
|
||||
*
|
||||
* @param {number[]} data
|
||||
* @param {Object} params
|
||||
* @param {string?} command path data instruction
|
||||
* @return {string}
|
||||
*/
|
||||
exports.cleanupOutData = function(data, params, command) {
|
||||
var str = '',
|
||||
delimiter,
|
||||
prev;
|
||||
|
||||
data.forEach(function(item, i) {
|
||||
// space delimiter by default
|
||||
delimiter = ' ';
|
||||
|
||||
// no extra space in front of first number
|
||||
if (i == 0) delimiter = '';
|
||||
|
||||
// no extra space after 'arcto' command flags
|
||||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
||||
var pos = i % 7;
|
||||
if (pos == 4 || pos == 5) delimiter = '';
|
||||
}
|
||||
|
||||
// remove floating-point numbers leading zeros
|
||||
// 0.5 → .5
|
||||
// -0.5 → -.5
|
||||
if (params.leadingZero) {
|
||||
item = removeLeadingZero(item);
|
||||
}
|
||||
|
||||
// no extra space in front of negative number or
|
||||
// in front of a floating number if a previous number is floating too
|
||||
if (
|
||||
params.negativeExtraSpace &&
|
||||
delimiter != '' &&
|
||||
(item < 0 ||
|
||||
(String(item).charCodeAt(0) == 46 && prev % 1 !== 0)
|
||||
)
|
||||
) {
|
||||
delimiter = '';
|
||||
}
|
||||
// save prev item value
|
||||
prev = item;
|
||||
str += delimiter + item;
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove floating-point numbers leading zero.
|
||||
*
|
||||
* @example
|
||||
* 0.5 → .5
|
||||
*
|
||||
* @example
|
||||
* -0.5 → -.5
|
||||
*
|
||||
* @param {Float} num input number
|
||||
*
|
||||
* @return {String} output number as string
|
||||
*/
|
||||
var removeLeadingZero = exports.removeLeadingZero = function(num) {
|
||||
var strNum = num.toString();
|
||||
|
||||
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
|
||||
strNum = strNum.slice(1);
|
||||
} else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
|
||||
strNum = strNum.charAt(0) + strNum.slice(2);
|
||||
}
|
||||
return strNum;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT.
|
||||
* @param {string} path
|
||||
*/
|
||||
exports.checkIsDir = function(path) {
|
||||
try {
|
||||
return FS.lstatSync(path).isDirectory();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue