mirror of
https://github.com/svg/svgo.git
synced 2025-07-28 09:22:00 +03:00
go!
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
*.sublime-*
|
||||
*~
|
||||
*.lock
|
||||
*.DS_Store
|
||||
*.swp
|
||||
*.min.svg
|
119
.svgo
Normal file
119
.svgo
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"saxXMLParser": {
|
||||
"strict": true,
|
||||
"options": {
|
||||
"trim": true,
|
||||
"normalize": true,
|
||||
"lowercase": true,
|
||||
"xmlns": true,
|
||||
"position": false
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"directPass": [
|
||||
{
|
||||
"name": "removeDoctype",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeXMLProcInst",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeComments",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeMetadata",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeEditorsNSData",
|
||||
"active": true
|
||||
},{
|
||||
"name": "convertStyleToAttrs",
|
||||
"active": true
|
||||
},{
|
||||
"name": "cleanupAttrs",
|
||||
"active": true,
|
||||
"params": {
|
||||
"newlines": true,
|
||||
"trim": true,
|
||||
"spaces": true
|
||||
}
|
||||
},{
|
||||
"name": "removeEmptyAttrs",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeDefaultPx",
|
||||
"active": true
|
||||
},{
|
||||
"name": "removeHiddenElems",
|
||||
"active": true,
|
||||
"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,
|
||||
"params": {
|
||||
"text": true,
|
||||
"tspan": true,
|
||||
"tref": true
|
||||
}
|
||||
},{
|
||||
"name": "removeSVGAttrs",
|
||||
"active": true,
|
||||
"params": {
|
||||
"id": true,
|
||||
"version": true,
|
||||
"x0": true,
|
||||
"y0": true,
|
||||
"xmlspace": true
|
||||
}
|
||||
},{
|
||||
"name": "convertColors",
|
||||
"active": true,
|
||||
"params": {
|
||||
"names2hex": true,
|
||||
"rgb2hex": true,
|
||||
"shorthex": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"reversePass": [
|
||||
{
|
||||
"name": "removeEmptyContainers",
|
||||
"active": true,
|
||||
"params": {
|
||||
"a": true,
|
||||
"defs": true,
|
||||
"glyph": true,
|
||||
"g": true,
|
||||
"marker": true,
|
||||
"mask": true,
|
||||
"missing-glyph": true,
|
||||
"pattern": true,
|
||||
"svg": true,
|
||||
"switch": true,
|
||||
"symbol": true
|
||||
}
|
||||
},{
|
||||
"name": "moveElemsAttrsToGroup",
|
||||
"active": true
|
||||
},{
|
||||
"name": "collapseGroups",
|
||||
"active": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
7
GNUmakefile
Normal file
7
GNUmakefile
Normal file
@ -0,0 +1,7 @@
|
||||
test:
|
||||
@./node_modules/.bin/mocha --reporter min
|
||||
|
||||
test-v:
|
||||
@./node_modules/.bin/mocha --reporter spec
|
||||
|
||||
.PHONY: test test-v
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2012 Kir Belevich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
76
README.md
76
README.md
@ -1,2 +1,74 @@
|
||||
svgo
|
||||
====
|
||||
```
|
||||
o-o o o o--o o-o
|
||||
\ \ / | | | |
|
||||
o-o o o--o o-o
|
||||
|
|
||||
o--o
|
||||
```
|
||||
|
||||
## SVGO
|
||||
|
||||
**SVG** **O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files.
|
||||
|
||||
## Why?
|
||||
|
||||
SVG files, especially exported from various editors, usually contains a lot of redundant and useless information such as editor metadata, comments, hidden elements and other stuff that can be safely removed without affecting SVG rendering result.
|
||||
|
||||
## What it can do
|
||||
|
||||
SVGO has a plugin-based architecture, so almost every optimization is a separate plugin.
|
||||
|
||||
Today we have:
|
||||
|
||||
* [ [>](svgo/tree/master/plugins/cleanupAttrs.js) ] cleanup attributes from newlines, trailing and repeating spaces
|
||||
* [ [>](svgo/tree/master/plugins/removeDoctype.js) ] remove doctype declaration
|
||||
* [ [>](svgo/tree/master/plugins/removeXMLProcInst.js) ] remove XML processing instructions
|
||||
* [ [>](svgo/tree/master/plugins/removeComments.js) ] remove comments
|
||||
* [ [>](svgo/tree/master/plugins/removeMetadata.js) ] remove metadata
|
||||
* [ [>](svgo/tree/master/plugins/removeEditorsNSData.js) ] remove editors namespaces, elements and attributes
|
||||
* [ [>](svgo/tree/master/plugins/removeEmptyAttrs.js) ] remove empty attributes
|
||||
* [ [>](svgo/tree/master/plugins/removeDefaultPx.js) ] remove default "px" unit
|
||||
* [ [>](svgo/tree/master/plugins/removeHiddenElems.js) ] remove a lot of hidden elements
|
||||
* [ [>](svgo/tree/master/plugins/removeEmptyText.js) ] remove empty Text elements
|
||||
* [ [>](svgo/tree/master/plugins/removeEmptyContainers.js) ] remove empty Container elements
|
||||
* [ [>](svgo/tree/master/plugins/convertStyleToAttrs.js) ] convert styles into attributes
|
||||
* [ [>](svgo/tree/master/plugins/convertColors.js) ] convert colors
|
||||
* [ [>](svgo/tree/master/plugins/moveElemsAttrsToGroup.js) ] move elements attributes to the existing group wrapper
|
||||
* [ [>](svgo/tree/master/plugins/collapseGroups.js) ] collapse groups
|
||||
|
||||
But it's not only about rude removing, SVG has a strict [specification](http://www.w3.org/TR/SVG/expanded-toc.html) with a lot of opportunities for optimizations, default values, geometry hacking and more.
|
||||
|
||||
How-to instructions and plugins API docs will coming ASAP.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
```
|
||||
npm install -g svgo
|
||||
```
|
||||
|
||||
```
|
||||
Usage:
|
||||
svgo [OPTIONS] [ARGS]
|
||||
|
||||
|
||||
Options:
|
||||
-h, --help : Help
|
||||
-v, --version : Version
|
||||
-c CONFIG, --config=CONFIG : Local config
|
||||
-i INPUT, --input=INPUT : Input file (default: stdin)
|
||||
-o OUTPUT, --output=OUTPUT : Output file (default: stdout)
|
||||
```
|
||||
|
||||
```
|
||||
svgo -i myTestFile.svg -o myTestFile.min.svg
|
||||
```
|
||||
|
||||
## TODO
|
||||
It's only the very first public alpha :)
|
||||
|
||||
1. documentation!
|
||||
2. phantomjs-based server-side SVG rendering "before vs after" tests
|
||||
3. more unit tests
|
||||
4. more plugins
|
||||
5. …
|
3
bin/svgo
Executable file
3
bin/svgo
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/coa').run();
|
60
lib/coa.js
Normal file
60
lib/coa.js
Normal file
@ -0,0 +1,60 @@
|
||||
var Q = require('q'),
|
||||
info = JSON.parse(require('fs').readFileSync(__dirname + '/../package.json'));
|
||||
|
||||
module.exports = require('coa').Cmd()
|
||||
.helpful()
|
||||
.name(info.name)
|
||||
.title(info.description)
|
||||
.opt()
|
||||
.name('version').title('Version')
|
||||
.short('v').long('version')
|
||||
.only()
|
||||
.flag()
|
||||
.act(function(opts) {
|
||||
return info.version;
|
||||
})
|
||||
.end()
|
||||
.opt()
|
||||
.name('config').title('Local config')
|
||||
.short('c').long('config')
|
||||
.end()
|
||||
.opt()
|
||||
.name('input').title('Input file (default: stdin)')
|
||||
.short('i').long('input')
|
||||
.input()
|
||||
.end()
|
||||
.opt()
|
||||
.name('output').title('Output file (default: stdout)')
|
||||
.short('o').long('output')
|
||||
.output()
|
||||
.end()
|
||||
.act(function(options) {
|
||||
|
||||
var input = [],
|
||||
deferred = Q.defer(),
|
||||
SVGO = require('./svgo');
|
||||
|
||||
options.input
|
||||
.on('data', function(chunk) {
|
||||
input.push(chunk);
|
||||
})
|
||||
.once('end', function() {
|
||||
deferred.resolve(input.join());
|
||||
})
|
||||
.resume();
|
||||
|
||||
return deferred.promise
|
||||
.then(function(svg) {
|
||||
return SVGO(svg, options);
|
||||
})
|
||||
.then(function(svgmin) {
|
||||
var output = options.output;
|
||||
|
||||
output.write(svgmin);
|
||||
|
||||
output === process.stdout ?
|
||||
output.write('\n') :
|
||||
output.end();
|
||||
});
|
||||
|
||||
});
|
52
lib/config.js
Normal file
52
lib/config.js
Normal file
@ -0,0 +1,52 @@
|
||||
var QFS = require('q-fs'),
|
||||
PATH = require('path'),
|
||||
extend = require('./tools').extend;
|
||||
|
||||
module.exports = function(options) {
|
||||
|
||||
var defaultConfigPath = PATH.resolve(__dirname, '../.svgo');
|
||||
|
||||
if (!options || !options.config) return readConfig(defaultConfigPath);
|
||||
|
||||
return readConfig(defaultConfigPath)
|
||||
.then(function(defaultConfig) {
|
||||
|
||||
if (typeof options.config === 'string') {
|
||||
|
||||
var localConfigPath = PATH.resolve(process.cwd, options.config);
|
||||
|
||||
return QFS.exists(localConfigPath)
|
||||
.then(function(exists) {
|
||||
|
||||
if (!exists) return defaultConfig;
|
||||
|
||||
return readConfig(localConfigPath)
|
||||
.then(function(localConfig) {
|
||||
return extend(true, defaultConfig, localConfig);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} else if (Object.prototype.toString.call(options.config) === '[object Object]') {
|
||||
|
||||
return extend(true, defaultConfig, options.config);
|
||||
|
||||
} else {
|
||||
|
||||
// TODO: ...
|
||||
throw new Error('...');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function readConfig(path) {
|
||||
|
||||
return QFS.read(path)
|
||||
.then(function(data) {
|
||||
return JSON.parse(data);
|
||||
});
|
||||
|
||||
};
|
229
lib/js2svg.js
Normal file
229
lib/js2svg.js
Normal file
@ -0,0 +1,229 @@
|
||||
var INHERIT = require('inherit');
|
||||
|
||||
/**
|
||||
* Convert SVG-as-JS object to SVG (XML) string.
|
||||
*
|
||||
* @param {Object} data svg-as-js data object
|
||||
* @param {Class} [converter] custom converter class
|
||||
* @return {Converter} Converter instance
|
||||
*/
|
||||
module.exports = function(data, converter) {
|
||||
|
||||
return new (converter || Converter)(data).run(data);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Converter
|
||||
*/
|
||||
var Converter = exports.Converter = INHERIT(
|
||||
/**
|
||||
* @lends Nodes.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
* @private
|
||||
*/
|
||||
__constructor: function() {
|
||||
|
||||
/**
|
||||
* Converter options.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
this.options = {
|
||||
doctypeStart: '<!DOCTYPE',
|
||||
doctypeEnd: '>',
|
||||
|
||||
procInstStart: '<?',
|
||||
procInstEnd: '?>',
|
||||
|
||||
tagOpenStart: '<',
|
||||
tagOpenEnd: '>',
|
||||
tagCloseStart: '</',
|
||||
tagCloseEnd: '>',
|
||||
|
||||
tagShortStart: '<',
|
||||
tagShortEnd: '/>',
|
||||
|
||||
attrStart: '="',
|
||||
attrEnd: '"',
|
||||
|
||||
commentStart: '<!--',
|
||||
commentEnd: '-->',
|
||||
|
||||
cdataStart: '<![CDATA[',
|
||||
cdataEnd: ']]>'
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Start conversion.
|
||||
*
|
||||
* @param {Object} svg-as-js data object
|
||||
* @return {String}
|
||||
*/
|
||||
run: function(data) {
|
||||
|
||||
var svg = '';
|
||||
|
||||
if (data.content) {
|
||||
|
||||
data.content.forEach(function(item) {
|
||||
|
||||
if (item.isElem()) {
|
||||
svg += this.createElem(item);
|
||||
} else if (item.isText()) {
|
||||
svg += item.text;
|
||||
} else if (item.isDoctype()) {
|
||||
svg += this.createDoctype(item.doctype);
|
||||
} else if (item.isProcInst()) {
|
||||
svg += this.createProcInst(item.processinginstruction);
|
||||
} else if (item.isComment()) {
|
||||
svg += this.createComment(item.comment);
|
||||
} else if (item.isCDATA()) {
|
||||
svg += this.createCDATA(item.cdata);
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
}
|
||||
|
||||
return svg;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create doctype tag.
|
||||
*
|
||||
* @param {String} doctype doctype body string
|
||||
* @return {String}
|
||||
*/
|
||||
createDoctype: function(doctype) {
|
||||
|
||||
return this.options.doctypeStart +
|
||||
doctype +
|
||||
this.options.doctypeEnd;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create XML Processing Instruction tag.
|
||||
*
|
||||
* @param {Object} instruction instruction object
|
||||
* @return {String}
|
||||
*/
|
||||
createProcInst: function(instruction) {
|
||||
|
||||
return this.options.procInstStart +
|
||||
instruction.name +
|
||||
' ' +
|
||||
instruction.body +
|
||||
this.options.procInstEnd;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create comment tag.
|
||||
*
|
||||
* @param {String} comment comment body
|
||||
* @return {String}
|
||||
*/
|
||||
createComment: function(comment) {
|
||||
|
||||
return this.options.commentStart +
|
||||
comment +
|
||||
this.options.commentEnd;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create CDATA section.
|
||||
*
|
||||
* @param {String} cdata CDATA body
|
||||
* @return {String}
|
||||
*/
|
||||
createCDATA: function(cdata) {
|
||||
|
||||
return this.options.cdataStart +
|
||||
cdata +
|
||||
this.options.cdataEnd;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create element tag.
|
||||
*
|
||||
* @param {Object} data element object
|
||||
* @return {String}
|
||||
*/
|
||||
createElem: function(data) {
|
||||
|
||||
// empty element and short tag
|
||||
if (data.isEmpty()) {
|
||||
|
||||
return this.options.tagShortStart +
|
||||
data.elem +
|
||||
this.createAttrs(data) +
|
||||
this.options.tagShortEnd;
|
||||
|
||||
// non-empty element
|
||||
} else {
|
||||
|
||||
return this.options.tagOpenStart +
|
||||
data.elem +
|
||||
this.createAttrs(data) +
|
||||
this.options.tagOpenEnd +
|
||||
this.run(data) +
|
||||
this.options.tagCloseStart +
|
||||
data.elem +
|
||||
this.options.tagCloseEnd;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Create element attributes.
|
||||
*
|
||||
* @param {Object} elem attributes object
|
||||
* @return {String}
|
||||
*/
|
||||
createAttrs: function(elem) {
|
||||
|
||||
var self = this,
|
||||
attrs = '';
|
||||
|
||||
if (elem.hasAttr()) {
|
||||
|
||||
elem.eachAttr(function(attr) {
|
||||
|
||||
attrs += ' ' +
|
||||
attr.name +
|
||||
self.options.attrStart +
|
||||
attr.value +
|
||||
self.options.attrEnd;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return attrs;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
var MyConv = INHERIT(Converter, {
|
||||
|
||||
__constructor: function(options) {
|
||||
this.__base();
|
||||
}
|
||||
|
||||
});
|
||||
*/
|
249
lib/jsAPI.js
Normal file
249
lib/jsAPI.js
Normal file
@ -0,0 +1,249 @@
|
||||
var INHERIT = require('inherit'),
|
||||
extend = require('./tools').extend;
|
||||
|
||||
/**
|
||||
* @class SVG-as-JS Nodes API.
|
||||
*/
|
||||
exports.Nodes = INHERIT(
|
||||
/**
|
||||
* @lends Nodes.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
* @private
|
||||
*/
|
||||
__constructor: function(data) {
|
||||
|
||||
extend(this, data);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove item.
|
||||
*/
|
||||
remove: function() {
|
||||
|
||||
delete this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
|
||||
|
||||
return !!this.elem && this.elem === param;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item is a doctype.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDoctype: function() {
|
||||
|
||||
return !!this.doctype;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item is a XML Processing Instruction.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isProcInst: function() {
|
||||
|
||||
return !!this.processinginstruction;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item is a CDATA block.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isCDATA: function() {
|
||||
|
||||
return !!this.cdata;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item is a comment node.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isComment: function() {
|
||||
|
||||
return !!this.comment;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item is a text node.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isText: function() {
|
||||
|
||||
return !!this.text;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if element is empty.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEmpty: function() {
|
||||
|
||||
return !this.content || !this.content.length;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterates over all attributes.
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @return {Boolean} false if the are no any attributes
|
||||
*/
|
||||
eachAttr: function(callback) {
|
||||
|
||||
if (!this.hasAttr()) return false;
|
||||
|
||||
Object.getOwnPropertyNames(this.attrs).forEach(function(name) {
|
||||
callback(this.attrs[name]);
|
||||
}, this);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String|Object} [name] attribute name or object
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
hasAttr: function(attr, val) {
|
||||
|
||||
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||
|
||||
if (!arguments.length) return !!this.attrs;
|
||||
|
||||
if (typeof attr === 'object') {
|
||||
val = attr.value;
|
||||
attr = attr.name;
|
||||
}
|
||||
|
||||
if (val !== undefined) return !!this.attrs[attr] && this.attrs[attr].value === val.toString();
|
||||
|
||||
return !!this.attrs[attr];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
attr: function(name, val) {
|
||||
|
||||
if (!this.hasAttr() || !arguments.length) return undefined;
|
||||
|
||||
if (val !== undefined) return this.hasAttr(name, val) && this.attrs[name];
|
||||
|
||||
return this.hasAttr(name) && this.attrs[name];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific attribute.
|
||||
*
|
||||
* @param {String|Object} attr attribute name or object
|
||||
* @param {String} [val] attribute value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
removeAttr: function(attr, val) {
|
||||
|
||||
if (!this.hasAttr(attr)) return false;
|
||||
|
||||
if (!arguments.length) {
|
||||
delete this.attrs;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof attr === 'object') {
|
||||
val = attr.value;
|
||||
attr = attr.name;
|
||||
}
|
||||
|
||||
if (val && this.attrs[attr].value !== val) return false;
|
||||
|
||||
delete this.attrs[attr];
|
||||
|
||||
if (!Object.keys(this.attrs).length) delete this.attrs;
|
||||
|
||||
return true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Add attribute.
|
||||
*
|
||||
* @param {Object} attr attribute object
|
||||
*/
|
||||
addAttr: function(attr, val) {
|
||||
|
||||
this.attrs = this.attrs || {};
|
||||
|
||||
this.attrs[attr.name] = attr;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item's content has an element(s)
|
||||
* (any or by name or by names array).
|
||||
*
|
||||
* @param {String|Array} [name] element name or names array
|
||||
* @return {Boolean}
|
||||
*/
|
||||
hasElem: function(name) {
|
||||
|
||||
return this.content.some(function(item) {
|
||||
return name ? item.isElem(name) : item.isElem();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if item's content has only elements
|
||||
* (any or by name or by names array).
|
||||
*
|
||||
* @param {String|Array} [name] element name or names array
|
||||
* @return {Boolean}
|
||||
*/
|
||||
hasAllElems: function(name) {
|
||||
|
||||
return this.content.every(function(item) {
|
||||
return name ? item.isElem(name) : item.isElem();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
84
lib/plugins.js
Normal file
84
lib/plugins.js
Normal file
@ -0,0 +1,84 @@
|
||||
var INHERIT = require('inherit');
|
||||
|
||||
module.exports = function(json, plugins, pluginsEngine) {
|
||||
|
||||
var engine = new (pluginsEngine || PluginsEngine)();
|
||||
|
||||
json = engine.pass(json, plugins.directPass);
|
||||
json = engine.pass(json, plugins.reversePass, true);
|
||||
|
||||
return json;
|
||||
|
||||
};
|
||||
|
||||
var PluginsEngine = exports.PluginsEngine = INHERIT({
|
||||
|
||||
_makePluginsList: function(arr) {
|
||||
|
||||
return arr.map(function(plugin) {
|
||||
plugin.fn = require('../plugins/' + plugin.name)[plugin.name];
|
||||
|
||||
return plugin;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
pass: function(json, plugins, reverse) {
|
||||
|
||||
plugins = this._makePluginsList(plugins);
|
||||
|
||||
function monkeys(data) {
|
||||
|
||||
data.content = data.content.filter(function(item) {
|
||||
|
||||
if (reverse && item.content) {
|
||||
monkeys.call(this, item);
|
||||
}
|
||||
|
||||
var filter = true;
|
||||
|
||||
plugins.forEach(function(plugin) {
|
||||
if (plugin.active && plugin.fn(item, plugin.params) === false) {
|
||||
filter = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!reverse && item.content) {
|
||||
monkeys.call(this, item);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
}, this);
|
||||
|
||||
return data;
|
||||
|
||||
};
|
||||
|
||||
return monkeys.call(this, json);
|
||||
|
||||
},
|
||||
|
||||
full: function() {
|
||||
|
||||
this.fullList.forEach(function(plugin) {
|
||||
if (plugin.active) {
|
||||
json = plugin.fn(json, plugin.params);
|
||||
}
|
||||
});
|
||||
|
||||
return json;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
var MyPluginsEngine = INHERIT(PluginsEngine, {
|
||||
|
||||
__constructor: function(options) {
|
||||
this.__base();
|
||||
}
|
||||
|
||||
});
|
||||
*/
|
126
lib/svg2js.js
Normal file
126
lib/svg2js.js
Normal file
@ -0,0 +1,126 @@
|
||||
var Q = require('q'),
|
||||
SAX = require('sax'),
|
||||
TOOLS = require('./tools'),
|
||||
JSAPI = require('./jsAPI');
|
||||
|
||||
/**
|
||||
* Convert SVG (XML) string to SVG-as-JS object.
|
||||
*
|
||||
* @param {String} svg SVG (XML) string
|
||||
* @param {Object} config sax xml parser config
|
||||
* @return {Object}
|
||||
*/
|
||||
module.exports = function(svg, config) {
|
||||
|
||||
config = config || {
|
||||
strict: true,
|
||||
options: {
|
||||
trim: true,
|
||||
normalize: true,
|
||||
lowercase: true,
|
||||
xmlns: true,
|
||||
position: false
|
||||
}
|
||||
};
|
||||
|
||||
var deferred = Q.defer(),
|
||||
sax = SAX.parser(config.strict, config.options),
|
||||
root = {},
|
||||
current = root,
|
||||
stack = [];
|
||||
|
||||
function pushToContent(content) {
|
||||
|
||||
content = new JSAPI.Nodes(content);
|
||||
|
||||
(current.content = current.content || []).push(content);
|
||||
|
||||
return content;
|
||||
|
||||
};
|
||||
|
||||
sax.ondoctype = function(doctype) {
|
||||
|
||||
pushToContent({
|
||||
doctype: doctype
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.onprocessinginstruction = function(data) {
|
||||
|
||||
pushToContent({
|
||||
processinginstruction: data
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.oncomment = function(comment) {
|
||||
|
||||
pushToContent({
|
||||
comment: comment
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.oncdata = function(cdata) {
|
||||
|
||||
pushToContent({
|
||||
cdata: cdata
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.onopentag = function(data) {
|
||||
|
||||
var elem = {
|
||||
elem: data.name,
|
||||
prefix: data.prefix,
|
||||
local: data.local
|
||||
};
|
||||
|
||||
if (Object.keys(data.attributes).length) {
|
||||
elem.attrs = {};
|
||||
|
||||
Object.getOwnPropertyNames(data.attributes).forEach(function(name) {
|
||||
elem.attrs[name] = {
|
||||
name: name,
|
||||
value: data.attributes[name].value,
|
||||
prefix: data.attributes[name].prefix,
|
||||
local: data.attributes[name].local
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
elem = pushToContent(elem);
|
||||
current = elem;
|
||||
stack.push(elem);
|
||||
|
||||
};
|
||||
|
||||
sax.ontext = function(text) {
|
||||
|
||||
pushToContent({
|
||||
text: text
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sax.onclosetag = function() {
|
||||
|
||||
stack.pop();
|
||||
current = stack[stack.length - 1];
|
||||
|
||||
};
|
||||
|
||||
sax.onend = function() {
|
||||
|
||||
deferred.resolve(root);
|
||||
|
||||
};
|
||||
|
||||
sax.write(svg).close();
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
};
|
20
lib/svgo.js
Normal file
20
lib/svgo.js
Normal file
@ -0,0 +1,20 @@
|
||||
var CONFIG = require('./config'),
|
||||
SVG2JS = require('./svg2js'),
|
||||
PLUGINS = require('./plugins'),
|
||||
JS2SVG = require('./js2svg');
|
||||
|
||||
module.exports = function(svg, options) {
|
||||
|
||||
return CONFIG(options)
|
||||
.then(function(config) {
|
||||
|
||||
return SVG2JS(svg, config.saxXMLParser)
|
||||
.then(function(json) {
|
||||
|
||||
return JS2SVG(PLUGINS(json, config.plugins));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
118
lib/tools.js
Normal file
118
lib/tools.js
Normal file
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Adopted from jquery's extend method. Under the terms of MIT License.
|
||||
*
|
||||
* http://code.jquery.com/jquery-1.4.2.js
|
||||
*
|
||||
* Modified by mscdex to use Array.isArray instead of the custom isArray method
|
||||
*/
|
||||
var extend = exports.extend = function() {
|
||||
// copy reference to target object
|
||||
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
|
||||
|
||||
// Handle a deep copy situation
|
||||
if (typeof target === 'boolean') {
|
||||
deep = target;
|
||||
target = arguments[1] || {};
|
||||
// skip the boolean and the target
|
||||
i = 2;
|
||||
}
|
||||
|
||||
// Handle case when target is a string or something (possible in deep copy)
|
||||
if (typeof target !== 'object' && !typeof target === 'function')
|
||||
target = {};
|
||||
|
||||
var isPlainObject = function(obj) {
|
||||
// Must be an Object.
|
||||
// Because of IE, we also have to check the presence of the constructor property.
|
||||
// Make sure that DOM nodes and window objects don't pass through, as well
|
||||
if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval)
|
||||
return false;
|
||||
|
||||
var has_own_constructor = hasOwnProperty.call(obj, 'constructor');
|
||||
var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf');
|
||||
// Not own constructor property must be Object
|
||||
if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
|
||||
return false;
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
|
||||
var key, last_key;
|
||||
for (key in obj)
|
||||
last_key = key;
|
||||
|
||||
return typeof last_key === 'undefined' || hasOwnProperty.call(obj, last_key);
|
||||
};
|
||||
|
||||
|
||||
for (; i < length; i++) {
|
||||
// Only deal with non-null/undefined values
|
||||
if ((options = arguments[i]) !== null) {
|
||||
// Extend the base object
|
||||
for (name in options) {
|
||||
if (!hasOwnProperty.call(options, name))
|
||||
continue;
|
||||
src = target[name];
|
||||
copy = options[name];
|
||||
|
||||
// Prevent never-ending loop
|
||||
if (target === copy)
|
||||
continue;
|
||||
|
||||
// Recurse if we're merging object literal values or arrays
|
||||
if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
|
||||
var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};
|
||||
|
||||
// Never move original objects, clone them
|
||||
target[name] = extend(deep, clone, copy);
|
||||
|
||||
// Don't bring in undefined values
|
||||
} else if (typeof copy !== 'undefined')
|
||||
target[name] = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
};
|
||||
|
||||
exports.inspect = require('eyes').inspector({ maxLength: 99999 });
|
||||
|
||||
exports.flatten = function(array) {
|
||||
var result = [],
|
||||
that = arguments.callee;
|
||||
|
||||
array.forEach(function(item) {
|
||||
Array.prototype.push.apply(
|
||||
result,
|
||||
Array.isArray(item) ? that(item) : [item]
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.intersectAttrs = function(a, b) {
|
||||
var c = {};
|
||||
|
||||
Object.getOwnPropertyNames(a).forEach(function(n) {
|
||||
if (
|
||||
b.hasOwnProperty(n) &&
|
||||
a[n].name === b[n].name &&
|
||||
a[n].value === b[n].value &&
|
||||
a[n].prefix === b[n].prefix &&
|
||||
a[n].local === b[n].local
|
||||
) {
|
||||
c[n] = a[n];
|
||||
}
|
||||
});
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
exports.intersectArrays = function(a, b) {
|
||||
return a.filter(function(n) {
|
||||
return b.indexOf(n) > -1;
|
||||
});
|
||||
};
|
43
package.json
Normal file
43
package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "svgo",
|
||||
"version": "0.0.1",
|
||||
"description": "SVG Optimizer",
|
||||
"keywords": ["svgo", "svg", "optimize", "minify"],
|
||||
"homepage": " ",
|
||||
"bugs": {
|
||||
"url": "https://github.com/deepsweet/svgo/issues",
|
||||
"email": "kir@soulshine.in"
|
||||
},
|
||||
"author": "Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)",
|
||||
"contributors": [
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/deepsweet/svgo.git"
|
||||
},
|
||||
"main": "./lib/svgo.js",
|
||||
"directories": {
|
||||
"bin": "./bin",
|
||||
"lib": "./lib"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "./bin/svgo"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/mocha --reporter min",
|
||||
"test-v": "./node_modules/.bin/mocha --reporter spec"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherit": "*",
|
||||
"q": "0.8",
|
||||
"q-fs": "",
|
||||
"coa": "0.3.x",
|
||||
"sax": "0.4.x",
|
||||
"mocha": "1.4.*",
|
||||
"should": "",
|
||||
"eyes": ""
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
}
|
640
plugins/_collections.js
Normal file
640
plugins/_collections.js
Normal file
@ -0,0 +1,640 @@
|
||||
// http://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
|
||||
exports.editorNamespaces = [
|
||||
'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
|
||||
'http://www.inkscape.org/namespaces/inkscape',
|
||||
'http://ns.adobe.com/AdobeIllustrator/10.0/',
|
||||
'http://ns.adobe.com/Graphs/1.0/',
|
||||
'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
|
||||
'http://ns.adobe.com/Variables/1.0/',
|
||||
'http://ns.adobe.com/SaveForWeb/1.0/',
|
||||
'http://ns.adobe.com/Extensibility/1.0/',
|
||||
'http://ns.adobe.com/Flows/1.0/',
|
||||
'http://ns.adobe.com/ImageReplacement/1.0/',
|
||||
'http://ns.adobe.com/GenericCustomNamespace/1.0/',
|
||||
'http://ns.adobe.com/XPath/1.0/'
|
||||
];
|
||||
|
||||
// http://www.w3.org/TR/SVG/styling.html#SVGStylingProperties
|
||||
exports.stylingProps = [
|
||||
'font',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-weight',,
|
||||
'direction',
|
||||
'letter-spacing',
|
||||
'text-decoration',
|
||||
'unicode-bidi',
|
||||
'word-spacing',
|
||||
'clip',
|
||||
'color',
|
||||
'cursor',
|
||||
'display',
|
||||
'overflow',
|
||||
'visibility',
|
||||
'clip-path',
|
||||
'clip-rule',
|
||||
'mask',
|
||||
'opacity',
|
||||
'enable-background',
|
||||
'filter',
|
||||
'flood-color',
|
||||
'flood-opacity',
|
||||
'lighting-color',
|
||||
'stop-color',
|
||||
'stop-opacity',
|
||||
'pointer-events',
|
||||
'color-interpolation',
|
||||
'color-interpolation-filters',
|
||||
'color-profile',
|
||||
'color-rendering',
|
||||
'fill',
|
||||
'fill-opacity',
|
||||
'fill-rule',
|
||||
'image-rendering',
|
||||
'marker',
|
||||
'marker-end',
|
||||
'marker-mid',
|
||||
'marker-start',
|
||||
'shape-rendering',
|
||||
'stroke',
|
||||
'stroke-dasharray',
|
||||
'stroke-dashoffset',
|
||||
'stroke-linecap',
|
||||
'stroke-linejoin',
|
||||
'stroke-miterlimit',
|
||||
'stroke-opacity',
|
||||
'stroke-width',
|
||||
'text-rendering',
|
||||
'alignment-baseline',
|
||||
'baseline-shift',
|
||||
'dominant-baseline',
|
||||
'glyph-orientation-horizontal',
|
||||
'glyph-orientation-vertical',
|
||||
'kerning',
|
||||
'text-anchor',
|
||||
'writing-mode'
|
||||
];
|
||||
|
||||
exports.defaultValues = {
|
||||
// http://www.w3.org/TR/SVG/attindex.html#RegularAttributes
|
||||
// http://www.w3.org/TR/SVG/attindex.html#PresentationAttributes
|
||||
'accumulate': { 'val': 'none', 'inh': true },
|
||||
'additive': { 'val': 'replace', 'inh': true },
|
||||
'alphabetic': { 'val': '0', 'inh': true },
|
||||
'amplitude': { 'val': '1', 'inh': false },
|
||||
'arabic-form': { 'val': 'initial', 'inh': true },
|
||||
|
||||
// http://www.w3.org/TR/SVG/propidx.html
|
||||
'baseline-shift': { 'val': 'baseline', 'inh': false },
|
||||
'clip': { 'val': 'auto', 'inh': false },
|
||||
'clip-path': { 'val': 'none', 'inh': false },
|
||||
'color-interpolation': { 'val': 'sRGB', 'inh': true },
|
||||
'color-interpolation-filters': { 'val': 'linearRGB', 'inh': true },
|
||||
'color-profile': { 'val': 'auto', 'inh': true },
|
||||
'color-rendering': { 'val': 'auto', 'inh': true },
|
||||
'cursor': { 'val': 'auto', 'inh': true },
|
||||
'direction': { 'val': 'ltr', 'inh': true },
|
||||
'display': { 'val': 'inline', 'inh': false },
|
||||
'dominant-baseline': { 'val': 'auto', 'inh': false },
|
||||
'enable-background': { 'val': 'accumulate', 'inh': false },
|
||||
'fill': { 'val': ['black', '#000000', '#000'], 'inh': true },
|
||||
'fill-opacity': { 'val': '1', 'inh': true },
|
||||
'filter': { 'val': 'none', 'inh': false },
|
||||
'flood-color': { 'val': ['black', '#000000', '#000'], 'inh': false },
|
||||
'flood-opacity': { 'val': '1', 'inh': false },
|
||||
'font-size': { 'val': 'medium', 'inh': true },
|
||||
'font-size-adjust': { 'val': 'none', 'inh': true },
|
||||
'font-stretch': { 'val': 'normal', 'inh': true },
|
||||
'font-style': { 'val': 'normal', 'inh': true },
|
||||
'font-variant': { 'val': 'normal', 'inh': true },
|
||||
'font-weight': { 'val': 'normal', 'inh': true },
|
||||
'glyph-orientation-horizontal': { 'val': '0deg', 'inh': true },
|
||||
'glyph-orientation-vertical': { 'val': 'auto', 'inh': true },
|
||||
'image-rendering': { 'val': 'auto', 'inh': true },
|
||||
'kerning': { 'val': 'auto', 'inh': true },
|
||||
'letter-spacing': { 'val': 'normal', 'inh': true },
|
||||
'lighting-color': { 'val': ['white', '#ffffff', '#fff'], 'inh': false },
|
||||
'marker-end': { 'val': 'none', 'inh': true },
|
||||
'marker-mid': { 'val': 'none', 'inh': true },
|
||||
'marker-start': { 'val': 'none', 'inh': true },
|
||||
'mask': { 'val': 'none', 'inh': false },
|
||||
'opacity': { 'val': '1', 'inh': false },
|
||||
'pointer-events': { 'val': 'visiblePainted', 'inh': true },
|
||||
'shape-rendering': { 'val': 'auto', 'inh': true },
|
||||
'stop-color': { 'val': ['black', '#000000', '#000'], 'inh': false },
|
||||
'stop-opacity': { 'val': '1', 'inh': false },
|
||||
'stroke': { 'val': 'none', 'inh': true },
|
||||
'stroke-dasharray': { 'val': 'none', 'inh': true },
|
||||
'stroke-dashoffset': { 'val': '0', 'inh': true },
|
||||
'stroke-linecap': { 'val': 'butt', 'inh': true },
|
||||
'stroke-linejoin': { 'val': 'miter', 'inh': true },
|
||||
'stroke-miterlimit': { 'val': '4', 'inh': true },
|
||||
'stroke-opacity': { 'val': '1', 'inh': true },
|
||||
'stroke-width': { 'val': '1', 'inh': true },
|
||||
'text-anchor': { 'val': 'start', 'inh': true },
|
||||
'text-decoration': { 'val': 'none', 'inh': false },
|
||||
'text-rendering': { 'val': 'auto', 'inh': true },
|
||||
'unicode-bidi': { 'val': 'normal', 'inh': false },
|
||||
'visibility': { 'val': 'visible', 'inh': true },
|
||||
'word-spacing': { 'val': 'normal', 'inh': true },
|
||||
'writing-mode': { 'val': 'lr-tb', 'inh': true }
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/SVG/intro.html#Definitions
|
||||
var elems = exports.elems = {
|
||||
'animation': ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'],
|
||||
'descriptive': ['desc', 'metadata', 'title'],
|
||||
'shape': ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'],
|
||||
'structural': ['defs', 'g', 'svg', 'symbol', 'use'],
|
||||
'gradient': ['linearGradient', 'radialGradient'],
|
||||
// http://www.w3.org/TR/SVG/intro.html#TermContainerElement
|
||||
'container': ['a', 'defs', 'glyph', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol']
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/SVG/intro.html#Definitions
|
||||
var attrs = exports.attrs = {
|
||||
'animationAddition': ['additive', 'accumulate'],
|
||||
'animationAttributeTarget': ['attributeType', 'attributeName'],
|
||||
'animationEvent': ['onbegin', 'onend', 'onrepeat', 'onload'],
|
||||
'animationTiming': ['begin', 'dur', 'end', 'min', 'max', 'restart', 'repeatCount', 'repeatDur', 'fill'],
|
||||
'animationValue': ['calcMode', 'values', 'keyTimes', 'keySplines', 'from', 'to', 'by'],
|
||||
'conditionalProcessing': ['requiredFeatures', 'requiredExtensions', 'systemLanguage'],
|
||||
'core': ['id', 'xml:base', 'xml:lang', 'xml:space'],
|
||||
'graphicalEvent': ['onfocusin', 'onfocusout', 'onactivate', 'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onload'],
|
||||
'presentation': ['alignment-baseline', 'baseline-shift', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'unicode-bidi', 'visibility', 'word-spacing', 'writing-mode'],
|
||||
'xlink': ['xlink:href', 'xlink:show', 'xlink:actuate', 'xlink:type', 'xlink:role', 'xlink:arcrole', 'xlink:title']
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/SVG/eltindex.html
|
||||
exports.possibles = {
|
||||
'a': {
|
||||
'attrs': [attrs.conditionalProcessing, attrs.core, attrs.graphicalEvent, attrs.presentation, attrs.xlink, 'class', 'style', 'externalResourcesRequired', 'transform', 'target'],
|
||||
'content': [elems.animation, elems.descriptive, elems.shape, elems.structural, elems.gradient, 'a', 'altGlyphDef', 'clipPath', 'color-profile', 'cursor', 'filter', 'font', 'font-face', 'foreignObject', 'image', 'marker', 'mask', 'pattern', 'script', 'style', 'switch', 'text', 'view']
|
||||
},
|
||||
'altGlyph': {
|
||||
'attrs': [attrs.conditionalProcessing, attrs.core, attrs.graphicalEvent, attrs.presentation, attrs.xlink, 'class', 'style', 'externalResourcesRequired', 'x', 'y', 'dx', 'dy', 'glyphRef', 'format', 'rotate'],
|
||||
'content': true
|
||||
},
|
||||
'altGlyphDef': {
|
||||
'attrs': [attrs.core],
|
||||
'content': ['glyphRef']
|
||||
},
|
||||
'altGlyphItem': {
|
||||
'attrs': [attrs.core],
|
||||
'content': ['glyphRef', 'altGlyphItem']
|
||||
},
|
||||
'animate': {
|
||||
'attrs': [attrs.conditionalProcessing, attrs.core, attrs.animationAddition, attrs.animationAttributeTarget, attrs.animationEvent, attrs.animationTiming, attrs.animationValue, attrs.presentation, attrs.xlink, 'externalResourcesRequired'],
|
||||
'content': [elems.descriptive]
|
||||
},
|
||||
'animateColor': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'animateMotion': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'animateTransform': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'circle': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'clipPath': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'color-profile': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'cursor': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'defs': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'desc': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'ellipse': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feBlend': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feColorMatrix': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feComponentTransfer': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feComposite': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feConvolveMatrix': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feDiffuseLighting': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feDisplacementMap': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feDistantLight': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feFlood': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feFuncA': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feFuncB': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feFuncG': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feFuncR': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feGaussianBlur': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feImage': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feMerge': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feMergeNode': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feMorphology': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feOffset': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'fePointLight': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feSpecularLighting': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feSpotLight': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feTile': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'feTurbulence': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'filter': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font-face': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font-face-format': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font-face-name': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font-face-src': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'font-face-uri': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'foreignObject': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'g': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'glyph': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'glyphRef': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'hkern': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'image': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'line': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'linearGradient': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'marker': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'mask': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'metadata': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'missing-glyph': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'mpath': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'path': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'pattern': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'polygon': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'polyline': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'radialGradient': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'rect': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'script': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'set': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'stop': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'style': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'svg': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'switch': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'symbol': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'text': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'textPath': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'title': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'tref': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'tspan': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'use': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'view': {
|
||||
'attrs': [],
|
||||
'content': []
|
||||
},
|
||||
'vkern': []
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords
|
||||
exports.colorsNames = {
|
||||
'aliceblue': '#f0f8ff',
|
||||
'antiquewhite': '#faebd7',
|
||||
'aqua': '#00ffff',
|
||||
'aquamarine': '#7fffd4',
|
||||
'azure': '#f0ffff',
|
||||
'beige': '#f5f5dc',
|
||||
'bisque': '#ffe4c4',
|
||||
'black': '#000000',
|
||||
'blanchedalmond': '#ffebcd',
|
||||
'blue': '#0000ff',
|
||||
'blueviolet': '#8a2be2',
|
||||
'brown': '#a52a2a',
|
||||
'burlywood': '#deb887',
|
||||
'cadetblue': '#5f9ea0',
|
||||
'chartreuse': '#7fff00',
|
||||
'chocolate': '#d2691e',
|
||||
'coral': '#ff7f50',
|
||||
'cornflowerblue': '#6495ed',
|
||||
'cornsilk': '#fff8dc',
|
||||
'crimson': '#dc143c',
|
||||
'cyan': '#00ffff',
|
||||
'darkblue': '#00008b',
|
||||
'darkcyan': '#008b8b',
|
||||
'darkgoldenrod': '#b8860b',
|
||||
'darkgray': '#a9a9a9',
|
||||
'darkgreen': '#006400',
|
||||
'darkkhaki': '#bdb76b',
|
||||
'darkmagenta': '#8b008b',
|
||||
'darkolivegreen': '#556b2f',
|
||||
'darkorange': '#ff8c00',
|
||||
'darkorchid': '#9932cc',
|
||||
'darkred': '#8b0000',
|
||||
'darksalmon': '#e9967a',
|
||||
'darkseagreen': '#8fbc8f',
|
||||
'darkslateblue': '#483d8b',
|
||||
'darkslategray': '#2f4f4f',
|
||||
'darkturquoise': '#00ced1',
|
||||
'darkviolet': '#9400d3',
|
||||
'deeppink': '#ff1493',
|
||||
'deepskyblue': '#00bfff',
|
||||
'dimgray': '#696969',
|
||||
'dodgerblue': '#1e90ff',
|
||||
'firebrick': '#b22222',
|
||||
'floralwhite': '#fffaf0',
|
||||
'forestgreen': '#228b22',
|
||||
'fuchsia': '#ff00ff',
|
||||
'gainsboro': '#dcdcdc',
|
||||
'ghostwhite': '#f8f8ff',
|
||||
'gold': '#ffd700',
|
||||
'goldenrod': '#daa520',
|
||||
'gray': '#808080',
|
||||
'green': '#008000',
|
||||
'greenyellow': '#adff2f',
|
||||
'honeydew': '#f0fff0',
|
||||
'hotpink': '#ff69b4',
|
||||
'indianred': '#cd5c5c',
|
||||
'indigo': '#4b0082',
|
||||
'ivory': '#fffff0',
|
||||
'khaki': '#f0e68c',
|
||||
'lavender': '#e6e6fa',
|
||||
'lavenderblush': '#fff0f5',
|
||||
'lawngreen': '#7cfc00',
|
||||
'lemonchiffon': '#fffacd',
|
||||
'lightblue': '#add8e6',
|
||||
'lightcoral': '#f08080',
|
||||
'lightcyan': '#e0ffff',
|
||||
'lightgoldenrodyellow': '#fafad2',
|
||||
'lightgreen': '#90ee90',
|
||||
'lightgrey': '#d3d3d3',
|
||||
'lightpink': '#ffb6c1',
|
||||
'lightsalmon': '#ffa07a',
|
||||
'lightseagreen': '#20b2aa',
|
||||
'lightskyblue': '#87cefa',
|
||||
'lightslategray': '#778899',
|
||||
'lightsteelblue': '#b0c4de',
|
||||
'lightyellow': '#ffffe0',
|
||||
'lime': '#00ff00',
|
||||
'limegreen': '#32cd32',
|
||||
'linen': '#faf0e6',
|
||||
'magenta': '#ff00ff',
|
||||
'maroon': '#800000',
|
||||
'mediumaquamarine': '#66cdaa',
|
||||
'mediumblue': '#0000cd',
|
||||
'mediumorchid': '#ba55d3',
|
||||
'mediumpurple': '#9370db',
|
||||
'mediumseagreen': '#3cb371',
|
||||
'mediumslateblue': '#7b68ee',
|
||||
'mediumspringgreen': '#00fa9a',
|
||||
'mediumturquoise': '#48d1cc',
|
||||
'mediumvioletred': '#c71585',
|
||||
'midnightblue': '#191970',
|
||||
'mintcream': '#f5fffa',
|
||||
'mistyrose': '#ffe4e1',
|
||||
'moccasin': '#ffe4b5',
|
||||
'navajowhite': '#ffdead',
|
||||
'navy': '#000080',
|
||||
'oldlace': '#fdf5e6',
|
||||
'olive': '#808000',
|
||||
'olivedrab': '#6b8e23',
|
||||
'orange': '#ffa500',
|
||||
'orangered': '#ff4500',
|
||||
'orchid': '#da70d6',
|
||||
'palegoldenrod': '#eee8aa',
|
||||
'palegreen': '#98fb98',
|
||||
'paleturquoise': '#afeeee',
|
||||
'palevioletred': '#db7093',
|
||||
'papayawhip': '#ffefd5',
|
||||
'peachpuff': '#ffdab9',
|
||||
'peru': '#cd853f',
|
||||
'pink': '#ffc0cb',
|
||||
'plum': '#dda0dd',
|
||||
'powderblue': '#b0e0e6',
|
||||
'purple': '#800080',
|
||||
'red': '#ff0000',
|
||||
'rosybrown': '#bc8f8f',
|
||||
'royalblue': '#4169e1',
|
||||
'saddlebrown': '#8b4513',
|
||||
'salmon': '#fa8072',
|
||||
'sandybrown': '#f4a460',
|
||||
'seagreen': '#2e8b57',
|
||||
'seashell': '#fff5ee',
|
||||
'sienna': '#a0522d',
|
||||
'silver': '#c0c0c0',
|
||||
'skyblue': '#87ceeb',
|
||||
'slateblue': '#6a5acd',
|
||||
'slategray': '#708090',
|
||||
'snow': '#fffafa',
|
||||
'springgreen': '#00ff7f',
|
||||
'steelblue': '#4682b4',
|
||||
'tan': '#d2b48c',
|
||||
'teal': '#008080',
|
||||
'thistle': '#d8bfd8',
|
||||
'tomato': '#ff6347',
|
||||
'turquoise': '#40e0d0',
|
||||
'violet': '#ee82ee',
|
||||
'wheat': '#f5deb3',
|
||||
'white': '#ffffff',
|
||||
'whitesmoke': '#f5f5f5',
|
||||
'yellow': '#ffff00',
|
||||
'yellowgreen': '#9acd32'
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/SVG/single-page.html#types-DataTypeColor
|
||||
exports.colorsProps = [
|
||||
'color', 'fill', 'stroke', 'stop-color', 'flood-color', 'lighting-color'
|
||||
];
|
35
plugins/cleanupAttrs.js
Normal file
35
plugins/cleanupAttrs.js
Normal file
@ -0,0 +1,35 @@
|
||||
var regNewlines = /\n/g,
|
||||
regSpaces = /\s{2,}/g;
|
||||
|
||||
/**
|
||||
* Cleanup attributes values from newlines, trailing and repeating spaces.
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.cleanupAttrs = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr()) {
|
||||
|
||||
item.eachAttr(function(attr) {
|
||||
|
||||
if (params.newlines) {
|
||||
attr.value = attr.value.replace(regNewlines, '');
|
||||
}
|
||||
|
||||
if (params.trim) {
|
||||
attr.value = attr.value.trim();
|
||||
}
|
||||
|
||||
if (params.spaces) {
|
||||
attr.value = attr.value.replace(regSpaces, '');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
62
plugins/collapseGroups.js
Normal file
62
plugins/collapseGroups.js
Normal file
@ -0,0 +1,62 @@
|
||||
var flatten = require('../lib/tools').flatten;
|
||||
|
||||
/*
|
||||
* Collapse useless groups.
|
||||
*
|
||||
* @example
|
||||
* <g>
|
||||
* <g attr1="val1">
|
||||
* <path d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <g>
|
||||
* <g>
|
||||
* <path attr1="val1" d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <path d="..."/>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.collapseGroups = function(item, params) {
|
||||
|
||||
// non-empty elements
|
||||
if (item.isElem() && !item.isEmpty()) {
|
||||
|
||||
item.content.forEach(function(g, i) {
|
||||
|
||||
// non-empty groups
|
||||
if (g.isElem('g') && !g.isEmpty()) {
|
||||
|
||||
// move group attibutes to the single content element
|
||||
if (g.hasAttr() && g.content.length === 1) {
|
||||
var inner = g.content[0];
|
||||
|
||||
if (inner.isElem()) {
|
||||
g.eachAttr(function(attr) {
|
||||
if (!inner.hasAttr(attr.name)) {
|
||||
inner.addAttr(attr);
|
||||
}
|
||||
g.removeAttr(attr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// collapse groups without attributes
|
||||
if (!g.hasAttr()) {
|
||||
item.content.splice(i, 1, g.content);
|
||||
item.content = flatten(item.content);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
88
plugins/convertColors.js
Normal file
88
plugins/convertColors.js
Normal file
@ -0,0 +1,88 @@
|
||||
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.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/types.html#DataTypeColor
|
||||
* @see http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords
|
||||
*
|
||||
* @example
|
||||
* Convert color name keyword to long hex:
|
||||
* fuchsia ➡ #ff00ff
|
||||
*
|
||||
* Convert rgb() to long hex:
|
||||
* rgb(255, 0, 255) ➡ #ff00ff
|
||||
* rgb(50%, 100, 100%) ➡ #7f64ff
|
||||
*
|
||||
* Convert long hex to short hex:
|
||||
* #aabbcc ➡ #abc
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.convertColors = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr()) {
|
||||
|
||||
item.eachAttr(function(attr) {
|
||||
|
||||
if (collections.colorsProps.indexOf(attr.name) > -1) {
|
||||
|
||||
var val = attr.value.toLowerCase(),
|
||||
tmp = val,
|
||||
match;
|
||||
|
||||
// Convert color name keyword to long hex
|
||||
if (params.names2hex && val in collections.colorsNames) {
|
||||
val = collections.colorsNames[val];
|
||||
}
|
||||
|
||||
// Convert rgb() to long hex
|
||||
if (params.rgb2hex && (match = val.match(regRGB))) {
|
||||
match = match.slice(1, 4).map(function(m) {
|
||||
if (m.indexOf('%') > -1) {
|
||||
m = Math.round(parseFloat(m) * 2.55);
|
||||
}
|
||||
|
||||
return +m;
|
||||
});
|
||||
|
||||
val = rgb2hex(match);
|
||||
}
|
||||
|
||||
// Convert long hex to short hex
|
||||
if (params.shorthex && (match = val.match(regHEX))) {
|
||||
val = '#' + match[0][1] + match[0][3] + match[0][5];
|
||||
}
|
||||
|
||||
if (tmp !== val) attr.value = val;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
70
plugins/convertStyleToAttrs.js
Normal file
70
plugins/convertStyleToAttrs.js
Normal file
@ -0,0 +1,70 @@
|
||||
var extend = require('../lib/tools').extend,
|
||||
stylingProps = require('./_collections').stylingProps;
|
||||
|
||||
/**
|
||||
* Convert style in attributes.
|
||||
*
|
||||
* @example
|
||||
* <g style="fill:#000; color: #fff;">
|
||||
* ⬇
|
||||
* <g fill="#000" color="#fff">
|
||||
*
|
||||
* @example
|
||||
* <g style="fill:#000; color: #fff; -webkit-blah: blah">
|
||||
* ⬇
|
||||
* <g fill="#000" color="#fff" slyle="-webkit-blah: blah">
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.convertStyleToAttrs = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr('style')) {
|
||||
// ['opacity: 1', 'color: #000']
|
||||
var styles = item.attr('style').value.split(';').filter(function(style) {
|
||||
return style;
|
||||
}),
|
||||
attrs = {};
|
||||
|
||||
if (styles.length) {
|
||||
|
||||
styles = styles.filter(function(style) {
|
||||
if (style) {
|
||||
// ['opacity', 1]
|
||||
style = style.split(':');
|
||||
|
||||
var prop = style[0].trim(),
|
||||
val = style[1].replace(/^[\'\"](.+)[\'\"]$/, "$1").trim();
|
||||
|
||||
if (stylingProps.indexOf(prop) > -1) {
|
||||
|
||||
attrs[prop] = {
|
||||
name: prop,
|
||||
value: val,
|
||||
local: prop,
|
||||
prefix: ''
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
extend(item.attrs, attrs);
|
||||
|
||||
if (styles.length) {
|
||||
item.attr('style').value = styles.join(';');
|
||||
} else {
|
||||
item.removeAttr('style');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
57
plugins/moveElemsAttrsToGroup.js
Normal file
57
plugins/moveElemsAttrsToGroup.js
Normal file
@ -0,0 +1,57 @@
|
||||
var intersectAttrs = require('../lib/tools').intersectAttrs;
|
||||
|
||||
/**
|
||||
* Collapse content's intersected attributes to the existing group wrapper.
|
||||
*
|
||||
* @example
|
||||
* <g attr1="val1">
|
||||
* <g attr2="val2">
|
||||
* text
|
||||
* </g>
|
||||
* <circle attr2="val2" attr3="val3"/>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <g attr1="val1" attr2="val2">
|
||||
* <g>
|
||||
* text
|
||||
* </g>
|
||||
* <circle attr3="val3"/>
|
||||
* </g>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.moveElemsAttrsToGroup = function(item, params) {
|
||||
|
||||
if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) {
|
||||
|
||||
var intersection = {},
|
||||
every = item.content.every(function(g) {
|
||||
if (g.isElem() && g.hasAttr()) {
|
||||
if (!Object.keys(intersection).length) {
|
||||
intersection = g.attrs;
|
||||
} else {
|
||||
intersection = intersectAttrs(intersection, g.attrs)
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (every && Object.keys(intersection).length) {
|
||||
|
||||
item.content.forEach(function(g) {
|
||||
for (var name in intersection) {
|
||||
g.removeAttr(name);
|
||||
item.addAttr(intersection[name]);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
18
plugins/removeComments.js
Normal file
18
plugins/removeComments.js
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Remove comments.
|
||||
*
|
||||
* @example
|
||||
* <!-- Generator: Adobe Illustrator 15.0.0, SVG Export
|
||||
* Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeComments = function(item, params) {
|
||||
|
||||
return !item.isComment();
|
||||
|
||||
};
|
26
plugins/removeDefaultPx.js
Normal file
26
plugins/removeDefaultPx.js
Normal file
@ -0,0 +1,26 @@
|
||||
var regValPx = /^(\d+)px$/;
|
||||
|
||||
/**
|
||||
* Remove default "px" unit from attributes values.
|
||||
*
|
||||
* "One px unit is defined to be equal to one user unit.
|
||||
* Thus, a length of 5px is the same as a length of 5"
|
||||
* http://www.w3.org/TR/SVG/coords.html#Units
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeDefaultPx = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr()) {
|
||||
|
||||
item.eachAttr(function(attr) {
|
||||
attr.value = attr.value.replace(regValPx, "$1");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
25
plugins/removeDoctype.js
Normal file
25
plugins/removeDoctype.js
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Remove DOCTYPE declaration.
|
||||
*
|
||||
* "Unfortunately the SVG DTDs are a source of so many
|
||||
* issues that the SVG WG has decided not to write one
|
||||
* for the upcoming SVG 1.2 standard. In fact SVG WG
|
||||
* members are even telling people not to use a DOCTYPE
|
||||
* declaration in SVG 1.0 and 1.1 documents"
|
||||
* https://jwatt.org/svg/authoring/#doctype-declaration
|
||||
*
|
||||
* @example
|
||||
* <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
* q"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeDoctype = function(item, params) {
|
||||
|
||||
return !item.isDoctype();
|
||||
|
||||
};
|
49
plugins/removeEditorsNSData.js
Normal file
49
plugins/removeEditorsNSData.js
Normal file
@ -0,0 +1,49 @@
|
||||
var editorNamespaces = require('./_collections').editorNamespaces,
|
||||
prefixes = [];
|
||||
|
||||
/**
|
||||
* Remove editors namespaces, elements and attributes.
|
||||
*
|
||||
* @example
|
||||
* <svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
|
||||
* <sodipodi:namedview/>
|
||||
* <path sodipodi:nodetypes="cccc"/>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeEditorsNSData = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr()) {
|
||||
|
||||
if (item.isElem('svg')) {
|
||||
|
||||
item.eachAttr(function(attr) {
|
||||
if (attr.prefix === 'xmlns' && editorNamespaces.indexOf(attr.value) > -1) {
|
||||
prefixes.push(attr.local);
|
||||
|
||||
// <svg xmlns:sodipodi="">
|
||||
item.removeAttr(attr.name);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// <sodipodi:*>
|
||||
if (prefixes.indexOf(item.prefix) > -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <* sodipodi:*="">
|
||||
item.eachAttr(function(attr) {
|
||||
if (prefixes.indexOf(attr.prefix) > -1) {
|
||||
item.removeAttr(attr.name);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
22
plugins/removeEmptyAttrs.js
Normal file
22
plugins/removeEmptyAttrs.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Remove attributes with empty values.
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeEmptyAttrs = function(item, params) {
|
||||
|
||||
if (item.isElem() && item.hasAttr()) {
|
||||
|
||||
item.eachAttr(function(attr) {
|
||||
if (attr.value === '') {
|
||||
item.removeAttr(attr.name);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
24
plugins/removeEmptyContainers.js
Normal file
24
plugins/removeEmptyContainers.js
Normal file
@ -0,0 +1,24 @@
|
||||
var container = require('./_collections').elems.container;
|
||||
|
||||
/**
|
||||
* Remove empty containers.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/intro.html#TermContainerElement
|
||||
*
|
||||
* @example
|
||||
* <defs/>
|
||||
*
|
||||
* @example
|
||||
* <g><marker><a/></marker></g>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeEmptyContainers = function(item, params) {
|
||||
|
||||
return !(item.isElem(container) && item.isEmpty());
|
||||
|
||||
};
|
45
plugins/removeEmptyText.js
Normal file
45
plugins/removeEmptyText.js
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Remove empty Text elements.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/text.html
|
||||
*
|
||||
* @example
|
||||
* Remove empty text element:
|
||||
* <text/>
|
||||
*
|
||||
* Remove empty tspan element:
|
||||
* <tspan/>
|
||||
*
|
||||
* Remove tref with empty xlink:href attribute:
|
||||
* <tref xlink:href=""/>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeEmptyText = function(item, params) {
|
||||
|
||||
// Remove empty text element
|
||||
if (
|
||||
params.text &&
|
||||
item.isElem('text') &&
|
||||
item.isEmpty()
|
||||
) return false;
|
||||
|
||||
// Remove empty tspan element
|
||||
if (
|
||||
params.tspan &&
|
||||
item.isElem('tspan') &&
|
||||
item.isEmpty()
|
||||
) return false;
|
||||
|
||||
// Remove tref with empty xlink:href attribute
|
||||
if (
|
||||
params.tref &&
|
||||
item.isElem('tref') &&
|
||||
!item.hasAttr('xlink:href')
|
||||
) return false;
|
||||
|
||||
};
|
186
plugins/removeHiddenElems.js
Normal file
186
plugins/removeHiddenElems.js
Normal file
@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Remove hidden elements with disabled rendering:
|
||||
* - display="none"
|
||||
* - opacity="0"
|
||||
* - circle with zero radius
|
||||
* - ellipse with zero x-axis or y-axis radius
|
||||
* - rectangle with zero width or height
|
||||
* - pattern with zero width or height
|
||||
* - image with zero width or height
|
||||
* - path with empty data
|
||||
* - polyline with empty points
|
||||
* - polygon with empty points
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeHiddenElems = function(item, params) {
|
||||
|
||||
if (item.isElem()) {
|
||||
|
||||
// display="none"
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/painting.html#DisplayProperty
|
||||
// "A value of display: none indicates that the given element
|
||||
// and its children shall not be rendered directly"
|
||||
if (
|
||||
params.displayNone &&
|
||||
item.hasAttr('display', 'none')
|
||||
) return false;
|
||||
|
||||
// opacity="0"
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/masking.html#ObjectAndGroupOpacityProperties
|
||||
if (
|
||||
params.opacity0 &&
|
||||
item.hasAttr('opacity', '0')
|
||||
) return false;
|
||||
|
||||
// Circles with zero radius
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#CircleElementRAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <circle r="0">
|
||||
if (
|
||||
params.circleR0 &&
|
||||
item.isElem('circle') &&
|
||||
item.hasAttr('r', '0')
|
||||
) return false;
|
||||
|
||||
// Ellipse with zero x-axis radius
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRXAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse rx="0">
|
||||
if (
|
||||
params.ellipseRX0 &&
|
||||
item.isElem('ellipse') &&
|
||||
item.hasAttr('rx', '0')
|
||||
) return false;
|
||||
|
||||
// Ellipse with zero y-axis radius
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRYAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse ry="0">
|
||||
if (
|
||||
params.ellipseRY0 &&
|
||||
item.isElem('ellipse') &&
|
||||
item.hasAttr('ry', '0')
|
||||
) return false;
|
||||
|
||||
// Rectangle with zero width
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#RectElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect width="0">
|
||||
if (
|
||||
params.rectWidth0 &&
|
||||
item.isElem('rect') &&
|
||||
item.hasAttr('width', '0')
|
||||
) return false;
|
||||
|
||||
// Rectangle with zero height
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#RectElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect height="0">
|
||||
if (
|
||||
params.rectHeight0 &&
|
||||
params.rectWidth0 &&
|
||||
item.isElem('rect') &&
|
||||
item.hasAttr('height', '0')
|
||||
) return false;
|
||||
|
||||
// Pattern with zero width
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/pservers.html#PatternElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern width="0">
|
||||
if (
|
||||
params.patternWidth0 &&
|
||||
item.isElem('pattern') &&
|
||||
item.hasAttr('width', '0')
|
||||
) return false;
|
||||
|
||||
// Pattern with zero height
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/pservers.html#PatternElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern height="0">
|
||||
if (
|
||||
params.patternHeight0 &&
|
||||
item.isElem('pattern') &&
|
||||
item.hasAttr('height', '0')
|
||||
) return false;
|
||||
|
||||
// Image with zero width
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image width="0">
|
||||
if (
|
||||
params.imageWidth0 &&
|
||||
item.isElem('image') &&
|
||||
item.hasAttr('width', '0')
|
||||
) return false;
|
||||
|
||||
// Image with zero height
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/struct.html#ImageElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image height="0">
|
||||
if (
|
||||
params.imageHeight0 &&
|
||||
item.isElem('image') &&
|
||||
item.hasAttr('height', '0')
|
||||
) return false;
|
||||
|
||||
// Path with empty data
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/paths.html#DAttribute
|
||||
//
|
||||
// <path d=""/>
|
||||
if (
|
||||
params.pathEmptyD &&
|
||||
item.isElem('path') &&
|
||||
!item.hasAttr('d')
|
||||
) return false;
|
||||
|
||||
// Polyline with empty points
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#PolylineElementPointsAttribute
|
||||
//
|
||||
// <polyline points="">
|
||||
if (
|
||||
params.polylineEmptyPoints &&
|
||||
item.isElem('polyline') &&
|
||||
!item.hasAttr('points')
|
||||
) return false;
|
||||
|
||||
// Polygon with empty points
|
||||
//
|
||||
// http://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
|
||||
//
|
||||
// <polygon points="">
|
||||
if (
|
||||
params.polygonEmptyPoints &&
|
||||
item.isElem('polygon') &&
|
||||
!item.hasAttr('points')
|
||||
) return false;
|
||||
|
||||
}
|
||||
|
||||
};
|
16
plugins/removeMetadata.js
Normal file
16
plugins/removeMetadata.js
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Remove <metadata>.
|
||||
*
|
||||
* http://www.w3.org/TR/SVG/metadata.html
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeMetadata = function(item, params) {
|
||||
|
||||
return !item.isElem('metadata');
|
||||
|
||||
};
|
48
plugins/removeSVGAttrs.js
Normal file
48
plugins/removeSVGAttrs.js
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Remove some useless svg element attributes.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/struct.html#SVGElement
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeSVGAttrs = function(item, params) {
|
||||
|
||||
if (item.isElem('svg') && item.hasAttr()) {
|
||||
|
||||
/**
|
||||
* Remove id attribute.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/struct.html#IDAttribute
|
||||
*
|
||||
* @example
|
||||
* <svg id="svg49">
|
||||
*/
|
||||
if (params.id) item.removeAttr('id');
|
||||
|
||||
/**
|
||||
* Remove version attribute.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/struct.html#SVGElementVersionAttribute
|
||||
*
|
||||
* @example
|
||||
* <svg version="1.1">
|
||||
*/
|
||||
if (params.version) item.removeAttr('version');
|
||||
|
||||
/**
|
||||
* Remove xnl:space attribute.
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG/struct.html#XMLSpaceAttribute
|
||||
*
|
||||
* @example
|
||||
* <svg xml:space="preserve">
|
||||
*/
|
||||
if (params.xmlspace) item.removeAttr('xml:space');
|
||||
|
||||
}
|
||||
|
||||
};
|
17
plugins/removeXMLProcInst.js
Normal file
17
plugins/removeXMLProcInst.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Remove XML Processing Instruction.
|
||||
*
|
||||
* @example
|
||||
* <?xml version="1.0" encoding="utf-8"?>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @param {Object} params plugin params
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.removeXMLProcInst = function(item, params) {
|
||||
|
||||
return !item.isProcInst();
|
||||
|
||||
};
|
13
test/config.cfg
Normal file
13
test/config.cfg
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"plugins": {
|
||||
"directPass": [
|
||||
{
|
||||
"name": "removeDoctype",
|
||||
"active": false
|
||||
},{
|
||||
"name": "myTestPlugin",
|
||||
"active": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
113
test/config.js
Normal file
113
test/config.js
Normal file
@ -0,0 +1,113 @@
|
||||
var should = require('should'),
|
||||
config = require('../lib/config');
|
||||
|
||||
describe('config', function() {
|
||||
|
||||
describe('default config', function() {
|
||||
|
||||
var result;
|
||||
|
||||
before(function(done) {
|
||||
config().then(function(data) {
|
||||
result = data;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('result should exists', function() {
|
||||
result.should.exist;
|
||||
});
|
||||
|
||||
it('result should be an instance of Object', function() {
|
||||
result.should.be.an.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('result should have property "saxXMLParser" with instance of Object', function() {
|
||||
result.should.have.property('saxXMLParser').with.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('result should have property "plugins" with instance of Object', function() {
|
||||
result.should.have.property('plugins').with.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('plugins should have property "directPass" with instance of Array', function() {
|
||||
result.plugins.should.have.property('directPass').with.instanceOf(Array);
|
||||
});
|
||||
|
||||
it('directPass should include "removeDoctype" plugin with default params', function() {
|
||||
result.plugins.directPass.should.includeEql({
|
||||
name: 'removeDoctype',
|
||||
active: true
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('extending default config with object', function() {
|
||||
|
||||
var myConfig = {
|
||||
config: {
|
||||
plugins: {
|
||||
directPass: [
|
||||
{ name: 'removeDoctype', active: false },
|
||||
{ name: 'myTestPlugin', active: true }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
result;
|
||||
|
||||
before(function(done) {
|
||||
config(myConfig).then(function(data) {
|
||||
result = data;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('directPass should include extended "removeDoctype" plugin', function() {
|
||||
result.plugins.directPass.should.includeEql({
|
||||
name: 'removeDoctype',
|
||||
active: false
|
||||
});
|
||||
});
|
||||
|
||||
it('directPass should include new "myTestPlugin" plugin', function() {
|
||||
result.plugins.directPass.should.includeEql({
|
||||
name: 'myTestPlugin',
|
||||
active: true
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('extending default config with file', function() {
|
||||
|
||||
var myConfig = {
|
||||
config: './test/config.cfg'
|
||||
},
|
||||
result;
|
||||
|
||||
before(function(done) {
|
||||
config(myConfig).then(function(data) {
|
||||
result = data;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('directPass should include extended "removeDoctype" plugin', function() {
|
||||
result.plugins.directPass.should.includeEql({
|
||||
name: 'removeDoctype',
|
||||
active: false
|
||||
});
|
||||
});
|
||||
|
||||
it('directPass should include new "myTestPlugin" plugin', function() {
|
||||
result.plugins.directPass.should.includeEql({
|
||||
name: 'myTestPlugin',
|
||||
active: true
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
180
test/svg2js.js
Normal file
180
test/svg2js.js
Normal file
@ -0,0 +1,180 @@
|
||||
var should = require('should'),
|
||||
svg2js = require('../lib/svg2js');
|
||||
|
||||
describe('svg2json\n\n | <svg xmlns="http://www.w3.org/2000/svg">\n | <g>123</g>\n | </svg>\n', function() {
|
||||
|
||||
var svg = '<svg xmlns="http://www.w3.org/2000/svg"><g/></svg>',
|
||||
result;
|
||||
|
||||
before(function(done) {
|
||||
svg2js(svg).then(function(data) {
|
||||
result = data;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parser', function() {
|
||||
|
||||
it('result should exist', function() {
|
||||
result.should.exist;
|
||||
});
|
||||
|
||||
it('result should be an instance of Object', function() {
|
||||
result.content.should.be.an.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('result should have property "content" with instance of Array', function() {
|
||||
result.should.have.property('content').with.instanceOf(Array);
|
||||
});
|
||||
|
||||
it('content should have length 1', function() {
|
||||
result.content.should.have.length(1);
|
||||
});
|
||||
|
||||
it('content[0] should have property "elem" with value "svg"', function() {
|
||||
result.content[0].should.have.property('elem', 'svg');
|
||||
});
|
||||
|
||||
it('svg should have properties "prefix" and "local', function() {
|
||||
result.content[0].should.have.property('prefix');
|
||||
result.content[0].should.have.property('local');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attributes', function() {
|
||||
|
||||
it('svg should have property "attrs" with instance of Object', function() {
|
||||
result.content[0].should.have.property('attrs').with.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('svg.attrs should have property "xmlns" with instance of Object', function() {
|
||||
result.content[0].attrs.should.have.property('xmlns').with.instanceOf(Object);
|
||||
});
|
||||
|
||||
it('svg.attrs.xmlns should have properties "name", "value", "prefix", "local" and "uri"', function() {
|
||||
result.content[0].attrs.xmlns.should.have.property('name');
|
||||
result.content[0].attrs.xmlns.should.have.property('prefix');
|
||||
result.content[0].attrs.xmlns.should.have.property('local');
|
||||
});
|
||||
|
||||
it('svg.attrs.xmlns.name shoud be equal "xmlns"', function() {
|
||||
result.content[0].attrs.xmlns.name.should.equal('xmlns');
|
||||
});
|
||||
|
||||
it('svg.attrs.xmlns.value shoud be equal "http://www.w3.org/2000/svg"', function() {
|
||||
result.content[0].attrs.xmlns.value.should.equal('http://www.w3.org/2000/svg');
|
||||
});
|
||||
|
||||
it('svg.attrs.xmlns.prefix shoud be equal "xmlns"', function() {
|
||||
result.content[0].attrs.xmlns.prefix.should.equal('xmlns');
|
||||
});
|
||||
|
||||
it('svg.attrs.xmlns.local shoud be empty ""', function() {
|
||||
result.content[0].attrs.xmlns.local.should.be.empty;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('content', function() {
|
||||
|
||||
it('svg should have property "content" with instance of Array', function() {
|
||||
result.content[0].should.have.property('content').with.instanceOf(Array);
|
||||
});
|
||||
|
||||
it('svg.content should have length 1', function() {
|
||||
result.content[0].content.should.have.length(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('API', function() {
|
||||
|
||||
describe('isElem()', function() {
|
||||
|
||||
it('svg should have property "isElem" with instance of Function', function() {
|
||||
result.content[0].should.have.property('isElem').with.instanceOf(Function);
|
||||
});
|
||||
|
||||
it('svg.sNode() should be true', function() {
|
||||
result.content[0].isElem().should.be.true;
|
||||
});
|
||||
|
||||
it('svg.isElem("svg") should be true', function() {
|
||||
result.content[0].isElem('svg').should.be.true;
|
||||
});
|
||||
|
||||
it('svg.isElem("trololo") should be false', function() {
|
||||
result.content[0].isElem('123').should.be.false;
|
||||
});
|
||||
|
||||
it('svg.isElem(["svg", "trololo"]) should be true', function() {
|
||||
result.content[0].isElem(['svg', 'trololo']).should.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hasAttr()', function() {
|
||||
|
||||
it('svg should have property "hasAttr" with instance of Function', function() {
|
||||
result.content[0].should.have.property('hasAttr').with.instanceOf(Function);
|
||||
});
|
||||
|
||||
it('svg.hasAttr() should be true', function() {
|
||||
result.content[0].hasAttr().should.be.true;
|
||||
});
|
||||
|
||||
it('svg.hasAttr("xmlns") should be true', function() {
|
||||
result.content[0].hasAttr('xmlns').should.be.true;
|
||||
});
|
||||
|
||||
it('svg.hasAttr("xmlns", "http://www.w3.org/2000/svg") should be true', function() {
|
||||
result.content[0].hasAttr('xmlns', 'http://www.w3.org/2000/svg').should.be.true;
|
||||
});
|
||||
|
||||
it('svg.hasAttr("xmlns", "trololo") should be false', function() {
|
||||
result.content[0].hasAttr('xmlns', 'trololo').should.be.false;
|
||||
});
|
||||
|
||||
it('svg.hasAttr("trololo") should be false', function() {
|
||||
result.content[0].hasAttr('trololo').should.be.false;
|
||||
});
|
||||
|
||||
it('svg.hasAttr("trololo", "ololo") should be false', function() {
|
||||
result.content[0].hasAttr('trololo', 'ololo').should.be.false;
|
||||
});
|
||||
|
||||
it('svg.g.hasAttr() should be false', function() {
|
||||
result.content[0].content[0].hasAttr().should.be.false;
|
||||
});
|
||||
|
||||
it('svg.g.hasAttr("trololo") should be false', function() {
|
||||
result.content[0].content[0].hasAttr('trololo').should.be.false;
|
||||
});
|
||||
|
||||
it('svg.g.hasAttr("trololo", "ololo") should be false', function() {
|
||||
result.content[0].content[0].hasAttr('trololo', 'ololo').should.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isEmpty()', function() {
|
||||
|
||||
it('svg should have property "isEmpty" with instance of Function', function() {
|
||||
result.content[0].should.have.property('isEmpty').with.instanceOf(Function);
|
||||
});
|
||||
|
||||
it('svg.isEmpty() should be false', function() {
|
||||
result.content[0].isEmpty().should.be.false;
|
||||
});
|
||||
|
||||
it('svg.g.isEmpty() should be true', function() {
|
||||
result.content[0].content[0].isEmpty().should.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
Reference in New Issue
Block a user