mirror of
https://github.com/svg/svgo.git
synced 2025-07-31 07:44:22 +03:00
188
lib/css-tools.js
188
lib/css-tools.js
@ -1,10 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var csstree = require('css-tree'),
|
var csstree = require('css-tree'),
|
||||||
List = csstree.List,
|
List = csstree.List,
|
||||||
stable = require('stable'),
|
stable = require('stable'),
|
||||||
specificity = require('csso/lib/restructure/prepare/specificity');
|
specificity = require('csso/lib/restructure/prepare/specificity');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flatten a CSS AST to a selectors list.
|
* Flatten a CSS AST to a selectors list.
|
||||||
@ -13,39 +12,48 @@ var csstree = require('css-tree'),
|
|||||||
* @return {Array} selectors
|
* @return {Array} selectors
|
||||||
*/
|
*/
|
||||||
function flattenToSelectors(cssAst) {
|
function flattenToSelectors(cssAst) {
|
||||||
var selectors = [];
|
var selectors = [];
|
||||||
|
|
||||||
csstree.walk(cssAst, {visit: 'Rule', enter: function(node) {
|
csstree.walk(cssAst, {
|
||||||
if (node.type !== 'Rule') {
|
visit: 'Rule',
|
||||||
return;
|
enter: function (node) {
|
||||||
}
|
if (node.type !== 'Rule') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var atrule = this.atrule;
|
var atrule = this.atrule;
|
||||||
var rule = node;
|
var rule = node;
|
||||||
|
|
||||||
node.prelude.children.each(function(selectorNode, selectorItem) {
|
node.prelude.children.each(function (selectorNode, selectorItem) {
|
||||||
var selector = {
|
var selector = {
|
||||||
item: selectorItem,
|
item: selectorItem,
|
||||||
atrule: atrule,
|
atrule: atrule,
|
||||||
rule: rule,
|
rule: rule,
|
||||||
pseudos: []
|
pseudos: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
selectorNode.children.each(function(selectorChildNode, selectorChildItem, selectorChildList) {
|
selectorNode.children.each(function (
|
||||||
if (selectorChildNode.type === 'PseudoClassSelector' ||
|
selectorChildNode,
|
||||||
selectorChildNode.type === 'PseudoElementSelector') {
|
selectorChildItem,
|
||||||
selector.pseudos.push({
|
selectorChildList
|
||||||
item: selectorChildItem,
|
) {
|
||||||
list: 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,21 +64,23 @@ function flattenToSelectors(cssAst) {
|
|||||||
* @return {Array} Filtered selectors that match the passed media queries
|
* @return {Array} Filtered selectors that match the passed media queries
|
||||||
*/
|
*/
|
||||||
function filterByMqs(selectors, useMqs) {
|
function filterByMqs(selectors, useMqs) {
|
||||||
return selectors.filter(function(selector) {
|
return selectors.filter(function (selector) {
|
||||||
if (selector.atrule === null) {
|
if (selector.atrule === null) {
|
||||||
return ~useMqs.indexOf('');
|
return ~useMqs.indexOf('');
|
||||||
}
|
}
|
||||||
|
|
||||||
var mqName = selector.atrule.name;
|
var mqName = selector.atrule.name;
|
||||||
var mqStr = mqName;
|
var mqStr = mqName;
|
||||||
if (selector.atrule.expression &&
|
if (
|
||||||
selector.atrule.expression.children.first().type === 'MediaQueryList') {
|
selector.atrule.expression &&
|
||||||
var mqExpr = csstree.generate(selector.atrule.expression);
|
selector.atrule.expression.children.first().type === 'MediaQueryList'
|
||||||
mqStr = [mqName, mqExpr].join(' ');
|
) {
|
||||||
}
|
var mqExpr = csstree.generate(selector.atrule.expression);
|
||||||
|
mqStr = [mqName, mqExpr].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
return ~useMqs.indexOf(mqStr);
|
return ~useMqs.indexOf(mqStr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,15 +91,17 @@ function filterByMqs(selectors, useMqs) {
|
|||||||
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
|
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
|
||||||
*/
|
*/
|
||||||
function filterByPseudos(selectors, usePseudos) {
|
function filterByPseudos(selectors, usePseudos) {
|
||||||
return selectors.filter(function(selector) {
|
return selectors.filter(function (selector) {
|
||||||
var pseudoSelectorsStr = csstree.generate({
|
var pseudoSelectorsStr = csstree.generate({
|
||||||
type: 'Selector',
|
type: 'Selector',
|
||||||
children: new List().fromArray(selector.pseudos.map(function(pseudo) {
|
children: new List().fromArray(
|
||||||
return pseudo.item.data;
|
selector.pseudos.map(function (pseudo) {
|
||||||
}))
|
return pseudo.item.data;
|
||||||
});
|
})
|
||||||
return ~usePseudos.indexOf(pseudoSelectorsStr);
|
),
|
||||||
});
|
});
|
||||||
|
return ~usePseudos.indexOf(pseudoSelectorsStr);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,14 +111,13 @@ function filterByPseudos(selectors, usePseudos) {
|
|||||||
* @return {Array} Selectors without pseudo-elements and/or -classes
|
* @return {Array} Selectors without pseudo-elements and/or -classes
|
||||||
*/
|
*/
|
||||||
function cleanPseudos(selectors) {
|
function cleanPseudos(selectors) {
|
||||||
selectors.forEach(function(selector) {
|
selectors.forEach(function (selector) {
|
||||||
selector.pseudos.forEach(function(pseudo) {
|
selector.pseudos.forEach(function (pseudo) {
|
||||||
pseudo.list.remove(pseudo.item);
|
pseudo.list.remove(pseudo.item);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two selector specificities.
|
* Compares two selector specificities.
|
||||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||||
@ -116,18 +127,17 @@ function cleanPseudos(selectors) {
|
|||||||
* @return {Number} Score of selector specificity A compared to selector specificity B
|
* @return {Number} Score of selector specificity A compared to selector specificity B
|
||||||
*/
|
*/
|
||||||
function compareSpecificity(aSpecificity, bSpecificity) {
|
function compareSpecificity(aSpecificity, bSpecificity) {
|
||||||
for (var i = 0; i < 4; i += 1) {
|
for (var i = 0; i < 4; i += 1) {
|
||||||
if (aSpecificity[i] < bSpecificity[i]) {
|
if (aSpecificity[i] < bSpecificity[i]) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (aSpecificity[i] > bSpecificity[i]) {
|
} else if (aSpecificity[i] > bSpecificity[i]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two simple selectors.
|
* Compare two simple selectors.
|
||||||
*
|
*
|
||||||
@ -136,16 +146,15 @@ function compareSpecificity(aSpecificity, bSpecificity) {
|
|||||||
* @return {Number} Score of selector A compared to selector B
|
* @return {Number} Score of selector A compared to selector B
|
||||||
*/
|
*/
|
||||||
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
|
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
|
||||||
var aSpecificity = specificity(aSimpleSelectorNode),
|
var aSpecificity = specificity(aSimpleSelectorNode),
|
||||||
bSpecificity = specificity(bSimpleSelectorNode);
|
bSpecificity = specificity(bSimpleSelectorNode);
|
||||||
return compareSpecificity(aSpecificity, bSpecificity);
|
return compareSpecificity(aSpecificity, bSpecificity);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bySelectorSpecificity(selectorA, selectorB) {
|
function _bySelectorSpecificity(selectorA, selectorB) {
|
||||||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
|
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort selectors stably by their specificity.
|
* Sort selectors stably by their specificity.
|
||||||
*
|
*
|
||||||
@ -153,10 +162,9 @@ function _bySelectorSpecificity(selectorA, selectorB) {
|
|||||||
* @return {Array} Stable sorted selectors
|
* @return {Array} Stable sorted selectors
|
||||||
*/
|
*/
|
||||||
function sortSelectors(selectors) {
|
function sortSelectors(selectors) {
|
||||||
return stable(selectors, _bySelectorSpecificity);
|
return stable(selectors, _bySelectorSpecificity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
|
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
|
||||||
*
|
*
|
||||||
@ -164,17 +172,16 @@ function sortSelectors(selectors) {
|
|||||||
* @return {Object} CSSStyleDeclaration property
|
* @return {Object} CSSStyleDeclaration property
|
||||||
*/
|
*/
|
||||||
function csstreeToStyleDeclaration(declaration) {
|
function csstreeToStyleDeclaration(declaration) {
|
||||||
var propertyName = declaration.property,
|
var propertyName = declaration.property,
|
||||||
propertyValue = csstree.generate(declaration.value),
|
propertyValue = csstree.generate(declaration.value),
|
||||||
propertyPriority = (declaration.important ? 'important' : '');
|
propertyPriority = declaration.important ? 'important' : '';
|
||||||
return {
|
return {
|
||||||
name: propertyName,
|
name: propertyName,
|
||||||
value: propertyValue,
|
value: propertyValue,
|
||||||
priority: propertyPriority
|
priority: propertyPriority,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the CSS string of a style element
|
* Gets the CSS string of a style element
|
||||||
*
|
*
|
||||||
@ -182,7 +189,7 @@ function csstreeToStyleDeclaration(declaration) {
|
|||||||
* @return {String|Array} CSS string or empty array if no styles are set
|
* @return {String|Array} CSS string or empty array if no styles are set
|
||||||
*/
|
*/
|
||||||
function getCssStr(elem) {
|
function getCssStr(elem) {
|
||||||
return elem.content[0].text || elem.content[0].cdata || [];
|
return elem.content[0].text || elem.content[0].cdata || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,18 +200,17 @@ function getCssStr(elem) {
|
|||||||
* @return {Object} reference to field with CSS
|
* @return {Object} reference to field with CSS
|
||||||
*/
|
*/
|
||||||
function setCssStr(elem, css) {
|
function setCssStr(elem, css) {
|
||||||
// in case of cdata field
|
// in case of cdata field
|
||||||
if(elem.content[0].cdata) {
|
if (elem.content[0].cdata) {
|
||||||
elem.content[0].cdata = css;
|
elem.content[0].cdata = css;
|
||||||
return elem.content[0].cdata;
|
return elem.content[0].cdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case of text field + if nothing was set yet
|
// in case of text field + if nothing was set yet
|
||||||
elem.content[0].text = css;
|
elem.content[0].text = css;
|
||||||
return elem.content[0].text;
|
return elem.content[0].text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports.flattenToSelectors = flattenToSelectors;
|
module.exports.flattenToSelectors = flattenToSelectors;
|
||||||
|
|
||||||
module.exports.filterByMqs = filterByMqs;
|
module.exports.filterByMqs = filterByMqs;
|
||||||
|
@ -8,7 +8,7 @@ const {
|
|||||||
createContentItem,
|
createContentItem,
|
||||||
} = require('./svgo.js');
|
} = require('./svgo.js');
|
||||||
|
|
||||||
const importConfig = async configFile => {
|
const importConfig = async (configFile) => {
|
||||||
const config = require(configFile);
|
const config = require(configFile);
|
||||||
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
|
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
|
||||||
throw Error(`Invalid config file "${configFile}"`);
|
throw Error(`Invalid config file "${configFile}"`);
|
||||||
@ -23,7 +23,7 @@ const isFile = async (file) => {
|
|||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const loadConfig = async (configFile, cwd = process.cwd()) => {
|
const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||||
if (configFile != null) {
|
if (configFile != null) {
|
||||||
@ -36,7 +36,7 @@ const loadConfig = async (configFile, cwd = process.cwd()) => {
|
|||||||
let dir = cwd;
|
let dir = cwd;
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
const file = path.join(dir, "svgo.config.js");
|
const file = path.join(dir, 'svgo.config.js');
|
||||||
if (await isFile(file)) {
|
if (await isFile(file)) {
|
||||||
return await importConfig(file);
|
return await importConfig(file);
|
||||||
}
|
}
|
||||||
|
16
lib/svgo.js
16
lib/svgo.js
@ -13,7 +13,7 @@
|
|||||||
const {
|
const {
|
||||||
defaultPlugins,
|
defaultPlugins,
|
||||||
resolvePluginConfig,
|
resolvePluginConfig,
|
||||||
extendDefaultPlugins
|
extendDefaultPlugins,
|
||||||
} = require('./svgo/config.js');
|
} = require('./svgo/config.js');
|
||||||
const svg2js = require('./svgo/svg2js.js');
|
const svg2js = require('./svgo/svg2js.js');
|
||||||
const js2svg = require('./svgo/js2svg.js');
|
const js2svg = require('./svgo/js2svg.js');
|
||||||
@ -28,12 +28,12 @@ const optimize = (input, config) => {
|
|||||||
config = {};
|
config = {};
|
||||||
}
|
}
|
||||||
if (typeof config !== 'object') {
|
if (typeof config !== 'object') {
|
||||||
throw Error('Config should be an object')
|
throw Error('Config should be an object');
|
||||||
}
|
}
|
||||||
const maxPassCount = config.multipass ? 10 : 1;
|
const maxPassCount = config.multipass ? 10 : 1;
|
||||||
let prevResultSize = Number.POSITIVE_INFINITY;
|
let prevResultSize = Number.POSITIVE_INFINITY;
|
||||||
let svgjs = null;
|
let svgjs = null;
|
||||||
const info = {}
|
const info = {};
|
||||||
if (config.path != null) {
|
if (config.path != null) {
|
||||||
info.path = config.path;
|
info.path = config.path;
|
||||||
}
|
}
|
||||||
@ -48,9 +48,13 @@ const optimize = (input, config) => {
|
|||||||
}
|
}
|
||||||
const plugins = config.plugins || defaultPlugins;
|
const plugins = config.plugins || defaultPlugins;
|
||||||
if (Array.isArray(plugins) === false) {
|
if (Array.isArray(plugins) === false) {
|
||||||
throw Error('Invalid plugins list. Provided \'plugins\' in config should be an array.');
|
throw Error(
|
||||||
|
"Invalid plugins list. Provided 'plugins' in config should be an array."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const resolvedPlugins = plugins.map(plugin => resolvePluginConfig(plugin, config))
|
const resolvedPlugins = plugins.map((plugin) =>
|
||||||
|
resolvePluginConfig(plugin, config)
|
||||||
|
);
|
||||||
svgjs = invokePlugins(svgjs, info, resolvedPlugins);
|
svgjs = invokePlugins(svgjs, info, resolvedPlugins);
|
||||||
svgjs = js2svg(svgjs, config.js2svg);
|
svgjs = js2svg(svgjs, config.js2svg);
|
||||||
if (svgjs.error) {
|
if (svgjs.error) {
|
||||||
@ -58,7 +62,7 @@ const optimize = (input, config) => {
|
|||||||
}
|
}
|
||||||
if (svgjs.data.length < prevResultSize) {
|
if (svgjs.data.length < prevResultSize) {
|
||||||
input = svgjs.data;
|
input = svgjs.data;
|
||||||
prevResultSize = svgjs.data.length
|
prevResultSize = svgjs.data.length;
|
||||||
} else {
|
} else {
|
||||||
if (config.datauri) {
|
if (config.datauri) {
|
||||||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
|
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
|
||||||
|
569
lib/svgo/coa.js
569
lib/svgo/coa.js
@ -14,182 +14,228 @@ const regSVGFile = /\.svg$/i;
|
|||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
function checkIsDir(path) {
|
function checkIsDir(path) {
|
||||||
try {
|
try {
|
||||||
return FS.lstatSync(path).isDirectory();
|
return FS.lstatSync(path).isDirectory();
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function makeProgram(program) {
|
module.exports = function makeProgram(program) {
|
||||||
program
|
program
|
||||||
.name(PKG.name)
|
.name(PKG.name)
|
||||||
.description(PKG.description, {
|
.description(PKG.description, {
|
||||||
INPUT: 'Alias to --input'
|
INPUT: 'Alias to --input',
|
||||||
})
|
})
|
||||||
.version(PKG.version, '-v, --version')
|
.version(PKG.version, '-v, --version')
|
||||||
.arguments('[INPUT...]')
|
.arguments('[INPUT...]')
|
||||||
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
|
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
|
||||||
.option('-s, --string <STRING>', 'Input SVG data string')
|
.option('-s, --string <STRING>', 'Input SVG data string')
|
||||||
.option('-f, --folder <FOLDER>', 'Input folder, optimize and rewrite all *.svg files')
|
.option(
|
||||||
.option('-o, --output <OUTPUT...>', 'Output file or folder (by default the same as the input), "-" for STDOUT')
|
'-f, --folder <FOLDER>',
|
||||||
.option('-p, --precision <INTEGER>', 'Set number of digits in the fractional part, overrides plugins params')
|
'Input folder, optimize and rewrite all *.svg files'
|
||||||
.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(
|
||||||
.option('--multipass', 'Pass over SVGs multiple times to ensure all optimizations are applied')
|
'-o, --output <OUTPUT...>',
|
||||||
.option('--pretty', 'Make SVG pretty printed')
|
'Output file or folder (by default the same as the input), "-" for STDOUT'
|
||||||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
|
)
|
||||||
.option('-r, --recursive', 'Use with \'-f\'. Optimizes *.svg files in folders recursively.')
|
.option(
|
||||||
.option('-q, --quiet', 'Only output error messages, not regular status messages')
|
'-p, --precision <INTEGER>',
|
||||||
.option('--show-plugins', 'Show available plugins and exit')
|
'Set number of digits in the fractional part, overrides plugins params'
|
||||||
.action(action);
|
)
|
||||||
}
|
.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) {
|
async function action(args, opts, command) {
|
||||||
var input = opts.input || args;
|
var input = opts.input || args;
|
||||||
var output = opts.output;
|
var output = opts.output;
|
||||||
var config = {}
|
var config = {};
|
||||||
|
|
||||||
if (opts.precision != null) {
|
if (opts.precision != null) {
|
||||||
const number = Number.parseInt(opts.precision, 0);
|
const number = Number.parseInt(opts.precision, 0);
|
||||||
if (Number.isNaN(number)) {
|
if (Number.isNaN(number)) {
|
||||||
console.error("error: option '-p, --precision' argument must be an integer number");
|
console.error(
|
||||||
process.exit(1)
|
"error: option '-p, --precision' argument must be an integer number"
|
||||||
} else {
|
);
|
||||||
opts.precision = number;
|
process.exit(1);
|
||||||
}
|
} else {
|
||||||
|
opts.precision = number;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.datauri != null) {
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --show-plugins
|
|
||||||
if (opts.showPlugins) {
|
|
||||||
showAvailablePlugins();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// w/o anything
|
|
||||||
if (
|
if (
|
||||||
(input.length === 0 || input[0] === '-') &&
|
opts.datauri !== 'base64' &&
|
||||||
!opts.string &&
|
opts.datauri !== 'enc' &&
|
||||||
!opts.stdin &&
|
opts.datauri !== 'unenc'
|
||||||
!opts.folder &&
|
|
||||||
process.stdin.isTTY === true
|
|
||||||
) {
|
) {
|
||||||
return command.help();
|
console.error(
|
||||||
|
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
|
if (opts.indent != null) {
|
||||||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
const number = Number.parseInt(opts.indent, 0);
|
||||||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
|
if (Number.isNaN(number)) {
|
||||||
throw Error(`${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --config
|
||||||
|
const loadedConfig = await loadConfig(opts.config);
|
||||||
|
if (loadedConfig != null) {
|
||||||
|
config = loadedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --quiet
|
||||||
|
if (opts.quiet) {
|
||||||
|
config.quiet = opts.quiet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (input.length) {
|
||||||
|
output = input;
|
||||||
|
} else if (opts.string) {
|
||||||
|
output = '-';
|
||||||
|
}
|
||||||
|
|
||||||
// --config
|
if (opts.datauri) {
|
||||||
const loadedConfig = await loadConfig(opts.config);
|
config.datauri = opts.datauri;
|
||||||
if (loadedConfig != null) {
|
}
|
||||||
config = loadedConfig;
|
|
||||||
|
// --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];
|
||||||
|
|
||||||
|
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]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --quiet
|
|
||||||
if (opts.quiet) {
|
|
||||||
config.quiet = opts.quiet;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (input.length) {
|
|
||||||
output = input;
|
|
||||||
} else if (opts.string) {
|
|
||||||
output = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.datauri) {
|
|
||||||
config.datauri = opts.datauri;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --folder
|
|
||||||
if (opts.folder) {
|
|
||||||
var ouputFolder = output && output[0] || opts.folder;
|
|
||||||
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];
|
|
||||||
|
|
||||||
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
|
// --string
|
||||||
} else if (opts.string) {
|
} else if (opts.string) {
|
||||||
var data = decodeSVGDatauri(opts.string);
|
var data = decodeSVGDatauri(opts.string);
|
||||||
|
|
||||||
return processSVGData(config, {input: 'string'}, data, output[0]);
|
return processSVGData(config, { input: 'string' }, data, output[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,10 +246,12 @@ async function action(args, opts, command) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function optimizeFolder(config, dir, output) {
|
function optimizeFolder(config, dir, output) {
|
||||||
if (!config.quiet) {
|
if (!config.quiet) {
|
||||||
console.log(`Processing directory '${dir}':\n`);
|
console.log(`Processing directory '${dir}':\n`);
|
||||||
}
|
}
|
||||||
return FS.promises.readdir(dir).then(files => processDirectory(config, dir, files, output));
|
return FS.promises
|
||||||
|
.readdir(dir)
|
||||||
|
.then((files) => processDirectory(config, dir, files, output));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,12 +263,22 @@ function optimizeFolder(config, dir, output) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function processDirectory(config, dir, files, output) {
|
function processDirectory(config, dir, files, output) {
|
||||||
// take only *.svg files, recursively if necessary
|
// take only *.svg files, recursively if necessary
|
||||||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||||
|
|
||||||
return svgFilesDescriptions.length ?
|
return svgFilesDescriptions.length
|
||||||
Promise.all(svgFilesDescriptions.map(fileDescription => optimizeFile(config, fileDescription.inputPath, fileDescription.outputPath))) :
|
? Promise.all(
|
||||||
Promise.reject(new Error(`No SVG files have been found in '${dir}' directory.`));
|
svgFilesDescriptions.map((fileDescription) =>
|
||||||
|
optimizeFile(
|
||||||
|
config,
|
||||||
|
fileDescription.inputPath,
|
||||||
|
fileDescription.outputPath
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: Promise.reject(
|
||||||
|
new Error(`No SVG files have been found in '${dir}' directory.`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -232,27 +290,32 @@ function processDirectory(config, dir, files, output) {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
function getFilesDescriptions(config, dir, files, output) {
|
function getFilesDescriptions(config, dir, files, output) {
|
||||||
const filesInThisFolder = files
|
const filesInThisFolder = files
|
||||||
.filter(name => regSVGFile.test(name))
|
.filter((name) => regSVGFile.test(name))
|
||||||
.map(name => ({
|
.map((name) => ({
|
||||||
inputPath: PATH.resolve(dir, name),
|
inputPath: PATH.resolve(dir, name),
|
||||||
outputPath: PATH.resolve(output, name),
|
outputPath: PATH.resolve(output, name),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return config.recursive ?
|
return config.recursive
|
||||||
[].concat(
|
? [].concat(
|
||||||
filesInThisFolder,
|
filesInThisFolder,
|
||||||
files
|
files
|
||||||
.filter(name => checkIsDir(PATH.resolve(dir, name)))
|
.filter((name) => checkIsDir(PATH.resolve(dir, name)))
|
||||||
.map(subFolderName => {
|
.map((subFolderName) => {
|
||||||
const subFolderPath = PATH.resolve(dir, subFolderName);
|
const subFolderPath = PATH.resolve(dir, subFolderName);
|
||||||
const subFolderFiles = FS.readdirSync(subFolderPath);
|
const subFolderFiles = FS.readdirSync(subFolderPath);
|
||||||
const subFolderOutput = PATH.resolve(output, subFolderName);
|
const subFolderOutput = PATH.resolve(output, subFolderName);
|
||||||
return getFilesDescriptions(config, subFolderPath, subFolderFiles, subFolderOutput);
|
return getFilesDescriptions(
|
||||||
})
|
config,
|
||||||
.reduce((a, b) => [].concat(a, b), [])
|
subFolderPath,
|
||||||
) :
|
subFolderFiles,
|
||||||
filesInThisFolder;
|
subFolderOutput
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.reduce((a, b) => [].concat(a, b), [])
|
||||||
|
)
|
||||||
|
: filesInThisFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,10 +326,11 @@ function getFilesDescriptions(config, dir, files, output) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function optimizeFile(config, file, output) {
|
function optimizeFile(config, file, output) {
|
||||||
return FS.promises.readFile(file, 'utf8').then(
|
return FS.promises.readFile(file, 'utf8').then(
|
||||||
data => processSVGData(config, {input: 'file', path: file}, data, output, file),
|
(data) =>
|
||||||
error => checkOptimizeFileError(config, file, output, error)
|
processSVGData(config, { input: 'file', path: file }, data, output, file),
|
||||||
);
|
(error) => checkOptimizeFileError(config, file, output, error)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,33 +342,42 @@ function optimizeFile(config, file, output) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function processSVGData(config, info, data, output, input) {
|
function processSVGData(config, info, data, output, input) {
|
||||||
var startTime = Date.now(),
|
var startTime = Date.now(),
|
||||||
prevFileSize = Buffer.byteLength(data, 'utf8');
|
prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||||
|
|
||||||
const result = optimize(data, { ...config, ...info });
|
const result = optimize(data, { ...config, ...info });
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
let message = result.error;
|
let message = result.error;
|
||||||
if (result.path != null) {
|
if (result.path != null) {
|
||||||
message += `\nFile: ${result.path}`
|
message += `\nFile: ${result.path}`;
|
||||||
}
|
|
||||||
throw Error(message)
|
|
||||||
}
|
}
|
||||||
if (config.datauri) {
|
throw Error(message);
|
||||||
result.data = encodeSVGDatauri(result.data, config.datauri);
|
}
|
||||||
}
|
if (config.datauri) {
|
||||||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
result.data = encodeSVGDatauri(result.data, config.datauri);
|
||||||
processingTime = Date.now() - startTime;
|
}
|
||||||
|
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
||||||
|
processingTime = Date.now() - startTime;
|
||||||
|
|
||||||
return writeOutput(input, output, result.data).then(function() {
|
return writeOutput(input, output, result.data).then(
|
||||||
if (!config.quiet && output != '-') {
|
function () {
|
||||||
if (input) {
|
if (!config.quiet && output != '-') {
|
||||||
console.log(`\n${PATH.basename(input)}:`);
|
if (input) {
|
||||||
}
|
console.log(`\n${PATH.basename(input)}:`);
|
||||||
printTimeInfo(processingTime);
|
|
||||||
printProfitInfo(prevFileSize, resultFileSize);
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -315,23 +388,24 @@ function processSVGData(config, info, data, output, input) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function writeOutput(input, output, data) {
|
function writeOutput(input, output, data) {
|
||||||
if (output == '-') {
|
if (output == '-') {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
return Promise.resolve();
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a time taken by optimization.
|
* Write a time taken by optimization.
|
||||||
* @param {number} time time in milliseconds.
|
* @param {number} time time in milliseconds.
|
||||||
*/
|
*/
|
||||||
function printTimeInfo(time) {
|
function printTimeInfo(time) {
|
||||||
console.log(`Done in ${time} ms!`);
|
console.log(`Done in ${time} ms!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,14 +414,17 @@ function printTimeInfo(time) {
|
|||||||
* @param {number} outBytes size after optimization.
|
* @param {number} outBytes size after optimization.
|
||||||
*/
|
*/
|
||||||
function printProfitInfo(inBytes, outBytes) {
|
function printProfitInfo(inBytes, outBytes) {
|
||||||
var profitPercents = 100 - outBytes * 100 / inBytes;
|
var profitPercents = 100 - (outBytes * 100) / inBytes;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
(Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
|
Math.round((inBytes / 1024) * 1000) / 1000 +
|
||||||
(profitPercents < 0 ? ' + ' : ' - ') +
|
' KiB' +
|
||||||
chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' +
|
(profitPercents < 0 ? ' + ' : ' - ') +
|
||||||
(Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB'
|
chalk.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
|
||||||
);
|
' = ' +
|
||||||
|
Math.round((outBytes / 1024) * 1000) / 1000 +
|
||||||
|
' KiB'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -359,12 +436,14 @@ function printProfitInfo(inBytes, outBytes) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function checkOptimizeFileError(config, input, output, error) {
|
function checkOptimizeFileError(config, input, output, error) {
|
||||||
if (error.code == 'EISDIR') {
|
if (error.code == 'EISDIR') {
|
||||||
return optimizeFolder(config, input, output);
|
return optimizeFolder(config, input, output);
|
||||||
} else if (error.code == 'ENOENT') {
|
} else if (error.code == 'ENOENT') {
|
||||||
return Promise.reject(new Error(`Error: no such file or directory '${error.path}'.`));
|
return Promise.reject(
|
||||||
}
|
new Error(`Error: no such file or directory '${error.path}'.`)
|
||||||
return Promise.reject(error);
|
);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,22 +455,26 @@ function checkOptimizeFileError(config, input, output, error) {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
function checkWriteFileError(input, output, data, error) {
|
function checkWriteFileError(input, output, data, error) {
|
||||||
if (error.code == 'EISDIR' && input) {
|
if (error.code == 'EISDIR' && input) {
|
||||||
return FS.promises.writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8');
|
return FS.promises.writeFile(
|
||||||
} else {
|
PATH.resolve(output, PATH.basename(input)),
|
||||||
return Promise.reject(error);
|
data,
|
||||||
}
|
'utf8'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show list of available plugins with short description.
|
* Show list of available plugins with short description.
|
||||||
*/
|
*/
|
||||||
function showAvailablePlugins() {
|
function showAvailablePlugins() {
|
||||||
const list = Object.entries(pluginsMap)
|
const list = Object.entries(pluginsMap)
|
||||||
.sort(([a], [b]) => a.localeCompare(b))
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`)
|
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
console.log('Currently available plugins:\n' + list);
|
console.log('Currently available plugins:\n' + list);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.checkIsDir = checkIsDir;
|
module.exports.checkIsDir = checkIsDir;
|
||||||
|
@ -53,11 +53,14 @@ const pluginsOrder = [
|
|||||||
'removeOffCanvasPaths',
|
'removeOffCanvasPaths',
|
||||||
'reusePaths',
|
'reusePaths',
|
||||||
];
|
];
|
||||||
const defaultPlugins = pluginsOrder.filter(name => pluginsMap[name].active);
|
const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
|
||||||
exports.defaultPlugins = defaultPlugins;
|
exports.defaultPlugins = defaultPlugins;
|
||||||
|
|
||||||
const extendDefaultPlugins = (plugins) => {
|
const extendDefaultPlugins = (plugins) => {
|
||||||
const extendedPlugins = pluginsOrder.map(name => ({ name, ...pluginsMap[name] }));
|
const extendedPlugins = pluginsOrder.map((name) => ({
|
||||||
|
name,
|
||||||
|
...pluginsMap[name],
|
||||||
|
}));
|
||||||
for (const plugin of plugins) {
|
for (const plugin of plugins) {
|
||||||
const resolvedPlugin = resolvePluginConfig(plugin, {});
|
const resolvedPlugin = resolvePluginConfig(plugin, {});
|
||||||
const index = pluginsOrder.indexOf(resolvedPlugin.name);
|
const index = pluginsOrder.indexOf(resolvedPlugin.name);
|
||||||
@ -68,7 +71,7 @@ const extendDefaultPlugins = (plugins) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return extendedPlugins;
|
return extendedPlugins;
|
||||||
}
|
};
|
||||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||||
|
|
||||||
const resolvePluginConfig = (plugin, config) => {
|
const resolvePluginConfig = (plugin, config) => {
|
||||||
@ -86,7 +89,7 @@ const resolvePluginConfig = (plugin, config) => {
|
|||||||
...pluginConfig,
|
...pluginConfig,
|
||||||
name: plugin,
|
name: plugin,
|
||||||
active: true,
|
active: true,
|
||||||
params: { ...pluginConfig.params, ...configParams }
|
params: { ...pluginConfig.params, ...configParams },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (typeof plugin === 'object' && plugin != null) {
|
if (typeof plugin === 'object' && plugin != null) {
|
||||||
@ -98,7 +101,7 @@ const resolvePluginConfig = (plugin, config) => {
|
|||||||
return {
|
return {
|
||||||
active: true,
|
active: true,
|
||||||
...plugin,
|
...plugin,
|
||||||
params: { configParams, ...plugin.params }
|
params: { configParams, ...plugin.params },
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// resolve builtin plugin specified as object without implementation
|
// resolve builtin plugin specified as object without implementation
|
||||||
@ -110,7 +113,7 @@ const resolvePluginConfig = (plugin, config) => {
|
|||||||
...pluginConfig,
|
...pluginConfig,
|
||||||
active: true,
|
active: true,
|
||||||
...plugin,
|
...plugin,
|
||||||
params: { ...pluginConfig.params, ...configParams, ...plugin.params }
|
params: { ...pluginConfig.params, ...configParams, ...plugin.params },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var CSSClassList = function(node) {
|
var CSSClassList = function (node) {
|
||||||
this.parentNode = node;
|
this.parentNode = node;
|
||||||
this.classNames = new Set();
|
this.classNames = new Set();
|
||||||
this.classAttr = null;
|
this.classAttr = null;
|
||||||
//this.classValue = null;
|
//this.classValue = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,121 +12,115 @@ var CSSClassList = function(node) {
|
|||||||
*
|
*
|
||||||
* @param parentNode the parentNode to assign to the cloned result
|
* @param parentNode the parentNode to assign to the cloned result
|
||||||
*/
|
*/
|
||||||
CSSClassList.prototype.clone = function(parentNode) {
|
CSSClassList.prototype.clone = function (parentNode) {
|
||||||
var node = this;
|
var node = this;
|
||||||
var nodeData = {};
|
var nodeData = {};
|
||||||
|
|
||||||
Object.keys(node).forEach(function(key) {
|
Object.keys(node).forEach(function (key) {
|
||||||
if (key !== 'parentNode') {
|
if (key !== 'parentNode') {
|
||||||
nodeData[key] = node[key];
|
nodeData[key] = node[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Deep-clone node data.
|
// Deep-clone node data.
|
||||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||||
|
|
||||||
var clone = new CSSClassList(parentNode);
|
var clone = new CSSClassList(parentNode);
|
||||||
Object.assign(clone, nodeData);
|
Object.assign(clone, nodeData);
|
||||||
return clone;
|
return clone;
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.hasClass = function() {
|
CSSClassList.prototype.hasClass = function () {
|
||||||
this.classAttr = { // empty class attr
|
this.classAttr = {
|
||||||
'name': 'class',
|
// empty class attr
|
||||||
'value': null
|
name: 'class',
|
||||||
};
|
value: null,
|
||||||
|
};
|
||||||
|
|
||||||
this.addClassHandler();
|
this.addClassHandler();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// attr.class
|
// 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', {
|
this.addClassValueHandler();
|
||||||
get: this.getClassAttr.bind(this),
|
|
||||||
set: this.setClassAttr.bind(this),
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addClassValueHandler();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// attr.class.value
|
// attr.class.value
|
||||||
|
|
||||||
CSSClassList.prototype.addClassValueHandler = function() {
|
CSSClassList.prototype.addClassValueHandler = function () {
|
||||||
|
Object.defineProperty(this.classAttr, 'value', {
|
||||||
Object.defineProperty(this.classAttr, 'value', {
|
get: this.getClassValue.bind(this),
|
||||||
get: this.getClassValue.bind(this),
|
set: this.setClassValue.bind(this),
|
||||||
set: this.setClassValue.bind(this),
|
enumerable: true,
|
||||||
enumerable: true,
|
configurable: true,
|
||||||
configurable: true
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.getClassAttr = function() {
|
CSSClassList.prototype.getClassAttr = function () {
|
||||||
return this.classAttr;
|
return this.classAttr;
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.setClassAttr = function(newClassAttr) {
|
CSSClassList.prototype.setClassAttr = function (newClassAttr) {
|
||||||
this.setClassValue(newClassAttr.value); // must before applying value handler!
|
this.setClassValue(newClassAttr.value); // must before applying value handler!
|
||||||
|
|
||||||
this.classAttr = newClassAttr;
|
this.classAttr = newClassAttr;
|
||||||
this.addClassValueHandler();
|
this.addClassValueHandler();
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.getClassValue = function() {
|
CSSClassList.prototype.getClassValue = function () {
|
||||||
var arrClassNames = Array.from(this.classNames);
|
var arrClassNames = Array.from(this.classNames);
|
||||||
return arrClassNames.join(' ');
|
return arrClassNames.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.setClassValue = function(newValue) {
|
CSSClassList.prototype.setClassValue = function (newValue) {
|
||||||
if(typeof newValue === 'undefined') {
|
if (typeof newValue === 'undefined') {
|
||||||
this.classNames.clear();
|
this.classNames.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var arrClassNames = newValue.split(' ');
|
var arrClassNames = newValue.split(' ');
|
||||||
this.classNames = new Set(arrClassNames);
|
this.classNames = new Set(arrClassNames);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CSSClassList.prototype.add = function (/* variadic */) {
|
||||||
CSSClassList.prototype.add = function(/* variadic */) {
|
this.hasClass();
|
||||||
this.hasClass();
|
Object.values(arguments).forEach(this._addSingle.bind(this));
|
||||||
Object.values(arguments).forEach(this._addSingle.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype._addSingle = function(className) {
|
CSSClassList.prototype._addSingle = function (className) {
|
||||||
this.classNames.add(className);
|
this.classNames.add(className);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CSSClassList.prototype.remove = function (/* variadic */) {
|
||||||
CSSClassList.prototype.remove = function(/* variadic */) {
|
this.hasClass();
|
||||||
this.hasClass();
|
Object.values(arguments).forEach(this._removeSingle.bind(this));
|
||||||
Object.values(arguments).forEach(this._removeSingle.bind(this));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype._removeSingle = function(className) {
|
CSSClassList.prototype._removeSingle = function (className) {
|
||||||
|
this.classNames.delete(className);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSSClassList.prototype.item = function (index) {
|
||||||
|
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.delete(className);
|
||||||
|
}
|
||||||
|
this.classNames.add(className);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CSSClassList.prototype.contains = function (className) {
|
||||||
CSSClassList.prototype.item = function(index) {
|
return this.classNames.has(className);
|
||||||
var arrClassNames = Array.from(this.classNames);
|
|
||||||
return arrClassNames[index];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSClassList.prototype.toggle = function(className, force) {
|
|
||||||
if(this.contains(className) || force === false) {
|
|
||||||
this.classNames.delete(className);
|
|
||||||
}
|
|
||||||
this.classNames.add(className);
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSClassList.prototype.contains = function(className) {
|
|
||||||
return this.classNames.has(className);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = CSSClassList;
|
module.exports = CSSClassList;
|
||||||
|
@ -6,45 +6,44 @@ var baseCssAdapter = require('css-select-base-adapter');
|
|||||||
* DOMUtils API for SVGO AST (used by css-select)
|
* DOMUtils API for SVGO AST (used by css-select)
|
||||||
*/
|
*/
|
||||||
var svgoCssSelectAdapterMin = {
|
var svgoCssSelectAdapterMin = {
|
||||||
|
// is the node a tag?
|
||||||
|
// isTag: ( node:Node ) => isTag:Boolean
|
||||||
|
isTag: function (node) {
|
||||||
|
return node.isElem();
|
||||||
|
},
|
||||||
|
|
||||||
// is the node a tag?
|
// get the parent of the node
|
||||||
// isTag: ( node:Node ) => isTag:Boolean
|
// getParent: ( node:Node ) => parentNode:Node
|
||||||
isTag: function(node) {
|
// returns null when no parent exists
|
||||||
return node.isElem();
|
getParent: function (node) {
|
||||||
},
|
return node.parentNode || null;
|
||||||
|
},
|
||||||
|
|
||||||
// get the parent of the node
|
// get the node's children
|
||||||
// getParent: ( node:Node ) => parentNode:Node
|
// getChildren: ( node:Node ) => children:[Node]
|
||||||
// returns null when no parent exists
|
getChildren: function (node) {
|
||||||
getParent: function(node) {
|
return node.content || [];
|
||||||
return node.parentNode || null;
|
},
|
||||||
},
|
|
||||||
|
|
||||||
// get the node's children
|
// get the name of the tag
|
||||||
// getChildren: ( node:Node ) => children:[Node]
|
// getName: ( elem:ElementNode ) => tagName:String
|
||||||
getChildren: function(node) {
|
getName: function (elemAst) {
|
||||||
return node.content || [];
|
return elemAst.elem;
|
||||||
},
|
},
|
||||||
|
|
||||||
// get the name of the tag
|
// get the text content of the node, and its children if it has any
|
||||||
// getName: ( elem:ElementNode ) => tagName:String
|
// getText: ( node:Node ) => text:String
|
||||||
getName: function(elemAst) {
|
// returns empty string when there is no text
|
||||||
return elemAst.elem;
|
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
|
// get the attribute value
|
||||||
// getText: ( node:Node ) => text:String
|
// getAttributeValue: ( elem:ElementNode, name:String ) => value:String
|
||||||
// returns empty string when there is no text
|
// returns null when attribute doesn't exist
|
||||||
getText: function(node) {
|
getAttributeValue: function (elem, name) {
|
||||||
return node.content[0].text || node.content[0].cdata || '';
|
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// use base adapter for default implementation
|
// use base adapter for default implementation
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var csstree = require('css-tree'),
|
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.properties = new Map();
|
||||||
this.parentNode = node;
|
this.hasSynced = false;
|
||||||
|
|
||||||
this.properties = new Map();
|
this.styleAttr = null;
|
||||||
this.hasSynced = false;
|
this.styleValue = null;
|
||||||
|
|
||||||
this.styleAttr = null;
|
this.parseError = false;
|
||||||
this.styleValue = null;
|
|
||||||
|
|
||||||
this.parseError = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,124 +20,120 @@ var CSSStyleDeclaration = function(node) {
|
|||||||
*
|
*
|
||||||
* @param parentNode the parentNode to assign to the cloned result
|
* @param parentNode the parentNode to assign to the cloned result
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.clone = function(parentNode) {
|
CSSStyleDeclaration.prototype.clone = function (parentNode) {
|
||||||
var node = this;
|
var node = this;
|
||||||
var nodeData = {};
|
var nodeData = {};
|
||||||
|
|
||||||
Object.keys(node).forEach(function(key) {
|
Object.keys(node).forEach(function (key) {
|
||||||
if (key !== 'parentNode') {
|
if (key !== 'parentNode') {
|
||||||
nodeData[key] = node[key];
|
nodeData[key] = node[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Deep-clone node data.
|
// Deep-clone node data.
|
||||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||||
|
|
||||||
var clone = new CSSStyleDeclaration(parentNode);
|
var clone = new CSSStyleDeclaration(parentNode);
|
||||||
Object.assign(clone, nodeData);
|
Object.assign(clone, nodeData);
|
||||||
return clone;
|
return clone;
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.hasStyle = function() {
|
CSSStyleDeclaration.prototype.hasStyle = function () {
|
||||||
this.addStyleHandler();
|
this.addStyleHandler();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// attr.style
|
// attr.style
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.addStyleHandler = function() {
|
CSSStyleDeclaration.prototype.addStyleHandler = function () {
|
||||||
|
this.styleAttr = {
|
||||||
|
// empty style attr
|
||||||
|
name: 'style',
|
||||||
|
value: null,
|
||||||
|
};
|
||||||
|
|
||||||
this.styleAttr = { // empty style attr
|
Object.defineProperty(this.parentNode.attrs, 'style', {
|
||||||
'name': 'style',
|
get: this.getStyleAttr.bind(this),
|
||||||
'value': null
|
set: this.setStyleAttr.bind(this),
|
||||||
};
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(this.parentNode.attrs, 'style', {
|
this.addStyleValueHandler();
|
||||||
get: this.getStyleAttr.bind(this),
|
|
||||||
set: this.setStyleAttr.bind(this),
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addStyleValueHandler();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// attr.style.value
|
// attr.style.value
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
|
CSSStyleDeclaration.prototype.addStyleValueHandler = function () {
|
||||||
|
Object.defineProperty(this.styleAttr, 'value', {
|
||||||
|
get: this.getStyleValue.bind(this),
|
||||||
|
set: this.setStyleValue.bind(this),
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Object.defineProperty(this.styleAttr, 'value', {
|
CSSStyleDeclaration.prototype.getStyleAttr = function () {
|
||||||
get: this.getStyleValue.bind(this),
|
return this.styleAttr;
|
||||||
set: this.setStyleValue.bind(this),
|
};
|
||||||
enumerable: true,
|
|
||||||
configurable: true
|
CSSStyleDeclaration.prototype.setStyleAttr = function (newStyleAttr) {
|
||||||
|
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
|
||||||
|
|
||||||
|
this.styleAttr = newStyleAttr;
|
||||||
|
this.addStyleValueHandler();
|
||||||
|
this.hasSynced = false; // raw css changed
|
||||||
|
};
|
||||||
|
|
||||||
|
CSSStyleDeclaration.prototype.getStyleValue = function () {
|
||||||
|
return this.getCssText();
|
||||||
|
};
|
||||||
|
|
||||||
|
CSSStyleDeclaration.prototype.setStyleValue = function (newValue) {
|
||||||
|
this.properties.clear(); // reset all existing properties
|
||||||
|
this.styleValue = newValue;
|
||||||
|
this.hasSynced = false; // raw css changed
|
||||||
|
};
|
||||||
|
|
||||||
|
CSSStyleDeclaration.prototype._loadCssText = function () {
|
||||||
|
if (this.hasSynced) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
||||||
|
|
||||||
|
if (!this.styleValue || this.styleValue.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var inlineCssStr = this.styleValue;
|
||||||
|
|
||||||
|
var declarations = {};
|
||||||
|
try {
|
||||||
|
declarations = csstree.parse(inlineCssStr, {
|
||||||
|
context: 'declarationList',
|
||||||
|
parseValue: false,
|
||||||
});
|
});
|
||||||
};
|
} catch (parseError) {
|
||||||
|
this.parseError = parseError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.parseError = false;
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.getStyleAttr = function() {
|
var self = this;
|
||||||
return this.styleAttr;
|
declarations.children.each(function (declaration) {
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
|
|
||||||
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
|
|
||||||
|
|
||||||
this.styleAttr = newStyleAttr;
|
|
||||||
this.addStyleValueHandler();
|
|
||||||
this.hasSynced = false; // raw css changed
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.getStyleValue = function() {
|
|
||||||
return this.getCssText();
|
|
||||||
};
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
|
|
||||||
this.properties.clear(); // reset all existing properties
|
|
||||||
this.styleValue = newValue;
|
|
||||||
this.hasSynced = false; // raw css changed
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._loadCssText = function() {
|
|
||||||
if (this.hasSynced) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
|
||||||
|
|
||||||
if (!this.styleValue || this.styleValue.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var inlineCssStr = this.styleValue;
|
|
||||||
|
|
||||||
var declarations = {};
|
|
||||||
try {
|
try {
|
||||||
declarations = csstree.parse(inlineCssStr, {
|
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
|
||||||
context: 'declarationList',
|
self.setProperty(
|
||||||
parseValue: false
|
styleDeclaration.name,
|
||||||
});
|
styleDeclaration.value,
|
||||||
} catch (parseError) {
|
styleDeclaration.priority
|
||||||
this.parseError = parseError;
|
);
|
||||||
return;
|
} 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
|
// only reads from properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,39 +141,43 @@ CSSStyleDeclaration.prototype._loadCssText = function() {
|
|||||||
*
|
*
|
||||||
* @return {String} Textual representation of the declaration block (empty string for no properties)
|
* @return {String} Textual representation of the declaration block (empty string for no properties)
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.getCssText = function() {
|
CSSStyleDeclaration.prototype.getCssText = function () {
|
||||||
var properties = this.getProperties();
|
var properties = this.getProperties();
|
||||||
|
|
||||||
if (this.parseError) {
|
if (this.parseError) {
|
||||||
// in case of a parse error, pass through original styles
|
// in case of a parse error, pass through original styles
|
||||||
return this.styleValue;
|
return this.styleValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cssText = [];
|
var cssText = [];
|
||||||
properties.forEach(function(property, propertyName) {
|
properties.forEach(function (property, propertyName) {
|
||||||
var strImportant = property.priority === 'important' ? '!important' : '';
|
var strImportant = property.priority === 'important' ? '!important' : '';
|
||||||
cssText.push(propertyName.trim() + ':' + property.value.trim() + strImportant);
|
cssText.push(
|
||||||
});
|
propertyName.trim() + ':' + property.value.trim() + strImportant
|
||||||
return cssText.join(';');
|
);
|
||||||
|
});
|
||||||
|
return cssText.join(';');
|
||||||
};
|
};
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._handleParseError = function() {
|
CSSStyleDeclaration.prototype._handleParseError = function () {
|
||||||
if (this.parseError) {
|
if (this.parseError) {
|
||||||
console.warn('Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr(\'style\').value. Error details: ' + this.parseError);
|
console.warn(
|
||||||
}
|
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
||||||
|
this.parseError
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CSSStyleDeclaration.prototype._getProperty = function (propertyName) {
|
||||||
|
if (typeof propertyName === 'undefined') {
|
||||||
|
throw Error('1 argument required, but only 0 present.');
|
||||||
|
}
|
||||||
|
|
||||||
CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
|
var properties = this.getProperties();
|
||||||
if(typeof propertyName === 'undefined') {
|
this._handleParseError();
|
||||||
throw Error('1 argument required, but only 0 present.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var properties = this.getProperties();
|
var property = properties.get(propertyName.trim());
|
||||||
this._handleParseError();
|
return property;
|
||||||
|
|
||||||
var property = properties.get(propertyName.trim());
|
|
||||||
return property;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,9 +186,9 @@ CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
|
|||||||
* @param {String} propertyName representing the property name to be checked.
|
* @param {String} propertyName representing the property name to be checked.
|
||||||
* @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
* @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
|
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) {
|
||||||
var property = this._getProperty(propertyName);
|
var property = this._getProperty(propertyName);
|
||||||
return property ? property.priority : '';
|
return property ? property.priority : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,9 +197,9 @@ CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
|
|||||||
* @param {String} propertyName representing the property name to be checked.
|
* @param {String} propertyName representing the property name to be checked.
|
||||||
* @return {String} value containing the value of the property. If not set, returns the empty string.
|
* @return {String} value containing the value of the property. If not set, returns the empty string.
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
|
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) {
|
||||||
var property = this._getProperty(propertyName);
|
var property = this._getProperty(propertyName);
|
||||||
return property ? property.value : null;
|
return property ? property.value : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,15 +208,15 @@ CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
|
|||||||
* @param {Number} index of the node to be fetched. The index is zero-based.
|
* @param {Number} index of the node to be fetched. The index is zero-based.
|
||||||
* @return {String} propertyName that is the name of the CSS property at the specified index.
|
* @return {String} propertyName that is the name of the CSS property at the specified index.
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.item = function(index) {
|
CSSStyleDeclaration.prototype.item = function (index) {
|
||||||
if(typeof index === 'undefined') {
|
if (typeof index === 'undefined') {
|
||||||
throw Error('1 argument required, but only 0 present.');
|
throw Error('1 argument required, but only 0 present.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var properties = this.getProperties();
|
var properties = this.getProperties();
|
||||||
this._handleParseError();
|
this._handleParseError();
|
||||||
|
|
||||||
return Array.from(properties.keys())[index];
|
return Array.from(properties.keys())[index];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,12 +224,11 @@ CSSStyleDeclaration.prototype.item = function(index) {
|
|||||||
*
|
*
|
||||||
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.getProperties = function() {
|
CSSStyleDeclaration.prototype.getProperties = function () {
|
||||||
this._loadCssText();
|
this._loadCssText();
|
||||||
return this.properties;
|
return this.properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// writes to properties
|
// writes to properties
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,19 +237,19 @@ CSSStyleDeclaration.prototype.getProperties = function() {
|
|||||||
* @param {String} propertyName representing the property name to be removed.
|
* @param {String} propertyName representing the property name to be removed.
|
||||||
* @return {String} oldValue equal to the value of the CSS property before it was removed.
|
* @return {String} oldValue equal to the value of the CSS property before it was removed.
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
|
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) {
|
||||||
if(typeof propertyName === 'undefined') {
|
if (typeof propertyName === 'undefined') {
|
||||||
throw Error('1 argument required, but only 0 present.');
|
throw Error('1 argument required, but only 0 present.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hasStyle();
|
this.hasStyle();
|
||||||
|
|
||||||
var properties = this.getProperties();
|
var properties = this.getProperties();
|
||||||
this._handleParseError();
|
this._handleParseError();
|
||||||
|
|
||||||
var oldValue = this.getPropertyValue(propertyName);
|
var oldValue = this.getPropertyValue(propertyName);
|
||||||
properties.delete(propertyName.trim());
|
properties.delete(propertyName.trim());
|
||||||
return oldValue;
|
return oldValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,24 +260,27 @@ CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
|
|||||||
* @param {String} [priority] allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
|
* @param {String} [priority] allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priority) {
|
CSSStyleDeclaration.prototype.setProperty = function (
|
||||||
if(typeof propertyName === 'undefined') {
|
propertyName,
|
||||||
throw Error('propertyName argument required, but only not present.');
|
value,
|
||||||
}
|
priority
|
||||||
|
) {
|
||||||
|
if (typeof propertyName === 'undefined') {
|
||||||
|
throw Error('propertyName argument required, but only not present.');
|
||||||
|
}
|
||||||
|
|
||||||
this.hasStyle();
|
this.hasStyle();
|
||||||
|
|
||||||
var properties = this.getProperties();
|
var properties = this.getProperties();
|
||||||
this._handleParseError();
|
this._handleParseError();
|
||||||
|
|
||||||
var property = {
|
var property = {
|
||||||
value: value.trim(),
|
value: value.trim(),
|
||||||
priority: priority.trim()
|
priority: priority.trim(),
|
||||||
};
|
};
|
||||||
properties.set(propertyName.trim(), property);
|
properties.set(propertyName.trim(), property);
|
||||||
|
|
||||||
return property;
|
return property;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = CSSStyleDeclaration;
|
module.exports = CSSStyleDeclaration;
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EOL = require('os').EOL,
|
var EOL = require('os').EOL,
|
||||||
textElems = require('../../plugins/_collections.js').textElems;
|
textElems = require('../../plugins/_collections.js').textElems;
|
||||||
|
|
||||||
var defaults = {
|
var defaults = {
|
||||||
doctypeStart: '<!DOCTYPE',
|
doctypeStart: '<!DOCTYPE',
|
||||||
doctypeEnd: '>',
|
doctypeEnd: '>',
|
||||||
procInstStart: '<?',
|
procInstStart: '<?',
|
||||||
procInstEnd: '?>',
|
procInstEnd: '?>',
|
||||||
tagOpenStart: '<',
|
tagOpenStart: '<',
|
||||||
tagOpenEnd: '>',
|
tagOpenEnd: '>',
|
||||||
tagCloseStart: '</',
|
tagCloseStart: '</',
|
||||||
tagCloseEnd: '>',
|
tagCloseEnd: '>',
|
||||||
tagShortStart: '<',
|
tagShortStart: '<',
|
||||||
tagShortEnd: '/>',
|
tagShortEnd: '/>',
|
||||||
attrStart: '="',
|
attrStart: '="',
|
||||||
attrEnd: '"',
|
attrEnd: '"',
|
||||||
commentStart: '<!--',
|
commentStart: '<!--',
|
||||||
commentEnd: '-->',
|
commentEnd: '-->',
|
||||||
cdataStart: '<![CDATA[',
|
cdataStart: '<![CDATA[',
|
||||||
cdataEnd: ']]>',
|
cdataEnd: ']]>',
|
||||||
textStart: '',
|
textStart: '',
|
||||||
textEnd: '',
|
textEnd: '',
|
||||||
indent: 4,
|
indent: 4,
|
||||||
regEntities: /[&'"<>]/g,
|
regEntities: /[&'"<>]/g,
|
||||||
regValEntities: /[&"<>]/g,
|
regValEntities: /[&"<>]/g,
|
||||||
encodeEntity: encodeEntity,
|
encodeEntity: encodeEntity,
|
||||||
pretty: false,
|
pretty: false,
|
||||||
useShortTags: true
|
useShortTags: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var entities = {
|
var entities = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'\'': ''',
|
"'": ''',
|
||||||
'"': '"',
|
'"': '"',
|
||||||
'>': '>',
|
'>': '>',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert SVG-as-JS object to SVG (XML) string.
|
* Convert SVG-as-JS object to SVG (XML) string.
|
||||||
@ -46,45 +46,41 @@ var entities = {
|
|||||||
*
|
*
|
||||||
* @return {Object} output data
|
* @return {Object} output data
|
||||||
*/
|
*/
|
||||||
module.exports = function(data, config) {
|
module.exports = function (data, config) {
|
||||||
|
return new JS2SVG(config).convert(data);
|
||||||
return new JS2SVG(config).convert(data);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function JS2SVG(config) {
|
function JS2SVG(config) {
|
||||||
|
if (config) {
|
||||||
|
this.config = Object.assign({}, defaults, config);
|
||||||
|
} else {
|
||||||
|
this.config = Object.assign({}, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
if (config) {
|
var indent = this.config.indent;
|
||||||
this.config = Object.assign({}, defaults, config);
|
if (typeof indent == 'number' && !isNaN(indent)) {
|
||||||
} else {
|
this.config.indent = indent < 0 ? '\t' : ' '.repeat(indent);
|
||||||
this.config = Object.assign({}, defaults);
|
} else if (typeof indent != 'string') {
|
||||||
}
|
this.config.indent = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
var indent = this.config.indent;
|
if (this.config.pretty) {
|
||||||
if (typeof indent == 'number' && !isNaN(indent)) {
|
this.config.doctypeEnd += EOL;
|
||||||
this.config.indent = (indent < 0) ? '\t' : ' '.repeat(indent);
|
this.config.procInstEnd += EOL;
|
||||||
} else if (typeof indent != 'string') {
|
this.config.commentEnd += EOL;
|
||||||
this.config.indent = ' ';
|
this.config.cdataEnd += EOL;
|
||||||
}
|
this.config.tagShortEnd += EOL;
|
||||||
|
this.config.tagOpenEnd += EOL;
|
||||||
if (this.config.pretty) {
|
this.config.tagCloseEnd += EOL;
|
||||||
this.config.doctypeEnd += EOL;
|
this.config.textEnd += 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) {
|
function encodeEntity(char) {
|
||||||
return entities[char];
|
return entities[char];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,44 +90,38 @@ function encodeEntity(char) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,16 +129,14 @@ JS2SVG.prototype.convert = function(data) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,12 +146,8 @@ JS2SVG.prototype.createIndent = function() {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createDoctype = function(doctype) {
|
JS2SVG.prototype.createDoctype = function (doctype) {
|
||||||
|
return this.config.doctypeStart + doctype + this.config.doctypeEnd;
|
||||||
return this.config.doctypeStart +
|
|
||||||
doctype +
|
|
||||||
this.config.doctypeEnd;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,14 +157,14 @@ JS2SVG.prototype.createDoctype = function(doctype) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createProcInst = function(instruction) {
|
JS2SVG.prototype.createProcInst = function (instruction) {
|
||||||
|
return (
|
||||||
return this.config.procInstStart +
|
this.config.procInstStart +
|
||||||
instruction.name +
|
instruction.name +
|
||||||
' ' +
|
' ' +
|
||||||
instruction.body +
|
instruction.body +
|
||||||
this.config.procInstEnd;
|
this.config.procInstEnd
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,12 +174,8 @@ JS2SVG.prototype.createProcInst = function(instruction) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createComment = function(comment) {
|
JS2SVG.prototype.createComment = function (comment) {
|
||||||
|
return this.config.commentStart + comment + this.config.commentEnd;
|
||||||
return this.config.commentStart +
|
|
||||||
comment +
|
|
||||||
this.config.commentEnd;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,13 +185,10 @@ JS2SVG.prototype.createComment = function(comment) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createCDATA = function(cdata) {
|
JS2SVG.prototype.createCDATA = function (cdata) {
|
||||||
|
return (
|
||||||
return this.createIndent() +
|
this.createIndent() + this.config.cdataStart + cdata + this.config.cdataEnd
|
||||||
this.config.cdataStart +
|
);
|
||||||
cdata +
|
|
||||||
this.config.cdataEnd;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,80 +198,79 @@ JS2SVG.prototype.createCDATA = function(cdata) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createElem = function(data) {
|
JS2SVG.prototype.createElem = function (data) {
|
||||||
|
// beautiful injection for obtaining SVG information :)
|
||||||
|
if (data.isElem('svg') && data.hasAttr('width') && data.hasAttr('height')) {
|
||||||
|
this.width = data.attr('width').value;
|
||||||
|
this.height = data.attr('height').value;
|
||||||
|
}
|
||||||
|
|
||||||
// beautiful injection for obtaining SVG information :)
|
// empty element and short tag
|
||||||
if (
|
if (data.isEmpty()) {
|
||||||
data.isElem('svg') &&
|
if (this.config.useShortTags) {
|
||||||
data.hasAttr('width') &&
|
return (
|
||||||
data.hasAttr('height')
|
this.createIndent() +
|
||||||
) {
|
this.config.tagShortStart +
|
||||||
this.width = data.attr('width').value;
|
data.elem +
|
||||||
this.height = data.attr('height').value;
|
this.createAttrs(data) +
|
||||||
}
|
this.config.tagShortEnd
|
||||||
|
);
|
||||||
// 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 {
|
} else {
|
||||||
var tagOpenStart = this.config.tagOpenStart,
|
return (
|
||||||
tagOpenEnd = this.config.tagOpenEnd,
|
this.createIndent() +
|
||||||
tagCloseStart = this.config.tagCloseStart,
|
this.config.tagShortStart +
|
||||||
tagCloseEnd = this.config.tagCloseEnd,
|
data.elem +
|
||||||
openIndent = this.createIndent(),
|
this.createAttrs(data) +
|
||||||
closeIndent = this.createIndent(),
|
this.config.tagOpenEnd +
|
||||||
processedData = '',
|
this.config.tagCloseStart +
|
||||||
dataEnd = '';
|
data.elem +
|
||||||
|
this.config.tagCloseEnd
|
||||||
if (this.textContext) {
|
);
|
||||||
tagOpenStart = defaults.tagOpenStart;
|
}
|
||||||
tagOpenEnd = defaults.tagOpenEnd;
|
// non-empty element
|
||||||
tagCloseStart = defaults.tagCloseStart;
|
} else {
|
||||||
tagCloseEnd = defaults.tagCloseEnd;
|
var tagOpenStart = this.config.tagOpenStart,
|
||||||
openIndent = '';
|
tagOpenEnd = this.config.tagOpenEnd,
|
||||||
} else if (data.isElem(textElems)) {
|
tagCloseStart = this.config.tagCloseStart,
|
||||||
tagOpenEnd = defaults.tagOpenEnd;
|
tagCloseEnd = this.config.tagCloseEnd,
|
||||||
tagCloseStart = defaults.tagCloseStart;
|
openIndent = this.createIndent(),
|
||||||
closeIndent = '';
|
closeIndent = this.createIndent(),
|
||||||
this.textContext = data;
|
processedData = '',
|
||||||
}
|
dataEnd = '';
|
||||||
|
|
||||||
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) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (this.textContext == data) {
|
||||||
|
this.textContext = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
openIndent +
|
||||||
|
tagOpenStart +
|
||||||
|
data.elem +
|
||||||
|
this.createAttrs(data) +
|
||||||
|
tagOpenEnd +
|
||||||
|
processedData +
|
||||||
|
dataEnd +
|
||||||
|
closeIndent +
|
||||||
|
tagCloseStart +
|
||||||
|
data.elem +
|
||||||
|
tagCloseEnd
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -304,29 +280,26 @@ JS2SVG.prototype.createElem = function(data) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createAttrs = function(elem) {
|
JS2SVG.prototype.createAttrs = function (elem) {
|
||||||
|
var attrs = '';
|
||||||
|
|
||||||
var attrs = '';
|
elem.eachAttr(function (attr) {
|
||||||
|
if (attr.value !== undefined) {
|
||||||
elem.eachAttr(function(attr) {
|
attrs +=
|
||||||
|
' ' +
|
||||||
if (attr.value !== undefined) {
|
attr.name +
|
||||||
attrs += ' ' +
|
this.config.attrStart +
|
||||||
attr.name +
|
String(attr.value).replace(
|
||||||
this.config.attrStart +
|
this.config.regValEntities,
|
||||||
String(attr.value).replace(this.config.regValEntities, this.config.encodeEntity) +
|
this.config.encodeEntity
|
||||||
this.config.attrEnd;
|
) +
|
||||||
}
|
this.config.attrEnd;
|
||||||
else {
|
} else {
|
||||||
attrs += ' ' +
|
attrs += ' ' + attr.name;
|
||||||
attr.name;
|
}
|
||||||
}
|
}, this);
|
||||||
|
|
||||||
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
return attrs;
|
|
||||||
|
|
||||||
|
return attrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -336,11 +309,11 @@ JS2SVG.prototype.createAttrs = function(elem) {
|
|||||||
*
|
*
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
JS2SVG.prototype.createText = function(text) {
|
JS2SVG.prototype.createText = function (text) {
|
||||||
|
return (
|
||||||
return this.createIndent() +
|
this.createIndent() +
|
||||||
this.config.textStart +
|
this.config.textStart +
|
||||||
text.replace(this.config.regEntities, this.config.encodeEntity) +
|
text.replace(this.config.regEntities, this.config.encodeEntity) +
|
||||||
(this.textContext ? '' : this.config.textEnd);
|
(this.textContext ? '' : this.config.textEnd)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,57 +5,58 @@ const svgoCssSelectAdapter = require('./css-select-adapter');
|
|||||||
|
|
||||||
var cssSelectOpts = {
|
var cssSelectOpts = {
|
||||||
xmlMode: true,
|
xmlMode: true,
|
||||||
adapter: svgoCssSelectAdapter
|
adapter: svgoCssSelectAdapter,
|
||||||
};
|
};
|
||||||
|
|
||||||
var JSAPI = module.exports = function(data, parentNode) {
|
var JSAPI = function (data, parentNode) {
|
||||||
Object.assign(this, data);
|
Object.assign(this, data);
|
||||||
if (parentNode) {
|
if (parentNode) {
|
||||||
Object.defineProperty(this, 'parentNode', {
|
Object.defineProperty(this, 'parentNode', {
|
||||||
writable: true,
|
writable: true,
|
||||||
value: parentNode
|
value: parentNode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
module.exports = JSAPI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a deep clone of this node.
|
* Perform a deep clone of this node.
|
||||||
*
|
*
|
||||||
* @return {Object} element
|
* @return {Object} element
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.clone = function() {
|
JSAPI.prototype.clone = function () {
|
||||||
var node = this;
|
var node = this;
|
||||||
var nodeData = {};
|
var nodeData = {};
|
||||||
|
|
||||||
Object.keys(node).forEach(function(key) {
|
Object.keys(node).forEach(function (key) {
|
||||||
if (key !== 'class' && key !== 'style' && key !== 'content') {
|
if (key !== 'class' && key !== 'style' && key !== 'content') {
|
||||||
nodeData[key] = node[key];
|
nodeData[key] = node[key];
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deep-clone node data.
|
||||||
|
nodeData = JSON.parse(JSON.stringify(nodeData));
|
||||||
|
|
||||||
|
// parentNode gets set to a proper object by the parent clone,
|
||||||
|
// but it needs to be true/false now to do the right thing
|
||||||
|
// in the constructor.
|
||||||
|
var clonedNode = new JSAPI(nodeData, !!node.parentNode);
|
||||||
|
|
||||||
|
if (node.class) {
|
||||||
|
clonedNode.class = node.class.clone(clonedNode);
|
||||||
|
}
|
||||||
|
if (node.style) {
|
||||||
|
clonedNode.style = node.style.clone(clonedNode);
|
||||||
|
}
|
||||||
|
if (node.content) {
|
||||||
|
clonedNode.content = node.content.map(function (childNode) {
|
||||||
|
var clonedChild = childNode.clone();
|
||||||
|
clonedChild.parentNode = clonedNode;
|
||||||
|
return clonedChild;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Deep-clone node data.
|
return clonedNode;
|
||||||
nodeData = JSON.parse(JSON.stringify(nodeData));
|
|
||||||
|
|
||||||
// parentNode gets set to a proper object by the parent clone,
|
|
||||||
// but it needs to be true/false now to do the right thing
|
|
||||||
// in the constructor.
|
|
||||||
var clonedNode = new JSAPI(nodeData, !!node.parentNode);
|
|
||||||
|
|
||||||
if (node.class) {
|
|
||||||
clonedNode.class = node.class.clone(clonedNode);
|
|
||||||
}
|
|
||||||
if (node.style) {
|
|
||||||
clonedNode.style = node.style.clone(clonedNode);
|
|
||||||
}
|
|
||||||
if (node.content) {
|
|
||||||
clonedNode.content = node.content.map(function(childNode) {
|
|
||||||
var clonedChild = childNode.clone();
|
|
||||||
clonedChild.parentNode = clonedNode;
|
|
||||||
return clonedChild;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return clonedNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,14 +66,12 @@ JSAPI.prototype.clone = function() {
|
|||||||
* @param {String|Array} [param] element name or names arrays
|
* @param {String|Array} [param] element name or names arrays
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,13 +80,10 @@ JSAPI.prototype.isElem = function(param) {
|
|||||||
* @param {String} name new element name
|
* @param {String} name new element name
|
||||||
* @return {Object} element
|
* @return {Object} element
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,10 +91,8 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.isEmpty = function() {
|
JSAPI.prototype.isEmpty = function () {
|
||||||
|
return !this.content || !this.content.length;
|
||||||
return !this.content || !this.content.length;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,12 +101,12 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
*
|
*
|
||||||
* @return {?Object}
|
* @return {?Object}
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.closestElem = function(elemName) {
|
JSAPI.prototype.closestElem = function (elemName) {
|
||||||
var elem = this;
|
var elem = this;
|
||||||
|
|
||||||
while ((elem = elem.parentNode) && !elem.isElem(elemName));
|
while ((elem = elem.parentNode) && !elem.isElem(elemName));
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,18 +117,17 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {Array|Object} [insertion] Elements to add to the content.
|
* @param {Array|Object} [insertion] Elements to add to the content.
|
||||||
* @return {Array} Removed elements.
|
* @return {Array} Removed elements.
|
||||||
*/
|
*/
|
||||||
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);
|
|
||||||
|
|
||||||
return this.content.splice.apply(this.content, [start, n].concat(insertion));
|
|
||||||
|
|
||||||
|
insertion.forEach(function (inner) {
|
||||||
|
inner.parentNode = this;
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
return this.content.splice.apply(this.content, [start, n].concat(insertion));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,16 +138,15 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} [val] attribute value (will be toString()'ed)
|
* @param {String} [val] attribute value (will be toString()'ed)
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.hasAttr = function(name, val) {
|
JSAPI.prototype.hasAttr = function (name, val) {
|
||||||
|
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||||
|
|
||||||
if (!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];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,39 +157,44 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
|
* @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.hasAttrLocal = function(localName, val) {
|
JSAPI.prototype.hasAttrLocal = function (localName, val) {
|
||||||
|
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||||
|
|
||||||
if (!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) {
|
function nameTest(attr) {
|
||||||
case 'Number': // same as String
|
return attr.local === localName;
|
||||||
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) {
|
function stringValueTest(attr) {
|
||||||
return attr.local === localName;
|
return attr.local === localName && val == attr.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringValueTest(attr) {
|
function regexpValueTest(attr) {
|
||||||
return attr.local === localName && val == attr.value;
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -208,14 +205,13 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} [val] attribute value (will be toString()'ed)
|
* @param {String} [val] attribute value (will be toString()'ed)
|
||||||
* @return {Object|Undefined}
|
* @return {Object|Undefined}
|
||||||
*/
|
*/
|
||||||
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];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,17 +220,20 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} name attribute name
|
* @param {String} name attribute name
|
||||||
* @return {Object|Undefined}
|
* @return {Object|Undefined}
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.computedAttr = function(name, val) {
|
JSAPI.prototype.computedAttr = function (name, val) {
|
||||||
if (!arguments.length) return;
|
if (!arguments.length) return;
|
||||||
|
|
||||||
for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode);
|
for (
|
||||||
|
var elem = this;
|
||||||
if (val != null) {
|
elem && (!elem.hasAttr(name) || !elem.attr(name).value);
|
||||||
return elem ? elem.hasAttr(name, val) : false;
|
elem = elem.parentNode
|
||||||
} 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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,25 +243,23 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} [val] attribute value
|
* @param {String} [val] attribute value
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
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)) {
|
if (!this.hasAttr(name)) return false;
|
||||||
name.forEach(this.removeAttr, this);
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -271,27 +268,30 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {Object} [attr={}] attribute object
|
* @param {Object} [attr={}] attribute object
|
||||||
* @return {Object|Boolean} created attribute or false if no attr was passed in
|
* @return {Object|Boolean} created attribute or false if no attr was passed in
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.addAttr = function(attr) {
|
JSAPI.prototype.addAttr = function (attr) {
|
||||||
attr = attr || {};
|
attr = attr || {};
|
||||||
|
|
||||||
if (attr.name === undefined ||
|
if (
|
||||||
attr.prefix === undefined ||
|
attr.name === undefined ||
|
||||||
attr.local === undefined
|
attr.prefix === undefined ||
|
||||||
) return false;
|
attr.local === undefined
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
|
||||||
this.attrs = this.attrs || {};
|
this.attrs = this.attrs || {};
|
||||||
this.attrs[attr.name] = attr;
|
this.attrs[attr.name] = attr;
|
||||||
|
|
||||||
if(attr.name === 'class') { // newly added class attribute
|
if (attr.name === 'class') {
|
||||||
this.class.hasClass();
|
// newly added class attribute
|
||||||
}
|
this.class.hasClass();
|
||||||
|
}
|
||||||
|
|
||||||
if(attr.name === 'style') { // newly added style attribute
|
if (attr.name === 'style') {
|
||||||
this.style.hasStyle();
|
// newly added style attribute
|
||||||
}
|
this.style.hasStyle();
|
||||||
|
}
|
||||||
return this.attrs[attr.name];
|
|
||||||
|
|
||||||
|
return this.attrs[attr.name];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,16 +301,14 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {Object} [context] callback context
|
* @param {Object} [context] callback context
|
||||||
* @return {Boolean} false if there are no any attributes
|
* @return {Boolean} false if there are no any attributes
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.eachAttr = function(callback, context) {
|
JSAPI.prototype.eachAttr = function (callback, context) {
|
||||||
|
if (!this.hasAttr()) return false;
|
||||||
|
|
||||||
if (!this.hasAttr()) return false;
|
for (const attr of Object.values(this.attrs)) {
|
||||||
|
callback.call(context, attr);
|
||||||
for (const attr of Object.values(this.attrs)) {
|
}
|
||||||
callback.call(context, attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -320,16 +318,14 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {Object} [context] callback context
|
* @param {Object} [context] callback context
|
||||||
* @return {Boolean} false if there are no any attributes
|
* @return {Boolean} false if there are no any attributes
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.someAttr = function(callback, context) {
|
JSAPI.prototype.someAttr = function (callback, context) {
|
||||||
|
if (!this.hasAttr()) return false;
|
||||||
|
|
||||||
if (!this.hasAttr()) return false;
|
for (const attr of Object.values(this.attrs)) {
|
||||||
|
if (callback.call(context, attr)) return true;
|
||||||
for (const attr of Object.values(this.attrs)) {
|
}
|
||||||
if (callback.call(context, attr)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -338,12 +334,10 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} selectors CSS selector(s) string
|
* @param {String} selectors CSS selector(s) string
|
||||||
* @return {Array} null if no elements matched
|
* @return {Array} null if no elements matched
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -352,10 +346,8 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} selectors CSS selector(s) string
|
* @param {String} selectors CSS selector(s) string
|
||||||
* @return {Array} null if no element matched
|
* @return {Array} null if no element matched
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.querySelector = function(selectors) {
|
JSAPI.prototype.querySelector = function (selectors) {
|
||||||
|
return selectOne(selectors, this, cssSelectOpts);
|
||||||
return selectOne(selectors, this, cssSelectOpts);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -364,8 +356,6 @@ JSAPI.prototype.renameElem = function(name) {
|
|||||||
* @param {String} selector CSS selector string
|
* @param {String} selector CSS selector string
|
||||||
* @return {Boolean} true if element would be selected by selector string, false if it does not
|
* @return {Boolean} true if element would be selected by selector string, false if it does not
|
||||||
*/
|
*/
|
||||||
JSAPI.prototype.matches = function(selector) {
|
JSAPI.prototype.matches = function (selector) {
|
||||||
|
return is(this, selector, cssSelectOpts);
|
||||||
return is(this, selector, cssSelectOpts);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* @param {Object} plugins plugins object from config
|
* @param {Object} plugins plugins object from config
|
||||||
* @return {Object} output data
|
* @return {Object} output data
|
||||||
*/
|
*/
|
||||||
module.exports = function(data, info, plugins) {
|
module.exports = function (data, info, plugins) {
|
||||||
// Try to group sequential elements of plugins array
|
// Try to group sequential elements of plugins array
|
||||||
// to optimize ast traversing
|
// to optimize ast traversing
|
||||||
const groups = [];
|
const groups = [];
|
||||||
@ -24,7 +24,7 @@ module.exports = function(data, info, plugins) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
switch(group[0].type) {
|
switch (group[0].type) {
|
||||||
case 'perItem':
|
case 'perItem':
|
||||||
data = perItem(data, info, group);
|
data = perItem(data, info, group);
|
||||||
break;
|
break;
|
||||||
@ -49,42 +49,36 @@ module.exports = function(data, info, plugins) {
|
|||||||
* @return {Object} output data
|
* @return {Object} output data
|
||||||
*/
|
*/
|
||||||
function perItem(data, info, plugins, reverse) {
|
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 (plugin.active && plugin.fn(item, plugin.params, info) === false) {
|
||||||
if (reverse && item.content) {
|
filter = false;
|
||||||
monkeys(item);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// main filter
|
// direct pass
|
||||||
var filter = true;
|
if (!reverse && item.content) {
|
||||||
|
monkeys(item);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; filter && i < plugins.length; i++) {
|
return filter;
|
||||||
var plugin = plugins[i];
|
});
|
||||||
|
|
||||||
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
|
return items;
|
||||||
filter = false;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// direct pass
|
|
||||||
if (!reverse && item.content) {
|
|
||||||
monkeys(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return items;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return monkeys(data);
|
|
||||||
|
|
||||||
|
return monkeys(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,13 +90,11 @@ function perItem(data, info, plugins, reverse) {
|
|||||||
* @return {Object} output data
|
* @return {Object} output data
|
||||||
*/
|
*/
|
||||||
function full(data, info, plugins) {
|
function full(data, info, plugins) {
|
||||||
|
plugins.forEach(function (plugin) {
|
||||||
|
if (plugin.active) {
|
||||||
|
data = plugin.fn(data, plugin.params, info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
plugins.forEach(function(plugin) {
|
return data;
|
||||||
if (plugin.active) {
|
|
||||||
data = plugin.fn(data, plugin.params, info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var SAX = require('@trysound/sax'),
|
var SAX = require('@trysound/sax'),
|
||||||
JSAPI = require('./jsAPI.js'),
|
JSAPI = require('./jsAPI.js'),
|
||||||
CSSClassList = require('./css-class-list'),
|
CSSClassList = require('./css-class-list'),
|
||||||
CSSStyleDeclaration = require('./css-style-declaration'),
|
CSSStyleDeclaration = require('./css-style-declaration'),
|
||||||
textElems = require('../../plugins/_collections.js').textElems,
|
textElems = require('../../plugins/_collections.js').textElems,
|
||||||
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
|
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
strict: true,
|
strict: true,
|
||||||
trim: false,
|
trim: false,
|
||||||
normalize: false,
|
normalize: false,
|
||||||
lowercase: true,
|
lowercase: true,
|
||||||
xmlns: true,
|
xmlns: true,
|
||||||
position: true
|
position: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,132 +21,118 @@ var config = {
|
|||||||
*
|
*
|
||||||
* @param {String} data input data
|
* @param {String} data input data
|
||||||
*/
|
*/
|
||||||
module.exports = function(data) {
|
module.exports = function (data) {
|
||||||
|
var sax = SAX.parser(config.strict, config),
|
||||||
|
root = new JSAPI({ elem: '#document', content: [] }),
|
||||||
|
current = root,
|
||||||
|
stack = [root];
|
||||||
|
|
||||||
var sax = SAX.parser(config.strict, config),
|
function pushToContent(content) {
|
||||||
root = new JSAPI({ elem: '#document', content: [] }),
|
content = new JSAPI(content, current);
|
||||||
current = root,
|
|
||||||
stack = [root];
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
sax.ondoctype = function(doctype) {
|
while ((entityMatch = entityDeclaration.exec(data)) != null) {
|
||||||
|
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
|
||||||
pushToContent({
|
|
||||||
doctype: doctype
|
|
||||||
});
|
|
||||||
|
|
||||||
var subsetStart = doctype.indexOf('['),
|
|
||||||
entityMatch;
|
|
||||||
|
|
||||||
if (subsetStart >= 0) {
|
|
||||||
entityDeclaration.lastIndex = subsetStart;
|
|
||||||
|
|
||||||
while ((entityMatch = entityDeclaration.exec(data)) != null) {
|
|
||||||
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
sax.onprocessinginstruction = function(data) {
|
|
||||||
|
|
||||||
pushToContent({
|
|
||||||
processinginstruction: data
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
sax.oncomment = function(comment) {
|
|
||||||
|
|
||||||
pushToContent({
|
|
||||||
comment: comment.trim()
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
sax.oncdata = function(cdata) {
|
|
||||||
|
|
||||||
pushToContent({
|
|
||||||
cdata: cdata
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
sax.onopentag = function(data) {
|
|
||||||
|
|
||||||
var elem = {
|
|
||||||
elem: data.name,
|
|
||||||
prefix: data.prefix,
|
|
||||||
local: data.local,
|
|
||||||
attrs: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
elem.class = new CSSClassList(elem);
|
|
||||||
elem.style = new CSSStyleDeclaration(elem);
|
|
||||||
|
|
||||||
if (Object.keys(data.attributes).length) {
|
|
||||||
for (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();
|
|
||||||
}
|
|
||||||
|
|
||||||
elem.attrs[name] = {
|
|
||||||
name: name,
|
|
||||||
value: attr.value,
|
|
||||||
prefix: attr.prefix,
|
|
||||||
local: attr.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.onprocessinginstruction = function (data) {
|
||||||
|
pushToContent({
|
||||||
|
processinginstruction: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sax.oncomment = function (comment) {
|
||||||
|
pushToContent({
|
||||||
|
comment: comment.trim(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sax.oncdata = function (cdata) {
|
||||||
|
pushToContent({
|
||||||
|
cdata: cdata,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sax.onopentag = function (data) {
|
||||||
|
var elem = {
|
||||||
|
elem: data.name,
|
||||||
|
prefix: data.prefix,
|
||||||
|
local: data.local,
|
||||||
|
attrs: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
sax.onclosetag = function() {
|
elem.class = new CSSClassList(elem);
|
||||||
stack.pop();
|
elem.style = new CSSStyleDeclaration(elem);
|
||||||
current = stack[stack.length - 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
sax.onerror = function(e) {
|
if (Object.keys(data.attributes).length) {
|
||||||
|
for (const [name, attr] of Object.entries(data.attributes)) {
|
||||||
e.message = 'Error in parsing SVG: ' + e.message;
|
if (name === 'class') {
|
||||||
if (e.message.indexOf('Unexpected end') < 0) {
|
// has class attribute
|
||||||
throw e;
|
elem.class.hasClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
if (name === 'style') {
|
||||||
|
// has style attribute
|
||||||
|
elem.style.hasStyle();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
elem.attrs[name] = {
|
||||||
sax.write(data).close();
|
name: name,
|
||||||
return root;
|
value: attr.value,
|
||||||
} catch (e) {
|
prefix: attr.prefix,
|
||||||
return { error: e.message };
|
local: attr.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];
|
||||||
|
};
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,20 +7,20 @@
|
|||||||
* @param {String} type Data URI type
|
* @param {String} type Data URI type
|
||||||
* @return {String} output string
|
* @return {String} output string
|
||||||
*/
|
*/
|
||||||
exports.encodeSVGDatauri = function(str, type) {
|
exports.encodeSVGDatauri = function (str, type) {
|
||||||
var prefix = 'data:image/svg+xml';
|
var prefix = 'data:image/svg+xml';
|
||||||
if (!type || type === 'base64') {
|
if (!type || type === 'base64') {
|
||||||
// base64
|
// base64
|
||||||
prefix += ';base64,';
|
prefix += ';base64,';
|
||||||
str = prefix + Buffer.from(str).toString('base64');
|
str = prefix + Buffer.from(str).toString('base64');
|
||||||
} else if (type === 'enc') {
|
} else if (type === 'enc') {
|
||||||
// URI encoded
|
// URI encoded
|
||||||
str = prefix + ',' + encodeURIComponent(str);
|
str = prefix + ',' + encodeURIComponent(str);
|
||||||
} else if (type === 'unenc') {
|
} else if (type === 'unenc') {
|
||||||
// unencoded
|
// unencoded
|
||||||
str = prefix + ',' + str;
|
str = prefix + ',' + str;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,32 +29,32 @@ exports.encodeSVGDatauri = function(str, type) {
|
|||||||
* @param {string} str input string
|
* @param {string} str input string
|
||||||
* @return {String} output string
|
* @return {String} output string
|
||||||
*/
|
*/
|
||||||
exports.decodeSVGDatauri = function(str) {
|
exports.decodeSVGDatauri = function (str) {
|
||||||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||||
var match = regexp.exec(str);
|
var match = regexp.exec(str);
|
||||||
|
|
||||||
// plain string
|
// plain string
|
||||||
if (!match) return str;
|
if (!match) return str;
|
||||||
|
|
||||||
var data = match[3];
|
var data = match[3];
|
||||||
|
|
||||||
if (match[2]) {
|
if (match[2]) {
|
||||||
// base64
|
// base64
|
||||||
str = Buffer.from(data, 'base64').toString('utf8');
|
str = Buffer.from(data, 'base64').toString('utf8');
|
||||||
} else if (data.charAt(0) === '%') {
|
} else if (data.charAt(0) === '%') {
|
||||||
// URI encoded
|
// URI encoded
|
||||||
str = decodeURIComponent(data);
|
str = decodeURIComponent(data);
|
||||||
} else if (data.charAt(0) === '<') {
|
} else if (data.charAt(0) === '<') {
|
||||||
// unencoded
|
// unencoded
|
||||||
str = data;
|
str = data;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.intersectArrays = function(a, b) {
|
exports.intersectArrays = function (a, b) {
|
||||||
return a.filter(function(n) {
|
return a.filter(function (n) {
|
||||||
return b.indexOf(n) > -1;
|
return b.indexOf(n) > -1;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,48 +68,46 @@ exports.intersectArrays = function(a, b) {
|
|||||||
* @param {string?} command path data instruction
|
* @param {string?} command path data instruction
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
exports.cleanupOutData = function(data, params, command) {
|
exports.cleanupOutData = function (data, params, command) {
|
||||||
var str = '',
|
var str = '',
|
||||||
delimiter,
|
delimiter,
|
||||||
prev;
|
prev;
|
||||||
|
|
||||||
data.forEach(function(item, i) {
|
data.forEach(function (item, i) {
|
||||||
// space delimiter by default
|
// space delimiter by default
|
||||||
delimiter = ' ';
|
delimiter = ' ';
|
||||||
|
|
||||||
// no extra space in front of first number
|
// no extra space in front of first number
|
||||||
if (i == 0) delimiter = '';
|
if (i == 0) delimiter = '';
|
||||||
|
|
||||||
// no extra space after 'arcto' command flags(large-arc and sweep flags)
|
// no extra space after 'arcto' command flags(large-arc and sweep flags)
|
||||||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
||||||
var pos = i % 7;
|
var pos = i % 7;
|
||||||
if (pos == 4 || pos == 5) delimiter = '';
|
if (pos == 4 || pos == 5) delimiter = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove floating-point numbers leading zeros
|
// remove floating-point numbers leading zeros
|
||||||
// 0.5 → .5
|
// 0.5 → .5
|
||||||
// -0.5 → -.5
|
// -0.5 → -.5
|
||||||
if (params.leadingZero) {
|
if (params.leadingZero) {
|
||||||
item = removeLeadingZero(item);
|
item = removeLeadingZero(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no extra space in front of negative number or
|
// no extra space in front of negative number or
|
||||||
// in front of a floating number if a previous number is floating too
|
// in front of a floating number if a previous number is floating too
|
||||||
if (
|
if (
|
||||||
params.negativeExtraSpace &&
|
params.negativeExtraSpace &&
|
||||||
delimiter != '' &&
|
delimiter != '' &&
|
||||||
(item < 0 ||
|
(item < 0 || (String(item).charCodeAt(0) == 46 && prev % 1 !== 0))
|
||||||
(String(item).charCodeAt(0) == 46 && prev % 1 !== 0)
|
) {
|
||||||
)
|
delimiter = '';
|
||||||
) {
|
}
|
||||||
delimiter = '';
|
// save prev item value
|
||||||
}
|
prev = item;
|
||||||
// save prev item value
|
str += delimiter + item;
|
||||||
prev = item;
|
});
|
||||||
str += delimiter + item;
|
return str;
|
||||||
});
|
|
||||||
return str;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,13 +123,14 @@ exports.cleanupOutData = function(data, params, command) {
|
|||||||
*
|
*
|
||||||
* @return {String} output number as string
|
* @return {String} output number as string
|
||||||
*/
|
*/
|
||||||
var removeLeadingZero = exports.removeLeadingZero = function(num) {
|
var removeLeadingZero = function (num) {
|
||||||
var strNum = num.toString();
|
var strNum = num.toString();
|
||||||
|
|
||||||
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
|
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
|
||||||
strNum = strNum.slice(1);
|
strNum = strNum.slice(1);
|
||||||
} else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
|
} else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
|
||||||
strNum = strNum.charAt(0) + strNum.slice(2);
|
strNum = strNum.charAt(0) + strNum.slice(2);
|
||||||
}
|
}
|
||||||
return strNum;
|
return strNum;
|
||||||
};
|
};
|
||||||
|
exports.removeLeadingZero = removeLeadingZero;
|
||||||
|
Reference in New Issue
Block a user