Comparing version 2.1.0 to 2.2.0
'use strict'; | ||
var csstree = require('css-tree'), | ||
List = csstree.List, | ||
stable = require('stable'), | ||
specificity = require('csso/lib/restructure/prepare/specificity'); | ||
var csstree = require('css-tree'), | ||
List = csstree.List, | ||
stable = require('stable'), | ||
specificity = require('csso/lib/restructure/prepare/specificity'); | ||
/** | ||
@@ -16,35 +15,44 @@ * Flatten a CSS AST to a selectors list. | ||
function flattenToSelectors(cssAst) { | ||
var selectors = []; | ||
var selectors = []; | ||
csstree.walk(cssAst, {visit: 'Rule', enter: function(node) { | ||
if (node.type !== 'Rule') { | ||
return; | ||
} | ||
csstree.walk(cssAst, { | ||
visit: 'Rule', | ||
enter: function (node) { | ||
if (node.type !== 'Rule') { | ||
return; | ||
} | ||
var atrule = this.atrule; | ||
var rule = node; | ||
var atrule = this.atrule; | ||
var rule = node; | ||
node.prelude.children.each(function(selectorNode, selectorItem) { | ||
var selector = { | ||
item: selectorItem, | ||
atrule: atrule, | ||
rule: rule, | ||
pseudos: [] | ||
}; | ||
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 | ||
}); | ||
} | ||
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; | ||
selectors.push(selector); | ||
}); | ||
}, | ||
}); | ||
return selectors; | ||
} | ||
@@ -60,17 +68,19 @@ | ||
function filterByMqs(selectors, useMqs) { | ||
return selectors.filter(function(selector) { | ||
if (selector.atrule === null) { | ||
return ~useMqs.indexOf(''); | ||
} | ||
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(' '); | ||
} | ||
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); | ||
}); | ||
return ~useMqs.indexOf(mqStr); | ||
}); | ||
} | ||
@@ -86,11 +96,13 @@ | ||
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); | ||
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); | ||
}); | ||
} | ||
@@ -105,10 +117,9 @@ | ||
function cleanPseudos(selectors) { | ||
selectors.forEach(function(selector) { | ||
selector.pseudos.forEach(function(pseudo) { | ||
pseudo.list.remove(pseudo.item); | ||
}); | ||
selectors.forEach(function (selector) { | ||
selector.pseudos.forEach(function (pseudo) { | ||
pseudo.list.remove(pseudo.item); | ||
}); | ||
}); | ||
} | ||
/** | ||
@@ -123,14 +134,13 @@ * Compares two selector specificities. | ||
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; | ||
} | ||
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; | ||
return 0; | ||
} | ||
/** | ||
@@ -144,12 +154,11 @@ * Compare two simple selectors. | ||
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) { | ||
var aSpecificity = specificity(aSimpleSelectorNode), | ||
bSpecificity = specificity(bSimpleSelectorNode); | ||
return compareSpecificity(aSpecificity, bSpecificity); | ||
var aSpecificity = specificity(aSimpleSelectorNode), | ||
bSpecificity = specificity(bSimpleSelectorNode); | ||
return compareSpecificity(aSpecificity, bSpecificity); | ||
} | ||
function _bySelectorSpecificity(selectorA, selectorB) { | ||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data); | ||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data); | ||
} | ||
/** | ||
@@ -162,6 +171,5 @@ * Sort selectors stably by their specificity. | ||
function sortSelectors(selectors) { | ||
return stable(selectors, _bySelectorSpecificity); | ||
return stable(selectors, _bySelectorSpecificity); | ||
} | ||
/** | ||
@@ -174,13 +182,12 @@ * Convert a css-tree AST style declaration to 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 | ||
}; | ||
var propertyName = declaration.property, | ||
propertyValue = csstree.generate(declaration.value), | ||
propertyPriority = declaration.important ? 'important' : ''; | ||
return { | ||
name: propertyName, | ||
value: propertyValue, | ||
priority: propertyPriority, | ||
}; | ||
} | ||
/** | ||
@@ -193,3 +200,3 @@ * Gets the CSS string of a style element | ||
function getCssStr(elem) { | ||
return elem.content[0].text || elem.content[0].cdata || []; | ||
return elem.content[0].text || elem.content[0].cdata || []; | ||
} | ||
@@ -205,14 +212,13 @@ | ||
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 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; | ||
// in case of text field + if nothing was set yet | ||
elem.content[0].text = css; | ||
return elem.content[0].text; | ||
} | ||
module.exports.flattenToSelectors = flattenToSelectors; | ||
@@ -219,0 +225,0 @@ |
@@ -11,3 +11,3 @@ 'use strict'; | ||
const importConfig = async configFile => { | ||
const importConfig = async (configFile) => { | ||
const config = require(configFile); | ||
@@ -27,3 +27,3 @@ if (config == null || typeof config !== 'object' || Array.isArray(config)) { | ||
} | ||
} | ||
}; | ||
@@ -41,3 +41,3 @@ const loadConfig = async (configFile, cwd = process.cwd()) => { | ||
while (true) { | ||
const file = path.join(dir, "svgo.config.js"); | ||
const file = path.join(dir, 'svgo.config.js'); | ||
if (await isFile(file)) { | ||
@@ -44,0 +44,0 @@ return await importConfig(file); |
@@ -16,3 +16,3 @@ 'use strict'; | ||
resolvePluginConfig, | ||
extendDefaultPlugins | ||
extendDefaultPlugins, | ||
} = require('./svgo/config.js'); | ||
@@ -32,3 +32,3 @@ const svg2js = require('./svgo/svg2js.js'); | ||
if (typeof config !== 'object') { | ||
throw Error('Config should be an object') | ||
throw Error('Config should be an object'); | ||
} | ||
@@ -38,3 +38,3 @@ const maxPassCount = config.multipass ? 10 : 1; | ||
let svgjs = null; | ||
const info = {} | ||
const info = {}; | ||
if (config.path != null) { | ||
@@ -54,5 +54,9 @@ info.path = config.path; | ||
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); | ||
@@ -65,3 +69,3 @@ svgjs = js2svg(svgjs, config.js2svg); | ||
input = svgjs.data; | ||
prevResultSize = svgjs.data.length | ||
prevResultSize = svgjs.data.length; | ||
} else { | ||
@@ -68,0 +72,0 @@ if (config.datauri) { |
@@ -17,178 +17,224 @@ 'use strict'; | ||
function checkIsDir(path) { | ||
try { | ||
return FS.lstatSync(path).isDirectory(); | ||
} catch(e) { | ||
return false; | ||
} | ||
try { | ||
return FS.lstatSync(path).isDirectory(); | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
module.exports = function makeProgram(program) { | ||
program | ||
.name(PKG.name) | ||
.description(PKG.description, { | ||
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('--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('--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('--show-plugins', 'Show available plugins and exit') | ||
.action(action); | ||
} | ||
program | ||
.name(PKG.name) | ||
.description(PKG.description, { | ||
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('--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('--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('--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 input = opts.input || args; | ||
var output = opts.output; | ||
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) | ||
} else { | ||
opts.precision = number; | ||
} | ||
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); | ||
} 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 != 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.indent != null) { | ||
const number = Number.parseInt(opts.indent, 0); | ||
if (Number.isNaN(number)) { | ||
console.error("error: option '--indent' argument must be an integer number"); | ||
process.exit(1); | ||
} else { | ||
opts.indent = number; | ||
} | ||
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" | ||
); | ||
process.exit(1); | ||
} else { | ||
opts.indent = number; | ||
} | ||
} | ||
// --show-plugins | ||
if (opts.showPlugins) { | ||
showAvailablePlugins(); | ||
return; | ||
} | ||
// --show-plugins | ||
if (opts.showPlugins) { | ||
showAvailablePlugins(); | ||
return; | ||
} | ||
// w/o anything | ||
if ( | ||
(input.length === 0 || input[0] === '-') && | ||
!opts.string && | ||
!opts.stdin && | ||
!opts.folder && | ||
process.stdin.isTTY === true | ||
) { | ||
return command.help(); | ||
} | ||
// w/o anything | ||
if ( | ||
(input.length === 0 || input[0] === '-') && | ||
!opts.string && | ||
!opts.stdin && | ||
!opts.folder && | ||
process.stdin.isTTY === true | ||
) { | ||
return command.help(); | ||
} | ||
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.`); | ||
} | ||
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.` | ||
); | ||
} | ||
} | ||
// --config | ||
const loadedConfig = await loadConfig(opts.config); | ||
if (loadedConfig != null) { | ||
config = loadedConfig; | ||
} | ||
// --config | ||
const loadedConfig = await loadConfig(opts.config); | ||
if (loadedConfig != null) { | ||
config = loadedConfig; | ||
} | ||
// --quiet | ||
if (opts.quiet) { | ||
config.quiet = opts.quiet; | ||
} | ||
// --quiet | ||
if (opts.quiet) { | ||
config.quiet = opts.quiet; | ||
} | ||
// --recursive | ||
if (opts.recursive) { | ||
config.recursive = opts.recursive; | ||
} | ||
// --recursive | ||
if (opts.recursive) { | ||
config.recursive = opts.recursive; | ||
} | ||
// --precision | ||
if (opts.precision != null) { | ||
var precision = Math.min(Math.max(0, opts.precision), 20); | ||
config.floatPrecision = precision; | ||
} | ||
// --precision | ||
if (opts.precision != null) { | ||
var precision = Math.min(Math.max(0, opts.precision), 20); | ||
config.floatPrecision = precision; | ||
} | ||
// --multipass | ||
if (opts.multipass) { | ||
config.multipass = true; | ||
} | ||
// --multipass | ||
if (opts.multipass) { | ||
config.multipass = true; | ||
} | ||
// --pretty | ||
if (opts.pretty) { | ||
config.js2svg = config.js2svg || {}; | ||
config.js2svg.pretty = true; | ||
if (opts.indent != null) { | ||
config.js2svg.indent = opts.indent; | ||
} | ||
// --pretty | ||
if (opts.pretty) { | ||
config.js2svg = config.js2svg || {}; | ||
config.js2svg.pretty = true; | ||
if (opts.indent != null) { | ||
config.js2svg.indent = opts.indent; | ||
} | ||
} | ||
// --output | ||
if (output) { | ||
if (input.length && 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)); | ||
} | ||
// --output | ||
if (output) { | ||
if (input.length && 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 (input.length) { | ||
output = input; | ||
} else if (opts.string) { | ||
output = '-'; | ||
} else if (output.length < input.length) { | ||
output = output.concat(input.slice(output.length)); | ||
} | ||
} | ||
} else if (input.length) { | ||
output = input; | ||
} else if (opts.string) { | ||
output = '-'; | ||
} | ||
if (opts.datauri) { | ||
config.datauri = opts.datauri; | ||
} | ||
if (opts.datauri) { | ||
config.datauri = opts.datauri; | ||
} | ||
// --folder | ||
if (opts.folder) { | ||
var ouputFolder = output && output[0] || opts.folder; | ||
await optimizeFolder(config, opts.folder, ouputFolder); | ||
} | ||
// --folder | ||
if (opts.folder) { | ||
var ouputFolder = (output && output[0]) || opts.folder; | ||
await optimizeFolder(config, opts.folder, ouputFolder); | ||
} | ||
// --input | ||
if (input.length !== 0) { | ||
// STDIN | ||
if (input[0] === '-') { | ||
return new Promise((resolve, reject) => { | ||
var data = '', | ||
file = output[0]; | ||
// --input | ||
if (input.length !== 0) { | ||
// 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 { | ||
await Promise.all( | ||
input.map((file, n) => optimizeFile(config, file, output[n])) | ||
); | ||
} | ||
process.stdin | ||
.on('data', (chunk) => (data += chunk)) | ||
.once('end', () => | ||
processSVGData(config, { input: 'string' }, data, file).then( | ||
resolve, | ||
reject | ||
) | ||
); | ||
}); | ||
// file | ||
} else { | ||
await Promise.all( | ||
input.map((file, n) => optimizeFile(config, file, output[n])) | ||
); | ||
} | ||
// --string | ||
} else if (opts.string) { | ||
var data = decodeSVGDatauri(opts.string); | ||
} 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]); | ||
} | ||
} | ||
@@ -204,6 +250,8 @@ | ||
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)); | ||
if (!config.quiet) { | ||
console.log(`Processing directory '${dir}':\n`); | ||
} | ||
return FS.promises | ||
.readdir(dir) | ||
.then((files) => processDirectory(config, dir, files, output)); | ||
} | ||
@@ -220,8 +268,18 @@ | ||
function processDirectory(config, dir, files, output) { | ||
// take only *.svg files, recursively if necessary | ||
var svgFilesDescriptions = getFilesDescriptions(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.`) | ||
); | ||
} | ||
@@ -238,23 +296,28 @@ | ||
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), | ||
})); | ||
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; | ||
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; | ||
} | ||
@@ -270,6 +333,7 @@ | ||
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) | ||
); | ||
return FS.promises.readFile(file, 'utf8').then( | ||
(data) => | ||
processSVGData(config, { input: 'file', path: file }, data, output, file), | ||
(error) => checkOptimizeFileError(config, file, output, error) | ||
); | ||
} | ||
@@ -286,29 +350,38 @@ | ||
function processSVGData(config, info, data, output, input) { | ||
var startTime = Date.now(), | ||
prevFileSize = Buffer.byteLength(data, 'utf8'); | ||
var startTime = Date.now(), | ||
prevFileSize = Buffer.byteLength(data, 'utf8'); | ||
const result = optimize(data, { ...config, ...info }); | ||
if (result.error) { | ||
let message = result.error; | ||
if (result.path != null) { | ||
message += `\nFile: ${result.path}` | ||
} | ||
throw Error(message) | ||
const result = optimize(data, { ...config, ...info }); | ||
if (result.error) { | ||
let message = result.error; | ||
if (result.path != null) { | ||
message += `\nFile: ${result.path}`; | ||
} | ||
if (config.datauri) { | ||
result.data = encodeSVGDatauri(result.data, config.datauri); | ||
} | ||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'), | ||
processingTime = Date.now() - startTime; | ||
throw Error(message); | ||
} | ||
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); | ||
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))); | ||
(error) => | ||
Promise.reject( | ||
new Error( | ||
error.code === 'ENOTDIR' | ||
? `Error: output '${output}' is not a directory.` | ||
: error | ||
) | ||
) | ||
); | ||
} | ||
@@ -324,13 +397,14 @@ | ||
function writeOutput(input, output, data) { | ||
if (output == '-') { | ||
console.log(data); | ||
return Promise.resolve(); | ||
} | ||
if (output == '-') { | ||
console.log(data); | ||
return Promise.resolve(); | ||
} | ||
FS.mkdirSync(PATH.dirname(output), { recursive: true }); | ||
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)); | ||
} | ||
/** | ||
@@ -341,3 +415,3 @@ * Write a time taken by optimization. | ||
function printTimeInfo(time) { | ||
console.log(`Done in ${time} ms!`); | ||
console.log(`Done in ${time} ms!`); | ||
} | ||
@@ -351,10 +425,13 @@ | ||
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' + | ||
(profitPercents < 0 ? ' + ' : ' - ') + | ||
chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' + | ||
(Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB' | ||
); | ||
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' | ||
); | ||
} | ||
@@ -371,8 +448,10 @@ | ||
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); | ||
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); | ||
} | ||
@@ -389,7 +468,11 @@ | ||
function checkWriteFileError(input, output, data, error) { | ||
if (error.code == 'EISDIR' && input) { | ||
return FS.promises.writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8'); | ||
} else { | ||
return Promise.reject(error); | ||
} | ||
if (error.code == 'EISDIR' && input) { | ||
return FS.promises.writeFile( | ||
PATH.resolve(output, PATH.basename(input)), | ||
data, | ||
'utf8' | ||
); | ||
} else { | ||
return Promise.reject(error); | ||
} | ||
} | ||
@@ -401,9 +484,9 @@ | ||
function showAvailablePlugins() { | ||
const list = Object.entries(pluginsMap) | ||
.sort(([a], [b]) => a.localeCompare(b)) | ||
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
console.log('Currently available plugins:\n' + list); | ||
const list = Object.entries(pluginsMap) | ||
.sort(([a], [b]) => a.localeCompare(b)) | ||
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
console.log('Currently available plugins:\n' + list); | ||
} | ||
module.exports.checkIsDir = checkIsDir; |
@@ -56,7 +56,10 @@ 'use strict'; | ||
]; | ||
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) { | ||
@@ -72,3 +75,3 @@ const resolvedPlugin = resolvePluginConfig(plugin, {}); | ||
return extendedPlugins; | ||
} | ||
}; | ||
exports.extendDefaultPlugins = extendDefaultPlugins; | ||
@@ -91,3 +94,3 @@ | ||
active: true, | ||
params: { ...pluginConfig.params, ...configParams } | ||
params: { ...pluginConfig.params, ...configParams }, | ||
}; | ||
@@ -104,3 +107,3 @@ } | ||
...plugin, | ||
params: { configParams, ...plugin.params } | ||
params: { configParams, ...plugin.params }, | ||
}; | ||
@@ -117,3 +120,3 @@ } else { | ||
...plugin, | ||
params: { ...pluginConfig.params, ...configParams, ...plugin.params } | ||
params: { ...pluginConfig.params, ...configParams, ...plugin.params }, | ||
}; | ||
@@ -120,0 +123,0 @@ } |
'use strict'; | ||
var CSSClassList = function(node) { | ||
this.parentNode = node; | ||
this.classNames = new Set(); | ||
this.classAttr = null; | ||
//this.classValue = null; | ||
var CSSClassList = function (node) { | ||
this.parentNode = node; | ||
this.classNames = new Set(); | ||
this.classAttr = null; | ||
//this.classValue = null; | ||
}; | ||
@@ -15,42 +15,41 @@ | ||
*/ | ||
CSSClassList.prototype.clone = function(parentNode) { | ||
var node = this; | ||
var nodeData = {}; | ||
CSSClassList.prototype.clone = function (parentNode) { | ||
var node = this; | ||
var nodeData = {}; | ||
Object.keys(node).forEach(function(key) { | ||
if (key !== 'parentNode') { | ||
nodeData[key] = node[key]; | ||
} | ||
}); | ||
Object.keys(node).forEach(function (key) { | ||
if (key !== 'parentNode') { | ||
nodeData[key] = node[key]; | ||
} | ||
}); | ||
// Deep-clone node data. | ||
nodeData = JSON.parse(JSON.stringify(nodeData)); | ||
// Deep-clone node data. | ||
nodeData = JSON.parse(JSON.stringify(nodeData)); | ||
var clone = new CSSClassList(parentNode); | ||
Object.assign(clone, nodeData); | ||
return clone; | ||
var clone = new CSSClassList(parentNode); | ||
Object.assign(clone, nodeData); | ||
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(); | ||
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, | ||
}); | ||
Object.defineProperty(this.parentNode.attrs, 'class', { | ||
get: this.getClassAttr.bind(this), | ||
set: this.setClassAttr.bind(this), | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
this.addClassValueHandler(); | ||
this.addClassValueHandler(); | ||
}; | ||
@@ -60,75 +59,70 @@ | ||
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.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.getClassAttr = function () { | ||
return this.classAttr; | ||
}; | ||
CSSClassList.prototype.setClassAttr = function(newClassAttr) { | ||
this.setClassValue(newClassAttr.value); // must before applying value handler! | ||
CSSClassList.prototype.setClassAttr = function (newClassAttr) { | ||
this.setClassValue(newClassAttr.value); // must before applying value handler! | ||
this.classAttr = newClassAttr; | ||
this.addClassValueHandler(); | ||
this.classAttr = newClassAttr; | ||
this.addClassValueHandler(); | ||
}; | ||
CSSClassList.prototype.getClassValue = function() { | ||
var arrClassNames = Array.from(this.classNames); | ||
return arrClassNames.join(' '); | ||
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.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.add = function (/* variadic */) { | ||
this.hasClass(); | ||
Object.values(arguments).forEach(this._addSingle.bind(this)); | ||
}; | ||
CSSClassList.prototype._addSingle = function(className) { | ||
this.classNames.add(className); | ||
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.remove = function (/* variadic */) { | ||
this.hasClass(); | ||
Object.values(arguments).forEach(this._removeSingle.bind(this)); | ||
}; | ||
CSSClassList.prototype._removeSingle = function(className) { | ||
this.classNames.delete(className); | ||
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.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.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); | ||
CSSClassList.prototype.contains = function (className) { | ||
return this.classNames.has(className); | ||
}; | ||
module.exports = CSSClassList; |
@@ -9,41 +9,40 @@ 'use strict'; | ||
var svgoCssSelectAdapterMin = { | ||
// is the node a tag? | ||
// isTag: ( node:Node ) => isTag:Boolean | ||
isTag: function (node) { | ||
return node.isElem(); | ||
}, | ||
// 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 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 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 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 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; | ||
} | ||
// 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; | ||
}, | ||
}; | ||
@@ -50,0 +49,0 @@ |
'use strict'; | ||
var csstree = require('css-tree'), | ||
csstools = require('../css-tools'); | ||
csstools = require('../css-tools'); | ||
var CSSStyleDeclaration = function (node) { | ||
this.parentNode = node; | ||
var CSSStyleDeclaration = function(node) { | ||
this.parentNode = node; | ||
this.properties = new Map(); | ||
this.hasSynced = false; | ||
this.properties = new Map(); | ||
this.hasSynced = false; | ||
this.styleAttr = null; | ||
this.styleValue = null; | ||
this.styleAttr = null; | ||
this.styleValue = null; | ||
this.parseError = false; | ||
this.parseError = false; | ||
}; | ||
@@ -24,44 +23,41 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.clone = function(parentNode) { | ||
var node = this; | ||
var nodeData = {}; | ||
CSSStyleDeclaration.prototype.clone = function (parentNode) { | ||
var node = this; | ||
var nodeData = {}; | ||
Object.keys(node).forEach(function(key) { | ||
if (key !== 'parentNode') { | ||
nodeData[key] = node[key]; | ||
} | ||
}); | ||
Object.keys(node).forEach(function (key) { | ||
if (key !== 'parentNode') { | ||
nodeData[key] = node[key]; | ||
} | ||
}); | ||
// Deep-clone node data. | ||
nodeData = JSON.parse(JSON.stringify(nodeData)); | ||
// Deep-clone node data. | ||
nodeData = JSON.parse(JSON.stringify(nodeData)); | ||
var clone = new CSSStyleDeclaration(parentNode); | ||
Object.assign(clone, nodeData); | ||
return clone; | ||
var clone = new CSSStyleDeclaration(parentNode); | ||
Object.assign(clone, nodeData); | ||
return clone; | ||
}; | ||
CSSStyleDeclaration.prototype.hasStyle = function() { | ||
this.addStyleHandler(); | ||
CSSStyleDeclaration.prototype.hasStyle = function () { | ||
this.addStyleHandler(); | ||
}; | ||
// attr.style | ||
CSSStyleDeclaration.prototype.addStyleHandler = function() { | ||
CSSStyleDeclaration.prototype.addStyleHandler = function () { | ||
this.styleAttr = { | ||
// empty style attr | ||
name: 'style', | ||
value: null, | ||
}; | ||
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, | ||
}); | ||
Object.defineProperty(this.parentNode.attrs, 'style', { | ||
get: this.getStyleAttr.bind(this), | ||
set: this.setStyleAttr.bind(this), | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
this.addStyleValueHandler(); | ||
this.addStyleValueHandler(); | ||
}; | ||
@@ -71,74 +67,73 @@ | ||
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.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.getStyleAttr = function () { | ||
return this.styleAttr; | ||
}; | ||
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) { | ||
this.setStyleValue(newStyleAttr.value); // must before applying value handler! | ||
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 | ||
this.styleAttr = newStyleAttr; | ||
this.addStyleValueHandler(); | ||
this.hasSynced = false; // raw css changed | ||
}; | ||
CSSStyleDeclaration.prototype.getStyleValue = function() { | ||
return this.getCssText(); | ||
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.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; | ||
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 = {}; | ||
var self = this; | ||
declarations.children.each(function (declaration) { | ||
try { | ||
declarations = csstree.parse(inlineCssStr, { | ||
context: 'declarationList', | ||
parseValue: false | ||
}); | ||
} catch (parseError) { | ||
this.parseError = parseError; | ||
return; | ||
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; | ||
} | ||
} | ||
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 | ||
@@ -151,35 +146,39 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.getCssText = function() { | ||
var properties = this.getProperties(); | ||
CSSStyleDeclaration.prototype.getCssText = function () { | ||
var properties = this.getProperties(); | ||
if (this.parseError) { | ||
// in case of a parse error, pass through original styles | ||
return this.styleValue; | ||
} | ||
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(';'); | ||
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._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.'); | ||
} | ||
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 properties = this.getProperties(); | ||
this._handleParseError(); | ||
var property = properties.get(propertyName.trim()); | ||
return property; | ||
var property = properties.get(propertyName.trim()); | ||
return property; | ||
}; | ||
@@ -193,5 +192,5 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) { | ||
var property = this._getProperty(propertyName); | ||
return property ? property.priority : ''; | ||
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) { | ||
var property = this._getProperty(propertyName); | ||
return property ? property.priority : ''; | ||
}; | ||
@@ -205,5 +204,5 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) { | ||
var property = this._getProperty(propertyName); | ||
return property ? property.value : null; | ||
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) { | ||
var property = this._getProperty(propertyName); | ||
return property ? property.value : null; | ||
}; | ||
@@ -217,11 +216,11 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.item = function(index) { | ||
if(typeof index === 'undefined') { | ||
throw Error('1 argument required, but only 0 present.'); | ||
} | ||
CSSStyleDeclaration.prototype.item = function (index) { | ||
if (typeof index === 'undefined') { | ||
throw Error('1 argument required, but only 0 present.'); | ||
} | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
return Array.from(properties.keys())[index]; | ||
return Array.from(properties.keys())[index]; | ||
}; | ||
@@ -234,8 +233,7 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.getProperties = function() { | ||
this._loadCssText(); | ||
return this.properties; | ||
CSSStyleDeclaration.prototype.getProperties = function () { | ||
this._loadCssText(); | ||
return this.properties; | ||
}; | ||
// writes to properties | ||
@@ -249,15 +247,15 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.removeProperty = function(propertyName) { | ||
if(typeof propertyName === 'undefined') { | ||
throw Error('1 argument required, but only 0 present.'); | ||
} | ||
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) { | ||
if (typeof propertyName === 'undefined') { | ||
throw Error('1 argument required, but only 0 present.'); | ||
} | ||
this.hasStyle(); | ||
this.hasStyle(); | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
var oldValue = this.getPropertyValue(propertyName); | ||
properties.delete(propertyName.trim()); | ||
return oldValue; | ||
var oldValue = this.getPropertyValue(propertyName); | ||
properties.delete(propertyName.trim()); | ||
return oldValue; | ||
}; | ||
@@ -273,22 +271,25 @@ | ||
*/ | ||
CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priority) { | ||
if(typeof propertyName === 'undefined') { | ||
throw Error('propertyName argument required, but only not present.'); | ||
} | ||
CSSStyleDeclaration.prototype.setProperty = function ( | ||
propertyName, | ||
value, | ||
priority | ||
) { | ||
if (typeof propertyName === 'undefined') { | ||
throw Error('propertyName argument required, but only not present.'); | ||
} | ||
this.hasStyle(); | ||
this.hasStyle(); | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
var properties = this.getProperties(); | ||
this._handleParseError(); | ||
var property = { | ||
value: value.trim(), | ||
priority: priority.trim() | ||
}; | ||
properties.set(propertyName.trim(), property); | ||
var property = { | ||
value: value.trim(), | ||
priority: priority.trim(), | ||
}; | ||
properties.set(propertyName.trim(), property); | ||
return property; | ||
return property; | ||
}; | ||
module.exports = CSSStyleDeclaration; |
'use strict'; | ||
var EOL = require('os').EOL, | ||
textElems = require('../../plugins/_collections.js').textElems; | ||
textElems = require('../../plugins/_collections.js').textElems; | ||
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 | ||
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 = { | ||
'&': '&', | ||
'\'': ''', | ||
'"': '"', | ||
'>': '>', | ||
'<': '<', | ||
}; | ||
'&': '&', | ||
"'": ''', | ||
'"': '"', | ||
'>': '>', | ||
'<': '<', | ||
}; | ||
@@ -49,41 +49,37 @@ /** | ||
*/ | ||
module.exports = function(data, config) { | ||
return new JS2SVG(config).convert(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); | ||
} | ||
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 = ' '; | ||
} | ||
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; | ||
} | ||
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; | ||
this.indentLevel = 0; | ||
this.textContext = null; | ||
} | ||
function encodeEntity(char) { | ||
return entities[char]; | ||
return entities[char]; | ||
} | ||
@@ -98,40 +94,34 @@ | ||
*/ | ||
JS2SVG.prototype.convert = function(data) { | ||
JS2SVG.prototype.convert = function (data) { | ||
var svg = ''; | ||
var svg = ''; | ||
if (data.content) { | ||
this.indentLevel++; | ||
if (data.content) { | ||
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++; | ||
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 | ||
} | ||
}; | ||
return { | ||
data: svg, | ||
info: { | ||
width: this.width, | ||
height: this.height, | ||
}, | ||
}; | ||
}; | ||
@@ -144,12 +134,10 @@ | ||
*/ | ||
JS2SVG.prototype.createIndent = function() { | ||
JS2SVG.prototype.createIndent = function () { | ||
var indent = ''; | ||
var indent = ''; | ||
if (this.config.pretty && !this.textContext) { | ||
indent = this.config.indent.repeat(this.indentLevel - 1); | ||
} | ||
if (this.config.pretty && !this.textContext) { | ||
indent = this.config.indent.repeat(this.indentLevel - 1); | ||
} | ||
return indent; | ||
return indent; | ||
}; | ||
@@ -164,8 +152,4 @@ | ||
*/ | ||
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; | ||
}; | ||
@@ -180,10 +164,10 @@ | ||
*/ | ||
JS2SVG.prototype.createProcInst = function(instruction) { | ||
return this.config.procInstStart + | ||
instruction.name + | ||
' ' + | ||
instruction.body + | ||
this.config.procInstEnd; | ||
JS2SVG.prototype.createProcInst = function (instruction) { | ||
return ( | ||
this.config.procInstStart + | ||
instruction.name + | ||
' ' + | ||
instruction.body + | ||
this.config.procInstEnd | ||
); | ||
}; | ||
@@ -198,8 +182,4 @@ | ||
*/ | ||
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; | ||
}; | ||
@@ -214,9 +194,6 @@ | ||
*/ | ||
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 | ||
); | ||
}; | ||
@@ -231,76 +208,75 @@ | ||
*/ | ||
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')) { | ||
this.width = data.attr('width').value; | ||
this.height = data.attr('height').value; | ||
} | ||
// 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 | ||
); | ||
} | ||
// 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(), | ||
closeIndent = this.createIndent(), | ||
processedData = '', | ||
dataEnd = ''; | ||
} else { | ||
var tagOpenStart = this.config.tagOpenStart, | ||
tagOpenEnd = this.config.tagOpenEnd, | ||
tagCloseStart = this.config.tagCloseStart, | ||
tagCloseEnd = this.config.tagCloseEnd, | ||
openIndent = this.createIndent(), | ||
closeIndent = this.createIndent(), | ||
processedData = '', | ||
dataEnd = ''; | ||
if (this.textContext) { | ||
tagOpenStart = defaults.tagOpenStart; | ||
tagOpenEnd = defaults.tagOpenEnd; | ||
tagCloseStart = defaults.tagCloseStart; | ||
tagCloseEnd = defaults.tagCloseEnd; | ||
openIndent = ''; | ||
} else if (data.isElem(textElems)) { | ||
tagOpenEnd = defaults.tagOpenEnd; | ||
tagCloseStart = defaults.tagCloseStart; | ||
closeIndent = ''; | ||
this.textContext = data; | ||
} | ||
if (this.textContext) { | ||
tagOpenStart = defaults.tagOpenStart; | ||
tagOpenEnd = defaults.tagOpenEnd; | ||
tagCloseStart = defaults.tagCloseStart; | ||
tagCloseEnd = defaults.tagCloseEnd; | ||
openIndent = ''; | ||
} else if (data.isElem(textElems)) { | ||
tagOpenEnd = defaults.tagOpenEnd; | ||
tagCloseStart = defaults.tagCloseStart; | ||
closeIndent = ''; | ||
this.textContext = data; | ||
} | ||
processedData += this.convert(data).data; | ||
processedData += this.convert(data).data; | ||
if (this.textContext == data) { | ||
this.textContext = null; | ||
} | ||
return openIndent + | ||
tagOpenStart + | ||
data.elem + | ||
this.createAttrs(data) + | ||
tagOpenEnd + | ||
processedData + | ||
dataEnd + | ||
closeIndent + | ||
tagCloseStart + | ||
data.elem + | ||
tagCloseEnd; | ||
if (this.textContext == data) { | ||
this.textContext = null; | ||
} | ||
return ( | ||
openIndent + | ||
tagOpenStart + | ||
data.elem + | ||
this.createAttrs(data) + | ||
tagOpenEnd + | ||
processedData + | ||
dataEnd + | ||
closeIndent + | ||
tagCloseStart + | ||
data.elem + | ||
tagCloseEnd | ||
); | ||
} | ||
}; | ||
@@ -315,25 +291,22 @@ | ||
*/ | ||
JS2SVG.prototype.createAttrs = function(elem) { | ||
JS2SVG.prototype.createAttrs = function (elem) { | ||
var attrs = ''; | ||
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); | ||
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; | ||
return attrs; | ||
}; | ||
@@ -348,9 +321,9 @@ | ||
*/ | ||
JS2SVG.prototype.createText = function(text) { | ||
return this.createIndent() + | ||
this.config.textStart + | ||
text.replace(this.config.regEntities, this.config.encodeEntity) + | ||
(this.textContext ? '' : this.config.textEnd); | ||
JS2SVG.prototype.createText = function (text) { | ||
return ( | ||
this.createIndent() + | ||
this.config.textStart + | ||
text.replace(this.config.regEntities, this.config.encodeEntity) + | ||
(this.textContext ? '' : this.config.textEnd) | ||
); | ||
}; |
@@ -8,14 +8,15 @@ 'use strict'; | ||
xmlMode: true, | ||
adapter: svgoCssSelectAdapter | ||
adapter: svgoCssSelectAdapter, | ||
}; | ||
var JSAPI = module.exports = function(data, parentNode) { | ||
Object.assign(this, data); | ||
if (parentNode) { | ||
Object.defineProperty(this, 'parentNode', { | ||
writable: true, | ||
value: parentNode | ||
}); | ||
} | ||
var JSAPI = function (data, parentNode) { | ||
Object.assign(this, data); | ||
if (parentNode) { | ||
Object.defineProperty(this, 'parentNode', { | ||
writable: true, | ||
value: parentNode, | ||
}); | ||
} | ||
}; | ||
module.exports = JSAPI; | ||
@@ -27,35 +28,35 @@ /** | ||
*/ | ||
JSAPI.prototype.clone = function() { | ||
var node = this; | ||
var nodeData = {}; | ||
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]; | ||
} | ||
}); | ||
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)); | ||
// 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); | ||
// 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; | ||
}); | ||
} | ||
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; | ||
return clonedNode; | ||
}; | ||
@@ -70,10 +71,8 @@ | ||
*/ | ||
JSAPI.prototype.isElem = function(param) { | ||
JSAPI.prototype.isElem = function (param) { | ||
if (!param) return !!this.elem; | ||
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; | ||
return !!this.elem && this.elem === param; | ||
}; | ||
@@ -87,9 +86,6 @@ | ||
*/ | ||
JSAPI.prototype.renameElem = function(name) { | ||
JSAPI.prototype.renameElem = function (name) { | ||
if (name && typeof name === 'string') this.elem = this.local = name; | ||
if (name && typeof name === 'string') | ||
this.elem = this.local = name; | ||
return this; | ||
return this; | ||
}; | ||
@@ -102,6 +98,4 @@ | ||
*/ | ||
JSAPI.prototype.isEmpty = function() { | ||
return !this.content || !this.content.length; | ||
JSAPI.prototype.isEmpty = function () { | ||
return !this.content || !this.content.length; | ||
}; | ||
@@ -115,8 +109,8 @@ | ||
*/ | ||
JSAPI.prototype.closestElem = function(elemName) { | ||
var elem = this; | ||
JSAPI.prototype.closestElem = function (elemName) { | ||
var elem = this; | ||
while ((elem = elem.parentNode) && !elem.isElem(elemName)); | ||
while ((elem = elem.parentNode) && !elem.isElem(elemName)); | ||
return elem; | ||
return elem; | ||
}; | ||
@@ -132,14 +126,13 @@ | ||
*/ | ||
JSAPI.prototype.spliceContent = function(start, n, insertion) { | ||
JSAPI.prototype.spliceContent = function (start, n, insertion) { | ||
if (arguments.length < 2) return []; | ||
if (arguments.length < 2) return []; | ||
if (!Array.isArray(insertion)) | ||
insertion = Array.apply(null, arguments).slice(2); | ||
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)); | ||
return this.content.splice.apply(this.content, [start, n].concat(insertion)); | ||
}; | ||
@@ -155,12 +148,11 @@ | ||
*/ | ||
JSAPI.prototype.hasAttr = function(name, val) { | ||
JSAPI.prototype.hasAttr = function (name, val) { | ||
if (!this.attrs || !Object.keys(this.attrs).length) return false; | ||
if (!this.attrs || !Object.keys(this.attrs).length) return false; | ||
if (!arguments.length) return !!this.attrs; | ||
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]; | ||
return !!this.attrs[name]; | ||
}; | ||
@@ -176,35 +168,40 @@ | ||
*/ | ||
JSAPI.prototype.hasAttrLocal = function(localName, val) { | ||
JSAPI.prototype.hasAttrLocal = function (localName, val) { | ||
if (!this.attrs || !Object.keys(this.attrs).length) return false; | ||
if (!this.attrs || !Object.keys(this.attrs).length) return false; | ||
if (!arguments.length) return !!this.attrs; | ||
if (!arguments.length) return !!this.attrs; | ||
var callback; | ||
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); | ||
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 nameTest(attr) { | ||
return attr.local === localName; | ||
} | ||
function stringValueTest(attr) { | ||
return attr.local === localName && val == attr.value; | ||
} | ||
function stringValueTest(attr) { | ||
return attr.local === localName && val == attr.value; | ||
} | ||
function regexpValueTest(attr) { | ||
return attr.local === localName && val.test(attr.value); | ||
} | ||
function regexpValueTest(attr) { | ||
return attr.local === localName && val.test(attr.value); | ||
} | ||
function funcValueTest(attr) { | ||
return attr.local === localName && val(attr.value); | ||
} | ||
function funcValueTest(attr) { | ||
return attr.local === localName && val(attr.value); | ||
} | ||
}; | ||
@@ -220,10 +217,9 @@ | ||
*/ | ||
JSAPI.prototype.attr = function(name, val) { | ||
JSAPI.prototype.attr = function (name, val) { | ||
if (!this.hasAttr() || !arguments.length) return undefined; | ||
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]; | ||
return this.attrs[name]; | ||
}; | ||
@@ -237,13 +233,16 @@ | ||
*/ | ||
JSAPI.prototype.computedAttr = function(name, val) { | ||
if (!arguments.length) return; | ||
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; | ||
} | ||
if (val != null) { | ||
return elem ? elem.hasAttr(name, val) : false; | ||
} else if (elem && elem.hasAttr(name)) { | ||
return elem.attrs[name].value; | ||
} | ||
}; | ||
@@ -258,21 +257,19 @@ | ||
*/ | ||
JSAPI.prototype.removeAttr = function(name, val, recursive) { | ||
JSAPI.prototype.removeAttr = function (name, val, recursive) { | ||
if (!arguments.length) return false; | ||
if (!arguments.length) return false; | ||
if (Array.isArray(name)) { | ||
name.forEach(this.removeAttr, this); | ||
return false; | ||
} | ||
if (Array.isArray(name)) { | ||
name.forEach(this.removeAttr, this); | ||
return false; | ||
} | ||
if (!this.hasAttr(name)) return false; | ||
if (!this.hasAttr(name)) return false; | ||
if (!recursive && val && this.attrs[name].value !== val) return false; | ||
if (!recursive && val && this.attrs[name].value !== val) return false; | ||
delete this.attrs[name]; | ||
delete this.attrs[name]; | ||
if (!Object.keys(this.attrs).length) delete this.attrs; | ||
if (!Object.keys(this.attrs).length) delete this.attrs; | ||
return true; | ||
return true; | ||
}; | ||
@@ -286,23 +283,26 @@ | ||
*/ | ||
JSAPI.prototype.addAttr = function(attr) { | ||
attr = attr || {}; | ||
JSAPI.prototype.addAttr = function (attr) { | ||
attr = attr || {}; | ||
if (attr.name === undefined || | ||
attr.prefix === undefined || | ||
attr.local === undefined | ||
) return false; | ||
if ( | ||
attr.name === undefined || | ||
attr.prefix === undefined || | ||
attr.local === undefined | ||
) | ||
return false; | ||
this.attrs = this.attrs || {}; | ||
this.attrs[attr.name] = attr; | ||
this.attrs = this.attrs || {}; | ||
this.attrs[attr.name] = attr; | ||
if(attr.name === 'class') { // newly added class attribute | ||
this.class.hasClass(); | ||
} | ||
if (attr.name === 'class') { | ||
// newly added class attribute | ||
this.class.hasClass(); | ||
} | ||
if(attr.name === 'style') { // newly added style attribute | ||
this.style.hasStyle(); | ||
} | ||
if (attr.name === 'style') { | ||
// newly added style attribute | ||
this.style.hasStyle(); | ||
} | ||
return this.attrs[attr.name]; | ||
return this.attrs[attr.name]; | ||
}; | ||
@@ -317,12 +317,10 @@ | ||
*/ | ||
JSAPI.prototype.eachAttr = function(callback, context) { | ||
JSAPI.prototype.eachAttr = function (callback, context) { | ||
if (!this.hasAttr()) return false; | ||
if (!this.hasAttr()) return false; | ||
for (const attr of Object.values(this.attrs)) { | ||
callback.call(context, attr); | ||
} | ||
for (var name in this.attrs) { | ||
callback.call(context, this.attrs[name]); | ||
} | ||
return true; | ||
return true; | ||
}; | ||
@@ -337,12 +335,10 @@ | ||
*/ | ||
JSAPI.prototype.someAttr = function(callback, context) { | ||
JSAPI.prototype.someAttr = function (callback, context) { | ||
if (!this.hasAttr()) return false; | ||
if (!this.hasAttr()) return false; | ||
for (const attr of Object.values(this.attrs)) { | ||
if (callback.call(context, attr)) return true; | ||
} | ||
for (var name in this.attrs) { | ||
if (callback.call(context, this.attrs[name])) return true; | ||
} | ||
return false; | ||
return false; | ||
}; | ||
@@ -356,8 +352,6 @@ | ||
*/ | ||
JSAPI.prototype.querySelectorAll = function(selectors) { | ||
JSAPI.prototype.querySelectorAll = function (selectors) { | ||
var matchedEls = selectAll(selectors, this, cssSelectOpts); | ||
var matchedEls = selectAll(selectors, this, cssSelectOpts); | ||
return matchedEls.length > 0 ? matchedEls : null; | ||
return matchedEls.length > 0 ? matchedEls : null; | ||
}; | ||
@@ -371,6 +365,4 @@ | ||
*/ | ||
JSAPI.prototype.querySelector = function(selectors) { | ||
return selectOne(selectors, this, cssSelectOpts); | ||
JSAPI.prototype.querySelector = function (selectors) { | ||
return selectOne(selectors, this, cssSelectOpts); | ||
}; | ||
@@ -384,6 +376,4 @@ | ||
*/ | ||
JSAPI.prototype.matches = function(selector) { | ||
return is(this, selector, cssSelectOpts); | ||
JSAPI.prototype.matches = function (selector) { | ||
return is(this, selector, cssSelectOpts); | ||
}; |
@@ -13,3 +13,3 @@ 'use strict'; | ||
*/ | ||
module.exports = function(data, info, plugins) { | ||
module.exports = function (data, info, plugins) { | ||
// Try to group sequential elements of plugins array | ||
@@ -28,3 +28,3 @@ // to optimize ast traversing | ||
for (const group of groups) { | ||
switch(group[0].type) { | ||
switch (group[0].type) { | ||
case 'perItem': | ||
@@ -54,38 +54,32 @@ data = perItem(data, info, group); | ||
function perItem(data, info, plugins, reverse) { | ||
function monkeys(items) { | ||
items.content = items.content.filter(function (item) { | ||
// reverse pass | ||
if (reverse && item.content) { | ||
monkeys(item); | ||
} | ||
function monkeys(items) { | ||
// main filter | ||
var filter = true; | ||
items.content = items.content.filter(function(item) { | ||
for (var i = 0; filter && i < plugins.length; i++) { | ||
var plugin = plugins[i]; | ||
// reverse pass | ||
if (reverse && item.content) { | ||
monkeys(item); | ||
} | ||
if (plugin.active && plugin.fn(item, plugin.params, info) === false) { | ||
filter = false; | ||
} | ||
} | ||
// main filter | ||
var filter = true; | ||
// direct pass | ||
if (!reverse && item.content) { | ||
monkeys(item); | ||
} | ||
for (var i = 0; filter && i < plugins.length; i++) { | ||
var plugin = plugins[i]; | ||
return filter; | ||
}); | ||
if (plugin.active && plugin.fn(item, plugin.params, info) === false) { | ||
filter = false; | ||
} | ||
} | ||
return items; | ||
} | ||
// direct pass | ||
if (!reverse && item.content) { | ||
monkeys(item); | ||
} | ||
return filter; | ||
}); | ||
return items; | ||
} | ||
return monkeys(data); | ||
return monkeys(data); | ||
} | ||
@@ -102,11 +96,9 @@ | ||
function full(data, info, plugins) { | ||
plugins.forEach(function (plugin) { | ||
if (plugin.active) { | ||
data = plugin.fn(data, plugin.params, info); | ||
} | ||
}); | ||
plugins.forEach(function(plugin) { | ||
if (plugin.active) { | ||
data = plugin.fn(data, plugin.params, info); | ||
} | ||
}); | ||
return data; | ||
return data; | ||
} |
'use strict'; | ||
var SAX = require('@trysound/sax'), | ||
JSAPI = require('./jsAPI.js'), | ||
CSSClassList = require('./css-class-list'), | ||
CSSStyleDeclaration = require('./css-style-declaration'), | ||
textElems = require('../../plugins/_collections.js').textElems, | ||
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g; | ||
JSAPI = require('./jsAPI.js'), | ||
CSSClassList = require('./css-class-list'), | ||
CSSStyleDeclaration = require('./css-style-declaration'), | ||
textElems = require('../../plugins/_collections.js').textElems, | ||
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g; | ||
var config = { | ||
strict: true, | ||
trim: false, | ||
normalize: false, | ||
lowercase: true, | ||
xmlns: true, | ||
position: true | ||
strict: true, | ||
trim: false, | ||
normalize: false, | ||
lowercase: true, | ||
xmlns: true, | ||
position: true, | ||
}; | ||
@@ -24,130 +24,116 @@ | ||
*/ | ||
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]; | ||
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); | ||
function pushToContent(content) { | ||
(current.content = current.content || []).push(content); | ||
content = new JSAPI(content, current); | ||
return content; | ||
} | ||
(current.content = current.content || []).push(content); | ||
sax.ondoctype = function (doctype) { | ||
pushToContent({ | ||
doctype: doctype, | ||
}); | ||
return content; | ||
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.ondoctype = function(doctype) { | ||
sax.onprocessinginstruction = function (data) { | ||
pushToContent({ | ||
processinginstruction: data, | ||
}); | ||
}; | ||
pushToContent({ | ||
doctype: doctype | ||
}); | ||
sax.oncomment = function (comment) { | ||
pushToContent({ | ||
comment: comment.trim(), | ||
}); | ||
}; | ||
var subsetStart = doctype.indexOf('['), | ||
entityMatch; | ||
sax.oncdata = function (cdata) { | ||
pushToContent({ | ||
cdata: cdata, | ||
}); | ||
}; | ||
if (subsetStart >= 0) { | ||
entityDeclaration.lastIndex = subsetStart; | ||
while ((entityMatch = entityDeclaration.exec(data)) != null) { | ||
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3]; | ||
} | ||
} | ||
sax.onopentag = function (data) { | ||
var elem = { | ||
elem: data.name, | ||
prefix: data.prefix, | ||
local: data.local, | ||
attrs: {}, | ||
}; | ||
sax.onprocessinginstruction = function(data) { | ||
elem.class = new CSSClassList(elem); | ||
elem.style = new CSSStyleDeclaration(elem); | ||
pushToContent({ | ||
processinginstruction: data | ||
}); | ||
if (Object.keys(data.attributes).length) { | ||
for (const [name, attr] of Object.entries(data.attributes)) { | ||
if (name === 'class') { | ||
// has class attribute | ||
elem.class.hasClass(); | ||
} | ||
}; | ||
if (name === 'style') { | ||
// has style attribute | ||
elem.style.hasStyle(); | ||
} | ||
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.attrs[name] = { | ||
name: name, | ||
value: attr.value, | ||
prefix: attr.prefix, | ||
local: attr.local, | ||
}; | ||
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; | ||
stack.push(elem); | ||
}; | ||
sax.ontext = function(text) { | ||
// prevent trimming of meaningful whitespace inside textual tags | ||
if (textElems.includes(current.elem) && !data.prefix) { | ||
pushToContent({ text: text }); | ||
} else if (/\S/.test(text)) { | ||
pushToContent({ text: text.trim() }); | ||
} | ||
}; | ||
} | ||
sax.onclosetag = function() { | ||
stack.pop(); | ||
current = stack[stack.length - 1]; | ||
}; | ||
elem = pushToContent(elem); | ||
current = elem; | ||
sax.onerror = function(e) { | ||
stack.push(elem); | ||
}; | ||
e.message = 'Error in parsing SVG: ' + e.message; | ||
if (e.message.indexOf('Unexpected end') < 0) { | ||
throw e; | ||
} | ||
sax.ontext = function (text) { | ||
// prevent trimming of meaningful whitespace inside textual tags | ||
if (textElems.includes(current.elem) && !data.prefix) { | ||
pushToContent({ text: text }); | ||
} else if (/\S/.test(text)) { | ||
pushToContent({ text: text.trim() }); | ||
} | ||
}; | ||
}; | ||
sax.onclosetag = function () { | ||
stack.pop(); | ||
current = stack[stack.length - 1]; | ||
}; | ||
try { | ||
sax.write(data).close(); | ||
return root; | ||
} catch (e) { | ||
return { error: e.message }; | ||
sax.onerror = function (e) { | ||
e.message = 'Error in parsing SVG: ' + e.message; | ||
if (e.message.indexOf('Unexpected end') < 0) { | ||
throw e; | ||
} | ||
}; | ||
try { | ||
sax.write(data).close(); | ||
return root; | ||
} catch (e) { | ||
return { error: e.message }; | ||
} | ||
}; |
@@ -10,16 +10,16 @@ 'use strict'; | ||
*/ | ||
exports.encodeSVGDatauri = function(str, type) { | ||
var prefix = 'data:image/svg+xml'; | ||
if (!type || type === 'base64') { | ||
// base64 | ||
prefix += ';base64,'; | ||
str = prefix + Buffer.from(str).toString('base64'); | ||
} else if (type === 'enc') { | ||
// URI encoded | ||
str = prefix + ',' + encodeURIComponent(str); | ||
} else if (type === 'unenc') { | ||
// unencoded | ||
str = prefix + ',' + str; | ||
} | ||
return str; | ||
exports.encodeSVGDatauri = function (str, type) { | ||
var prefix = 'data:image/svg+xml'; | ||
if (!type || type === 'base64') { | ||
// base64 | ||
prefix += ';base64,'; | ||
str = prefix + Buffer.from(str).toString('base64'); | ||
} else if (type === 'enc') { | ||
// URI encoded | ||
str = prefix + ',' + encodeURIComponent(str); | ||
} else if (type === 'unenc') { | ||
// unencoded | ||
str = prefix + ',' + str; | ||
} | ||
return str; | ||
}; | ||
@@ -33,28 +33,28 @@ | ||
*/ | ||
exports.decodeSVGDatauri = function(str) { | ||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/; | ||
var match = regexp.exec(str); | ||
exports.decodeSVGDatauri = function (str) { | ||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/; | ||
var match = regexp.exec(str); | ||
// plain string | ||
if (!match) return str; | ||
// plain string | ||
if (!match) return str; | ||
var data = match[3]; | ||
var data = match[3]; | ||
if (match[2]) { | ||
// base64 | ||
str = Buffer.from(data, 'base64').toString('utf8'); | ||
} else if (data.charAt(0) === '%') { | ||
// URI encoded | ||
str = decodeURIComponent(data); | ||
} else if (data.charAt(0) === '<') { | ||
// unencoded | ||
str = data; | ||
} | ||
return str; | ||
if (match[2]) { | ||
// base64 | ||
str = Buffer.from(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; | ||
}); | ||
exports.intersectArrays = function (a, b) { | ||
return a.filter(function (n) { | ||
return b.indexOf(n) > -1; | ||
}); | ||
}; | ||
@@ -73,44 +73,42 @@ | ||
*/ | ||
exports.cleanupOutData = function(data, params, command) { | ||
var str = '', | ||
delimiter, | ||
prev; | ||
exports.cleanupOutData = function (data, params, command) { | ||
var str = '', | ||
delimiter, | ||
prev; | ||
data.forEach(function(item, i) { | ||
// space delimiter by default | ||
delimiter = ' '; | ||
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 in front of first number | ||
if (i == 0) delimiter = ''; | ||
// no extra space after 'arcto' command flags(large-arc and sweep flags) | ||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) { | ||
var pos = i % 7; | ||
if (pos == 4 || pos == 5) delimiter = ''; | ||
} | ||
// no extra space after 'arcto' command flags(large-arc and sweep flags) | ||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
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); | ||
} | ||
// 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; | ||
// 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; | ||
}; | ||
@@ -131,11 +129,12 @@ | ||
*/ | ||
var removeLeadingZero = exports.removeLeadingZero = function(num) { | ||
var strNum = num.toString(); | ||
var 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; | ||
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; | ||
}; | ||
exports.removeLeadingZero = removeLeadingZero; |
{ | ||
"name": "svgo", | ||
"version": "2.1.0", | ||
"version": "2.2.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -24,3 +24,3 @@ "keywords": [ | ||
"email": "peimei@ya.ru", | ||
"url": "http://github.com/arikon" | ||
"url": "https://github.com/arikon" | ||
}, | ||
@@ -30,3 +30,3 @@ { | ||
"email": "lev.sun@ya.ru", | ||
"url": "http://github.com/GreLI" | ||
"url": "https://github.com/GreLI" | ||
}, | ||
@@ -36,3 +36,3 @@ { | ||
"email": "trysound@yandex.ru", | ||
"url": "http://github.com/TrySound" | ||
"url": "https://github.com/TrySound" | ||
} | ||
@@ -55,3 +55,3 @@ ], | ||
"scripts": { | ||
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\"", | ||
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\" \"**/*.test.js\" --ignore=\"node_modules/**\"", | ||
"lint": "eslint .", | ||
@@ -61,2 +61,5 @@ "test-browser": "rollup -c && node ./test/browser.js", | ||
}, | ||
"prettier": { | ||
"singleQuote": true | ||
}, | ||
"eslintConfig": { | ||
@@ -88,3 +91,4 @@ "ignorePatterns": [ | ||
"files": [ | ||
"test/**/*.js" | ||
"test/**/*.js", | ||
"**/*.test.js" | ||
], | ||
@@ -118,2 +122,3 @@ "env": { | ||
"playwright": "^1.8.1", | ||
"prettier": "^2.2.1", | ||
"rollup": "^2.39.0" | ||
@@ -120,0 +125,0 @@ }, |
'use strict'; | ||
// http://www.w3.org/TR/SVG11/intro.html#Definitions | ||
// https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
exports.elemsGroups = { | ||
@@ -22,3 +22,3 @@ animation: ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'], | ||
// http://www.w3.org/TR/SVG11/intro.html#Definitions | ||
// https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
exports.attrsGroups = { | ||
@@ -162,3 +162,3 @@ animationAddition: ['additive', 'accumulate'], | ||
// http://www.w3.org/TR/SVG11/eltindex.html | ||
// https://www.w3.org/TR/SVG11/eltindex.html | ||
exports.elems = { | ||
@@ -208,3 +208,5 @@ a: { | ||
'text', | ||
'view' | ||
'view', | ||
// not spec compliant | ||
'tspan', | ||
] | ||
@@ -2273,3 +2275,3 @@ }, | ||
// http://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes | ||
// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes | ||
exports.editorNamespaces = [ | ||
@@ -2300,3 +2302,3 @@ 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', | ||
// http://www.w3.org/TR/SVG11/linking.html#processingIRI | ||
// https://www.w3.org/TR/SVG11/linking.html#processingIRI | ||
exports.referencesProps = [ | ||
@@ -2315,3 +2317,3 @@ 'clip-path', | ||
// http://www.w3.org/TR/SVG11/propidx.html | ||
// https://www.w3.org/TR/SVG11/propidx.html | ||
exports.inheritableAttrs = [ | ||
@@ -2377,3 +2379,3 @@ 'clip-rule', | ||
// http://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords | ||
// https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords | ||
exports.colorsNames = { | ||
@@ -2565,5 +2567,5 @@ 'aliceblue': '#f0f8ff', | ||
// http://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor | ||
// https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor | ||
exports.colorsProps = [ | ||
'color', 'fill', 'stroke', 'stop-color', 'flood-color', 'lighting-color' | ||
]; |
'use strict'; | ||
var rNumber = String.raw`[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?\s*`, | ||
rCommaWsp = String.raw`(?:\s,?\s*|,\s*)`, | ||
rNumberCommaWsp = `(${rNumber})` + rCommaWsp, | ||
rFlagCommaWsp = `([01])${rCommaWsp}?`, | ||
rCoordinatePair = String.raw`(${rNumber})${rCommaWsp}?(${rNumber})`, | ||
rArcSeq = (rNumberCommaWsp + '?').repeat(2) + rNumberCommaWsp + rFlagCommaWsp.repeat(2) + rCoordinatePair; | ||
const { parsePathData, stringifyPathData } = require('../lib/path.js'); | ||
var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/, | ||
regCoordinateSequence = new RegExp(rNumber, 'g'), | ||
regArcArgumentSequence = new RegExp(rArcSeq, 'g'), | ||
regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/, | ||
var regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g, | ||
transform2js = require('./_transforms').transform2js, | ||
@@ -20,3 +12,2 @@ transformsMultiply = require('./_transforms').transformsMultiply, | ||
defaultStrokeWidth = collections.attrsGroupsDefaults.presentation['stroke-width'], | ||
cleanupOutData = require('../lib/svgo/tools').cleanupOutData, | ||
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero, | ||
@@ -33,73 +24,18 @@ prevCtrlPoint; | ||
exports.path2js = function(path) { | ||
if (path.pathJS) return path.pathJS; | ||
var paramsLength = { // Number of parameters of every path command | ||
H: 1, V: 1, M: 2, L: 2, T: 2, Q: 4, S: 4, C: 6, A: 7, | ||
h: 1, v: 1, m: 2, l: 2, t: 2, q: 4, s: 4, c: 6, a: 7 | ||
}, | ||
pathData = [], // JS representation of the path data | ||
instruction, // current instruction context | ||
startMoveto = false; | ||
// splitting path string into array like ['M', '10 50', 'L', '20 30'] | ||
path.attr('d').value.split(regPathInstructions).forEach(function(data) { | ||
if (!data) return; | ||
if (!startMoveto) { | ||
if (data == 'M' || data == 'm') { | ||
startMoveto = true; | ||
} else return; | ||
} | ||
// instruction item | ||
if (regPathInstructions.test(data)) { | ||
instruction = data; | ||
// z - instruction w/o data | ||
if (instruction == 'Z' || instruction == 'z') { | ||
pathData.push({ | ||
instruction: 'z' | ||
}); | ||
} | ||
// data item | ||
} else { | ||
if (instruction == 'A' || instruction == 'a') { | ||
var newData = []; | ||
for (var args; (args = regArcArgumentSequence.exec(data));) { | ||
for (var i = 1; i < args.length; i++) { | ||
newData.push(args[i]); | ||
} | ||
} | ||
data = newData; | ||
} else { | ||
data = data.match(regCoordinateSequence); | ||
} | ||
if (!data) return; | ||
data = data.map(Number); | ||
// Subsequent moveto pairs of coordinates are threated as implicit lineto commands | ||
// http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands | ||
if (instruction == 'M' || instruction == 'm') { | ||
pathData.push({ | ||
instruction: pathData.length == 0 ? 'M' : instruction, | ||
data: data.splice(0, 2) | ||
}); | ||
instruction = instruction == 'M' ? 'L' : 'l'; | ||
} | ||
for (var pair = paramsLength[instruction]; data.length;) { | ||
pathData.push({ | ||
instruction: instruction, | ||
data: data.splice(0, pair) | ||
}); | ||
} | ||
} | ||
}); | ||
// First moveto is actually absolute. Subsequent coordinates were separated above. | ||
if (pathData.length && pathData[0].instruction == 'm') { | ||
pathData[0].instruction = 'M'; | ||
if (path.pathJS) return path.pathJS; | ||
const pathData = []; // JS representation of the path data | ||
const newPathData = parsePathData(path.attr('d').value); | ||
for (const { command, args } of newPathData) { | ||
if (command === 'Z' || command === 'z') { | ||
pathData.push({ instruction: 'z' }); | ||
} else { | ||
pathData.push({ instruction: command, data: args }); | ||
} | ||
path.pathJS = pathData; | ||
return pathData; | ||
} | ||
// First moveto is actually absolute. Subsequent coordinates were separated above. | ||
if (pathData.length && pathData[0].instruction == 'm') { | ||
pathData[0].instruction = 'M'; | ||
} | ||
path.pathJS = pathData; | ||
return pathData; | ||
}; | ||
@@ -192,9 +128,16 @@ | ||
exports.applyTransforms = function(elem, path, params) { | ||
// if there are no 'stroke' attr and references to other objects such as | ||
// gradiends or clip-path which are also subjects to transform. | ||
if (!elem.hasAttr('transform') || !elem.attr('transform').value || | ||
elem.someAttr(function(attr) { | ||
return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url('); | ||
})) | ||
return path; | ||
// if there are no 'stroke' attr and references to other objects such as | ||
// gradiends or clip-path which are also subjects to transform. | ||
if ( | ||
!elem.hasAttr('transform') || | ||
!elem.attr('transform').value || | ||
// styles are not considered when applying transform | ||
// can be fixed properly with new style engine | ||
elem.hasAttr('style') || | ||
elem.someAttr((attr) => | ||
referencesProps.includes(attr.name) && attr.value.includes('url(') | ||
) | ||
) { | ||
return path; | ||
} | ||
@@ -246,2 +189,14 @@ var matrix = transformsMultiply(transform2js(elem.attr('transform').value)), | ||
} | ||
if (elem.hasAttr('stroke-dashoffset')) { | ||
elem.attrs['stroke-dashoffset'].value = elem.attrs['stroke-dashoffset'].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
if (elem.hasAttr('stroke-dasharray')) { | ||
elem.attrs['stroke-dasharray'].value = elem.attrs['stroke-dasharray'].value | ||
.trim() | ||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale)); | ||
} | ||
} | ||
@@ -349,3 +304,3 @@ } | ||
* | ||
* @see http://processingjs.nihongoresources.com/bezierinfo/ | ||
* @see https://pomax.github.io/bezierinfo/ | ||
* | ||
@@ -457,3 +412,3 @@ * @param {Float} xa | ||
* | ||
* @see http://processingjs.nihongoresources.com/bezierinfo/ | ||
* @see https://pomax.github.io/bezierinfo/ | ||
* | ||
@@ -553,60 +508,28 @@ * @param {Float} xa | ||
path.pathJS = data; | ||
path.pathJS = data; | ||
if (params.collapseRepeated) { | ||
data = collapseRepeated(data); | ||
const pathData = []; | ||
for (const item of data) { | ||
// remove moveto commands which are followed by moveto commands | ||
if ( | ||
pathData.length !== 0 && | ||
(item.instruction === 'M' || item.instruction === 'm') | ||
) { | ||
const last = pathData[pathData.length - 1]; | ||
if (last.command === 'M' || last.command === 'm') { | ||
pathData.pop(); | ||
} | ||
} | ||
pathData.push({ | ||
command: item.instruction, | ||
args: item.data || [], | ||
}); | ||
} | ||
path.attr('d').value = data.reduce(function(pathString, item) { | ||
var strData = ''; | ||
if (item.data) { | ||
strData = cleanupOutData(item.data, params, item.instruction); | ||
} | ||
return pathString += item.instruction + strData; | ||
}, ''); | ||
path.attr('d').value = stringifyPathData({ | ||
pathData, | ||
precision: params.floatPrecision, | ||
}); | ||
}; | ||
/** | ||
* Collapse repeated instructions data | ||
* | ||
* @param {Array} path input path data | ||
* @return {Array} output path data | ||
*/ | ||
function collapseRepeated(data) { | ||
var prev, | ||
prevIndex; | ||
// copy an array and modifieds item to keep original data untouched | ||
data = data.reduce(function(newPath, item) { | ||
if ( | ||
prev && item.data && | ||
item.instruction == prev.instruction | ||
) { | ||
// concat previous data with current | ||
if (item.instruction != 'M') { | ||
prev = newPath[prevIndex] = { | ||
instruction: prev.instruction, | ||
data: prev.data.concat(item.data), | ||
coords: item.coords, | ||
base: prev.base | ||
}; | ||
} else { | ||
prev.data = item.data; | ||
prev.coords = item.coords; | ||
} | ||
} else { | ||
newPath.push(item); | ||
prev = item; | ||
prevIndex = newPath.length - 1; | ||
} | ||
return newPath; | ||
}, []); | ||
return data; | ||
} | ||
function set(dest, source) { | ||
@@ -621,3 +544,3 @@ dest[0] = source[source.length - 2]; | ||
* collision using Gilbert-Johnson-Keerthi distance algorithm | ||
* http://entropyinteractive.com/2011/04/gjk-algorithm/ | ||
* https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/ | ||
* | ||
@@ -851,3 +774,3 @@ * @param {Array} path1 JS path representation | ||
* Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm. | ||
* http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain | ||
* https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain | ||
* | ||
@@ -914,3 +837,3 @@ * @param points An array of [X, Y] coordinates | ||
// for more information of where this Math came from visit: | ||
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes | ||
// https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes | ||
var _120 = Math.PI * 120 / 180, | ||
@@ -917,0 +840,0 @@ rad = Math.PI / 180 * (+angle || 0), |
@@ -118,3 +118,3 @@ 'use strict'; | ||
* Decompose matrix into simple transforms. See | ||
* http://frederic-wang.fr/decomposition-of-2d-transform-matrices.html | ||
* https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html | ||
* | ||
@@ -121,0 +121,0 @@ * @param {Object} data matrix transform object |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/filters.html#EnableBackgroundProperty | ||
* @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty | ||
* | ||
@@ -15,0 +15,0 @@ * @example |
@@ -27,4 +27,4 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/types.html#DataTypeColor | ||
* @see http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords | ||
* @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor | ||
* @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords | ||
* | ||
@@ -31,0 +31,0 @@ * @example |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/shapes.html | ||
* @see https://www.w3.org/TR/SVG11/shapes.html | ||
* | ||
@@ -15,0 +15,0 @@ * @param {Object} item current iteration item |
@@ -51,3 +51,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/paths.html#PathData | ||
* @see https://www.w3.org/TR/SVG11/paths.html#PathData | ||
* | ||
@@ -73,5 +73,7 @@ * @param {Object} item current iteration item | ||
var stroke = item.computedAttr('stroke'), | ||
strokeLinecap = item.computedAttr('stroke'); | ||
hasStrokeLinecap = stroke && stroke != 'none' && strokeLinecap && strokeLinecap != 'butt'; | ||
const stroke = item.computedAttr('stroke'); | ||
const strokeLinecap = item.computedAttr('stroke-linecap'); | ||
// stroke-linecap may exist in inline styles which are not parsed for now | ||
hasStrokeLinecap = item.hasAttr('style') || | ||
stroke && stroke != 'none' && strokeLinecap && strokeLinecap != 'butt'; | ||
@@ -78,0 +80,0 @@ var data = path2js(item); |
@@ -21,3 +21,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/shapes.html | ||
* @see https://www.w3.org/TR/SVG11/shapes.html | ||
* | ||
@@ -24,0 +24,0 @@ * @param {Object} item current iteration item |
@@ -38,3 +38,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined | ||
* @see https://www.w3.org/TR/SVG11/coords.html#TransformMatrixDefined | ||
* | ||
@@ -41,0 +41,0 @@ * @param {Object} item current iteration item |
@@ -57,9 +57,3 @@ 'use strict'; | ||
function cloneObject(obj) { | ||
var result = {}; | ||
for (var key in obj) { | ||
result[key] = obj[key]; | ||
} | ||
return result; | ||
return {...obj}; | ||
} | ||
@@ -153,5 +147,5 @@ | ||
for (var key in rawData) { | ||
for (const [key, data] of Object.entries(rawData)) { | ||
if (shouldFilter(options, key)) { | ||
usageData[key] = Object.keys(rawData[key]); | ||
usageData[key] = Object.keys(data); | ||
hasData = true; | ||
@@ -158,0 +152,0 @@ } |
@@ -66,3 +66,3 @@ 'use strict'; | ||
for (var name in intersection) { | ||
for (const [name, attr] of Object.entries(intersection)) { | ||
@@ -76,5 +76,5 @@ if (!allPath && !hasClip || name !== 'transform') { | ||
if (item.hasAttr('transform')) { | ||
item.attr('transform').value += ' ' + intersection[name].value; | ||
item.attr('transform').value += ' ' + attr.value; | ||
} else { | ||
item.addAttr(intersection[name]); | ||
item.addAttr(attr); | ||
} | ||
@@ -85,3 +85,3 @@ | ||
} else { | ||
item.addAttr(intersection[name]); | ||
item.addAttr(attr); | ||
} | ||
@@ -112,3 +112,3 @@ | ||
for (var n in a) { | ||
for (const [n, attr] of Object.entries(a)) { | ||
if ( | ||
@@ -118,8 +118,8 @@ // eslint-disable-next-line no-prototype-builtins | ||
inheritableAttrs.indexOf(n) > -1 && | ||
a[n].name === b[n].name && | ||
a[n].value === b[n].value && | ||
a[n].prefix === b[n].prefix && | ||
a[n].local === b[n].local | ||
attr.name === b[n].name && | ||
attr.value === b[n].value && | ||
attr.prefix === b[n].prefix && | ||
attr.local === b[n].local | ||
) { | ||
c[n] = a[n]; | ||
c[n] = attr; | ||
} | ||
@@ -126,0 +126,0 @@ } |
@@ -14,3 +14,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/intro.html#TermContainerElement | ||
* @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement | ||
* | ||
@@ -36,4 +36,6 @@ * @example | ||
// to be created and filled with pattern. | ||
(item.isElem('g') && item.hasAttr('filter')) | ||
(item.isElem('g') && item.hasAttr('filter')) || | ||
// empty <mask> hides masked element | ||
(item.isElem('mask') && item.hasAttr('id')) | ||
); | ||
}; |
@@ -18,3 +18,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/text.html | ||
* @see https://www.w3.org/TR/SVG11/text.html | ||
* | ||
@@ -21,0 +21,0 @@ * @example |
@@ -55,3 +55,5 @@ 'use strict'; | ||
params.isHidden && | ||
item.hasAttr('visibility', 'hidden') | ||
item.hasAttr('visibility', 'hidden') && | ||
// keep if any descendant enables visibility | ||
item.querySelector('[visibility=visible]') == null | ||
) return false; | ||
@@ -61,3 +63,3 @@ | ||
// | ||
// http://www.w3.org/TR/SVG/painting.html#DisplayProperty | ||
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty | ||
// "A value of display: none indicates that the given element | ||
@@ -72,6 +74,8 @@ // and its children shall not be rendered directly" | ||
// | ||
// http://www.w3.org/TR/SVG/masking.html#ObjectAndGroupOpacityProperties | ||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties | ||
if ( | ||
params.opacity0 && | ||
item.hasAttr('opacity', '0') | ||
item.hasAttr('opacity', '0') && | ||
// transparent element inside clipPath still affect clipped elements | ||
item.closestElem('clipPath') == null | ||
) return false; | ||
@@ -81,3 +85,3 @@ | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#CircleElementRAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -95,3 +99,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRXAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -109,3 +113,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRYAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -123,3 +127,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#RectElementWidthAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -137,3 +141,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#RectElementHeightAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -152,3 +156,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/pservers.html#PatternElementWidthAttribute | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
@@ -165,3 +169,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/pservers.html#PatternElementHeightAttribute | ||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute | ||
// "A value of zero disables rendering of the element (i.e., no paint is applied)" | ||
@@ -178,3 +182,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -191,3 +195,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/struct.html#ImageElementHeightAttribute | ||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute | ||
// "A value of zero disables rendering of the element" | ||
@@ -204,3 +208,3 @@ // | ||
// | ||
// http://www.w3.org/TR/SVG/paths.html#DAttribute | ||
// https://www.w3.org/TR/SVG11/paths.html#DAttribute | ||
// | ||
@@ -216,3 +220,3 @@ // <path d=""/> | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#PolylineElementPointsAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute | ||
// | ||
@@ -228,3 +232,3 @@ // <polyline points=""> | ||
// | ||
// http://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute | ||
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute | ||
// | ||
@@ -231,0 +235,0 @@ // <polygon points=""> |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* http://www.w3.org/TR/SVG/metadata.html | ||
* https://www.w3.org/TR/SVG11/metadata.html | ||
* | ||
@@ -15,0 +15,0 @@ * @param {Object} item current iteration item |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* https://www.w3.org/TR/SVG/script.html | ||
* https://www.w3.org/TR/SVG11/script.html | ||
* | ||
@@ -15,0 +15,0 @@ * @param {Object} item current iteration item |
@@ -12,3 +12,3 @@ 'use strict'; | ||
* | ||
* http://www.w3.org/TR/SVG/styling.html#StyleElement | ||
* https://www.w3.org/TR/SVG11/styling.html#StyleElement | ||
* | ||
@@ -15,0 +15,0 @@ * @param {Object} item current iteration item |
@@ -28,5 +28,3 @@ 'use strict'; | ||
// collect and extend all references | ||
for (var elem in elems) { | ||
elem = elems[elem]; | ||
for (const elem of Object.values(elems)) { | ||
if (elem.attrsGroups) { | ||
@@ -43,4 +41,4 @@ elem.attrs = elem.attrs || []; | ||
for (var attrName in groupDefaults) { | ||
elem.defaults[attrName] = groupDefaults[attrName]; | ||
for (const [attrName, attr] of Object.entries(groupDefaults)) { | ||
elem.defaults[attrName] = attr; | ||
} | ||
@@ -47,0 +45,0 @@ } |
@@ -44,23 +44,30 @@ 'use strict'; | ||
params.stroke && | ||
(!stroke || | ||
stroke == 'none' || | ||
item.computedAttr('stroke-opacity', '0') || | ||
item.computedAttr('stroke-width', '0') | ||
( | ||
!stroke || | ||
stroke == 'none' || | ||
item.computedAttr('stroke-opacity', '0') || | ||
item.computedAttr('stroke-width', '0') | ||
) | ||
) { | ||
var parentStroke = item.parentNode.computedAttr('stroke'), | ||
declineStroke = parentStroke && parentStroke != 'none'; | ||
// stroke-width may affect the size of marker-end | ||
if ( | ||
item.computedAttr('stroke-width', '0') === true || | ||
item.computedAttr('marker-end') == null | ||
) { | ||
var parentStroke = item.parentNode.computedAttr('stroke'), | ||
declineStroke = parentStroke && parentStroke != 'none'; | ||
item.eachAttr(function(attr) { | ||
if (regStrokeProps.test(attr.name)) { | ||
item.removeAttr(attr.name); | ||
} | ||
}); | ||
item.eachAttr(function(attr) { | ||
if (regStrokeProps.test(attr.name)) { | ||
item.removeAttr(attr.name); | ||
} | ||
}); | ||
if (declineStroke) item.addAttr({ | ||
name: 'stroke', | ||
value: 'none', | ||
prefix: '', | ||
local: 'stroke' | ||
}); | ||
if (declineStroke) item.addAttr({ | ||
name: 'stroke', | ||
value: 'none', | ||
prefix: '', | ||
local: 'stroke' | ||
}); | ||
} | ||
} | ||
@@ -67,0 +74,0 @@ |
@@ -14,3 +14,3 @@ 'use strict'; | ||
* | ||
* @see http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute | ||
* @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute | ||
* | ||
@@ -17,0 +17,0 @@ * @example |
@@ -1,8 +0,10 @@ | ||
<img src="https://svg.github.io/svgo-logo.svg" width="200" height="200" alt="logo"/> | ||
<div align="center"> | ||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt="SVGO logo"/> | ||
</div> | ||
## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo) | ||
**SVG O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files. | ||
![](https://mc.yandex.ru/watch/18431326) | ||
## SVGO [![npm version](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![Discord](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE) | ||
**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files. | ||
## Why? | ||
@@ -17,3 +19,5 @@ | ||
``` | ||
or | ||
```sh | ||
@@ -29,3 +33,4 @@ yarn global add svgo | ||
Or use --folder, -f flag to optimize whole folder of svg icons | ||
Or use the `--folder`/`-f` flag to optimize a whole folder of SVG icons | ||
```sh | ||
@@ -36,2 +41,3 @@ svgo -f ./path/to/folder/with/svg/files -o ./path/to/folder/with/svg/output | ||
See help for advanced usage | ||
```sh | ||
@@ -43,3 +49,3 @@ svgo --help | ||
Some options can be configured with CLI though it may be easier to have configuration in separate file. | ||
Some options can be configured with CLI though it may be easier to have the configuration in a separate file. | ||
SVGO automatically loads configuration from `svgo.config.js` or module specified with `--config` flag. | ||
@@ -59,3 +65,3 @@ | ||
SVGO has a plugin-based architecture, so almost every optimization is a separate plugin. | ||
There is a set of [builtin plugins](#builtin-plugins). See how to configure them: | ||
There is a set of [built-in plugins](#built-in-plugins). See how to configure them: | ||
@@ -65,3 +71,3 @@ ```js | ||
plugins: [ | ||
// enable builtin plugin by name | ||
// enable a built-in plugin by name | ||
'builtinPluginName', | ||
@@ -83,4 +89,4 @@ // or by expanded version | ||
If `plugins` field is specified default list is fully overrided. To extend default | ||
list use `extendDefaultPlugins` utility: | ||
The default list is fully overridden if the `plugins` field is specified. To extend the default | ||
list use the `extendDefaultPlugins` utility: | ||
@@ -101,3 +107,3 @@ ```js | ||
To disable one of default plugins use `active` field: | ||
To disable one of the default plugins use the `active` field: | ||
@@ -116,3 +122,3 @@ ```js | ||
See the list of default plugins: | ||
See the list of the default plugins: | ||
@@ -130,3 +136,2 @@ ```js | ||
'minifyStyles', | ||
'convertStyleToAttrs', | ||
'cleanupIDs', | ||
@@ -161,3 +166,3 @@ 'removeUselessDefs', | ||
It's also possible to specify custom plugin: | ||
It's also possible to specify a custom plugin: | ||
@@ -202,3 +207,3 @@ ```js | ||
If you write a tool on top of svgo you might need a way to load svgo config. | ||
If you write a tool on top of SVGO you might need a way to load SVGO config. | ||
@@ -209,7 +214,7 @@ ```js | ||
// you can also specify relative or absolute path and customize current working directory | ||
// you can also specify a relative or absolute path and customize the current working directory | ||
const config = await loadConfig(configFile, cwd) | ||
``` | ||
## Builtin plugins | ||
## Built-in plugins | ||
@@ -220,3 +225,3 @@ | Plugin | Description | Default | | ||
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` | | ||
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove doctype declaration | `enabled` | | ||
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` | | ||
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` | | ||
@@ -228,3 +233,3 @@ | [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` | | ||
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` | | ||
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes `xmlns` attribute (for inline svg) | `disabled` | | ||
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` | | ||
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` | | ||
@@ -242,5 +247,5 @@ | [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` | | ||
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` | | ||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attrs with default values | `enabled` | | ||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` | | ||
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` | | ||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attrs | `enabled` | | ||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` | | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` | | ||
@@ -262,5 +267,5 @@ | [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a css selector | `disabled` | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by ID or className | `disabled` | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` | | ||
@@ -276,3 +281,3 @@ | [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` | | ||
* as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action) | ||
* as a Nodejs module – [examples](https://github.com/svg/svgo/tree/master/examples) | ||
* as a Node.js module – [examples](https://github.com/svg/svgo/tree/master/examples) | ||
* as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin) | ||
@@ -294,12 +299,8 @@ * as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) | ||
## Backers | ||
## Donators | ||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://rawgithub.com/fontello/fontello/master/fontello-image.svg" width="80">](http://fontello.com/) | | ||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/fontello-image.svg" width="80">](https://fontello.com/) | | ||
|:-:|:-:| | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](http://fontello.com/) | | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) | | ||
## Donations | ||
- PayPal: https://www.paypal.me/deepsweet | ||
## License and Copyright | ||
@@ -309,2 +310,2 @@ | ||
Logo by [Yegor Bolshakov](http://xizzzy.ru/). | ||
Logo by [André Castillo](https://github.com/DerianAndre). |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1177810
73
33788
292
12