diff --git a/.svgo.yml b/.svgo.yml index 991bf4a5..85c8fa9b 100644 --- a/.svgo.yml +++ b/.svgo.yml @@ -1,216 +1,40 @@ plugins: - - name: removeDoctype - active: true - type: perItem + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 - - name: removeXMLProcInst - active: true - type: perItem - - - name: removeComments - active: true - type: perItem - - - name: removeMetadata - active: true - type: perItem - - - name: removeEditorsNSData - active: true - type: perItem - - - name: cleanupAttrs - active: true - type: perItem - params: - newlines: true - trim: true - spaces: true - - - name: convertStyleToAttrs - active: true - type: perItem - - - 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: "" - procInstStart: "" - tagOpenStart: "<" - tagOpenEnd: ">" - tagCloseStart: "" - tagShortStart: "<" - tagShortEnd: "/>" - attrStart: "=\"" - attrEnd: "\"" - commentStart: "" - cdataStart: "" - textStart: "" - textEnd: "" - indent: " " - entities: - "&": "&" - "'": "'" - "\"": """ - ">": ">" - "<": "<" - pretty: false + - removeDoctype + - removeXMLProcInst + - removeComments + - removeMetadata + - removeEditorsNSData + - cleanupAttrs + - convertStyleToAttrs + - removeRasterImages + - cleanupNumericValues + - convertColors + - removeUnknownsAndDefaults + - removeNonInheritableGroupAttrs + - removeUselessStrokeAndFill + - removeViewBox + - cleanupEnableBackground + - removeHiddenElems + - removeEmptyText + - moveElemsAttrsToGroup + - collapseGroups + - moveGroupAttrsToElems + - convertPathData + - convertTransform + - removeEmptyAttrs + - removeEmptyContainers + - cleanupIDs + - removeUnusedNS + - cropAndCenterAlongPath diff --git a/lib/svgo.js b/lib/svgo.js index 845671fa..d193e0f3 100644 --- a/lib/svgo.js +++ b/lib/svgo.js @@ -5,118 +5,32 @@ * * @see http://deepsweet.github.com/svgo/ * - * @module svgo - * * @author Kir Belevich (https://github.com/deepsweet) * @copyright © 2012 Kir Belevich * @license MIT https://raw.github.com/deepsweet/svgo/master/LICENSE */ -var INHERIT = require('inherit'), - Q = require('q'), - FS = require('fs'), - CONFIG = require('./svgo/config'), +var CONFIG = require('./svgo/config'), SVG2JS = require('./svgo/svg2js'), PLUGINS = require('./svgo/plugins'), - JS2SVG = require('./svgo/js2svg'), - decodeSVGDatauri = require('./svgo/tools').decodeSVGDatauri; + JS2SVG = require('./svgo/js2svg'); -/** - * @class SVGO. - */ -module.exports = INHERIT(/** @lends SVGO.prototype */{ +var SVGO = module.exports = function(config) { - /** - * @param {Object} [config] custom config to extend default - * - * @constructs - * - * @private - */ - __constructor: function(config) { + this.config = CONFIG(config); - this.config = CONFIG(config); +}; - }, +SVGO.prototype.optimize = function(svgstr, callback) { - /** - * Optimize SVG data from string. - * - * @param {String} str input string - * - * @return {Object} output string deferred promise - */ - fromString: function(str) { + var config = this.config; - var startTime = Date.now(); + SVG2JS(svgstr, function(svgjs) { - str = decodeSVGDatauri(str); + svgjs = PLUGINS(svgjs, config.plugins); - return this.config - .then(function(config) { + callback(JS2SVG(svgjs, config.js2svg)); - 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' })); - - } - -}); +}; diff --git a/lib/svgo/coa.js b/lib/svgo/coa.js index e36f958a..73723c30 100644 --- a/lib/svgo/coa.js +++ b/lib/svgo/coa.js @@ -1,13 +1,13 @@ 'use strict'; require('colors'); +require('js-yaml'); var FS = require('fs'), - QFS = require('q-fs'), PATH = require('path'), UTIL = require('util'), SVGO = require('../svgo'), - info = JSON.parse(require('fs').readFileSync(__dirname + '/../../package.json')), + PKG = require('../../package.json'), encodeSVGDatauri = require('./tools').encodeSVGDatauri, regSVGFile = /\.svg$/; @@ -15,20 +15,18 @@ var FS = require('fs'), * Command-Option-Argument. * * @see https://github.com/veged/coa - * - * @module coa */ module.exports = require('coa').Cmd() .helpful() - .name(info.name) - .title(info.description) + .name(PKG.name) + .title(PKG.description) .opt() .name('version').title('Version') .short('v').long('version') .only() .flag() .act(function() { - return info.version; + return PKG.version; }) .end() .opt() @@ -99,109 +97,144 @@ module.exports = require('coa').Cmd() var input = args && args.input ? args.input : opts.input, output = args && args.output ? args.output : opts.output, - string = opts.string, - folder = opts.folder, - svgo; + config; + // w/o anything if ( (!input || input === '-') && - !string && + !opts.string && !opts.stdin && !opts.folder && process.stdin.isTTY ) 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 - if (folder) { - optimizeFolder(folder, opts); + if (opts.folder) { + optimizeFolder(opts.folder, config); + return; } - // --string - if (string) { - svgo = new SVGO({ coa: opts }).fromString(string); - } + // --inpput + if (input) { - // STDIN - else if (input === '-') { - svgo = new SVGO({ coa: opts }).fromStream(process.stdin); - } + // STDIN + if (input === '-') { - // file - else if (input) { - svgo = new SVGO({ coa: opts }).fromFile(input); - } + var data = ''; - return svgo.then(function(result) { + process.stdin.pause(); - // --datauri - if (opts.datauri) { - // convert to Data URI base64 string - result.data = encodeSVGDatauri(result.data); - } - - // stdout - if (output === '-' || (input === '-' && !output)) { - process.stdout.write(result.data + '\n'); - } + process.stdin + .on('data', function(chunk) { + data += chunk; + }) + .once('end', function() { + optimizeFromString(data, config, input, output); + }) + .resume(); // file - else { + } else { - // if input is from file - overwrite it - if (!output && input) { - output = input; - } + FS.readFile(input, 'utf8', function(err, data) { + if (err) { + throw err; + } - UTIL.puts('\r'); - saveFileAndPrintInfo(result, output, opts.pretty); + optimizeFromString(data, config, input, output); + }); } - }) - .done(); + // --string + } else if (opts.string) { + + optimizeFromString(opts.string, 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) { +function optimizeFromString(svgstr, config, input, output) { - // output file - output = FS.createWriteStream(output, { encoding: 'utf8' }); - output.write(result.data); - output.end(); + var startTime = Date.now(config), + time, + inBytes = Buffer.byteLength(svgstr, 'utf8'), + outBytes, + svgo = new SVGO(config); - // print time info - printTimeInfo(result.info.time); + svgo.optimize(svgstr, function(result) { - // print optimization profit info - printProfitInfo(result.info.inBytes, result.info.outBytes); + outBytes = Buffer.byteLength(result.data, 'utf8'); + 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) { 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) { var profitPercents = 100 - outBytes * 100 / inBytes; @@ -215,55 +248,66 @@ function printProfitInfo(inBytes, outBytes) { } -/** - * Optimize all *.svg files in a specific folder. - * - * @param {String} folder folder path - * @param {Object} opts COA options - */ -function optimizeFolder(folder, opts) { +function optimizeFolder(path, config) { - 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 - QFS.list(folder).then(function(list) { + // list folder content + 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 - QFS.isFile(item) - .then(function(isFile) { + // check if file name matches *.svg + if (regSVGFile.test(filepath)) { - // and file name matches *.svg - 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); - - }); + FS.readFile(filepath, 'utf8', function(err, data) { + if (err) { + throw err; } - }) - .fail(function(e) { - UTIL.puts(filename + ':\n' + String('Error! "' + e.message + '"').red + '\n'); + var startTime = Date.now(), + time, + 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(); + }); } diff --git a/lib/svgo/config.js b/lib/svgo/config.js index 9fa21286..00310892 100644 --- a/lib/svgo/config.js +++ b/lib/svgo/config.js @@ -1,177 +1,166 @@ 'use strict'; -var QFS = require('q-fs'), - PATH = require('path'), - YAML = require('js-yaml'), - extend = require('./tools').extend, - defaultConfigPath = PATH.resolve(__dirname, '../../.svgo.yml'); +require('js-yaml'); + +var EXTEND = require('node.extend'); /** * Read and/or extend default config file, * prepare and optimize plugins array. * - * @module config - * - * @param {Object} [params] config object to extend or coa params - * - * @return {Object} config deferred promise + * @param {Object} [config] input config + * @return {Object} output config */ -module.exports = function(params) { +module.exports = function(config) { - return _getConfig(params).then(function(config) { + var defaults = require('../../.svgo.yml'); - config.plugins = preparePluginsArray(config.plugins); - config.plugins = optimizePluginsArray(config.plugins); + defaults.plugins = preparePluginsArray(defaults.plugins); - return config; + if (config) { + defaults = extendConfig(defaults, config); + } - }); + defaults.plugins = optimizePluginsArray(defaults.plugins); + + return defaults; }; /** - * Get default or extended config. - * - * @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. + * Require() all plugins in array. * * @param {Array} plugins input plugins array - * * @return {Array} input plugins array of arrays */ function preparePluginsArray(plugins) { - return plugins.map(function(plugin) { - plugin.fn = require('../../plugins/' + plugin.name)[plugin.name]; + var plugin, + 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. * - * @param {Object} plugins input plugins array - * - * @return {Array} output plugins array + * @param {Object} plugins input plugins + * @return {Array} output plugins */ function optimizePluginsArray(plugins) { 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) { prev.push(item[0]); return false; } prev = item; + 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; - } diff --git a/lib/svgo/js2svg.js b/lib/svgo/js2svg.js index 39bb41e2..926a9e22 100644 --- a/lib/svgo/js2svg.js +++ b/lib/svgo/js2svg.js @@ -1,282 +1,290 @@ 'use strict'; -var INHERIT = require('inherit'), - extend = require('./tools').extend; +var EXTEND = require('./tools').extend; + +var defaults = { + doctypeStart: '', + procInstStart: '', + tagOpenStart: '<', + tagOpenEnd: '>', + tagCloseStart: '', + tagShortStart: '<', + tagShortEnd: '/>', + attrStart: '="', + attrEnd: '"', + commentStart: '', + cdataStart: '', + textStart: '', + textEnd: '', + indent: ' ', + entities: { + '&': '&', + '\'': ''', + '"': '"', + '>': '>', + '<': '<', + }, + pretty: false +}; /** * Convert SVG-as-JS object to SVG (XML) string. * - * @module js2svg - * - * @param {Object} jsdata input data + * @param {Object} data input data * @param {Object} config config * * @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() { - /** - * @constructs - * - * @private - */ - __constructor: function(config) { + var indent = ''; - /** - * Shallow copy of converter config. - * - * @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'; + if (this.config.pretty) { + for (var i = 1; i < this.indentLevel; i++) { + indent += this.config.indent; } + } - }, + 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) { - 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); - } + return this.config.procInstStart + + instruction.name + + ' ' + + instruction.body + + this.config.procInstEnd; - }, 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) { - /** - * Create indent string in accordance with the current node level. - * - * @return {String} - */ - createIndent: function() { + return this.config.cdataStart + + cdata + + this.config.cdataEnd; - var indent = ''; +}; - if (this.config.pretty) { - for (var i = 1; i < this.indentLevel; i++) { - indent += this.config.indent; - } - } +/** + * Create element tag. + * + * @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; + } - }, - - /** - * 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]); - } + // empty element and short tag + if (data.isEmpty()) { return this.createIndent() + - this.config.textStart + - text + - this.config.textEnd; + 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.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; + +}; diff --git a/lib/svgo/jsAPI.js b/lib/svgo/jsAPI.js index 4df2a2fc..c990b761 100644 --- a/lib/svgo/jsAPI.js +++ b/lib/svgo/jsAPI.js @@ -1,159 +1,139 @@ 'use strict'; -var INHERIT = require('inherit'), - extend = require('./tools').extend; +var EXTEND = require('node.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) { - /** - * @constructs - * - * @private - */ - __constructor: function(data) { + if (!param) return !!this.elem; - 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) { - /** - * Determine if element is empty. - * - * @return {Boolean} - */ - isEmpty: function() { + if (!this.attrs || !Object.keys(this.attrs).length) return false; - 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(); - /** - * 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) { + return !!this.attrs[name]; - 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; - /** - * Remove a specific attribute. - * - * @param {String} name attribute name - * @param {String} [val] attribute value - * - * @return {Boolean} - */ - removeAttr: function(name, val) { + delete this.attrs[name]; - 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) || - (attr && attr.value === undefined) || - (attr && attr.prefix === undefined) || - (attr && attr.local === undefined) - ) return false; +/** + * Iterates over all attributes. + * + * @param {Function} callback callback + * @param {Object} [context] callback context + * @return {Boolean} false if there are no any attributes + */ + JSAPI.prototype.eachAttr = function(callback, context) { - this.attrs = this.attrs || {}; - - 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; + if (!this.hasAttr()) return false; + for (var name in this.attrs) { + callback.call(context, this.attrs[name]); } -}); + return true; + +}; diff --git a/lib/svgo/plugins.js b/lib/svgo/plugins.js index 7098e766..7b087cad 100644 --- a/lib/svgo/plugins.js +++ b/lib/svgo/plugins.js @@ -7,7 +7,6 @@ * * @param {Object} data input data * @param {Object} plugins plugins object from config - * * @return {Object} output data */ module.exports = function(data, plugins) { @@ -38,7 +37,6 @@ module.exports = function(data, plugins) { * @param {Object} data input data * @param {Array} plugins plugins list to process * @param {Boolean} [reverse] reverse pass? - * * @return {Object} output data */ function perItem(data, plugins, reverse) { @@ -89,7 +87,6 @@ function perItem(data, plugins, reverse) { * * @param {Object} data input data * @param {Array} plugins plugins list to process - * * @return {Object} output data */ function full(data, plugins) { diff --git a/lib/svgo/svg2js.js b/lib/svgo/svg2js.js index 2c0c176c..cdf484cb 100644 --- a/lib/svgo/svg2js.js +++ b/lib/svgo/svg2js.js @@ -1,23 +1,26 @@ 'use strict'; -var Q = require('q'), - SAX = require('sax'), +var SAX = require('sax'), 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. * - * @module svg2js - * - * @param {String} svg input data - * @param {Object} config sax xml parser config - * - * @return {Object} output data deferred promise + * @param {String} data input data + * @param {Function} callback */ -module.exports = function(svg, config) { +module.exports = function(data, callback) { - var deferred = Q.defer(), - sax = SAX.parser(config.strict, config), + var sax = SAX.parser(config.strict, config), root = {}, current = root, stack = []; @@ -109,23 +112,21 @@ module.exports = function(svg, config) { sax.onerror = function(e) { - deferred.reject(new Error('svg2js: ' + e.message)); - // https://github.com/isaacs/sax-js#events // "The error will be hanging out on parser.error, // and must be deleted before parsing can continue" this.error = null; + throw new Error('svg2js: ' + e.message); + }; sax.onend = function() { - deferred.resolve(root); + callback(root); }; - sax.write(svg).close(); - - return deferred.promise; + sax.write(data).close(); }; diff --git a/package.json b/package.json index 1d5dfca4..beba40a2 100644 --- a/package.json +++ b/package.json @@ -32,25 +32,20 @@ "example": "./examples" }, "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", "jshint": "jshint --show-non-errors ." }, "dependencies": { "sax": "~0.5.0", - "q": "~0.8.10", - "q-fs": "~0.1.0", "coa": "~0.3.7", - "inherit": "", - "node.extend": "", "js-yaml": "", - "colors": "~0.6.0" + "colors": "~0.6.0", + "node.extend": "" }, "devDependencies": { "mocha": "~1.8.0", - "mocha-as-promised": "~1.2.0", "chai": "~1.5.0", - "chai-as-promised": "~3.2.3", "istanbul": "~0.1.0", "mocha-istanbul": "" }, diff --git a/plugins/cleanupAttrs.js b/plugins/cleanupAttrs.js index 91c56bdc..4e8ea7cd 100644 --- a/plugins/cleanupAttrs.js +++ b/plugins/cleanupAttrs.js @@ -1,5 +1,15 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + +exports.params = { + newlines: true, + trim: true, + spaces: true +}; + var regNewlines = /\n/g, regSpaces = /\s{2,}/g; @@ -12,7 +22,7 @@ var regNewlines = /\n/g, * * @author Kir Belevich */ -exports.cleanupAttrs = function(item, params) { +exports.fn = function(item, params) { if (item.isElem()) { diff --git a/plugins/cleanupEnableBackground.js b/plugins/cleanupEnableBackground.js index 0769e6cf..f27c5b38 100644 --- a/plugins/cleanupEnableBackground.js +++ b/plugins/cleanupEnableBackground.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/, elems = ['svg', 'mask', 'pattern']; @@ -18,7 +22,7 @@ var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+] * * @author Kir Belevich */ -exports.cleanupEnableBackground = function(item) { +exports.fn = function(item) { if ( item.isElem(elems) && diff --git a/plugins/cleanupIDs.js b/plugins/cleanupIDs.js index dba8037f..592b0fa1 100644 --- a/plugins/cleanupIDs.js +++ b/plugins/cleanupIDs.js @@ -1,5 +1,14 @@ 'use strict'; +exports.type = 'full'; + +exports.active = true; + +exports.params = { + remove: true, + minify: true +}; + var referencesProps = require('./_collections').referencesProps, regReferencesUrl = /^url\(#(.+?)\)$/, regReferencesHref = /^#(.+?)$/, @@ -19,7 +28,7 @@ var referencesProps = require('./_collections').referencesProps, * * @author Kir Belevich */ -exports.cleanupIDs = function(data, params) { +exports.fn = function(data, params) { var currentID, currentIDstring, diff --git a/plugins/cleanupNumericValues.js b/plugins/cleanupNumericValues.js index c570dd73..e23d4e26 100644 --- a/plugins/cleanupNumericValues.js +++ b/plugins/cleanupNumericValues.js @@ -1,5 +1,15 @@ '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|%)?$/, 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 */ -exports.cleanupNumericValues = function(item, params) { +exports.fn = function(item, params) { if (item.isElem()) { diff --git a/plugins/collapseGroups.js b/plugins/collapseGroups.js index a32a2d83..f6c29432 100644 --- a/plugins/collapseGroups.js +++ b/plugins/collapseGroups.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItemReverse'; + +exports.active = true; + var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel; /* @@ -25,7 +29,7 @@ var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel; * * @author Kir Belevich */ -exports.collapseGroups = function(item) { +exports.fn = function(item) { // non-empty elements if (item.elem && !item.isEmpty()) { diff --git a/plugins/convertColors.js b/plugins/convertColors.js index bba7f4ee..4f93fea8 100644 --- a/plugins/convertColors.js +++ b/plugins/convertColors.js @@ -1,26 +1,19 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + +exports.params = { + names2hex: true, + rgb2hex: true, + shorthex: true +}; + var collections = require('./_collections'), regRGB = /^rgb\((\d+%?),\s*(\d+%?),\s*(\d+%?)\)$/, 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. * @@ -44,7 +37,7 @@ function rgb2hex(rgb) { * * @author Kir Belevich */ -exports.convertColors = function(item, params) { +exports.fn = function(item, params) { 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); +} diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index 5afc62f7..d606a344 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -1,5 +1,21 @@ '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, path2js = require('./_path.js').path2js, js2path = require('./_path.js').js2path, @@ -22,7 +38,7 @@ var pathElems = require('./_collections.js').pathElems, * * @author Kir Belevich */ -exports.convertPathData = function(item, params) { +exports.fn = function(item, params) { if (item.isElem(pathElems) && item.hasAttr('d')) { diff --git a/plugins/convertStyleToAttrs.js b/plugins/convertStyleToAttrs.js index d634bbdd..0b45d16b 100644 --- a/plugins/convertStyleToAttrs.js +++ b/plugins/convertStyleToAttrs.js @@ -1,6 +1,10 @@ 'use strict'; -var extend = require('../lib/svgo/tools').extend, +exports.type = 'perItem'; + +exports.active = true; + +var EXTEND = require('node.extend'), stylingProps = require('./_collections').stylingProps, regCleanupStyle = /(:|;)\s+/g; @@ -22,7 +26,7 @@ var extend = require('../lib/svgo/tools').extend, * * @author Kir Belevich */ -exports.convertStyleToAttrs = function(item) { +exports.fn = function(item) { if (item.elem && item.hasAttr('style')) { // ['opacity: 1', 'color: #000'] @@ -57,7 +61,7 @@ exports.convertStyleToAttrs = function(item) { return true; }); - extend(item.attrs, attrs); + EXTEND(item.attrs, attrs); if (styles.length) { item.attr('style').value = styles.join(';') diff --git a/plugins/convertTransform.js b/plugins/convertTransform.js index df30dd07..24482436 100644 --- a/plugins/convertTransform.js +++ b/plugins/convertTransform.js @@ -1,5 +1,22 @@ '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, transform2js = require('./_transforms.js').transform2js, transformsMultiply = require('./_transforms.js').transformsMultiply, @@ -19,7 +36,7 @@ var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, * * @author Kir Belevich */ -exports.convertTransform = function(item, params) { +exports.fn = function(item, params) { if (item.elem) { diff --git a/plugins/cropAndCenterAlongPath.js b/plugins/cropAndCenterAlongPath.js index 1d9c12b4..1b7f6003 100644 --- a/plugins/cropAndCenterAlongPath.js +++ b/plugins/cropAndCenterAlongPath.js @@ -1,5 +1,17 @@ '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, computeCubicBoundingBox = require('./_path.js').computeCubicBoundingBox, computeQuadraticBoundingBox = require('./_path.js').computeQuadraticBoundingBox, @@ -7,7 +19,7 @@ var relative2absolute = require('./_path.js').relative2absolute, js2path = require('./_path.js').js2path, extend = require('../lib/svgo/tools').extend; -exports.cropAndCenterAlongPath = function(data, params) { +exports.fn = function(data, params) { data.content.forEach(function(item) { diff --git a/plugins/moveElemsAttrsToGroup.js b/plugins/moveElemsAttrsToGroup.js index 37a82a97..04d9ddf0 100644 --- a/plugins/moveElemsAttrsToGroup.js +++ b/plugins/moveElemsAttrsToGroup.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItemReverse'; + +exports.active = true; + var inheritableAttrs = require('./_collections').inheritableAttrs, pathElems = require('./_collections.js').pathElems; @@ -27,7 +31,7 @@ var inheritableAttrs = require('./_collections').inheritableAttrs, * * @author Kir Belevich */ -exports.moveElemsAttrsToGroup = function(item) { +exports.fn = function(item) { if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) { diff --git a/plugins/moveGroupAttrsToElems.js b/plugins/moveGroupAttrsToElems.js index 0a9f2aff..57ab51af 100644 --- a/plugins/moveGroupAttrsToElems.js +++ b/plugins/moveGroupAttrsToElems.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItemReverse'; + +exports.active = true; + var pathElems = require('./_collections.js').pathElems; /** @@ -21,7 +25,7 @@ var pathElems = require('./_collections.js').pathElems; * * @author Kir Belevich */ -exports.moveGroupAttrsToElems = function(item) { +exports.fn = function(item) { // move group transform attr to content's pathElems if ( diff --git a/plugins/removeComments.js b/plugins/removeComments.js index 5bc2699f..c53b997c 100644 --- a/plugins/removeComments.js +++ b/plugins/removeComments.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + /** * Remove comments. * @@ -12,7 +16,7 @@ * * @author Kir Belevich */ -exports.removeComments = function(item) { +exports.fn = function(item) { if (item.comment && item.comment.charAt(0) !== '!') { return false; diff --git a/plugins/removeDoctype.js b/plugins/removeDoctype.js index 55ccadc1..180bd242 100644 --- a/plugins/removeDoctype.js +++ b/plugins/removeDoctype.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + /** * Remove DOCTYPE declaration. * @@ -25,7 +29,7 @@ * * @author Kir Belevich */ -exports.removeDoctype = function(item) { +exports.fn = function(item) { // remove doctype only if custom XML entities declaration block does not presents // http://en.wikipedia.org/wiki/Document_Type_Definition#Entity_declarations diff --git a/plugins/removeEditorsNSData.js b/plugins/removeEditorsNSData.js index 7e2ad4cd..dc482ff4 100644 --- a/plugins/removeEditorsNSData.js +++ b/plugins/removeEditorsNSData.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + var editorNamespaces = require('./_collections').editorNamespaces, prefixes = []; @@ -16,7 +20,7 @@ var editorNamespaces = require('./_collections').editorNamespaces, * * @author Kir Belevich */ -exports.removeEditorsNSData = function(item) { +exports.fn = function(item) { if (item.elem) { diff --git a/plugins/removeEmptyAttrs.js b/plugins/removeEmptyAttrs.js index 519762fd..e4e072c3 100644 --- a/plugins/removeEmptyAttrs.js +++ b/plugins/removeEmptyAttrs.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + /** * Remove attributes with empty values. * @@ -8,7 +12,7 @@ * * @author Kir Belevich */ -exports.removeEmptyAttrs = function(item) { +exports.fn = function(item) { if (item.elem) { diff --git a/plugins/removeEmptyContainers.js b/plugins/removeEmptyContainers.js index ddf1c791..a790e0e3 100644 --- a/plugins/removeEmptyContainers.js +++ b/plugins/removeEmptyContainers.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItemReverse'; + +exports.active = true; + var container = require('./_collections').elemsGroups.container; /** @@ -18,7 +22,7 @@ var container = require('./_collections').elemsGroups.container; * * @author Kir Belevich */ -exports.removeEmptyContainers = function(item) { +exports.fn = function(item) { return !(item.isElem(container) && !item.isElem('svg') && item.isEmpty()); diff --git a/plugins/removeEmptyText.js b/plugins/removeEmptyText.js index 9439332e..ae314c6b 100644 --- a/plugins/removeEmptyText.js +++ b/plugins/removeEmptyText.js @@ -1,5 +1,15 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + +exports.params = { + text: true, + tspan: true, + tref: true +}; + /** * Remove empty Text elements. * @@ -21,7 +31,7 @@ * * @author Kir Belevich */ -exports.removeEmptyText = function(item, params) { +exports.fn = function(item, params) { // Remove empty text element if ( diff --git a/plugins/removeHiddenElems.js b/plugins/removeHiddenElems.js index 1ba4d730..8119a95f 100644 --- a/plugins/removeHiddenElems.js +++ b/plugins/removeHiddenElems.js @@ -1,5 +1,26 @@ '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: * - display="none" @@ -19,7 +40,7 @@ * * @author Kir Belevich */ -exports.removeHiddenElems = function(item, params) { +exports.fn = function(item, params) { if (item.elem) { diff --git a/plugins/removeMetadata.js b/plugins/removeMetadata.js index 28f3d04b..7573a452 100644 --- a/plugins/removeMetadata.js +++ b/plugins/removeMetadata.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + /** * Remove . * @@ -10,7 +14,7 @@ * * @author Kir Belevich */ -exports.removeMetadata = function(item) { +exports.fn = function(item) { return !item.isElem('metadata'); diff --git a/plugins/removeNonInheritableGroupAttrs.js b/plugins/removeNonInheritableGroupAttrs.js index 24a244c0..b4751897 100644 --- a/plugins/removeNonInheritableGroupAttrs.js +++ b/plugins/removeNonInheritableGroupAttrs.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + var inheritableAttrs = require('./_collections').inheritableAttrs, presentationAttrs = require('./_collections').attrsGroups.presentation, excludedAttrs = ['display', 'opacity']; @@ -12,7 +16,7 @@ var inheritableAttrs = require('./_collections').inheritableAttrs, * * @author Kir Belevich */ -exports.removeNonInheritableGroupAttrs = function(item) { +exports.fn = function(item) { if (item.isElem('g')) { diff --git a/plugins/removeRasterImages.js b/plugins/removeRasterImages.js index 068b601c..2a272027 100644 --- a/plugins/removeRasterImages.js +++ b/plugins/removeRasterImages.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = false; + /** * Remove raster images references in . * @@ -10,7 +14,7 @@ * * @author Kir Belevich */ -exports.removeRasterImages = function(item) { +exports.fn = function(item) { if ( item.isElem('image') && diff --git a/plugins/removeUnknownsAndDefaults.js b/plugins/removeUnknownsAndDefaults.js index 2723b806..100edf74 100644 --- a/plugins/removeUnknownsAndDefaults.js +++ b/plugins/removeUnknownsAndDefaults.js @@ -1,5 +1,16 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + +exports.params = { + SVGid: true, + unknownContent: true, + unknownAttrs: true, + defaultAttrs: true +}; + var collections = require('./_collections'), elems = collections.elems, attrsGroups = collections.attrsGroups, @@ -48,7 +59,7 @@ for (var elem in elems) { * * @author Kir Belevich */ -exports.removeUnknownsAndDefaults = function(item, params) { +exports.fn = function(item, params) { // elems w/o namespace prefix if (item.isElem() && !item.prefix) { diff --git a/plugins/removeUnusedNS.js b/plugins/removeUnusedNS.js index 72306d3a..3e4985f3 100644 --- a/plugins/removeUnusedNS.js +++ b/plugins/removeUnusedNS.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'full'; + +exports.active = true; + /** * Remove unused namespaces declaration. * @@ -8,7 +12,7 @@ * * @author Kir Belevich */ -exports.removeUnusedNS = function(data) { +exports.fn = function(data) { var svgElem, xmlnsCollection = []; diff --git a/plugins/removeUselessStrokeAndFill.js b/plugins/removeUselessStrokeAndFill.js index b1afd047..981b6c7a 100644 --- a/plugins/removeUselessStrokeAndFill.js +++ b/plugins/removeUselessStrokeAndFill.js @@ -1,5 +1,14 @@ 'use strict'; +exports.type = 'peItem'; + +exports.active = true; + +exports.params = { + stroke: true, + fill: true +}; + var regStrokeProps = /^stroke/, regFillProps = /^fill/; @@ -12,7 +21,7 @@ var regStrokeProps = /^stroke/, * * @author Kir Belevich */ -exports.removeUselessStrokeAndFill = function(item, params) { +exports.fn = function(item, params) { if (item.isElem()) { diff --git a/plugins/removeViewBox.js b/plugins/removeViewBox.js index 0a0a0b14..e44fff2d 100644 --- a/plugins/removeViewBox.js +++ b/plugins/removeViewBox.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/, viewBoxElems = ['svg', 'pattern']; @@ -18,7 +22,7 @@ var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE * * @author Kir Belevich */ -exports.removeViewBox = function(item) { +exports.fn = function(item) { if ( item.isElem(viewBoxElems) && diff --git a/plugins/removeXMLProcInst.js b/plugins/removeXMLProcInst.js index 83b867ae..41938f1e 100644 --- a/plugins/removeXMLProcInst.js +++ b/plugins/removeXMLProcInst.js @@ -1,5 +1,9 @@ 'use strict'; +exports.type = 'perItem'; + +exports.active = true; + /** * Remove XML Processing Instruction. * @@ -11,7 +15,7 @@ * * @author Kir Belevich */ -exports.removeXMLProcInst = function(item) { +exports.fn = function(item) { return !(item.processinginstruction && item.processinginstruction.name === 'xml');