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

global refactoring dump

This commit is contained in:
deepsweet
2013-04-09 22:06:27 +03:00
parent f01c7f4b98
commit 2816424ed7
36 changed files with 932 additions and 981 deletions

250
.svgo.yml
View File

@ -1,216 +1,40 @@
plugins: plugins:
- name: removeDoctype # - name
active: true #
type: perItem # or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- name: removeXMLProcInst - removeDoctype
active: true - removeXMLProcInst
type: perItem - removeComments
- removeMetadata
- name: removeComments - removeEditorsNSData
active: true - cleanupAttrs
type: perItem - convertStyleToAttrs
- removeRasterImages
- name: removeMetadata - cleanupNumericValues
active: true - convertColors
type: perItem - removeUnknownsAndDefaults
- removeNonInheritableGroupAttrs
- name: removeEditorsNSData - removeUselessStrokeAndFill
active: true - removeViewBox
type: perItem - cleanupEnableBackground
- removeHiddenElems
- name: cleanupAttrs - removeEmptyText
active: true - moveElemsAttrsToGroup
type: perItem - collapseGroups
params: - moveGroupAttrsToElems
newlines: true - convertPathData
trim: true - convertTransform
spaces: true - removeEmptyAttrs
- removeEmptyContainers
- name: convertStyleToAttrs - cleanupIDs
active: true - removeUnusedNS
type: perItem - cropAndCenterAlongPath
- name: removeRasterImages
active: false
type: perItem
- name: cleanupNumericValues
active: true
type: perItem
params:
floatPrecision: 3
leadingZero: true
defaultPx: true
- name: convertColors
active: true
type: perItem
params:
names2hex: true
rgb2hex: true
shorthex: true
- name: removeUnknownsAndDefaults
active: true
type: perItem
params:
SVGid: true
unknownContent: true
unknownAttrs: true
defaultAttrs: true
- name: removeNonInheritableGroupAttrs
active: true
type: perItem
- name: removeUselessStrokeAndFill
active: true
type: perItem
params:
stroke: true
fill: true
- name: removeViewBox
active: true
type: perItem
- name: cleanupEnableBackground
active: true
type: perItem
- name: removeHiddenElems
active: true
type: perItem
params:
displayNone: true
opacity0: true
circleR0: true
ellipseRX0: true
ellipseRY0: true
rectWidth0: true
rectHeight0: true
patternWidth0: true
patternHeight0: true
imageWidth0: true
imageHeight0: true
pathEmptyD: true
polylineEmptyPoints: true
polygonEmptyPoints: true
- name: removeEmptyText
active: true
type: perItem
params:
text: true
tspan: true
tref: true
- name: moveElemsAttrsToGroup
active: true
type: perItemReverse
- name: collapseGroups
active: true
type: perItemReverse
- name: moveGroupAttrsToElems
active: true
type: perItemReverse
- name: convertPathData
active: true
type: perItem
params:
applyTransforms: true
straightCurves: true
lineShorthands: true
curveSmoothShorthands: true
floatPrecision: 3
removeUseless: true
collapseRepeated: true
leadingZero: true
negativeExtraSpace: true
- name: convertTransform
active: true
type: perItem
params:
convertToShorts: true
floatPrecision: 3
matrixToTransform: true
shortTranslate: true
shortScale: true
shortRotate: true
removeUseless: true
collapseIntoOne: true
leadingZero: true
negativeExtraSpace: false
- name: removeEmptyAttrs
active: true
type: perItem
- name: removeEmptyContainers
active: true
type: perItemReverse
- name: cleanupIDs
active: true
type: full
params:
remove: true
minify: true
- name: removeUnusedNS
active: true
type: full
- name: cropAndCenterAlongPath
active: false
type: full
params:
hcrop: true
vcenter: true
floatPrecision: 3
leadingZero: true
negativeExtraSpace: true
svg2js:
strict: true
trim: true
normalize: true
lowercase: true
xmlns: true
position: false
js2svg:
doctypeStart: "<!DOCTYPE"
doctypeEnd: ">"
procInstStart: "<?"
procInstEnd: "?>"
tagOpenStart: "<"
tagOpenEnd: ">"
tagCloseStart: "</"
tagCloseEnd: ">"
tagShortStart: "<"
tagShortEnd: "/>"
attrStart: "=\""
attrEnd: "\""
commentStart: "<!--"
commentEnd: "-->"
cdataStart: "<![CDATA["
cdataEnd: "]]>"
textStart: ""
textEnd: ""
indent: " "
entities:
"&": "&amp;"
"'": "&apos;"
"\"": "&quot;"
">": "&gt;"
"<": "&lt;"
pretty: false

View File

@ -5,118 +5,32 @@
* *
* @see http://deepsweet.github.com/svgo/ * @see http://deepsweet.github.com/svgo/
* *
* @module svgo
*
* @author Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet) * @author Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)
* @copyright © 2012 Kir Belevich * @copyright © 2012 Kir Belevich
* @license MIT https://raw.github.com/deepsweet/svgo/master/LICENSE * @license MIT https://raw.github.com/deepsweet/svgo/master/LICENSE
*/ */
var INHERIT = require('inherit'), var CONFIG = require('./svgo/config'),
Q = require('q'),
FS = require('fs'),
CONFIG = require('./svgo/config'),
SVG2JS = require('./svgo/svg2js'), SVG2JS = require('./svgo/svg2js'),
PLUGINS = require('./svgo/plugins'), PLUGINS = require('./svgo/plugins'),
JS2SVG = require('./svgo/js2svg'), JS2SVG = require('./svgo/js2svg');
decodeSVGDatauri = require('./svgo/tools').decodeSVGDatauri;
/** var SVGO = module.exports = function(config) {
* @class SVGO.
*/
module.exports = INHERIT(/** @lends SVGO.prototype */{
/** this.config = CONFIG(config);
* @param {Object} [config] custom config to extend default
*
* @constructs
*
* @private
*/
__constructor: function(config) {
this.config = CONFIG(config); };
}, SVGO.prototype.optimize = function(svgstr, callback) {
/** var config = this.config;
* Optimize SVG data from string.
*
* @param {String} str input string
*
* @return {Object} output string deferred promise
*/
fromString: function(str) {
var startTime = Date.now(); SVG2JS(svgstr, function(svgjs) {
str = decodeSVGDatauri(str); svgjs = PLUGINS(svgjs, config.plugins);
return this.config callback(JS2SVG(svgjs, config.js2svg));
.then(function(config) {
return SVG2JS(str, config.svg2js) });
.then(function(jsdata) {
var result = JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg); };
result.info.inBytes = Buffer.byteLength(str, 'utf-8');
result.info.outBytes = Buffer.byteLength(result.data, 'utf-8');
result.info.time = Date.now() - startTime;
return result;
});
});
},
/**
* Optimize SVG data from Stream.
*
* @param {Object} stream input stream
*
* @return {Object} output string deferred promise
*/
fromStream: function(stream) {
var deferred = Q.defer(),
inputData = '',
self = this;
stream.pause();
stream
.on('data', function(chunk) {
inputData += chunk;
})
.once('end', function() {
deferred.resolve(inputData);
})
.resume();
return deferred.promise
.then(function(str) {
return self.fromString(str);
});
},
/**
* Optimize SVG data from file.
*
* @param {String} path file path
*
* @return {Object} output string deferred promise
*/
fromFile: function(path) {
return this.fromStream(FS.createReadStream(path, { encoding: 'utf8' }));
}
});

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
require('colors'); require('colors');
require('js-yaml');
var FS = require('fs'), var FS = require('fs'),
QFS = require('q-fs'),
PATH = require('path'), PATH = require('path'),
UTIL = require('util'), UTIL = require('util'),
SVGO = require('../svgo'), SVGO = require('../svgo'),
info = JSON.parse(require('fs').readFileSync(__dirname + '/../../package.json')), PKG = require('../../package.json'),
encodeSVGDatauri = require('./tools').encodeSVGDatauri, encodeSVGDatauri = require('./tools').encodeSVGDatauri,
regSVGFile = /\.svg$/; regSVGFile = /\.svg$/;
@ -15,20 +15,18 @@ var FS = require('fs'),
* Command-Option-Argument. * Command-Option-Argument.
* *
* @see https://github.com/veged/coa * @see https://github.com/veged/coa
*
* @module coa
*/ */
module.exports = require('coa').Cmd() module.exports = require('coa').Cmd()
.helpful() .helpful()
.name(info.name) .name(PKG.name)
.title(info.description) .title(PKG.description)
.opt() .opt()
.name('version').title('Version') .name('version').title('Version')
.short('v').long('version') .short('v').long('version')
.only() .only()
.flag() .flag()
.act(function() { .act(function() {
return info.version; return PKG.version;
}) })
.end() .end()
.opt() .opt()
@ -99,109 +97,144 @@ module.exports = require('coa').Cmd()
var input = args && args.input ? args.input : opts.input, var input = args && args.input ? args.input : opts.input,
output = args && args.output ? args.output : opts.output, output = args && args.output ? args.output : opts.output,
string = opts.string, config;
folder = opts.folder,
svgo;
// w/o anything
if ( if (
(!input || input === '-') && (!input || input === '-') &&
!string && !opts.string &&
!opts.stdin && !opts.stdin &&
!opts.folder && !opts.folder &&
process.stdin.isTTY process.stdin.isTTY
) return this.usage(); ) return this.usage();
// --config
if (opts.config) {
// string
if (opts.config.charAt(0) === '{') {
config = JSON.parse(opts.config);
// external file
} else {
config = require(opts.config);
}
}
// --pretty
if (opts.pretty) {
config = config || {};
config.js2svg = config.js2svg || {};
config.js2svg.pretty = true;
}
// --folder // --folder
if (folder) { if (opts.folder) {
optimizeFolder(folder, opts); optimizeFolder(opts.folder, config);
return; return;
} }
// --string // --inpput
if (string) { if (input) {
svgo = new SVGO({ coa: opts }).fromString(string);
}
// STDIN // STDIN
else if (input === '-') { if (input === '-') {
svgo = new SVGO({ coa: opts }).fromStream(process.stdin);
}
// file var data = '';
else if (input) {
svgo = new SVGO({ coa: opts }).fromFile(input);
}
return svgo.then(function(result) { process.stdin.pause();
// --datauri process.stdin
if (opts.datauri) { .on('data', function(chunk) {
// convert to Data URI base64 string data += chunk;
result.data = encodeSVGDatauri(result.data); })
} .once('end', function() {
optimizeFromString(data, config, input, output);
// stdout })
if (output === '-' || (input === '-' && !output)) { .resume();
process.stdout.write(result.data + '\n');
}
// file // file
else { } else {
// if input is from file - overwrite it FS.readFile(input, 'utf8', function(err, data) {
if (!output && input) { if (err) {
output = input; throw err;
} }
UTIL.puts('\r'); optimizeFromString(data, config, input, output);
saveFileAndPrintInfo(result, output, opts.pretty); });
} }
}) // --string
.done(); } else if (opts.string) {
optimizeFromString(opts.string, config, input, output);
}
}); });
/** function optimizeFromString(svgstr, config, input, output) {
* Save file and print info.
*
* @param {Object} result SVGO result
* @param {String} output output filename
* @param {Boolean} pretty is pretty printed?
*/
function saveFileAndPrintInfo(result, output, pretty) {
// output file var startTime = Date.now(config),
output = FS.createWriteStream(output, { encoding: 'utf8' }); time,
output.write(result.data); inBytes = Buffer.byteLength(svgstr, 'utf8'),
output.end(); outBytes,
svgo = new SVGO(config);
// print time info svgo.optimize(svgstr, function(result) {
printTimeInfo(result.info.time);
// print optimization profit info outBytes = Buffer.byteLength(result.data, 'utf8');
printProfitInfo(result.info.inBytes, result.info.outBytes); time = Date.now() - startTime;
// stdout
if (output === '-' || (input === '-' && !output)) {
process.stdout.write(result.data + '\n');
// file
} else {
// overwrite input file if there is no output
if (!output && input) {
output = input;
}
UTIL.puts('\r');
saveFileAndPrintInfo(result.data, output, inBytes, outBytes, time);
}
});
}
function saveFileAndPrintInfo(data, path, inBytes, outBytes, time) {
FS.writeFile(path, data, 'utf8', function() {
// print time info
printTimeInfo(time);
// print optimization profit info
printProfitInfo(inBytes, outBytes);
});
} }
/**
* Print time info.
*
* @param {Number} time working time in ms
*/
function printTimeInfo(time) { function printTimeInfo(time) {
UTIL.puts('Done in ' + time + ' ms!'); UTIL.puts('Done in ' + time + ' ms!');
} }
/**
* Print optimization profit info.
*
* @param {Number} inBytes input file byteLength
* @param {Number} outBytes output file byteLength
*/
function printProfitInfo(inBytes, outBytes) { function printProfitInfo(inBytes, outBytes) {
var profitPercents = 100 - outBytes * 100 / inBytes; var profitPercents = 100 - outBytes * 100 / inBytes;
@ -215,55 +248,66 @@ function printProfitInfo(inBytes, outBytes) {
} }
/** function optimizeFolder(path, config) {
* Optimize all *.svg files in a specific folder.
*
* @param {String} folder folder path
* @param {Object} opts COA options
*/
function optimizeFolder(folder, opts) {
var svgo = new SVGO({ coa: opts }); var svgo = new SVGO(config);
folder = PATH.resolve(process.cwd(), folder); // absoluted folder path
path = PATH.resolve(process.cwd(), path);
UTIL.puts('\n' + folder + ':\n'); UTIL.puts('\n' + path + ':\n');
// list directory // list folder content
QFS.list(folder).then(function(list) { FS.readdir(path, function(err, files) {
list.forEach(function(item) { if (err) {
throw err;
}
var filename = item; files.forEach(function(filename) {
item = folder + '/' + item; // absoluted file path
var filepath = PATH.resolve(path, filename);
// checkif item is a file // check if file name matches *.svg
QFS.isFile(item) if (regSVGFile.test(filepath)) {
.then(function(isFile) {
// and file name matches *.svg FS.readFile(filepath, 'utf8', function(err, data) {
if (isFile && regSVGFile.test(item)) {
// then optimize it and output profit information
return svgo.fromFile(item)
.then(function(result) {
UTIL.puts(filename + ':');
saveFileAndPrintInfo(result, item, opts.pretty);
});
if (err) {
throw err;
} }
}) var startTime = Date.now(),
.fail(function(e) { time,
UTIL.puts(filename + ':\n' + String('Error! "' + e.message + '"').red + '\n'); inBytes = Buffer.byteLength(data, 'utf8'),
outBytes;
svgo.optimize(data, function(result) {
outBytes = Buffer.byteLength(result.data, 'utf8');
time = Date.now() - startTime;
FS.writeFile(filepath, result.data, 'utf8', function() {
UTIL.puts(filename + ':');
// print time info
printTimeInfo(time);
// print optimization profit info
printProfitInfo(inBytes, outBytes);
});
});
}); });
}
}); });
}) });
.done();
} }

View File

@ -1,177 +1,166 @@
'use strict'; 'use strict';
var QFS = require('q-fs'), require('js-yaml');
PATH = require('path'),
YAML = require('js-yaml'), var EXTEND = require('node.extend');
extend = require('./tools').extend,
defaultConfigPath = PATH.resolve(__dirname, '../../.svgo.yml');
/** /**
* Read and/or extend default config file, * Read and/or extend default config file,
* prepare and optimize plugins array. * prepare and optimize plugins array.
* *
* @module config * @param {Object} [config] input config
* * @return {Object} output config
* @param {Object} [params] config object to extend or coa params
*
* @return {Object} config deferred promise
*/ */
module.exports = function(params) { module.exports = function(config) {
return _getConfig(params).then(function(config) { var defaults = require('../../.svgo.yml');
config.plugins = preparePluginsArray(config.plugins); defaults.plugins = preparePluginsArray(defaults.plugins);
config.plugins = optimizePluginsArray(config.plugins);
return config; if (config) {
defaults = extendConfig(defaults, config);
}
}); defaults.plugins = optimizePluginsArray(defaults.plugins);
return defaults;
}; };
/** /**
* Get default or extended config. * Require() all plugins in array.
*
* @param {Object} [params] config object to extend or coa params
*
* @return {Object} default or extended config
*
* @private
*/
function _getConfig(params) {
// if there are no any params then return default config
if (!params) return readConfig(defaultConfigPath);
// COA params
if (params.coa) {
params = params.coa;
return readConfig(defaultConfigPath)
.then(function(defaultConfig) {
// --pretty
if (params.pretty) defaultConfig.js2svg.pretty = true;
// --disable
if (params.disable) return changePluginsState(params.disable, false, defaultConfig);
// --enable
if (params.enable) return changePluginsState(params.enable, true, defaultConfig);
// --config
if (params.config) {
var localConfigPath = PATH.resolve(process.cwd, params.config);
// check for the local config file
return QFS.exists(localConfigPath)
.then(function(exist) {
// if it doesn't exists then return default
if (!exist) return defaultConfig;
// if it exists then return extended default
return readConfig(localConfigPath);
});
}
return defaultConfig;
});
// inline {} params
} else {
// return extended default
return readConfig(defaultConfigPath)
.then(function(defaultConfig) {
return extend(true, defaultConfig, params);
});
}
}
/**
* Read and YAML.parse config file by path.
*
* @param {String} path config path
*
* @return {Object} read config deferred promise
*/
function readConfig(path) {
return QFS.read(path)
.then(function(data) {
return YAML.load(data.toString());
});
}
/**
* Require() all plugins in array and convert it to array of arrays.
* *
* @param {Array} plugins input plugins array * @param {Array} plugins input plugins array
*
* @return {Array} input plugins array of arrays * @return {Array} input plugins array of arrays
*/ */
function preparePluginsArray(plugins) { function preparePluginsArray(plugins) {
return plugins.map(function(plugin) { var plugin,
plugin.fn = require('../../plugins/' + plugin.name)[plugin.name]; key;
return plugins.map(function(item) {
// {}
if (typeof item === 'object') {
key = Object.keys(item)[0];
plugin = require('../../plugins/' + key);
// name: {}
if (typeof item[key] === 'object') {
plugin.params = EXTEND(plugin.params || {}, item[key]);
// name: false
} else if (item[key] === false) {
plugin.active = false;
// name: true
} else if (item[key] === true) {
plugin.active = true;
}
plugin.name = key;
// name
} else {
plugin = require('../../plugins/' + item);
plugin.name = item;
}
return plugin;
return [plugin];
}); });
} }
/**
* Extend plugins with the custom config object.
*
* @param {Array} plugins input plugins
* @param {Object} config config
* @return {Array} output plugins
*/
function extendConfig(defaults, config) {
var key;
// plugins
if (config.plugins) {
config.plugins.forEach(function(item) {
// {}
if (typeof item === 'object') {
key = Object.keys(item)[0];
defaults.plugins.forEach(function(plugin) {
if (plugin.name === key) {
// name: {}
if (typeof item[key] === 'object') {
plugin.params = EXTEND(plugin.params || {}, item[key]);
plugin.active = true;
// name: false
} else if (item[key] === false) {
plugin.active = false;
// name: true
} else if (item[key] === true) {
plugin.active = true;
}
}
});
}
});
}
// svg2js
if (config.svg2js) {
defaults.svg2js = config.svg2js;
}
// js2svg
if (config.js2svg) {
defaults.js2svg = config.js2svg;
}
return defaults;
}
/** /**
* Try to group sequential elements of plugins array. * Try to group sequential elements of plugins array.
* *
* @param {Object} plugins input plugins array * @param {Object} plugins input plugins
* * @return {Array} output plugins
* @return {Array} output plugins array
*/ */
function optimizePluginsArray(plugins) { function optimizePluginsArray(plugins) {
var prev; var prev;
plugins = plugins.filter(function(item) { plugins = plugins.map(function(item) {
return [item];
});
return plugins.filter(function(item) {
if (prev && item[0].type === prev[0].type) { if (prev && item[0].type === prev[0].type) {
prev.push(item[0]); prev.push(item[0]);
return false; return false;
} }
prev = item; prev = item;
return true; return true;
}); });
return plugins;
}
/**
* Change plugins state by names array.
*
* @param {Array} names plugins names
* @param {Boolean} state active state
* @param {Object} config original config
*
* @return {Object} changed config
*/
function changePluginsState(names, state, config) {
config.plugins.forEach(function(plugin) {
if (names.indexOf(plugin.name) > -1) {
plugin.active = state;
}
});
return config;
} }

View File

@ -1,282 +1,290 @@
'use strict'; 'use strict';
var INHERIT = require('inherit'), var EXTEND = require('./tools').extend;
extend = require('./tools').extend;
var defaults = {
doctypeStart: '<!DOCTYPE',
doctypeEnd: '>',
procInstStart: '<?',
procInstEnd: '?>',
tagOpenStart: '<',
tagOpenEnd: '>',
tagCloseStart: '</',
tagCloseEnd: '>',
tagShortStart: '<',
tagShortEnd: '/>',
attrStart: '="',
attrEnd: '"',
commentStart: '<!--',
commentEnd: '-->',
cdataStart: '<![CDATA[',
cdataEnd: ']]>',
textStart: '',
textEnd: '',
indent: ' ',
entities: {
'&': '&amp;',
'\'': '&apos;',
'"': '&quot;',
'>': '&gt;',
'<': '&lt;',
},
pretty: false
};
/** /**
* Convert SVG-as-JS object to SVG (XML) string. * Convert SVG-as-JS object to SVG (XML) string.
* *
* @module js2svg * @param {Object} data input data
*
* @param {Object} jsdata input data
* @param {Object} config config * @param {Object} config config
* *
* @return {Object} output data * @return {Object} output data
*/ */
module.exports = function(jsdata, config) { module.exports = function(data, config) {
return new Converter(config).run(jsdata); return new JS2SVG(config).convert(data);
};
function JS2SVG(config) {
if (config) {
this.config = EXTEND(true, {}, defaults, config);
} else {
this.config = defaults;
}
if (this.config.pretty) {
this.config.doctypeEnd += '\n';
this.config.procInstEnd += '\n';
this.config.commentEnd += '\n';
this.config.cdataEnd += '\n';
this.config.tagShortEnd += '\n';
this.config.tagOpenEnd += '\n';
this.config.tagCloseEnd += '\n';
this.config.textEnd += '\n';
}
this.indentLevel = 0;
}
/**
* Start conversion.
*
* @param {Object} data input data
*
* @return {String}
*/
JS2SVG.prototype.convert = function(data) {
var svg = '';
if (data.content) {
this.indentLevel++;
data.content.forEach(function(item) {
if (item.elem) {
svg += this.createElem(item);
} else if (item.text) {
svg += this.createText(item.text);
} else if (item.doctype) {
svg += this.createDoctype(item.doctype);
} else if (item.processinginstruction) {
svg += this.createProcInst(item.processinginstruction);
} else if (item.comment) {
svg += this.createComment(item.comment);
} else if (item.cdata) {
svg += this.createCDATA(item.cdata);
}
}, this);
}
this.indentLevel--;
return {
data: svg,
info: {
width: this.width,
height: this.height
}
};
}; };
/** /**
* @class Converter * Create indent string in accordance with the current node level.
*
* @return {String}
*/ */
var Converter = INHERIT(/** @lends Nodes.prototype */{ JS2SVG.prototype.createIndent = function() {
/** var indent = '';
* @constructs
*
* @private
*/
__constructor: function(config) {
/** if (this.config.pretty) {
* Shallow copy of converter config. for (var i = 1; i < this.indentLevel; i++) {
* indent += this.config.indent;
* @type {Object}
*/
this.config = extend({}, config);
/**
* Current indent level hack.
*
* @type {Number}
*/
this.indentLevel = 0;
// pretty
if (this.config.pretty) {
this.config.doctypeEnd += '\n';
this.config.procInstEnd += '\n';
this.config.commentEnd += '\n';
this.config.cdataEnd += '\n';
this.config.tagShortEnd += '\n';
this.config.tagOpenEnd += '\n';
this.config.tagCloseEnd += '\n';
this.config.textEnd += '\n';
} }
}
}, return indent;
/** };
* Start conversion.
*
* @param {Object} svg-as-js data object
*
* @return {String}
*/
run: function(data) {
var svg = ''; /**
* Create doctype tag.
*
* @param {String} doctype doctype body string
*
* @return {String}
*/
JS2SVG.prototype.createDoctype = function(doctype) {
if (data.content) { return this.config.doctypeStart +
doctype +
this.config.doctypeEnd;
this.indentLevel++; };
data.content.forEach(function(item) { /**
* Create XML Processing Instruction tag.
*
* @param {Object} instruction instruction object
*
* @return {String}
*/
JS2SVG.prototype.createProcInst = function(instruction) {
if (item.elem) { return this.config.procInstStart +
svg += this.createElem(item); instruction.name +
} else if (item.text) { ' ' +
svg += this.createText(item.text); instruction.body +
} else if (item.doctype) { this.config.procInstEnd;
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); };
} /**
* Create comment tag.
*
* @param {String} comment comment body
*
* @return {String}
*/
JS2SVG.prototype.createComment = function(comment) {
this.indentLevel--; return this.config.commentStart +
comment +
this.config.commentEnd;
return { };
data: svg,
info: {
width: this.width,
height: this.height
}
};
}, /**
* Create CDATA section.
*
* @param {String} cdata CDATA body
*
* @return {String}
*/
JS2SVG.prototype.createCDATA = function(cdata) {
/** return this.config.cdataStart +
* Create indent string in accordance with the current node level. cdata +
* this.config.cdataEnd;
* @return {String}
*/
createIndent: function() {
var indent = ''; };
if (this.config.pretty) { /**
for (var i = 1; i < this.indentLevel; i++) { * Create element tag.
indent += this.config.indent; *
} * @param {Object} data element object
} *
* @return {String}
*/
JS2SVG.prototype.createElem = function(data) {
return indent; // beautiful injection for obtaining SVG information :)
if (
data.isElem('svg') &&
data.hasAttr('width') &&
data.hasAttr('height')
) {
this.width = data.attr('width').value;
this.height = data.attr('height').value;
}
}, // empty element and short tag
if (data.isEmpty()) {
/**
* Create doctype tag.
*
* @param {String} doctype doctype body string
*
* @return {String}
*/
createDoctype: function(doctype) {
return this.config.doctypeStart +
doctype +
this.config.doctypeEnd;
},
/**
* Create XML Processing Instruction tag.
*
* @param {Object} instruction instruction object
*
* @return {String}
*/
createProcInst: function(instruction) {
return this.config.procInstStart +
instruction.name +
' ' +
instruction.body +
this.config.procInstEnd;
},
/**
* Create comment tag.
*
* @param {String} comment comment body
*
* @return {String}
*/
createComment: function(comment) {
return this.config.commentStart +
comment +
this.config.commentEnd;
},
/**
* Create CDATA section.
*
* @param {String} cdata CDATA body
*
* @return {String}
*/
createCDATA: function(cdata) {
return this.config.cdataStart +
cdata +
this.config.cdataEnd;
},
/**
* Create element tag.
*
* @param {Object} data element object
*
* @return {String}
*/
createElem: function(data) {
// beautiful injection for obtaining SVG information :)
if (
data.isElem('svg') &&
data.hasAttr('width') &&
data.hasAttr('height')
) {
this.width = data.attr('width').value;
this.height = data.attr('height').value;
}
// empty element and short tag
if (data.isEmpty()) {
return this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagShortEnd;
// non-empty element
} else {
return this.createIndent() +
this.config.tagOpenStart +
data.elem +
this.createAttrs(data) +
this.config.tagOpenEnd +
this.run(data).data +
this.createIndent() +
this.config.tagCloseStart +
data.elem +
this.config.tagCloseEnd;
}
},
/**
* Create element attributes.
*
* @param {Object} elem attributes object
*
* @return {String}
*/
createAttrs: function(elem) {
var attrs = '';
elem.eachAttr(function(attr) {
attrs += ' ' +
attr.name +
this.config.attrStart +
attr.value +
this.config.attrEnd;
}, this);
return attrs;
},
/**
* Create text node.
*
* @param {String} text text
*
* @return {String}
*/
createText: function(text) {
// convert entities back
for (var entity in this.config.entities) {
text = text.split(entity).join(this.config.entities[entity]);
}
return this.createIndent() + return this.createIndent() +
this.config.textStart + this.config.tagShortStart +
text + data.elem +
this.config.textEnd; this.createAttrs(data) +
this.config.tagShortEnd;
// non-empty element
} else {
return this.createIndent() +
this.config.tagOpenStart +
data.elem +
this.createAttrs(data) +
this.config.tagOpenEnd +
this.convert(data).data +
this.createIndent() +
this.config.tagCloseStart +
data.elem +
this.config.tagCloseEnd;
} }
}); };
/**
* Create element attributes.
*
* @param {Object} elem attributes object
*
* @return {String}
*/
JS2SVG.prototype.createAttrs = function(elem) {
var attrs = '';
elem.eachAttr(function(attr) {
attrs += ' ' +
attr.name +
this.config.attrStart +
attr.value +
this.config.attrEnd;
}, this);
return attrs;
};
/**
* Create text node.
*
* @param {String} text text
*
* @return {String}
*/
JS2SVG.prototype.createText = function(text) {
// convert entities back
for (var entity in this.config.entities) {
text = text.split(entity).join(this.config.entities[entity]);
}
return this.createIndent() +
this.config.textStart +
text +
this.config.textEnd;
};

View File

@ -1,159 +1,139 @@
'use strict'; 'use strict';
var INHERIT = require('inherit'), var EXTEND = require('node.extend');
extend = require('./tools').extend;
var JSAPI = module.exports = function(data) {
EXTEND(this, data);
};
/** /**
* @module jsAPI * Determine if item is an element
* (any, with a specific name or in a names array).
* *
* @class SVG-as-JS Nodes API. * @param {String|Array} [param] element name or names arrays
* @return {Boolean}
*/ */
module.exports = INHERIT(/** @lends Nodes.prototype */{ JSAPI.prototype.isElem = function(param) {
/** if (!param) return !!this.elem;
* @constructs
*
* @private
*/
__constructor: function(data) {
extend(this, data); if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
}, return !!this.elem && this.elem === param;
/** };
* Determine if item is an element
* (any, with a specific name or in a names array).
*
* @param {String|Array} [param] element name or names arrays
*
* @return {Boolean}
*/
isElem: function(param) {
if (!param) return !!this.elem; /**
* Determine if element is empty.
*
* @return {Boolean}
*/
JSAPI.prototype.isEmpty = function() {
if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1); return !this.content || !this.content.length;
return !!this.elem && this.elem === param; };
}, /**
* Determine if element has an attribute
* (any, or by name or by name + value).
*
* @param {String} [name] attribute name
* @param {String} [val] attribute value (will be toString()'ed)
* @return {Boolean}
*/
JSAPI.prototype.hasAttr = function(name, val) {
/** if (!this.attrs || !Object.keys(this.attrs).length) return false;
* Determine if element is empty.
*
* @return {Boolean}
*/
isEmpty: function() {
return !this.content || !this.content.length; if (!arguments.length) return !!this.attrs;
}, if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString();
/** return !!this.attrs[name];
* Determine if element has an attribute
* (any, or by name or by name + value).
*
* @param {String} [name] attribute name
* @param {String} [val] attribute value (will be toString()'ed)
*
* @return {Boolean}
*/
hasAttr: function(name, val) {
if (!this.attrs || !Object.keys(this.attrs).length) return false; };
if (!arguments.length) return !!this.attrs; /**
* Get a specific attribute from an element
* (by name or name + value).
*
* @param {String} name attribute name
* @param {String} [val] attribute value (will be toString()'ed)
* @return {Object|Undefined}
*/
JSAPI.prototype.attr = function(name, val) {
if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString(); if (!this.hasAttr() || !arguments.length) return undefined;
return !!this.attrs[name]; if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined;
}, return this.attrs[name];
/** };
* Get a specific attribute from an element
* (by name or name + value).
*
* @param {String} name attribute name
* @param {String} [val] attribute value (will be toString()'ed)
*
* @return {Object|Undefined}
*/
attr: function(name, val) {
if (!this.hasAttr() || !arguments.length) return undefined; /**
* Remove a specific attribute.
*
* @param {String} name attribute name
* @param {String} [val] attribute value
* @return {Boolean}
*/
JSAPI.prototype.removeAttr = function(name, val) {
if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined; if (!arguments.length) return false;
return this.attrs[name]; if (!this.hasAttr(name)) return false;
}, if (val && this.attrs[name].value !== val) return false;
/** delete this.attrs[name];
* Remove a specific attribute.
*
* @param {String} name attribute name
* @param {String} [val] attribute value
*
* @return {Boolean}
*/
removeAttr: function(name, val) {
if (!arguments.length) return false; if (!Object.keys(this.attrs).length) delete this.attrs;
if (!this.hasAttr(name)) return false; return true;
if (val && this.attrs[name].value !== val) return false; };
delete this.attrs[name]; /**
* Add attribute.
*
* @param {Object} attr attribute object
* @return {Object} created attribute
*/
JSAPI.prototype.addAttr = function(attr) {
if (!Object.keys(this.attrs).length) delete this.attrs; if (!attr ||
(attr && attr.name === undefined) ||
(attr && attr.value === undefined) ||
(attr && attr.prefix === undefined) ||
(attr && attr.local === undefined)
) return false;
return true; this.attrs = this.attrs || {};
this.attrs[attr.name] = attr;
}, return this.attrs[attr.name];
/** };
* Add attribute.
*
* @param {Object} attr attribute object
*
* @return {Object} created attribute
*/
addAttr: function(attr) {
if (!attr || /**
(attr && attr.name === undefined) || * Iterates over all attributes.
(attr && attr.value === undefined) || *
(attr && attr.prefix === undefined) || * @param {Function} callback callback
(attr && attr.local === undefined) * @param {Object} [context] callback context
) return false; * @return {Boolean} false if there are no any attributes
*/
JSAPI.prototype.eachAttr = function(callback, context) {
this.attrs = this.attrs || {}; if (!this.hasAttr()) return false;
return (this.attrs[attr.name] = attr);
},
/**
* Iterates over all attributes.
*
* @param {Function} callback callback
* @param {Object} [context] callback context
*
* @return {Boolean} false if there are no any attributes
*/
eachAttr: function(callback, context) {
if (!this.hasAttr()) return false;
for (var name in this.attrs) {
callback.call(context, this.attrs[name]);
}
return true;
for (var name in this.attrs) {
callback.call(context, this.attrs[name]);
} }
}); return true;
};

View File

@ -7,7 +7,6 @@
* *
* @param {Object} data input data * @param {Object} data input data
* @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, plugins) { module.exports = function(data, plugins) {
@ -38,7 +37,6 @@ module.exports = function(data, plugins) {
* @param {Object} data input data * @param {Object} data input data
* @param {Array} plugins plugins list to process * @param {Array} plugins plugins list to process
* @param {Boolean} [reverse] reverse pass? * @param {Boolean} [reverse] reverse pass?
*
* @return {Object} output data * @return {Object} output data
*/ */
function perItem(data, plugins, reverse) { function perItem(data, plugins, reverse) {
@ -89,7 +87,6 @@ function perItem(data, plugins, reverse) {
* *
* @param {Object} data input data * @param {Object} data input data
* @param {Array} plugins plugins list to process * @param {Array} plugins plugins list to process
*
* @return {Object} output data * @return {Object} output data
*/ */
function full(data, plugins) { function full(data, plugins) {

View File

@ -1,23 +1,26 @@
'use strict'; 'use strict';
var Q = require('q'), var SAX = require('sax'),
SAX = require('sax'),
JSAPI = require('./jsAPI'); JSAPI = require('./jsAPI');
var config = {
strict: true,
trim: true,
normalize: true,
lowercase: true,
xmlns: true,
position: false
};
/** /**
* Convert SVG (XML) string to SVG-as-JS object. * Convert SVG (XML) string to SVG-as-JS object.
* *
* @module svg2js * @param {String} data input data
* * @param {Function} callback
* @param {String} svg input data
* @param {Object} config sax xml parser config
*
* @return {Object} output data deferred promise
*/ */
module.exports = function(svg, config) { module.exports = function(data, callback) {
var deferred = Q.defer(), var sax = SAX.parser(config.strict, config),
sax = SAX.parser(config.strict, config),
root = {}, root = {},
current = root, current = root,
stack = []; stack = [];
@ -109,23 +112,21 @@ module.exports = function(svg, config) {
sax.onerror = function(e) { sax.onerror = function(e) {
deferred.reject(new Error('svg2js: ' + e.message));
// https://github.com/isaacs/sax-js#events // https://github.com/isaacs/sax-js#events
// "The error will be hanging out on parser.error, // "The error will be hanging out on parser.error,
// and must be deleted before parsing can continue" // and must be deleted before parsing can continue"
this.error = null; this.error = null;
throw new Error('svg2js: ' + e.message);
}; };
sax.onend = function() { sax.onend = function() {
deferred.resolve(root); callback(root);
}; };
sax.write(svg).close(); sax.write(data).close();
return deferred.promise;
}; };

View File

@ -32,25 +32,20 @@
"example": "./examples" "example": "./examples"
}, },
"scripts": { "scripts": {
"test": "./node_modules/.bin/mocha --reporter spec --recursive", "test": "./node_modules/.bin/mocha --reporter spec test/plugins test/svg2js",
"cover": "./node_modules/.bin/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib && ./node_modules/.bin/mocha --reporter mocha-istanbul --recursive", "cover": "./node_modules/.bin/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib && ./node_modules/.bin/mocha --reporter mocha-istanbul --recursive",
"jshint": "jshint --show-non-errors ." "jshint": "jshint --show-non-errors ."
}, },
"dependencies": { "dependencies": {
"sax": "~0.5.0", "sax": "~0.5.0",
"q": "~0.8.10",
"q-fs": "~0.1.0",
"coa": "~0.3.7", "coa": "~0.3.7",
"inherit": "",
"node.extend": "",
"js-yaml": "", "js-yaml": "",
"colors": "~0.6.0" "colors": "~0.6.0",
"node.extend": ""
}, },
"devDependencies": { "devDependencies": {
"mocha": "~1.8.0", "mocha": "~1.8.0",
"mocha-as-promised": "~1.2.0",
"chai": "~1.5.0", "chai": "~1.5.0",
"chai-as-promised": "~3.2.3",
"istanbul": "~0.1.0", "istanbul": "~0.1.0",
"mocha-istanbul": "" "mocha-istanbul": ""
}, },

View File

@ -1,5 +1,15 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
newlines: true,
trim: true,
spaces: true
};
var regNewlines = /\n/g, var regNewlines = /\n/g,
regSpaces = /\s{2,}/g; regSpaces = /\s{2,}/g;
@ -12,7 +22,7 @@ var regNewlines = /\n/g,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.cleanupAttrs = function(item, params) { exports.fn = function(item, params) {
if (item.isElem()) { if (item.isElem()) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/, var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
elems = ['svg', 'mask', 'pattern']; elems = ['svg', 'mask', 'pattern'];
@ -18,7 +22,7 @@ var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.cleanupEnableBackground = function(item) { exports.fn = function(item) {
if ( if (
item.isElem(elems) && item.isElem(elems) &&

View File

@ -1,5 +1,14 @@
'use strict'; 'use strict';
exports.type = 'full';
exports.active = true;
exports.params = {
remove: true,
minify: true
};
var referencesProps = require('./_collections').referencesProps, var referencesProps = require('./_collections').referencesProps,
regReferencesUrl = /^url\(#(.+?)\)$/, regReferencesUrl = /^url\(#(.+?)\)$/,
regReferencesHref = /^#(.+?)$/, regReferencesHref = /^#(.+?)$/,
@ -19,7 +28,7 @@ var referencesProps = require('./_collections').referencesProps,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.cleanupIDs = function(data, params) { exports.fn = function(data, params) {
var currentID, var currentID,
currentIDstring, currentIDstring,

View File

@ -1,5 +1,15 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
floatPrecision: 3,
leadingZero: true,
defaultPx: true
};
var regNumericValues = /^([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/, var regNumericValues = /^([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero; removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero;
@ -13,7 +23,7 @@ var regNumericValues = /^([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)(px|pt|pc|mm|cm|m|in|f
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.cleanupNumericValues = function(item, params) { exports.fn = function(item, params) {
if (item.isElem()) { if (item.isElem()) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItemReverse';
exports.active = true;
var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel; var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel;
/* /*
@ -25,7 +29,7 @@ var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel;
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.collapseGroups = function(item) { exports.fn = function(item) {
// non-empty elements // non-empty elements
if (item.elem && !item.isEmpty()) { if (item.elem && !item.isEmpty()) {

View File

@ -1,26 +1,19 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
names2hex: true,
rgb2hex: true,
shorthex: true
};
var collections = require('./_collections'), var collections = require('./_collections'),
regRGB = /^rgb\((\d+%?),\s*(\d+%?),\s*(\d+%?)\)$/, regRGB = /^rgb\((\d+%?),\s*(\d+%?),\s*(\d+%?)\)$/,
regHEX = /^\#(([a-fA-F0-9])\2){3}$/; regHEX = /^\#(([a-fA-F0-9])\2){3}$/;
/**
* Convert [r, g, b] to #rrggbb.
*
* @see https://gist.github.com/983535
*
* @example
* rgb2hex([255, 255, 255]) // '#ffffff'
*
* @param {Array} rgb [r, g, b]
* @return {String} #rrggbb
*
* @author Jed Schmidt
*/
function rgb2hex(rgb) {
return '#' + ((256 + rgb[0] << 8 | rgb[1]) << 8 | rgb[2]).toString(16).slice(1);
}
/** /**
* Convert different colors formats in element attributes to hex. * Convert different colors formats in element attributes to hex.
* *
@ -44,7 +37,7 @@ function rgb2hex(rgb) {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.convertColors = function(item, params) { exports.fn = function(item, params) {
if (item.elem) { if (item.elem) {
@ -88,3 +81,20 @@ exports.convertColors = function(item, params) {
} }
}; };
/**
* Convert [r, g, b] to #rrggbb.
*
* @see https://gist.github.com/983535
*
* @example
* rgb2hex([255, 255, 255]) // '#ffffff'
*
* @param {Array} rgb [r, g, b]
* @return {String} #rrggbb
*
* @author Jed Schmidt
*/
function rgb2hex(rgb) {
return '#' + ((256 + rgb[0] << 8 | rgb[1]) << 8 | rgb[2]).toString(16).slice(1);
}

View File

@ -1,5 +1,21 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
applyTransforms: true,
straightCurves: true,
lineShorthands: true,
curveSmoothShorthands: true,
floatPrecision: 3,
removeUseless: true,
collapseRepeated: true,
leadingZero: true,
negativeExtraSpace: true
};
var pathElems = require('./_collections.js').pathElems, var pathElems = require('./_collections.js').pathElems,
path2js = require('./_path.js').path2js, path2js = require('./_path.js').path2js,
js2path = require('./_path.js').js2path, js2path = require('./_path.js').js2path,
@ -22,7 +38,7 @@ var pathElems = require('./_collections.js').pathElems,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.convertPathData = function(item, params) { exports.fn = function(item, params) {
if (item.isElem(pathElems) && item.hasAttr('d')) { if (item.isElem(pathElems) && item.hasAttr('d')) {

View File

@ -1,6 +1,10 @@
'use strict'; 'use strict';
var extend = require('../lib/svgo/tools').extend, exports.type = 'perItem';
exports.active = true;
var EXTEND = require('node.extend'),
stylingProps = require('./_collections').stylingProps, stylingProps = require('./_collections').stylingProps,
regCleanupStyle = /(:|;)\s+/g; regCleanupStyle = /(:|;)\s+/g;
@ -22,7 +26,7 @@ var extend = require('../lib/svgo/tools').extend,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.convertStyleToAttrs = function(item) { exports.fn = function(item) {
if (item.elem && item.hasAttr('style')) { if (item.elem && item.hasAttr('style')) {
// ['opacity: 1', 'color: #000'] // ['opacity: 1', 'color: #000']
@ -57,7 +61,7 @@ exports.convertStyleToAttrs = function(item) {
return true; return true;
}); });
extend(item.attrs, attrs); EXTEND(item.attrs, attrs);
if (styles.length) { if (styles.length) {
item.attr('style').value = styles.join(';') item.attr('style').value = styles.join(';')

View File

@ -1,5 +1,22 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
convertToShorts: true,
floatPrecision: 3,
matrixToTransform: true,
shortTranslate: true,
shortScale: true,
shortRotate: true,
removeUseless: true,
collapseIntoOne: true,
leadingZero: true,
negativeExtraSpace: false
};
var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
transform2js = require('./_transforms.js').transform2js, transform2js = require('./_transforms.js').transform2js,
transformsMultiply = require('./_transforms.js').transformsMultiply, transformsMultiply = require('./_transforms.js').transformsMultiply,
@ -19,7 +36,7 @@ var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.convertTransform = function(item, params) { exports.fn = function(item, params) {
if (item.elem) { if (item.elem) {

View File

@ -1,5 +1,17 @@
'use strict'; 'use strict';
exports.type = 'full';
exports.active = false;
exports.params = {
hcrop: true,
vcenter: true,
floatPrecision: 3,
leadingZero: true,
negativeExtraSpace: true
};
var relative2absolute = require('./_path.js').relative2absolute, var relative2absolute = require('./_path.js').relative2absolute,
computeCubicBoundingBox = require('./_path.js').computeCubicBoundingBox, computeCubicBoundingBox = require('./_path.js').computeCubicBoundingBox,
computeQuadraticBoundingBox = require('./_path.js').computeQuadraticBoundingBox, computeQuadraticBoundingBox = require('./_path.js').computeQuadraticBoundingBox,
@ -7,7 +19,7 @@ var relative2absolute = require('./_path.js').relative2absolute,
js2path = require('./_path.js').js2path, js2path = require('./_path.js').js2path,
extend = require('../lib/svgo/tools').extend; extend = require('../lib/svgo/tools').extend;
exports.cropAndCenterAlongPath = function(data, params) { exports.fn = function(data, params) {
data.content.forEach(function(item) { data.content.forEach(function(item) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItemReverse';
exports.active = true;
var inheritableAttrs = require('./_collections').inheritableAttrs, var inheritableAttrs = require('./_collections').inheritableAttrs,
pathElems = require('./_collections.js').pathElems; pathElems = require('./_collections.js').pathElems;
@ -27,7 +31,7 @@ var inheritableAttrs = require('./_collections').inheritableAttrs,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.moveElemsAttrsToGroup = function(item) { exports.fn = function(item) {
if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) { if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItemReverse';
exports.active = true;
var pathElems = require('./_collections.js').pathElems; var pathElems = require('./_collections.js').pathElems;
/** /**
@ -21,7 +25,7 @@ var pathElems = require('./_collections.js').pathElems;
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.moveGroupAttrsToElems = function(item) { exports.fn = function(item) {
// move group transform attr to content's pathElems // move group transform attr to content's pathElems
if ( if (

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
/** /**
* Remove comments. * Remove comments.
* *
@ -12,7 +16,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeComments = function(item) { exports.fn = function(item) {
if (item.comment && item.comment.charAt(0) !== '!') { if (item.comment && item.comment.charAt(0) !== '!') {
return false; return false;

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
/** /**
* Remove DOCTYPE declaration. * Remove DOCTYPE declaration.
* *
@ -25,7 +29,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeDoctype = function(item) { exports.fn = function(item) {
// remove doctype only if custom XML entities declaration block does not presents // remove doctype only if custom XML entities declaration block does not presents
// http://en.wikipedia.org/wiki/Document_Type_Definition#Entity_declarations // http://en.wikipedia.org/wiki/Document_Type_Definition#Entity_declarations

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
var editorNamespaces = require('./_collections').editorNamespaces, var editorNamespaces = require('./_collections').editorNamespaces,
prefixes = []; prefixes = [];
@ -16,7 +20,7 @@ var editorNamespaces = require('./_collections').editorNamespaces,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeEditorsNSData = function(item) { exports.fn = function(item) {
if (item.elem) { if (item.elem) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
/** /**
* Remove attributes with empty values. * Remove attributes with empty values.
* *
@ -8,7 +12,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeEmptyAttrs = function(item) { exports.fn = function(item) {
if (item.elem) { if (item.elem) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItemReverse';
exports.active = true;
var container = require('./_collections').elemsGroups.container; var container = require('./_collections').elemsGroups.container;
/** /**
@ -18,7 +22,7 @@ var container = require('./_collections').elemsGroups.container;
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeEmptyContainers = function(item) { exports.fn = function(item) {
return !(item.isElem(container) && !item.isElem('svg') && item.isEmpty()); return !(item.isElem(container) && !item.isElem('svg') && item.isEmpty());

View File

@ -1,5 +1,15 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
text: true,
tspan: true,
tref: true
};
/** /**
* Remove empty Text elements. * Remove empty Text elements.
* *
@ -21,7 +31,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeEmptyText = function(item, params) { exports.fn = function(item, params) {
// Remove empty text element // Remove empty text element
if ( if (

View File

@ -1,5 +1,26 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
displayNone: true,
opacity0: true,
circleR0: true,
ellipseRX0: true,
ellipseRY0: true,
rectWidth0: true,
rectHeight0: true,
patternWidth0: true,
patternHeight0: true,
imageWidth0: true,
imageHeight0: true,
pathEmptyD: true,
polylineEmptyPoints: true,
polygonEmptyPoints: true
};
/** /**
* Remove hidden elements with disabled rendering: * Remove hidden elements with disabled rendering:
* - display="none" * - display="none"
@ -19,7 +40,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeHiddenElems = function(item, params) { exports.fn = function(item, params) {
if (item.elem) { if (item.elem) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
/** /**
* Remove <metadata>. * Remove <metadata>.
* *
@ -10,7 +14,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeMetadata = function(item) { exports.fn = function(item) {
return !item.isElem('metadata'); return !item.isElem('metadata');

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
var inheritableAttrs = require('./_collections').inheritableAttrs, var inheritableAttrs = require('./_collections').inheritableAttrs,
presentationAttrs = require('./_collections').attrsGroups.presentation, presentationAttrs = require('./_collections').attrsGroups.presentation,
excludedAttrs = ['display', 'opacity']; excludedAttrs = ['display', 'opacity'];
@ -12,7 +16,7 @@ var inheritableAttrs = require('./_collections').inheritableAttrs,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeNonInheritableGroupAttrs = function(item) { exports.fn = function(item) {
if (item.isElem('g')) { if (item.isElem('g')) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = false;
/** /**
* Remove raster images references in <image>. * Remove raster images references in <image>.
* *
@ -10,7 +14,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeRasterImages = function(item) { exports.fn = function(item) {
if ( if (
item.isElem('image') && item.isElem('image') &&

View File

@ -1,5 +1,16 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
exports.params = {
SVGid: true,
unknownContent: true,
unknownAttrs: true,
defaultAttrs: true
};
var collections = require('./_collections'), var collections = require('./_collections'),
elems = collections.elems, elems = collections.elems,
attrsGroups = collections.attrsGroups, attrsGroups = collections.attrsGroups,
@ -48,7 +59,7 @@ for (var elem in elems) {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeUnknownsAndDefaults = function(item, params) { exports.fn = function(item, params) {
// elems w/o namespace prefix // elems w/o namespace prefix
if (item.isElem() && !item.prefix) { if (item.isElem() && !item.prefix) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'full';
exports.active = true;
/** /**
* Remove unused namespaces declaration. * Remove unused namespaces declaration.
* *
@ -8,7 +12,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeUnusedNS = function(data) { exports.fn = function(data) {
var svgElem, var svgElem,
xmlnsCollection = []; xmlnsCollection = [];

View File

@ -1,5 +1,14 @@
'use strict'; 'use strict';
exports.type = 'peItem';
exports.active = true;
exports.params = {
stroke: true,
fill: true
};
var regStrokeProps = /^stroke/, var regStrokeProps = /^stroke/,
regFillProps = /^fill/; regFillProps = /^fill/;
@ -12,7 +21,7 @@ var regStrokeProps = /^stroke/,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeUselessStrokeAndFill = function(item, params) { exports.fn = function(item, params) {
if (item.isElem()) { if (item.isElem()) {

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/, var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
viewBoxElems = ['svg', 'pattern']; viewBoxElems = ['svg', 'pattern'];
@ -18,7 +22,7 @@ var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeViewBox = function(item) { exports.fn = function(item) {
if ( if (
item.isElem(viewBoxElems) && item.isElem(viewBoxElems) &&

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
exports.type = 'perItem';
exports.active = true;
/** /**
* Remove XML Processing Instruction. * Remove XML Processing Instruction.
* *
@ -11,7 +15,7 @@
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.removeXMLProcInst = function(item) { exports.fn = function(item) {
return !(item.processinginstruction && item.processinginstruction.name === 'xml'); return !(item.processinginstruction && item.processinginstruction.name === 'xml');