1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-31 07:44:22 +03:00

Format lib with prettier (#1386)

Note: review with hidden whitespaces
This commit is contained in:
Bogdan Chadkin
2021-02-28 10:56:16 +03:00
committed by GitHub
parent a99cc08e4f
commit 71e47370bd
13 changed files with 1312 additions and 1282 deletions

View File

@ -5,7 +5,6 @@ var csstree = require('css-tree'),
stable = require('stable'),
specificity = require('csso/lib/restructure/prepare/specificity');
/**
* Flatten a CSS AST to a selectors list.
*
@ -15,7 +14,9 @@ var csstree = require('css-tree'),
function flattenToSelectors(cssAst) {
var selectors = [];
csstree.walk(cssAst, {visit: 'Rule', enter: function(node) {
csstree.walk(cssAst, {
visit: 'Rule',
enter: function (node) {
if (node.type !== 'Rule') {
return;
}
@ -23,27 +24,34 @@ function flattenToSelectors(cssAst) {
var atrule = this.atrule;
var rule = node;
node.prelude.children.each(function(selectorNode, selectorItem) {
node.prelude.children.each(function (selectorNode, selectorItem) {
var selector = {
item: selectorItem,
atrule: atrule,
rule: rule,
pseudos: []
pseudos: [],
};
selectorNode.children.each(function(selectorChildNode, selectorChildItem, selectorChildList) {
if (selectorChildNode.type === 'PseudoClassSelector' ||
selectorChildNode.type === 'PseudoElementSelector') {
selectorNode.children.each(function (
selectorChildNode,
selectorChildItem,
selectorChildList
) {
if (
selectorChildNode.type === 'PseudoClassSelector' ||
selectorChildNode.type === 'PseudoElementSelector'
) {
selector.pseudos.push({
item: selectorChildItem,
list: selectorChildList
list: selectorChildList,
});
}
});
selectors.push(selector);
});
}});
},
});
return selectors;
}
@ -56,15 +64,17 @@ function flattenToSelectors(cssAst) {
* @return {Array} Filtered selectors that match the passed media queries
*/
function filterByMqs(selectors, useMqs) {
return selectors.filter(function(selector) {
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') {
if (
selector.atrule.expression &&
selector.atrule.expression.children.first().type === 'MediaQueryList'
) {
var mqExpr = csstree.generate(selector.atrule.expression);
mqStr = [mqName, mqExpr].join(' ');
}
@ -81,12 +91,14 @@ function filterByMqs(selectors, useMqs) {
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
*/
function filterByPseudos(selectors, usePseudos) {
return selectors.filter(function(selector) {
return selectors.filter(function (selector) {
var pseudoSelectorsStr = csstree.generate({
type: 'Selector',
children: new List().fromArray(selector.pseudos.map(function(pseudo) {
children: new List().fromArray(
selector.pseudos.map(function (pseudo) {
return pseudo.item.data;
}))
})
),
});
return ~usePseudos.indexOf(pseudoSelectorsStr);
});
@ -99,14 +111,13 @@ function filterByPseudos(selectors, usePseudos) {
* @return {Array} Selectors without pseudo-elements and/or -classes
*/
function cleanPseudos(selectors) {
selectors.forEach(function(selector) {
selector.pseudos.forEach(function(pseudo) {
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
@ -127,7 +138,6 @@ function compareSpecificity(aSpecificity, bSpecificity) {
return 0;
}
/**
* Compare two simple selectors.
*
@ -145,7 +155,6 @@ function _bySelectorSpecificity(selectorA, selectorB) {
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
}
/**
* Sort selectors stably by their specificity.
*
@ -156,7 +165,6 @@ function sortSelectors(selectors) {
return stable(selectors, _bySelectorSpecificity);
}
/**
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
*
@ -166,15 +174,14 @@ function sortSelectors(selectors) {
function csstreeToStyleDeclaration(declaration) {
var propertyName = declaration.property,
propertyValue = csstree.generate(declaration.value),
propertyPriority = (declaration.important ? 'important' : '');
propertyPriority = declaration.important ? 'important' : '';
return {
name: propertyName,
value: propertyValue,
priority: propertyPriority
priority: propertyPriority,
};
}
/**
* Gets the CSS string of a style element
*
@ -194,7 +201,7 @@ function getCssStr(elem) {
*/
function setCssStr(elem, css) {
// in case of cdata field
if(elem.content[0].cdata) {
if (elem.content[0].cdata) {
elem.content[0].cdata = css;
return elem.content[0].cdata;
}
@ -204,7 +211,6 @@ function setCssStr(elem, css) {
return elem.content[0].text;
}
module.exports.flattenToSelectors = flattenToSelectors;
module.exports.filterByMqs = filterByMqs;

View File

@ -8,7 +8,7 @@ const {
createContentItem,
} = require('./svgo.js');
const importConfig = async configFile => {
const importConfig = async (configFile) => {
const config = require(configFile);
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
throw Error(`Invalid config file "${configFile}"`);
@ -23,7 +23,7 @@ const isFile = async (file) => {
} catch {
return false;
}
}
};
const loadConfig = async (configFile, cwd = process.cwd()) => {
if (configFile != null) {
@ -36,7 +36,7 @@ const loadConfig = async (configFile, cwd = process.cwd()) => {
let dir = cwd;
// eslint-disable-next-line no-constant-condition
while (true) {
const file = path.join(dir, "svgo.config.js");
const file = path.join(dir, 'svgo.config.js');
if (await isFile(file)) {
return await importConfig(file);
}

View File

@ -13,7 +13,7 @@
const {
defaultPlugins,
resolvePluginConfig,
extendDefaultPlugins
extendDefaultPlugins,
} = require('./svgo/config.js');
const svg2js = require('./svgo/svg2js.js');
const js2svg = require('./svgo/js2svg.js');
@ -28,12 +28,12 @@ const optimize = (input, config) => {
config = {};
}
if (typeof config !== 'object') {
throw Error('Config should be an object')
throw Error('Config should be an object');
}
const maxPassCount = config.multipass ? 10 : 1;
let prevResultSize = Number.POSITIVE_INFINITY;
let svgjs = null;
const info = {}
const info = {};
if (config.path != null) {
info.path = config.path;
}
@ -48,9 +48,13 @@ const optimize = (input, config) => {
}
const plugins = config.plugins || defaultPlugins;
if (Array.isArray(plugins) === false) {
throw Error('Invalid plugins list. Provided \'plugins\' in config should be an array.');
throw Error(
"Invalid plugins list. Provided 'plugins' in config should be an array."
);
}
const resolvedPlugins = plugins.map(plugin => resolvePluginConfig(plugin, config))
const resolvedPlugins = plugins.map((plugin) =>
resolvePluginConfig(plugin, config)
);
svgjs = invokePlugins(svgjs, info, resolvedPlugins);
svgjs = js2svg(svgjs, config.js2svg);
if (svgjs.error) {
@ -58,7 +62,7 @@ const optimize = (input, config) => {
}
if (svgjs.data.length < prevResultSize) {
input = svgjs.data;
prevResultSize = svgjs.data.length
prevResultSize = svgjs.data.length;
} else {
if (config.datauri) {
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);

View File

@ -16,7 +16,7 @@ const regSVGFile = /\.svg$/i;
function checkIsDir(path) {
try {
return FS.lstatSync(path).isDirectory();
} catch(e) {
} catch (e) {
return false;
}
}
@ -25,52 +25,83 @@ module.exports = function makeProgram(program) {
program
.name(PKG.name)
.description(PKG.description, {
INPUT: 'Alias to --input'
INPUT: 'Alias to --input',
})
.version(PKG.version, '-v, --version')
.arguments('[INPUT...]')
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
.option('-s, --string <STRING>', 'Input SVG data string')
.option('-f, --folder <FOLDER>', 'Input folder, optimize and rewrite all *.svg files')
.option('-o, --output <OUTPUT...>', 'Output file or folder (by default the same as the input), "-" for STDOUT')
.option('-p, --precision <INTEGER>', 'Set number of digits in the fractional part, overrides plugins params')
.option(
'-f, --folder <FOLDER>',
'Input folder, optimize and rewrite all *.svg files'
)
.option(
'-o, --output <OUTPUT...>',
'Output file or folder (by default the same as the input), "-" for STDOUT'
)
.option(
'-p, --precision <INTEGER>',
'Set number of digits in the fractional part, overrides plugins params'
)
.option('--config <CONFIG>', 'Custom config file, only .js is supported')
.option('--datauri <FORMAT>', 'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)')
.option('--multipass', 'Pass over SVGs multiple times to ensure all optimizations are applied')
.option(
'--datauri <FORMAT>',
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)'
)
.option(
'--multipass',
'Pass over SVGs multiple times to ensure all optimizations are applied'
)
.option('--pretty', 'Make SVG pretty printed')
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
.option('-r, --recursive', 'Use with \'-f\'. Optimizes *.svg files in folders recursively.')
.option('-q, --quiet', 'Only output error messages, not regular status messages')
.option(
'-r, --recursive',
"Use with '-f'. Optimizes *.svg files in folders recursively."
)
.option(
'-q, --quiet',
'Only output error messages, not regular status messages'
)
.option('--show-plugins', 'Show available plugins and exit')
.action(action);
}
};
async function action(args, opts, command) {
var input = opts.input || args;
var output = opts.output;
var config = {}
var config = {};
if (opts.precision != null) {
const number = Number.parseInt(opts.precision, 0);
if (Number.isNaN(number)) {
console.error("error: option '-p, --precision' argument must be an integer number");
process.exit(1)
console.error(
"error: option '-p, --precision' argument must be an integer number"
);
process.exit(1);
} else {
opts.precision = number;
}
}
if (opts.datauri != null) {
if (opts.datauri !== 'base64' && opts.datauri !== 'enc' && opts.datauri !== 'unenc') {
console.error("error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'")
process.exit(1)
if (
opts.datauri !== 'base64' &&
opts.datauri !== 'enc' &&
opts.datauri !== 'unenc'
) {
console.error(
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"
);
process.exit(1);
}
}
if (opts.indent != null) {
const number = Number.parseInt(opts.indent, 0);
if (Number.isNaN(number)) {
console.error("error: option '--indent' argument must be an integer number");
console.error(
"error: option '--indent' argument must be an integer number"
);
process.exit(1);
} else {
opts.indent = number;
@ -94,10 +125,18 @@ async function action(args, opts, command) {
return command.help();
}
if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
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)) {
throw Error(`${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
throw Error(
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`
);
}
}
@ -143,7 +182,9 @@ async function action(args, opts, command) {
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]));
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));
@ -161,7 +202,7 @@ async function action(args, opts, command) {
// --folder
if (opts.folder) {
var ouputFolder = output && output[0] || opts.folder;
var ouputFolder = (output && output[0]) || opts.folder;
await optimizeFolder(config, opts.folder, ouputFolder);
}
@ -174,8 +215,13 @@ async function action(args, opts, command) {
file = output[0];
process.stdin
.on('data', chunk => data += chunk)
.once('end', () => processSVGData(config, {input: 'string'}, data, file).then(resolve, reject));
.on('data', (chunk) => (data += chunk))
.once('end', () =>
processSVGData(config, { input: 'string' }, data, file).then(
resolve,
reject
)
);
});
// file
} else {
@ -188,7 +234,7 @@ async function action(args, opts, command) {
} else if (opts.string) {
var data = decodeSVGDatauri(opts.string);
return processSVGData(config, {input: 'string'}, data, output[0]);
return processSVGData(config, { input: 'string' }, data, output[0]);
}
}
@ -203,7 +249,9 @@ function optimizeFolder(config, dir, output) {
if (!config.quiet) {
console.log(`Processing directory '${dir}':\n`);
}
return FS.promises.readdir(dir).then(files => processDirectory(config, dir, files, output));
return FS.promises
.readdir(dir)
.then((files) => processDirectory(config, dir, files, output));
}
/**
@ -218,9 +266,19 @@ 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.`));
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.`)
);
}
/**
@ -233,26 +291,31 @@ function processDirectory(config, dir, files, output) {
*/
function getFilesDescriptions(config, dir, files, output) {
const filesInThisFolder = files
.filter(name => regSVGFile.test(name))
.map(name => ({
.filter((name) => regSVGFile.test(name))
.map((name) => ({
inputPath: PATH.resolve(dir, name),
outputPath: PATH.resolve(output, name),
}));
return config.recursive ?
[].concat(
return config.recursive
? [].concat(
filesInThisFolder,
files
.filter(name => checkIsDir(PATH.resolve(dir, name)))
.map(subFolderName => {
.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);
return getFilesDescriptions(
config,
subFolderPath,
subFolderFiles,
subFolderOutput
);
})
.reduce((a, b) => [].concat(a, b), [])
) :
filesInThisFolder;
)
: filesInThisFolder;
}
/**
@ -264,8 +327,9 @@ function getFilesDescriptions(config, dir, files, output) {
*/
function optimizeFile(config, file, output) {
return FS.promises.readFile(file, 'utf8').then(
data => processSVGData(config, {input: 'file', path: file}, data, output, file),
error => checkOptimizeFileError(config, file, output, error)
(data) =>
processSVGData(config, { input: 'file', path: file }, data, output, file),
(error) => checkOptimizeFileError(config, file, output, error)
);
}
@ -285,9 +349,9 @@ function processSVGData(config, info, data, output, input) {
if (result.error) {
let message = result.error;
if (result.path != null) {
message += `\nFile: ${result.path}`
message += `\nFile: ${result.path}`;
}
throw Error(message)
throw Error(message);
}
if (config.datauri) {
result.data = encodeSVGDatauri(result.data, config.datauri);
@ -295,7 +359,8 @@ function processSVGData(config, info, data, output, input) {
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
processingTime = Date.now() - startTime;
return writeOutput(input, output, result.data).then(function() {
return writeOutput(input, output, result.data).then(
function () {
if (!config.quiet && output != '-') {
if (input) {
console.log(`\n${PATH.basename(input)}:`);
@ -304,7 +369,15 @@ function processSVGData(config, info, data, output, input) {
printProfitInfo(prevFileSize, resultFileSize);
}
},
error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error)));
(error) =>
Promise.reject(
new Error(
error.code === 'ENOTDIR'
? `Error: output '${output}' is not a directory.`
: error
)
)
);
}
/**
@ -322,10 +395,11 @@ function writeOutput(input, output, data) {
FS.mkdirSync(PATH.dirname(output), { recursive: true });
return FS.promises.writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error));
return FS.promises
.writeFile(output, data, 'utf8')
.catch((error) => checkWriteFileError(input, output, data, error));
}
/**
* Write a time taken by optimization.
* @param {number} time time in milliseconds.
@ -340,13 +414,16 @@ function printTimeInfo(time) {
* @param {number} outBytes size after optimization.
*/
function printProfitInfo(inBytes, outBytes) {
var profitPercents = 100 - outBytes * 100 / inBytes;
var profitPercents = 100 - (outBytes * 100) / inBytes;
console.log(
(Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
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'
chalk.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
' = ' +
Math.round((outBytes / 1024) * 1000) / 1000 +
' KiB'
);
}
@ -362,7 +439,9 @@ 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(
new Error(`Error: no such file or directory '${error.path}'.`)
);
}
return Promise.reject(error);
}
@ -377,7 +456,11 @@ function checkOptimizeFileError(config, input, output, error) {
*/
function checkWriteFileError(input, output, data, error) {
if (error.code == 'EISDIR' && input) {
return FS.promises.writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8');
return FS.promises.writeFile(
PATH.resolve(output, PATH.basename(input)),
data,
'utf8'
);
} else {
return Promise.reject(error);
}

View File

@ -53,11 +53,14 @@ const pluginsOrder = [
'removeOffCanvasPaths',
'reusePaths',
];
const defaultPlugins = pluginsOrder.filter(name => pluginsMap[name].active);
const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
exports.defaultPlugins = defaultPlugins;
const extendDefaultPlugins = (plugins) => {
const extendedPlugins = pluginsOrder.map(name => ({ name, ...pluginsMap[name] }));
const extendedPlugins = pluginsOrder.map((name) => ({
name,
...pluginsMap[name],
}));
for (const plugin of plugins) {
const resolvedPlugin = resolvePluginConfig(plugin, {});
const index = pluginsOrder.indexOf(resolvedPlugin.name);
@ -68,7 +71,7 @@ const extendDefaultPlugins = (plugins) => {
}
}
return extendedPlugins;
}
};
exports.extendDefaultPlugins = extendDefaultPlugins;
const resolvePluginConfig = (plugin, config) => {
@ -86,7 +89,7 @@ const resolvePluginConfig = (plugin, config) => {
...pluginConfig,
name: plugin,
active: true,
params: { ...pluginConfig.params, ...configParams }
params: { ...pluginConfig.params, ...configParams },
};
}
if (typeof plugin === 'object' && plugin != null) {
@ -98,7 +101,7 @@ const resolvePluginConfig = (plugin, config) => {
return {
active: true,
...plugin,
params: { configParams, ...plugin.params }
params: { configParams, ...plugin.params },
};
} else {
// resolve builtin plugin specified as object without implementation
@ -110,7 +113,7 @@ const resolvePluginConfig = (plugin, config) => {
...pluginConfig,
active: true,
...plugin,
params: { ...pluginConfig.params, ...configParams, ...plugin.params }
params: { ...pluginConfig.params, ...configParams, ...plugin.params },
};
}
}

View File

@ -1,6 +1,6 @@
'use strict';
var CSSClassList = function(node) {
var CSSClassList = function (node) {
this.parentNode = node;
this.classNames = new Set();
this.classAttr = null;
@ -12,11 +12,11 @@ var CSSClassList = function(node) {
*
* @param parentNode the parentNode to assign to the cloned result
*/
CSSClassList.prototype.clone = function(parentNode) {
CSSClassList.prototype.clone = function (parentNode) {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
Object.keys(node).forEach(function (key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
@ -30,25 +30,24 @@ CSSClassList.prototype.clone = function(parentNode) {
return clone;
};
CSSClassList.prototype.hasClass = function() {
this.classAttr = { // empty class attr
'name': 'class',
'value': null
CSSClassList.prototype.hasClass = function () {
this.classAttr = {
// empty class attr
name: 'class',
value: null,
};
this.addClassHandler();
};
// attr.class
CSSClassList.prototype.addClassHandler = function() {
CSSClassList.prototype.addClassHandler = function () {
Object.defineProperty(this.parentNode.attrs, 'class', {
get: this.getClassAttr.bind(this),
set: this.setClassAttr.bind(this),
enumerable: true,
configurable: true
configurable: true,
});
this.addClassValueHandler();
@ -56,34 +55,33 @@ CSSClassList.prototype.addClassHandler = function() {
// attr.class.value
CSSClassList.prototype.addClassValueHandler = function() {
CSSClassList.prototype.addClassValueHandler = function () {
Object.defineProperty(this.classAttr, 'value', {
get: this.getClassValue.bind(this),
set: this.setClassValue.bind(this),
enumerable: true,
configurable: true
configurable: true,
});
};
CSSClassList.prototype.getClassAttr = function() {
CSSClassList.prototype.getClassAttr = function () {
return this.classAttr;
};
CSSClassList.prototype.setClassAttr = function(newClassAttr) {
CSSClassList.prototype.setClassAttr = function (newClassAttr) {
this.setClassValue(newClassAttr.value); // must before applying value handler!
this.classAttr = newClassAttr;
this.addClassValueHandler();
};
CSSClassList.prototype.getClassValue = function() {
CSSClassList.prototype.getClassValue = function () {
var arrClassNames = Array.from(this.classNames);
return arrClassNames.join(' ');
};
CSSClassList.prototype.setClassValue = function(newValue) {
if(typeof newValue === 'undefined') {
CSSClassList.prototype.setClassValue = function (newValue) {
if (typeof newValue === 'undefined') {
this.classNames.clear();
return;
}
@ -91,42 +89,38 @@ CSSClassList.prototype.setClassValue = function(newValue) {
this.classNames = new Set(arrClassNames);
};
CSSClassList.prototype.add = function(/* variadic */) {
CSSClassList.prototype.add = function (/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._addSingle.bind(this));
};
CSSClassList.prototype._addSingle = function(className) {
CSSClassList.prototype._addSingle = function (className) {
this.classNames.add(className);
};
CSSClassList.prototype.remove = function(/* variadic */) {
CSSClassList.prototype.remove = function (/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._removeSingle.bind(this));
};
CSSClassList.prototype._removeSingle = function(className) {
CSSClassList.prototype._removeSingle = function (className) {
this.classNames.delete(className);
};
CSSClassList.prototype.item = function(index) {
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) {
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) {
CSSClassList.prototype.contains = function (className) {
return this.classNames.has(className);
};
module.exports = CSSClassList;

View File

@ -6,45 +6,44 @@ 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) {
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) {
getParent: function (node) {
return node.parentNode || null;
},
// get the node's children
// getChildren: ( node:Node ) => children:[Node]
getChildren: function(node) {
getChildren: function (node) {
return node.content || [];
},
// get the name of the tag
// getName: ( elem:ElementNode ) => tagName:String
getName: function(elemAst) {
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) {
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) {
getAttributeValue: function (elem, name) {
return elem.hasAttr(name) ? elem.attr(name).value : null;
}
},
};
// use base adapter for default implementation

View File

@ -3,8 +3,7 @@
var csstree = require('css-tree'),
csstools = require('../css-tools');
var CSSStyleDeclaration = function(node) {
var CSSStyleDeclaration = function (node) {
this.parentNode = node;
this.properties = new Map();
@ -21,11 +20,11 @@ var CSSStyleDeclaration = function(node) {
*
* @param parentNode the parentNode to assign to the cloned result
*/
CSSStyleDeclaration.prototype.clone = function(parentNode) {
CSSStyleDeclaration.prototype.clone = function (parentNode) {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
Object.keys(node).forEach(function (key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
@ -39,27 +38,24 @@ CSSStyleDeclaration.prototype.clone = function(parentNode) {
return clone;
};
CSSStyleDeclaration.prototype.hasStyle = function() {
CSSStyleDeclaration.prototype.hasStyle = function () {
this.addStyleHandler();
};
// attr.style
CSSStyleDeclaration.prototype.addStyleHandler = function() {
this.styleAttr = { // empty style attr
'name': 'style',
'value': null
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
configurable: true,
});
this.addStyleValueHandler();
@ -67,21 +63,20 @@ CSSStyleDeclaration.prototype.addStyleHandler = function() {
// attr.style.value
CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
CSSStyleDeclaration.prototype.addStyleValueHandler = function () {
Object.defineProperty(this.styleAttr, 'value', {
get: this.getStyleValue.bind(this),
set: this.setStyleValue.bind(this),
enumerable: true,
configurable: true
configurable: true,
});
};
CSSStyleDeclaration.prototype.getStyleAttr = function() {
CSSStyleDeclaration.prototype.getStyleAttr = function () {
return this.styleAttr;
};
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
CSSStyleDeclaration.prototype.setStyleAttr = function (newStyleAttr) {
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
this.styleAttr = newStyleAttr;
@ -89,20 +84,17 @@ CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
this.hasSynced = false; // raw css changed
};
CSSStyleDeclaration.prototype.getStyleValue = function() {
CSSStyleDeclaration.prototype.getStyleValue = function () {
return this.getCssText();
};
CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
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() {
CSSStyleDeclaration.prototype._loadCssText = function () {
if (this.hasSynced) {
return;
}
@ -117,7 +109,7 @@ CSSStyleDeclaration.prototype._loadCssText = function() {
try {
declarations = csstree.parse(inlineCssStr, {
context: 'declarationList',
parseValue: false
parseValue: false,
});
} catch (parseError) {
this.parseError = parseError;
@ -126,19 +118,22 @@ CSSStyleDeclaration.prototype._loadCssText = function() {
this.parseError = false;
var self = this;
declarations.children.each(function(declaration) {
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.setProperty(
styleDeclaration.name,
styleDeclaration.value,
styleDeclaration.priority
);
} catch (styleError) {
if (styleError.message !== 'Unknown node type: undefined') {
self.parseError = styleError;
}
}
});
};
// only reads from properties
/**
@ -146,7 +141,7 @@ CSSStyleDeclaration.prototype._loadCssText = function() {
*
* @return {String} Textual representation of the declaration block (empty string for no properties)
*/
CSSStyleDeclaration.prototype.getCssText = function() {
CSSStyleDeclaration.prototype.getCssText = function () {
var properties = this.getProperties();
if (this.parseError) {
@ -155,22 +150,26 @@ CSSStyleDeclaration.prototype.getCssText = function() {
}
var cssText = [];
properties.forEach(function(property, propertyName) {
properties.forEach(function (property, propertyName) {
var strImportant = property.priority === 'important' ? '!important' : '';
cssText.push(propertyName.trim() + ':' + property.value.trim() + strImportant);
cssText.push(
propertyName.trim() + ':' + property.value.trim() + strImportant
);
});
return cssText.join(';');
};
CSSStyleDeclaration.prototype._handleParseError = function() {
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);
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') {
CSSStyleDeclaration.prototype._getProperty = function (propertyName) {
if (typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
@ -187,7 +186,7 @@ CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
* @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) {
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) {
var property = this._getProperty(propertyName);
return property ? property.priority : '';
};
@ -198,7 +197,7 @@ CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
* @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) {
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) {
var property = this._getProperty(propertyName);
return property ? property.value : null;
};
@ -209,8 +208,8 @@ CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
* @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') {
CSSStyleDeclaration.prototype.item = function (index) {
if (typeof index === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
@ -225,12 +224,11 @@ CSSStyleDeclaration.prototype.item = function(index) {
*
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
*/
CSSStyleDeclaration.prototype.getProperties = function() {
CSSStyleDeclaration.prototype.getProperties = function () {
this._loadCssText();
return this.properties;
};
// writes to properties
/**
@ -239,8 +237,8 @@ CSSStyleDeclaration.prototype.getProperties = function() {
* @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') {
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) {
if (typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
@ -262,8 +260,12 @@ CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
* @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') {
CSSStyleDeclaration.prototype.setProperty = function (
propertyName,
value,
priority
) {
if (typeof propertyName === 'undefined') {
throw Error('propertyName argument required, but only not present.');
}
@ -274,12 +276,11 @@ CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priori
var property = {
value: value.trim(),
priority: priority.trim()
priority: priority.trim(),
};
properties.set(propertyName.trim(), property);
return property;
};
module.exports = CSSStyleDeclaration;

View File

@ -27,16 +27,16 @@ var defaults = {
regValEntities: /[&"<>]/g,
encodeEntity: encodeEntity,
pretty: false,
useShortTags: true
useShortTags: true,
};
var entities = {
'&': '&amp;',
'\'': '&apos;',
"'": '&apos;',
'"': '&quot;',
'>': '&gt;',
'<': '&lt;',
};
};
/**
* Convert SVG-as-JS object to SVG (XML) string.
@ -46,14 +46,11 @@ var entities = {
*
* @return {Object} output data
*/
module.exports = function(data, config) {
module.exports = function (data, config) {
return new JS2SVG(config).convert(data);
};
function JS2SVG(config) {
if (config) {
this.config = Object.assign({}, defaults, config);
} else {
@ -62,7 +59,7 @@ function JS2SVG(config) {
var indent = this.config.indent;
if (typeof indent == 'number' && !isNaN(indent)) {
this.config.indent = (indent < 0) ? '\t' : ' '.repeat(indent);
this.config.indent = indent < 0 ? '\t' : ' '.repeat(indent);
} else if (typeof indent != 'string') {
this.config.indent = ' ';
}
@ -80,7 +77,6 @@ function JS2SVG(config) {
this.indentLevel = 0;
this.textContext = null;
}
function encodeEntity(char) {
@ -94,16 +90,13 @@ function encodeEntity(char) {
*
* @return {String}
*/
JS2SVG.prototype.convert = function(data) {
JS2SVG.prototype.convert = function (data) {
var svg = '';
if (data.content) {
this.indentLevel++;
data.content.forEach(function(item) {
data.content.forEach(function (item) {
if (item.elem) {
svg += this.createElem(item);
} else if (item.text) {
@ -117,9 +110,7 @@ JS2SVG.prototype.convert = function(data) {
} else if (item.cdata) {
svg += this.createCDATA(item.cdata);
}
}, this);
}
this.indentLevel--;
@ -128,10 +119,9 @@ JS2SVG.prototype.convert = function(data) {
data: svg,
info: {
width: this.width,
height: this.height
}
height: this.height,
},
};
};
/**
@ -139,8 +129,7 @@ JS2SVG.prototype.convert = function(data) {
*
* @return {String}
*/
JS2SVG.prototype.createIndent = function() {
JS2SVG.prototype.createIndent = function () {
var indent = '';
if (this.config.pretty && !this.textContext) {
@ -148,7 +137,6 @@ JS2SVG.prototype.createIndent = function() {
}
return indent;
};
/**
@ -158,12 +146,8 @@ JS2SVG.prototype.createIndent = function() {
*
* @return {String}
*/
JS2SVG.prototype.createDoctype = function(doctype) {
return this.config.doctypeStart +
doctype +
this.config.doctypeEnd;
JS2SVG.prototype.createDoctype = function (doctype) {
return this.config.doctypeStart + doctype + this.config.doctypeEnd;
};
/**
@ -173,14 +157,14 @@ JS2SVG.prototype.createDoctype = function(doctype) {
*
* @return {String}
*/
JS2SVG.prototype.createProcInst = function(instruction) {
return this.config.procInstStart +
JS2SVG.prototype.createProcInst = function (instruction) {
return (
this.config.procInstStart +
instruction.name +
' ' +
instruction.body +
this.config.procInstEnd;
this.config.procInstEnd
);
};
/**
@ -190,12 +174,8 @@ JS2SVG.prototype.createProcInst = function(instruction) {
*
* @return {String}
*/
JS2SVG.prototype.createComment = function(comment) {
return this.config.commentStart +
comment +
this.config.commentEnd;
JS2SVG.prototype.createComment = function (comment) {
return this.config.commentStart + comment + this.config.commentEnd;
};
/**
@ -205,13 +185,10 @@ JS2SVG.prototype.createComment = function(comment) {
*
* @return {String}
*/
JS2SVG.prototype.createCDATA = function(cdata) {
return this.createIndent() +
this.config.cdataStart +
cdata +
this.config.cdataEnd;
JS2SVG.prototype.createCDATA = function (cdata) {
return (
this.createIndent() + this.config.cdataStart + cdata + this.config.cdataEnd
);
};
/**
@ -221,14 +198,9 @@ JS2SVG.prototype.createCDATA = function(cdata) {
*
* @return {String}
*/
JS2SVG.prototype.createElem = function(data) {
JS2SVG.prototype.createElem = function (data) {
// beautiful injection for obtaining SVG information :)
if (
data.isElem('svg') &&
data.hasAttr('width') &&
data.hasAttr('height')
) {
if (data.isElem('svg') && data.hasAttr('width') && data.hasAttr('height')) {
this.width = data.attr('width').value;
this.height = data.attr('height').value;
}
@ -236,20 +208,24 @@ JS2SVG.prototype.createElem = function(data) {
// empty element and short tag
if (data.isEmpty()) {
if (this.config.useShortTags) {
return this.createIndent() +
return (
this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagShortEnd;
this.config.tagShortEnd
);
} else {
return this.createIndent() +
return (
this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagOpenEnd +
this.config.tagCloseStart +
data.elem +
this.config.tagCloseEnd;
this.config.tagCloseEnd
);
}
// non-empty element
} else {
@ -281,7 +257,8 @@ JS2SVG.prototype.createElem = function(data) {
this.textContext = null;
}
return openIndent +
return (
openIndent +
tagOpenStart +
data.elem +
this.createAttrs(data) +
@ -291,10 +268,9 @@ JS2SVG.prototype.createElem = function(data) {
closeIndent +
tagCloseStart +
data.elem +
tagCloseEnd;
tagCloseEnd
);
}
};
/**
@ -304,29 +280,26 @@ JS2SVG.prototype.createElem = function(data) {
*
* @return {String}
*/
JS2SVG.prototype.createAttrs = function(elem) {
JS2SVG.prototype.createAttrs = function (elem) {
var attrs = '';
elem.eachAttr(function(attr) {
elem.eachAttr(function (attr) {
if (attr.value !== undefined) {
attrs += ' ' +
attrs +=
' ' +
attr.name +
this.config.attrStart +
String(attr.value).replace(this.config.regValEntities, this.config.encodeEntity) +
String(attr.value).replace(
this.config.regValEntities,
this.config.encodeEntity
) +
this.config.attrEnd;
} else {
attrs += ' ' + attr.name;
}
else {
attrs += ' ' +
attr.name;
}
}, this);
return attrs;
};
/**
@ -336,11 +309,11 @@ JS2SVG.prototype.createAttrs = function(elem) {
*
* @return {String}
*/
JS2SVG.prototype.createText = function(text) {
return this.createIndent() +
JS2SVG.prototype.createText = function (text) {
return (
this.createIndent() +
this.config.textStart +
text.replace(this.config.regEntities, this.config.encodeEntity) +
(this.textContext ? '' : this.config.textEnd);
(this.textContext ? '' : this.config.textEnd)
);
};

View File

@ -5,29 +5,30 @@ const svgoCssSelectAdapter = require('./css-select-adapter');
var cssSelectOpts = {
xmlMode: true,
adapter: svgoCssSelectAdapter
adapter: svgoCssSelectAdapter,
};
var JSAPI = module.exports = function(data, parentNode) {
var JSAPI = function (data, parentNode) {
Object.assign(this, data);
if (parentNode) {
Object.defineProperty(this, 'parentNode', {
writable: true,
value: parentNode
value: parentNode,
});
}
};
module.exports = JSAPI;
/**
* Perform a deep clone of this node.
*
* @return {Object} element
*/
JSAPI.prototype.clone = function() {
JSAPI.prototype.clone = function () {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
Object.keys(node).forEach(function (key) {
if (key !== 'class' && key !== 'style' && key !== 'content') {
nodeData[key] = node[key];
}
@ -48,7 +49,7 @@ JSAPI.prototype.clone = function() {
clonedNode.style = node.style.clone(clonedNode);
}
if (node.content) {
clonedNode.content = node.content.map(function(childNode) {
clonedNode.content = node.content.map(function (childNode) {
var clonedChild = childNode.clone();
clonedChild.parentNode = clonedNode;
return clonedChild;
@ -65,14 +66,12 @@ JSAPI.prototype.clone = function() {
* @param {String|Array} [param] element name or names arrays
* @return {Boolean}
*/
JSAPI.prototype.isElem = function(param) {
JSAPI.prototype.isElem = function (param) {
if (!param) return !!this.elem;
if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
if (Array.isArray(param)) return !!this.elem && param.indexOf(this.elem) > -1;
return !!this.elem && this.elem === param;
};
/**
@ -81,13 +80,10 @@ JSAPI.prototype.isElem = function(param) {
* @param {String} name new element name
* @return {Object} element
*/
JSAPI.prototype.renameElem = function(name) {
if (name && typeof name === 'string')
this.elem = this.local = name;
JSAPI.prototype.renameElem = function (name) {
if (name && typeof name === 'string') this.elem = this.local = name;
return this;
};
/**
@ -95,10 +91,8 @@ JSAPI.prototype.renameElem = function(name) {
*
* @return {Boolean}
*/
JSAPI.prototype.isEmpty = function() {
JSAPI.prototype.isEmpty = function () {
return !this.content || !this.content.length;
};
/**
@ -107,7 +101,7 @@ JSAPI.prototype.renameElem = function(name) {
*
* @return {?Object}
*/
JSAPI.prototype.closestElem = function(elemName) {
JSAPI.prototype.closestElem = function (elemName) {
var elem = this;
while ((elem = elem.parentNode) && !elem.isElem(elemName));
@ -123,18 +117,17 @@ JSAPI.prototype.renameElem = function(name) {
* @param {Array|Object} [insertion] Elements to add to the content.
* @return {Array} Removed elements.
*/
JSAPI.prototype.spliceContent = function(start, n, insertion) {
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);
insertion.forEach(function (inner) {
inner.parentNode = this;
}, this);
return this.content.splice.apply(this.content, [start, n].concat(insertion));
};
/**
@ -145,16 +138,15 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} [val] attribute value (will be toString()'ed)
* @return {Boolean}
*/
JSAPI.prototype.hasAttr = function(name, val) {
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();
if (val !== undefined)
return !!this.attrs[name] && this.attrs[name].value === val.toString();
return !!this.attrs[name];
};
/**
@ -165,8 +157,7 @@ JSAPI.prototype.renameElem = function(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) {
JSAPI.prototype.hasAttrLocal = function (localName, val) {
if (!this.attrs || !Object.keys(this.attrs).length) return false;
if (!arguments.length) return !!this.attrs;
@ -175,10 +166,17 @@ JSAPI.prototype.renameElem = function(name) {
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;
case 'String':
callback = stringValueTest;
break;
case 'RegExp':
callback = regexpValueTest;
break;
case 'Function':
callback = funcValueTest;
break;
default:
callback = nameTest;
}
return this.someAttr(callback);
@ -197,7 +195,6 @@ JSAPI.prototype.renameElem = function(name) {
function funcValueTest(attr) {
return attr.local === localName && val(attr.value);
}
};
/**
@ -208,14 +205,13 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} [val] attribute value (will be toString()'ed)
* @return {Object|Undefined}
*/
JSAPI.prototype.attr = function(name, val) {
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;
if (val !== undefined)
return this.hasAttr(name, val) ? this.attrs[name] : undefined;
return this.attrs[name];
};
/**
@ -224,17 +220,20 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} name attribute name
* @return {Object|Undefined}
*/
JSAPI.prototype.computedAttr = function(name, val) {
JSAPI.prototype.computedAttr = function (name, val) {
if (!arguments.length) return;
for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode);
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;
}
};
/**
@ -244,8 +243,7 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} [val] attribute value
* @return {Boolean}
*/
JSAPI.prototype.removeAttr = function(name, val, recursive) {
JSAPI.prototype.removeAttr = function (name, val, recursive) {
if (!arguments.length) return false;
if (Array.isArray(name)) {
@ -262,7 +260,6 @@ JSAPI.prototype.renameElem = function(name) {
if (!Object.keys(this.attrs).length) delete this.attrs;
return true;
};
/**
@ -271,27 +268,30 @@ JSAPI.prototype.renameElem = function(name) {
* @param {Object} [attr={}] attribute object
* @return {Object|Boolean} created attribute or false if no attr was passed in
*/
JSAPI.prototype.addAttr = function(attr) {
JSAPI.prototype.addAttr = function (attr) {
attr = attr || {};
if (attr.name === undefined ||
if (
attr.name === undefined ||
attr.prefix === undefined ||
attr.local === undefined
) return false;
)
return false;
this.attrs = this.attrs || {};
this.attrs[attr.name] = attr;
if(attr.name === 'class') { // newly added class attribute
if (attr.name === 'class') {
// newly added class attribute
this.class.hasClass();
}
if(attr.name === 'style') { // newly added style attribute
if (attr.name === 'style') {
// newly added style attribute
this.style.hasStyle();
}
return this.attrs[attr.name];
};
/**
@ -301,8 +301,7 @@ JSAPI.prototype.renameElem = function(name) {
* @param {Object} [context] callback context
* @return {Boolean} false if there are no any attributes
*/
JSAPI.prototype.eachAttr = function(callback, context) {
JSAPI.prototype.eachAttr = function (callback, context) {
if (!this.hasAttr()) return false;
for (const attr of Object.values(this.attrs)) {
@ -310,7 +309,6 @@ JSAPI.prototype.renameElem = function(name) {
}
return true;
};
/**
@ -320,8 +318,7 @@ JSAPI.prototype.renameElem = function(name) {
* @param {Object} [context] callback context
* @return {Boolean} false if there are no any attributes
*/
JSAPI.prototype.someAttr = function(callback, context) {
JSAPI.prototype.someAttr = function (callback, context) {
if (!this.hasAttr()) return false;
for (const attr of Object.values(this.attrs)) {
@ -329,7 +326,6 @@ JSAPI.prototype.renameElem = function(name) {
}
return false;
};
/**
@ -338,12 +334,10 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} selectors CSS selector(s) string
* @return {Array} null if no elements matched
*/
JSAPI.prototype.querySelectorAll = function(selectors) {
JSAPI.prototype.querySelectorAll = function (selectors) {
var matchedEls = selectAll(selectors, this, cssSelectOpts);
return matchedEls.length > 0 ? matchedEls : null;
};
/**
@ -352,10 +346,8 @@ JSAPI.prototype.renameElem = function(name) {
* @param {String} selectors CSS selector(s) string
* @return {Array} null if no element matched
*/
JSAPI.prototype.querySelector = function(selectors) {
JSAPI.prototype.querySelector = function (selectors) {
return selectOne(selectors, this, cssSelectOpts);
};
/**
@ -364,8 +356,6 @@ JSAPI.prototype.renameElem = function(name) {
* @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) {
JSAPI.prototype.matches = function (selector) {
return is(this, selector, cssSelectOpts);
};

View File

@ -10,7 +10,7 @@
* @param {Object} plugins plugins object from config
* @return {Object} output data
*/
module.exports = function(data, info, plugins) {
module.exports = function (data, info, plugins) {
// Try to group sequential elements of plugins array
// to optimize ast traversing
const groups = [];
@ -24,7 +24,7 @@ module.exports = function(data, info, plugins) {
}
}
for (const group of groups) {
switch(group[0].type) {
switch (group[0].type) {
case 'perItem':
data = perItem(data, info, group);
break;
@ -49,11 +49,8 @@ module.exports = function(data, info, plugins) {
* @return {Object} output data
*/
function perItem(data, info, plugins, reverse) {
function monkeys(items) {
items.content = items.content.filter(function(item) {
items.content = items.content.filter(function (item) {
// reverse pass
if (reverse && item.content) {
monkeys(item);
@ -76,15 +73,12 @@ function perItem(data, info, plugins, reverse) {
}
return filter;
});
return items;
}
return monkeys(data);
}
/**
@ -96,13 +90,11 @@ function perItem(data, info, plugins, reverse) {
* @return {Object} output data
*/
function full(data, info, plugins) {
plugins.forEach(function(plugin) {
plugins.forEach(function (plugin) {
if (plugin.active) {
data = plugin.fn(data, plugin.params, info);
}
});
return data;
}

View File

@ -13,7 +13,7 @@ var config = {
normalize: false,
lowercase: true,
xmlns: true,
position: true
position: true,
};
/**
@ -21,27 +21,23 @@ var config = {
*
* @param {String} data input data
*/
module.exports = function(data) {
module.exports = function (data) {
var sax = SAX.parser(config.strict, config),
root = new JSAPI({ elem: '#document', content: [] }),
current = root,
stack = [root];
function pushToContent(content) {
content = new JSAPI(content, current);
(current.content = current.content || []).push(content);
return content;
}
sax.ondoctype = function(doctype) {
sax.ondoctype = function (doctype) {
pushToContent({
doctype: doctype
doctype: doctype,
});
var subsetStart = doctype.indexOf('['),
@ -56,37 +52,30 @@ module.exports = function(data) {
}
};
sax.onprocessinginstruction = function(data) {
sax.onprocessinginstruction = function (data) {
pushToContent({
processinginstruction: data
processinginstruction: data,
});
};
sax.oncomment = function(comment) {
sax.oncomment = function (comment) {
pushToContent({
comment: comment.trim()
comment: comment.trim(),
});
};
sax.oncdata = function(cdata) {
sax.oncdata = function (cdata) {
pushToContent({
cdata: cdata
cdata: cdata,
});
};
sax.onopentag = function(data) {
sax.onopentag = function (data) {
var elem = {
elem: data.name,
prefix: data.prefix,
local: data.local,
attrs: {}
attrs: {},
};
elem.class = new CSSClassList(elem);
@ -94,12 +83,13 @@ module.exports = function(data) {
if (Object.keys(data.attributes).length) {
for (const [name, attr] of Object.entries(data.attributes)) {
if (name === 'class') { // has class attribute
if (name === 'class') {
// has class attribute
elem.class.hasClass();
}
if (name === 'style') { // has style attribute
if (name === 'style') {
// has style attribute
elem.style.hasStyle();
}
@ -107,7 +97,7 @@ module.exports = function(data) {
name: name,
value: attr.value,
prefix: attr.prefix,
local: attr.local
local: attr.local,
};
}
}
@ -116,10 +106,9 @@ module.exports = function(data) {
current = elem;
stack.push(elem);
};
sax.ontext = function(text) {
sax.ontext = function (text) {
// prevent trimming of meaningful whitespace inside textual tags
if (textElems.includes(current.elem) && !data.prefix) {
pushToContent({ text: text });
@ -128,18 +117,16 @@ module.exports = function(data) {
}
};
sax.onclosetag = function() {
sax.onclosetag = function () {
stack.pop();
current = stack[stack.length - 1];
};
sax.onerror = function(e) {
sax.onerror = function (e) {
e.message = 'Error in parsing SVG: ' + e.message;
if (e.message.indexOf('Unexpected end') < 0) {
throw e;
}
};
try {
@ -148,5 +135,4 @@ module.exports = function(data) {
} catch (e) {
return { error: e.message };
}
};

View File

@ -7,7 +7,7 @@
* @param {String} type Data URI type
* @return {String} output string
*/
exports.encodeSVGDatauri = function(str, type) {
exports.encodeSVGDatauri = function (str, type) {
var prefix = 'data:image/svg+xml';
if (!type || type === 'base64') {
// base64
@ -29,7 +29,7 @@ exports.encodeSVGDatauri = function(str, type) {
* @param {string} str input string
* @return {String} output string
*/
exports.decodeSVGDatauri = function(str) {
exports.decodeSVGDatauri = function (str) {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
var match = regexp.exec(str);
@ -51,8 +51,8 @@ exports.decodeSVGDatauri = function(str) {
return str;
};
exports.intersectArrays = function(a, b) {
return a.filter(function(n) {
exports.intersectArrays = function (a, b) {
return a.filter(function (n) {
return b.indexOf(n) > -1;
});
};
@ -68,12 +68,12 @@ exports.intersectArrays = function(a, b) {
* @param {string?} command path data instruction
* @return {string}
*/
exports.cleanupOutData = function(data, params, command) {
exports.cleanupOutData = function (data, params, command) {
var str = '',
delimiter,
prev;
data.forEach(function(item, i) {
data.forEach(function (item, i) {
// space delimiter by default
delimiter = ' ';
@ -99,9 +99,7 @@ exports.cleanupOutData = function(data, params, command) {
if (
params.negativeExtraSpace &&
delimiter != '' &&
(item < 0 ||
(String(item).charCodeAt(0) == 46 && prev % 1 !== 0)
)
(item < 0 || (String(item).charCodeAt(0) == 46 && prev % 1 !== 0))
) {
delimiter = '';
}
@ -125,7 +123,7 @@ exports.cleanupOutData = function(data, params, command) {
*
* @return {String} output number as string
*/
var removeLeadingZero = exports.removeLeadingZero = function(num) {
var removeLeadingZero = function (num) {
var strNum = num.toString();
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
@ -135,3 +133,4 @@ var removeLeadingZero = exports.removeLeadingZero = function(num) {
}
return strNum;
};
exports.removeLeadingZero = removeLeadingZero;