1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-29 20:21:14 +03:00

Convert element children to xast

Ref https://github.com/syntax-tree/xast

Renamed content to children to match xast spec.
This commit is contained in:
Bogdan Chadkin
2021-03-11 01:49:00 +03:00
parent b9a682443c
commit 5314c12c99
28 changed files with 249 additions and 235 deletions

View File

@ -189,8 +189,8 @@ function csstreeToStyleDeclaration(declaration) {
* @return {string} CSS string or empty array if no styles are set * @return {string} CSS string or empty array if no styles are set
*/ */
function getCssStr(elem) { function getCssStr(elem) {
if (elem.content[0].type === 'text' || elem.content[0].type === 'cdata') { if (elem.children[0].type === 'text' || elem.children[0].type === 'cdata') {
return elem.content[0].value; return elem.children[0].value;
} }
return ''; return '';
} }
@ -203,9 +203,9 @@ function getCssStr(elem) {
* @return {string} reference to field with CSS * @return {string} reference to field with CSS
*/ */
function setCssStr(elem, css) { function setCssStr(elem, css) {
if (elem.content[0].type === 'text' || elem.content[0].type === 'cdata') { if (elem.children[0].type === 'text' || elem.children[0].type === 'cdata') {
elem.content[0].value = css; elem.children[0].value = css;
return elem.content[0].value; return elem.children[0].value;
} }
return css; return css;
} }

View File

@ -151,7 +151,7 @@ const computeStyle = (node) => {
styleNode.attributes.type === '' || styleNode.attributes.type === '' ||
styleNode.attributes.type === 'text/css' styleNode.attributes.type === 'text/css'
) { ) {
const children = styleNode.content || []; const children = styleNode.children;
for (const child of children) { for (const child of children) {
if (child.type === 'text' || child.type === 'cdata') { if (child.type === 'text' || child.type === 'cdata') {
stylesheet.push(...parseStylesheet(child.value, dynamic)); stylesheet.push(...parseStylesheet(child.value, dynamic));

View File

@ -23,7 +23,7 @@ const getAttributeValue = (elem, name) => {
}; };
const getChildren = (node) => { const getChildren = (node) => {
return node.content || []; return node.children || [];
}; };
const getName = (elemAst) => { const getName = (elemAst) => {
@ -40,8 +40,8 @@ const getSiblings = (elem) => {
}; };
const getText = (node) => { const getText = (node) => {
if (node.content[0].type === 'text' && node.content[0].type === 'cdata') { if (node.children[0].type === 'text' && node.children[0].type === 'cdata') {
return node.content[0].value; return node.children[0].value;
} }
return ''; return '';
}; };

View File

@ -93,29 +93,27 @@ function encodeEntity(char) {
JS2SVG.prototype.convert = function (data) { JS2SVG.prototype.convert = function (data) {
var svg = ''; var svg = '';
if (data.content) { this.indentLevel++;
this.indentLevel++;
data.content.forEach(function (item) { for (const item of data.children) {
if (item.type === 'element') { if (item.type === 'element') {
svg += this.createElem(item); svg += this.createElem(item);
} }
if (item.type === 'text') { if (item.type === 'text') {
svg += this.createText(item); svg += this.createText(item);
} }
if (item.type === 'doctype') { if (item.type === 'doctype') {
svg += this.createDoctype(item); svg += this.createDoctype(item);
} }
if (item.type === 'instruction') { if (item.type === 'instruction') {
svg += this.createProcInst(item); svg += this.createProcInst(item);
} }
if (item.type === 'comment') { if (item.type === 'comment') {
svg += this.createComment(item); svg += this.createComment(item);
} }
if (item.type === 'cdata') { if (item.type === 'cdata') {
svg += this.createCDATA(item); svg += this.createCDATA(item);
} }
}, this);
} }
this.indentLevel--; this.indentLevel--;
@ -211,7 +209,7 @@ JS2SVG.prototype.createElem = function (data) {
} }
// empty element and short tag // empty element and short tag
if (data.isEmpty()) { if (data.children.length === 0) {
if (this.config.useShortTags) { if (this.config.useShortTags) {
return ( return (
this.createIndent() + this.createIndent() +

View File

@ -30,7 +30,7 @@ JSAPI.prototype.clone = function () {
var nodeData = {}; var nodeData = {};
Object.keys(node).forEach(function (key) { Object.keys(node).forEach(function (key) {
if (key !== 'class' && key !== 'style' && key !== 'content') { if (key !== 'class' && key !== 'style' && key !== 'children') {
nodeData[key] = node[key]; nodeData[key] = node[key];
} }
}); });
@ -49,8 +49,8 @@ JSAPI.prototype.clone = function () {
if (node.style) { if (node.style) {
clonedNode.style = node.style.clone(clonedNode); clonedNode.style = node.style.clone(clonedNode);
} }
if (node.content) { if (node.children) {
clonedNode.content = node.content.map(function (childNode) { clonedNode.children = node.children.map(function (childNode) {
var clonedChild = childNode.clone(); var clonedChild = childNode.clone();
clonedChild.parentNode = clonedNode; clonedChild.parentNode = clonedNode;
return clonedChild; return clonedChild;
@ -98,7 +98,7 @@ JSAPI.prototype.renameElem = function (name) {
* @return {Boolean} * @return {Boolean}
*/ */
JSAPI.prototype.isEmpty = function () { JSAPI.prototype.isEmpty = function () {
return !this.content || !this.content.length; return !this.children || !this.children.length;
}; };
/** /**
@ -116,11 +116,11 @@ JSAPI.prototype.closestElem = function (elemName) {
}; };
/** /**
* Changes content by removing elements and/or adding new elements. * Changes children by removing elements and/or adding new elements.
* *
* @param {Number} start Index at which to start changing the content. * @param {Number} start Index at which to start changing the children.
* @param {Number} n Number of elements to remove. * @param {Number} n Number of elements to remove.
* @param {Array|Object} [insertion] Elements to add to the content. * @param {Array|Object} [insertion] Elements to add to the children.
* @return {Array} Removed elements. * @return {Array} Removed elements.
*/ */
JSAPI.prototype.spliceContent = function (start, n, insertion) { JSAPI.prototype.spliceContent = function (start, n, insertion) {
@ -133,7 +133,10 @@ JSAPI.prototype.spliceContent = function (start, n, insertion) {
inner.parentNode = this; inner.parentNode = this;
}, this); }, this);
return this.content.splice.apply(this.content, [start, n].concat(insertion)); return this.children.splice.apply(
this.children,
[start, n].concat(insertion)
);
}; };
/** /**

View File

@ -50,9 +50,9 @@ module.exports = function (data, info, plugins) {
*/ */
function perItem(data, info, plugins, reverse) { function perItem(data, info, plugins, reverse) {
function monkeys(items) { function monkeys(items) {
items.content = items.content.filter(function (item) { items.children = items.children.filter(function (item) {
// reverse pass // reverse pass
if (reverse && item.content) { if (reverse && item.children) {
monkeys(item); monkeys(item);
} }
@ -68,7 +68,7 @@ function perItem(data, info, plugins, reverse) {
} }
// direct pass // direct pass
if (!reverse && item.content) { if (!reverse && item.children) {
monkeys(item); monkeys(item);
} }

View File

@ -23,13 +23,13 @@ var config = {
*/ */
module.exports = function (data) { module.exports = function (data) {
var sax = SAX.parser(config.strict, config), var sax = SAX.parser(config.strict, config),
root = new JSAPI({ type: 'root', content: [] }), root = new JSAPI({ type: 'root', children: [] }),
current = root, current = root,
stack = [root]; stack = [root];
function pushToContent(content) { function pushToContent(node) {
const wrapped = new JSAPI(content, current); const wrapped = new JSAPI(node, current);
current.content.push(wrapped); current.children.push(wrapped);
return wrapped; return wrapped;
} }
@ -128,7 +128,7 @@ module.exports = function (data) {
} }
element.attributes = newAttributes; element.attributes = newAttributes;
}, },
content: [], children: [],
}; };
element.class = new CSSClassList(element); element.class = new CSSClassList(element);

View File

@ -57,7 +57,7 @@ exports.fn = function (data, params) {
} }
var attributes = params.attributes || [params.attribute], var attributes = params.attributes || [params.attribute],
svg = data.content[0]; svg = data.children[0];
if (svg.isElem('svg')) { if (svg.isElem('svg')) {
attributes.forEach(function (attribute) { attributes.forEach(function (attribute) {

View File

@ -45,7 +45,7 @@ exports.fn = function (data, params) {
} }
var classNames = params.classNames || [params.className], var classNames = params.classNames || [params.className],
svg = data.content[0]; svg = data.children[0];
if (svg.isElem('svg')) { if (svg.isElem('svg')) {
svg.class.add.apply(svg.class, classNames); svg.class.add.apply(svg.class, classNames);

View File

@ -60,10 +60,10 @@ exports.fn = function (data) {
} }
function monkeys(items, fn) { function monkeys(items, fn) {
items.content.forEach(function (item) { items.children.forEach(function (item) {
fn(item); fn(item);
if (item.content) { if (item.children) {
monkeys(item, fn); monkeys(item, fn);
} }
}); });

View File

@ -117,13 +117,12 @@ exports.fn = function (data, params) {
* @return {Array} output items * @return {Array} output items
*/ */
function monkeys(items) { function monkeys(items) {
for (var i = 0; i < items.content.length && !hasStyleOrScript; i++) { for (var i = 0; i < items.children.length && !hasStyleOrScript; i++) {
var item = items.content[i]; var item = items.children[i];
// quit if <style> or <script> present ('force' param prevents quitting) // quit if <style> or <script> present ('force' param prevents quitting)
if (!params.force) { if (!params.force) {
var isNotEmpty = item.isEmpty() === false; if (item.isElem(styleOrScript) && item.children.length !== 0) {
if (item.isElem(styleOrScript) && isNotEmpty) {
hasStyleOrScript = true; hasStyleOrScript = true;
continue; continue;
} }
@ -132,8 +131,8 @@ exports.fn = function (data, params) {
if (item.isElem('svg')) { if (item.isElem('svg')) {
var hasDefsOnly = true; var hasDefsOnly = true;
for (var j = 0; j < item.content.length; j++) { for (var j = 0; j < item.children.length; j++) {
if (!item.content[j].isElem('defs')) { if (!item.children[j].isElem('defs')) {
hasDefsOnly = false; hasDefsOnly = false;
break; break;
} }
@ -181,7 +180,7 @@ exports.fn = function (data, params) {
}); });
} }
// go deeper // go deeper
if (item.content) { if (item.children) {
monkeys(item); monkeys(item);
} }
} }

View File

@ -13,7 +13,9 @@ var collections = require('./_collections'),
function hasAnimatedAttr(item) { function hasAnimatedAttr(item) {
return ( return (
(item.isElem(animationElems) && item.hasAttr('attributeName', this)) || (item.isElem(animationElems) && item.hasAttr('attributeName', this)) ||
(!item.isEmpty() && item.content.some(hasAnimatedAttr, this)) (item.type === 'element' &&
item.children.length !== 0 &&
item.children.some(hasAnimatedAttr, this))
); );
} }
@ -42,13 +44,17 @@ function hasAnimatedAttr(item) {
*/ */
exports.fn = function (item) { exports.fn = function (item) {
// non-empty elements // non-empty elements
if (item.type === 'element' && !item.isElem('switch') && !item.isEmpty()) { if (
item.content.forEach(function (g, i) { item.type === 'element' &&
!item.isElem('switch') &&
item.children.length !== 0
) {
item.children.forEach(function (g, i) {
// non-empty groups // non-empty groups
if (g.isElem('g') && !g.isEmpty()) { if (g.isElem('g') && g.children.length !== 0) {
// move group attibutes to the single content element // move group attibutes to the single child element
if (g.hasAttr() && g.content.length === 1) { if (g.hasAttr() && g.children.length === 1) {
var inner = g.content[0]; var inner = g.children[0];
if ( if (
inner.type === 'element' && inner.type === 'element' &&
@ -61,7 +67,7 @@ exports.fn = function (item) {
!inner.hasAttr('transform'))) !inner.hasAttr('transform')))
) { ) {
g.eachAttr(function (attr) { g.eachAttr(function (attr) {
if (g.content.some(hasAnimatedAttr, attr.name)) return; if (g.children.some(hasAnimatedAttr, attr.name)) return;
if (!inner.hasAttr(attr.name)) { if (!inner.hasAttr(attr.name)) {
inner.addAttr(attr); inner.addAttr(attr);
@ -85,11 +91,11 @@ exports.fn = function (item) {
// collapse groups without attributes // collapse groups without attributes
if ( if (
!g.hasAttr() && !g.hasAttr() &&
!g.content.some(function (item) { !g.children.some(function (item) {
return item.isElem(animationElems); return item.isElem(animationElems);
}) })
) { ) {
item.spliceContent(i, 1, g.content); item.spliceContent(i, 1, g.children);
} }
} }
}); });

View File

@ -62,7 +62,7 @@ exports.fn = function (document, opts) {
continue; continue;
} }
// skip empty <style/>s or <foreignObject> content. // skip empty <style/>s or <foreignObject> content.
if (styleEl.isEmpty() || styleEl.closestElem('foreignObject')) { if (styleEl.children.length === 0 || styleEl.closestElem('foreignObject')) {
continue; continue;
} }
@ -244,15 +244,18 @@ exports.fn = function (document, opts) {
// clean up now emtpy <style/>s // clean up now emtpy <style/>s
var styleParentEl = style.styleEl.parentNode; var styleParentEl = style.styleEl.parentNode;
styleParentEl.spliceContent( styleParentEl.spliceContent(
styleParentEl.content.indexOf(style.styleEl), styleParentEl.children.indexOf(style.styleEl),
1 1
); );
if (styleParentEl.name === 'defs' && styleParentEl.content.length === 0) { if (
styleParentEl.name === 'defs' &&
styleParentEl.children.length === 0
) {
// also clean up now empty <def/>s // also clean up now empty <def/>s
var defsParentEl = styleParentEl.parentNode; var defsParentEl = styleParentEl.parentNode;
defsParentEl.spliceContent( defsParentEl.spliceContent(
defsParentEl.content.indexOf(styleParentEl), defsParentEl.children.indexOf(styleParentEl),
1 1
); );
} }

View File

@ -26,19 +26,19 @@ exports.params = {
* @author Kir Belevich, Lev Solntsev * @author Kir Belevich, Lev Solntsev
*/ */
exports.fn = function (item, params) { exports.fn = function (item, params) {
if (item.type !== 'element' || item.isEmpty()) return; if (item.type !== 'element' || item.children.length === 0) return;
var prevContentItem = null, var prevContentItem = null,
prevContentItemKeys = null; prevContentItemKeys = null;
item.content = item.content.filter(function (contentItem) { item.children = item.children.filter(function (contentItem) {
if ( if (
prevContentItem && prevContentItem &&
prevContentItem.isElem('path') && prevContentItem.isElem('path') &&
prevContentItem.isEmpty() && prevContentItem.children.length === 0 &&
prevContentItem.hasAttr('d') && prevContentItem.hasAttr('d') &&
contentItem.isElem('path') && contentItem.isElem('path') &&
contentItem.isEmpty() && contentItem.children.length === 0 &&
contentItem.hasAttr('d') contentItem.hasAttr('d')
) { ) {
const computedStyle = computeStyle(contentItem); const computedStyle = computeStyle(contentItem);

View File

@ -38,17 +38,20 @@ exports.fn = function (ast, options) {
elems.forEach(function (elem) { elems.forEach(function (elem) {
if (elem.isElem('style')) { if (elem.isElem('style')) {
if (elem.content[0].type === 'text' || elem.content[0].type === 'cdata') { if (
const styleCss = elem.content[0].value; elem.children[0].type === 'text' ||
elem.children[0].type === 'cdata'
) {
const styleCss = elem.children[0].value;
const minified = csso.minify(styleCss, minifyOptionsForStylesheet).css; const minified = csso.minify(styleCss, minifyOptionsForStylesheet).css;
// preserve cdata if necessary // preserve cdata if necessary
// TODO split cdata -> text optimisation into separate plugin // TODO split cdata -> text optimisation into separate plugin
if (styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0) { if (styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0) {
elem.content[0].type = 'cdata'; elem.children[0].type = 'cdata';
elem.content[0].value = minified; elem.children[0].value = minified;
} else { } else {
elem.content[0].type = 'text'; elem.children[0].type = 'text';
elem.content[0].value = minified; elem.children[0].value = minified;
} }
} }
} else { } else {
@ -71,15 +74,15 @@ function cloneObject(obj) {
function findStyleElems(ast) { function findStyleElems(ast) {
function walk(items, styles) { function walk(items, styles) {
for (var i = 0; i < items.content.length; i++) { for (var i = 0; i < items.children.length; i++) {
var item = items.content[i]; var item = items.children[i];
// go deeper // go deeper
if (item.content) { if (item.children) {
walk(item, styles); walk(item, styles);
} }
if (item.isElem('style') && !item.isEmpty()) { if (item.isElem('style') && item.children.length !== 0) {
styles.push(item); styles.push(item);
} else if (item.type === 'element' && item.hasAttr('style')) { } else if (item.type === 'element' && item.hasAttr('style')) {
styles.push(item); styles.push(item);
@ -106,11 +109,11 @@ function shouldFilter(options, name) {
function collectUsageData(ast, options) { function collectUsageData(ast, options) {
function walk(items, usageData) { function walk(items, usageData) {
for (var i = 0; i < items.content.length; i++) { for (var i = 0; i < items.children.length; i++) {
var item = items.content[i]; var item = items.children[i];
// go deeper // go deeper
if (item.content) { if (item.children) {
walk(item, usageData); walk(item, usageData);
} }

View File

@ -34,11 +34,11 @@ var inheritableAttrs = require('./_collections').inheritableAttrs,
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function (item) { exports.fn = function (item) {
if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) { if (item.isElem('g') && item.children.length > 1) {
var intersection = {}, var intersection = {},
hasTransform = false, hasTransform = false,
hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'), hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'),
intersected = item.content.every(function (inner) { intersected = item.children.every(function (inner) {
if (inner.type === 'element' && inner.hasAttr()) { if (inner.type === 'element' && inner.hasAttr()) {
// don't mess with possible styles (hack until CSS parsing is implemented) // don't mess with possible styles (hack until CSS parsing is implemented)
if (inner.hasAttr('class')) return false; if (inner.hasAttr('class')) return false;
@ -56,12 +56,12 @@ exports.fn = function (item) {
return true; return true;
} }
}), }),
allPath = item.content.every(function (inner) { allPath = item.children.every(function (inner) {
return inner.isElem(pathElems); return inner.isElem(pathElems);
}); });
if (intersected) { if (intersected) {
item.content.forEach(function (g) { item.children.forEach(function (g) {
for (const [name, value] of Object.entries(intersection)) { for (const [name, value] of Object.entries(intersection)) {
if ((!allPath && !hasClip) || name !== 'transform') { if ((!allPath && !hasClip) || name !== 'transform') {
g.removeAttr(name); g.removeAttr(name);

View File

@ -33,16 +33,16 @@ exports.fn = function (item) {
// move group transform attr to content's pathElems // move group transform attr to content's pathElems
if ( if (
item.isElem('g') && item.isElem('g') &&
item.children.length !== 0 &&
item.hasAttr('transform') && item.hasAttr('transform') &&
!item.isEmpty() &&
!item.someAttr(function (attr) { !item.someAttr(function (attr) {
return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url('); return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url(');
}) && }) &&
item.content.every(function (inner) { item.children.every(function (inner) {
return inner.isElem(pathElems) && !inner.hasAttr('id'); return inner.isElem(pathElems) && !inner.hasAttr('id');
}) })
) { ) {
item.content.forEach(function (inner) { item.children.forEach(function (inner) {
var attr = item.attr('transform'); var attr = item.attr('transform');
if (inner.hasAttr('transform')) { if (inner.hasAttr('transform')) {
inner.attr('transform').value = inner.attr('transform').value =

View File

@ -197,15 +197,15 @@ exports.fn = function (node, opts, extra) {
// <style/> property values // <style/> property values
if (node.name === 'style') { if (node.type === 'element' && node.name === 'style') {
if (node.isEmpty()) { if (node.children.length === 0) {
// skip empty <style/>s // skip empty <style/>s
return node; return node;
} }
var cssStr = ''; var cssStr = '';
if (node.content[0].type === 'text' || node.content[0].type === 'cdata') { if (node.children[0].type === 'text' || node.children[0].type === 'cdata') {
cssStr = node.content[0].value; cssStr = node.children[0].value;
} }
var cssAst = {}; var cssAst = {};
@ -249,7 +249,7 @@ exports.fn = function (node, opts, extra) {
}); });
// update <style>s // update <style>s
node.content[0].value = csstree.generate(cssAst); node.children[0].value = csstree.generate(cssAst);
return node; return node;
} }

View File

@ -29,9 +29,9 @@ exports.fn = function (item, params) {
!item.isElem('desc') || !item.isElem('desc') ||
!( !(
params.removeAny || params.removeAny ||
item.isEmpty() || item.children.length === 0 ||
(item.content[0].type === 'text' && (item.children[0].type === 'text' &&
standardDescs.test(item.content[0].value)) standardDescs.test(item.children[0].value))
) )
); );
}; };

View File

@ -27,7 +27,7 @@ var container = require('./_collections').elemsGroups.container;
exports.fn = function (item) { exports.fn = function (item) {
return ( return (
item.isElem(container) === false || item.isElem(container) === false ||
item.isEmpty() === false || (item.type === 'element' && item.children.length !== 0) ||
item.isElem('svg') || item.isElem('svg') ||
// empty patterns may contain reusable configuration // empty patterns may contain reusable configuration
(item.isElem('pattern') && Object.keys(item.attrs).length !== 0) || (item.isElem('pattern') && Object.keys(item.attrs).length !== 0) ||

View File

@ -35,12 +35,17 @@ exports.params = {
*/ */
exports.fn = function (item, params) { exports.fn = function (item, params) {
// Remove empty text element // Remove empty text element
if (params.text && item.isElem('text') && item.isEmpty()) return false; if (params.text && item.isElem('text') && item.children.length === 0) {
return false;
}
// Remove empty tspan element // Remove empty tspan element
if (params.tspan && item.isElem('tspan') && item.isEmpty()) return false; if (params.tspan && item.isElem('tspan') && item.children.length === 0) {
return false;
}
// Remove tref with empty xlink:href attribute // Remove tref with empty xlink:href attribute
if (params.tref && item.isElem('tref') && !item.hasAttrLocal('href')) if (params.tref && item.isElem('tref') && !item.hasAttrLocal('href')) {
return false; return false;
}
}; };

View File

@ -102,7 +102,7 @@ exports.fn = function (item, params) {
if ( if (
params.circleR0 && params.circleR0 &&
item.isElem('circle') && item.isElem('circle') &&
item.isEmpty() && item.children.length === 0 &&
item.hasAttr('r', '0') item.hasAttr('r', '0')
) { ) {
return false; return false;
@ -117,7 +117,7 @@ exports.fn = function (item, params) {
if ( if (
params.ellipseRX0 && params.ellipseRX0 &&
item.isElem('ellipse') && item.isElem('ellipse') &&
item.isEmpty() && item.children.length === 0 &&
item.hasAttr('rx', '0') item.hasAttr('rx', '0')
) { ) {
return false; return false;
@ -132,7 +132,7 @@ exports.fn = function (item, params) {
if ( if (
params.ellipseRY0 && params.ellipseRY0 &&
item.isElem('ellipse') && item.isElem('ellipse') &&
item.isEmpty() && item.children.length === 0 &&
item.hasAttr('ry', '0') item.hasAttr('ry', '0')
) { ) {
return false; return false;
@ -147,7 +147,7 @@ exports.fn = function (item, params) {
if ( if (
params.rectWidth0 && params.rectWidth0 &&
item.isElem('rect') && item.isElem('rect') &&
item.isEmpty() && item.children.length === 0 &&
item.hasAttr('width', '0') item.hasAttr('width', '0')
) { ) {
return false; return false;
@ -163,7 +163,7 @@ exports.fn = function (item, params) {
params.rectHeight0 && params.rectHeight0 &&
params.rectWidth0 && params.rectWidth0 &&
item.isElem('rect') && item.isElem('rect') &&
item.isEmpty() && item.children.length === 0 &&
item.hasAttr('height', '0') item.hasAttr('height', '0')
) { ) {
return false; return false;

View File

@ -74,11 +74,10 @@ exports.fn = function (item, params) {
// remove unknown element's content // remove unknown element's content
if ( if (
params.unknownContent && params.unknownContent &&
!item.isEmpty() &&
elems[elem] && // make sure we know of this element before checking its children elems[elem] && // make sure we know of this element before checking its children
elem !== 'foreignObject' // Don't check foreignObject elem !== 'foreignObject' // Don't check foreignObject
) { ) {
item.content.forEach(function (content, i) { item.children.forEach(function (content, i) {
if ( if (
content.type === 'element' && content.type === 'element' &&
!parseName(content.name).prefix && !parseName(content.name).prefix &&
@ -87,7 +86,7 @@ exports.fn = function (item, params) {
(!elems[elem].content && // we dont know about its permitted content (!elems[elem].content && // we dont know about its permitted content
!elems[content.name])) // check that we know about the element at all !elems[content.name])) // check that we know about the element at all
) { ) {
item.content.splice(i, 1); item.children.splice(i, 1);
} }
}); });
} }

View File

@ -43,10 +43,10 @@ exports.fn = function (data) {
*/ */
function monkeys(items) { function monkeys(items) {
var i = 0, var i = 0,
length = items.content.length; length = items.children.length;
while (i < length) { while (i < length) {
var item = items.content[i]; var item = items.children[i];
if (item.isElem('svg')) { if (item.isElem('svg')) {
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
@ -79,7 +79,7 @@ exports.fn = function (data) {
} }
// if nothing is found - go deeper // if nothing is found - go deeper
if (xmlnsCollection.length && item.content) { if (xmlnsCollection.length && item.children) {
monkeys(item); monkeys(item);
} }

View File

@ -18,25 +18,26 @@ var nonRendering = require('./_collections').elemsGroups.nonRendering;
*/ */
exports.fn = function (item) { exports.fn = function (item) {
if (item.isElem('defs')) { if (item.isElem('defs')) {
if (item.content) { item.children = getUsefulItems(item, []);
item.content = getUsefulItems(item, []); if (item.children.length === 0) {
return false;
} }
if (item.isEmpty()) return false;
} else if (item.isElem(nonRendering) && !item.hasAttr('id')) { } else if (item.isElem(nonRendering) && !item.hasAttr('id')) {
return false; return false;
} }
}; };
function getUsefulItems(item, usefulItems) { function getUsefulItems(item, usefulItems) {
item.content.forEach(function (child) { for (const child of item.children) {
if (child.hasAttr('id') || child.isElem('style')) { if (child.type === 'element') {
usefulItems.push(child); if (child.hasAttr('id') || child.isElem('style')) {
child.parentNode = item; usefulItems.push(child);
} else if (!child.isEmpty()) { child.parentNode = item;
child.content = getUsefulItems(child, usefulItems); } else {
child.children = getUsefulItems(child, usefulItems);
}
} }
}); }
return usefulItems; return usefulItems;
} }

View File

@ -51,12 +51,12 @@ exports.fn = function (data) {
{ {
type: 'element', type: 'element',
name: 'defs', name: 'defs',
content: [], children: [],
attrs: [], attrs: {},
}, },
data data
); );
data.content[0].spliceContent(0, 0, defsTag); data.children[0].spliceContent(0, 0, defsTag);
for (let def of defs) { for (let def of defs) {
// Remove class and style before copying to avoid circular refs in // Remove class and style before copying to avoid circular refs in
// JSON.stringify. This is fine because we don't actually want class or // JSON.stringify. This is fine because we don't actually want class or
@ -94,11 +94,10 @@ function convertToUse(item, href) {
/** */ /** */
function traverse(parent, callback) { function traverse(parent, callback) {
if (parent.isEmpty()) { if (parent.type === 'root' || parent.type === 'element') {
return; for (let child of parent.children) {
} callback(child);
for (let child of parent.content) { traverse(child, callback);
callback(child); }
traverse(child, callback);
} }
} }

View File

@ -17,28 +17,25 @@ exports.description = 'Sorts children of <defs> to improve compression';
*/ */
exports.fn = function (item) { exports.fn = function (item) {
if (item.isElem('defs')) { if (item.isElem('defs')) {
if (item.content) { var frequency = item.children.reduce(function (frequency, child) {
var frequency = item.content.reduce(function (frequency, child) { if (child.name in frequency) {
if (child.name in frequency) { frequency[child.name]++;
frequency[child.name]++; } else {
} else { frequency[child.name] = 1;
frequency[child.name] = 1; }
} return frequency;
return frequency; }, {});
}, {}); item.children.sort(function (a, b) {
item.content.sort(function (a, b) { var frequencyComparison = frequency[b.name] - frequency[a.name];
var frequencyComparison = frequency[b.name] - frequency[a.name]; if (frequencyComparison !== 0) {
if (frequencyComparison !== 0) { return frequencyComparison;
return frequencyComparison; }
} var lengthComparison = b.name.length - a.name.length;
var lengthComparison = b.name.length - a.name.length; if (lengthComparison !== 0) {
if (lengthComparison !== 0) { return lengthComparison;
return lengthComparison; }
} return a.name != b.name ? (a.name > b.name ? -1 : 1) : 0;
return a.name != b.name ? (a.name > b.name ? -1 : 1) : 0; });
});
}
return true; return true;
} }
}; };

View File

@ -34,23 +34,23 @@ describe('svg2js', function () {
expect(root).to.be.an.instanceOf(Object); expect(root).to.be.an.instanceOf(Object);
}); });
it('should have property "content"', function () { it('should have property "children"', function () {
expect(root).to.have.property('content'); expect(root).to.have.property('children');
}); });
}); });
describe('root.content', function () { describe('root.children', function () {
it('should be an instance of Array', function () { it('should be an instance of Array', function () {
expect(root.content).to.be.an.instanceOf(Array); expect(root.children).to.be.an.instanceOf(Array);
}); });
it('should have length 4', function () { it('should have length 4', function () {
expect(root.content).to.have.lengthOf(4); expect(root.children).to.have.lengthOf(4);
}); });
}); });
it('the first node should be instruction', () => { it('the first node should be instruction', () => {
expect(root.content[0]).to.include({ expect(root.children[0]).to.include({
type: 'instruction', type: 'instruction',
name: 'xml', name: 'xml',
value: 'version="1.0" encoding="utf-8"', value: 'version="1.0" encoding="utf-8"',
@ -58,7 +58,7 @@ describe('svg2js', function () {
}); });
it('the second node should be comment', () => { it('the second node should be comment', () => {
expect(root.content[1]).to.include({ expect(root.children[1]).to.include({
type: 'comment', type: 'comment',
value: value:
'Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)', 'Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)',
@ -66,7 +66,7 @@ describe('svg2js', function () {
}); });
it('the third node should be doctype', () => { it('the third node should be doctype', () => {
expect(root.content[2]).to.deep.include({ expect(root.children[2]).to.deep.include({
type: 'doctype', type: 'doctype',
name: 'svg', name: 'svg',
data: { data: {
@ -78,41 +78,41 @@ describe('svg2js', function () {
describe('name', function () { describe('name', function () {
it('should have property name: "svg"', function () { it('should have property name: "svg"', function () {
expect(root.content[3]).to.include({ expect(root.children[3]).to.include({
name: 'svg', name: 'svg',
}); });
}); });
}); });
describe('attributes', function () { describe('attributes', function () {
describe('root.content[3].attrs', function () { describe('root.children[3].attrs', function () {
it('should exist', function () { it('should exist', function () {
expect(root.content[3].attrs).to.exist; expect(root.children[3].attrs).to.exist;
}); });
it('should be an instance of Object', function () { it('should be an instance of Object', function () {
expect(root.content[3].attrs).to.be.an.instanceOf(Object); expect(root.children[3].attrs).to.be.an.instanceOf(Object);
}); });
}); });
describe('root.content[3].attrs.version', function () { describe('root.children[3].attrs.version', function () {
it('should exist', function () { it('should exist', function () {
expect(root.content[3].attrs.version).to.exist; expect(root.children[3].attrs.version).to.exist;
}); });
it('should be an instance of Object', function () { it('should be an instance of Object', function () {
expect(root.content[3].attrs.version).to.be.an.instanceOf(Object); expect(root.children[3].attrs.version).to.be.an.instanceOf(Object);
}); });
it('should have property name: "version"', function () { it('should have property name: "version"', function () {
expect(root.content[3].attrs.version).to.have.property( expect(root.children[3].attrs.version).to.have.property(
'name', 'name',
'version' 'version'
); );
}); });
it('should have property value: "1.1"', function () { it('should have property value: "1.1"', function () {
expect(root.content[3].attrs.version).to.have.property( expect(root.children[3].attrs.version).to.have.property(
'value', 'value',
'1.1' '1.1'
); );
@ -120,45 +120,45 @@ describe('svg2js', function () {
}); });
}); });
describe('content', function () { describe('children', function () {
it('should exist', function () { it('should exist', function () {
expect(root.content[3].content).to.exist; expect(root.children[3].children).to.exist;
}); });
it('should be an instance of Array', function () { it('should be an instance of Array', function () {
expect(root.content[3].content).to.be.an.instanceOf(Array); expect(root.children[3].children).to.be.an.instanceOf(Array);
}); });
it('should eventually have length 3', function () { it('should eventually have length 3', function () {
expect(root.content[3].content).to.have.lengthOf(3); expect(root.children[3].children).to.have.lengthOf(3);
}); });
}); });
describe('text nodes', function () { describe('text nodes', function () {
it('should contain preserved whitespace', function () { it('should contain preserved whitespace', function () {
const textNode = root.content[3].content[1].content[0].content[1]; const textNode = root.children[3].children[1].children[0].children[1];
return expect(textNode.content[0].value).to.equal(' test '); return expect(textNode.children[0].value).to.equal(' test ');
}); });
}); });
describe('API', function () { describe('API', function () {
describe('clone()', function () { describe('clone()', function () {
it('svg should have property "clone"', function () { it('svg should have property "clone"', function () {
expect(root.content[3]).to.have.property('clone'); expect(root.children[3]).to.have.property('clone');
}); });
it('svg.clone() should be an instance of JSAPI', function () { it('svg.clone() should be an instance of JSAPI', function () {
expect(root.content[3].clone()).to.be.instanceOf(JSAPI); expect(root.children[3].clone()).to.be.instanceOf(JSAPI);
}); });
it('root.content[3].content[0].clone() has a valid style property', function () { it('root.children[3].children[0].clone() has a valid style property', function () {
expect(root.content[3].content[0].clone().style).to.be.instanceof( expect(root.children[3].children[0].clone().style).to.be.instanceof(
CSSStyleDeclaration CSSStyleDeclaration
); );
}); });
it('root.content[3].content[2].clone() has a valid class property', function () { it('root.children[3].children[2].clone() has a valid class property', function () {
expect(root.content[3].content[2].clone().class).to.be.instanceof( expect(root.children[3].children[2].clone().class).to.be.instanceof(
CSSClassList CSSClassList
); );
}); });
@ -166,128 +166,129 @@ describe('svg2js', function () {
describe('isElem()', function () { describe('isElem()', function () {
it('svg should have property "isElem"', function () { it('svg should have property "isElem"', function () {
expect(root.content[3]).to.have.property('isElem'); expect(root.children[3]).to.have.property('isElem');
}); });
it('svg.isElem() should be true', function () { it('svg.isElem() should be true', function () {
expect(root.content[3].isElem()).to.be.true; expect(root.children[3].isElem()).to.be.true;
}); });
it('svg.isElem("svg") should be true', function () { it('svg.isElem("svg") should be true', function () {
expect(root.content[3].isElem('svg')).to.be.true; expect(root.children[3].isElem('svg')).to.be.true;
}); });
it('svg.isElem("trololo") should be false', function () { it('svg.isElem("trololo") should be false', function () {
expect(root.content[3].isElem('trololo')).to.be.false; expect(root.children[3].isElem('trololo')).to.be.false;
}); });
it('svg.isElem(["svg", "trololo"]) should be true', function () { it('svg.isElem(["svg", "trololo"]) should be true', function () {
expect(root.content[3].isElem(['svg', 'trololo'])).to.be.true; expect(root.children[3].isElem(['svg', 'trololo'])).to.be.true;
}); });
}); });
describe('isEmpty()', function () { describe('isEmpty()', function () {
it('svg should have property "isEmpty"', function () { it('svg should have property "isEmpty"', function () {
expect(root.content[3]).to.have.property('isEmpty'); expect(root.children[3]).to.have.property('isEmpty');
}); });
it('svg.isEmpty() should be false', function () { it('svg.isEmpty() should be false', function () {
expect(root.content[3].isEmpty()).to.be.false; expect(root.children[3].isEmpty()).to.be.false;
}); });
it('svg.content[0].content[0].isEmpty() should be true', function () { it('svg.children[0].children[0].isEmpty() should be true', function () {
expect(root.content[3].content[0].content[0].isEmpty()).to.be.true; expect(root.children[3].children[0].children[0].isEmpty()).to.be.true;
}); });
}); });
describe('hasAttr()', function () { describe('hasAttr()', function () {
it('svg should have property "hasAttr"', function () { it('svg should have property "hasAttr"', function () {
expect(root.content[3]).to.have.property('hasAttr'); expect(root.children[3]).to.have.property('hasAttr');
}); });
it('svg.hasAttr() should be true', function () { it('svg.hasAttr() should be true', function () {
expect(root.content[3].hasAttr()).to.be.true; expect(root.children[3].hasAttr()).to.be.true;
}); });
it('svg.hasAttr("xmlns") should be true', function () { it('svg.hasAttr("xmlns") should be true', function () {
expect(root.content[3].hasAttr('xmlns')).to.be.true; expect(root.children[3].hasAttr('xmlns')).to.be.true;
}); });
it('svg.hasAttr("xmlns", "http://www.w3.org/2000/svg") should be true', function () { it('svg.hasAttr("xmlns", "http://www.w3.org/2000/svg") should be true', function () {
expect(root.content[3].hasAttr('xmlns', 'http://www.w3.org/2000/svg')) expect(
.to.be.true; root.children[3].hasAttr('xmlns', 'http://www.w3.org/2000/svg')
).to.be.true;
}); });
it('svg.hasAttr("xmlns", "trololo") should be false', function () { it('svg.hasAttr("xmlns", "trololo") should be false', function () {
expect(root.content[3].hasAttr('xmlns', 'trololo')).to.be.false; expect(root.children[3].hasAttr('xmlns', 'trololo')).to.be.false;
}); });
it('svg.hasAttr("trololo") should be false', function () { it('svg.hasAttr("trololo") should be false', function () {
expect(root.content[3].hasAttr('trololo')).to.be.false; expect(root.children[3].hasAttr('trololo')).to.be.false;
}); });
it('svg.content[1].hasAttr() should be false', function () { it('svg.children[1].hasAttr() should be false', function () {
expect(root.content[3].content[1].hasAttr()).to.be.false; expect(root.children[3].children[1].hasAttr()).to.be.false;
}); });
}); });
describe('attr()', function () { describe('attr()', function () {
it('svg should have property "attr"', function () { it('svg should have property "attr"', function () {
expect(root.content[3]).to.have.property('attr'); expect(root.children[3]).to.have.property('attr');
}); });
it('svg.attr("xmlns") should be an instance of Object', function () { it('svg.attr("xmlns") should be an instance of Object', function () {
expect(root.content[3].attr('xmlns')).to.be.an.instanceOf(Object); expect(root.children[3].attr('xmlns')).to.be.an.instanceOf(Object);
}); });
it('svg.attr("xmlns", "http://www.w3.org/2000/svg") should be an instance of Object', function () { it('svg.attr("xmlns", "http://www.w3.org/2000/svg") should be an instance of Object', function () {
expect( expect(
root.content[3].attr('xmlns', 'http://www.w3.org/2000/svg') root.children[3].attr('xmlns', 'http://www.w3.org/2000/svg')
).to.be.an.instanceOf(Object); ).to.be.an.instanceOf(Object);
}); });
it('svg.attr("xmlns", "trololo") should be an undefined', function () { it('svg.attr("xmlns", "trololo") should be an undefined', function () {
expect(root.content[3].attr('xmlns', 'trololo')).to.not.exist; expect(root.children[3].attr('xmlns', 'trololo')).to.not.exist;
}); });
it('svg.attr("trololo") should be an undefined', function () { it('svg.attr("trololo") should be an undefined', function () {
expect(root.content[3].attr('trololo')).to.not.exist; expect(root.children[3].attr('trololo')).to.not.exist;
}); });
it('svg.attr() should be undefined', function () { it('svg.attr() should be undefined', function () {
expect(root.content[3].attr()).to.not.exist; expect(root.children[3].attr()).to.not.exist;
}); });
}); });
describe('removeAttr()', function () { describe('removeAttr()', function () {
it('svg should have property "removeAttr"', function () { it('svg should have property "removeAttr"', function () {
expect(root.content[3]).to.have.property('removeAttr'); expect(root.children[3]).to.have.property('removeAttr');
}); });
it('svg.removeAttr("width") should be true', function () { it('svg.removeAttr("width") should be true', function () {
expect(root.content[3].removeAttr('width')).to.be.true; expect(root.children[3].removeAttr('width')).to.be.true;
expect(root.content[3].hasAttr('width')).to.be.false; expect(root.children[3].hasAttr('width')).to.be.false;
}); });
it('svg.removeAttr("height", "120px") should be true', function () { it('svg.removeAttr("height", "120px") should be true', function () {
expect(root.content[3].removeAttr('height', '120px')).to.be.true; expect(root.children[3].removeAttr('height', '120px')).to.be.true;
expect(root.content[3].hasAttr('height')).to.be.false; expect(root.children[3].hasAttr('height')).to.be.false;
}); });
it('svg.removeAttr("x", "1px") should be false', function () { it('svg.removeAttr("x", "1px") should be false', function () {
expect(root.content[3].removeAttr('x', '1px')).to.be.false; expect(root.children[3].removeAttr('x', '1px')).to.be.false;
expect(root.content[3].hasAttr('x')).to.be.true; expect(root.children[3].hasAttr('x')).to.be.true;
}); });
it('svg.removeAttr("z") should be false', function () { it('svg.removeAttr("z") should be false', function () {
expect(root.content[3].removeAttr('z')).to.be.false; expect(root.children[3].removeAttr('z')).to.be.false;
}); });
it('svg.removeAttr() should be false', function () { it('svg.removeAttr() should be false', function () {
expect(root.content[3].removeAttr()).to.be.false; expect(root.children[3].removeAttr()).to.be.false;
}); });
}); });
@ -298,41 +299,41 @@ describe('svg2js', function () {
}; };
it('svg should have property "addAttr"', function () { it('svg should have property "addAttr"', function () {
expect(root.content[3]).to.have.property('addAttr'); expect(root.children[3]).to.have.property('addAttr');
}); });
it('svg.addAttr(attr) should be an instance of Object', function () { it('svg.addAttr(attr) should be an instance of Object', function () {
expect(root.content[3].addAttr(attr)).to.be.an.instanceOf(Object); expect(root.children[3].addAttr(attr)).to.be.an.instanceOf(Object);
}); });
it('svg.content[1].content[0].addAttr(attr) should be an instance of Object', function () { it('svg.children[1].children[0].addAttr(attr) should be an instance of Object', function () {
expect( expect(
root.content[3].content[1].content[0].addAttr(attr) root.children[3].children[1].children[0].addAttr(attr)
).to.be.an.instanceOf(Object); ).to.be.an.instanceOf(Object);
}); });
it('svg.addAttr() should be false', function () { it('svg.addAttr() should be false', function () {
expect(root.content[3].addAttr()).to.be.false; expect(root.children[3].addAttr()).to.be.false;
}); });
}); });
describe('eachAttr()', function () { describe('eachAttr()', function () {
it('svg should have property "eachAttr"', function () { it('svg should have property "eachAttr"', function () {
expect(root.content[3]).to.have.property('eachAttr'); expect(root.children[3]).to.have.property('eachAttr');
}); });
it('svg.content[0].eachAttr(function() {}) should be true', function () { it('svg.children[0].eachAttr(function() {}) should be true', function () {
expect( expect(
root.content[3].content[0].eachAttr(function (attr) { root.children[3].children[0].eachAttr(function (attr) {
attr.value = '1'; attr.value = '1';
}) })
).to.be.true; ).to.be.true;
expect(root.content[3].content[0].attr('type').value).to.equal('1'); expect(root.children[3].children[0].attr('type').value).to.equal('1');
}); });
it('svg.content[1].eachAttr(function() {}) should be false', function () { it('svg.children[1].eachAttr(function() {}) should be false', function () {
expect(root.content[3].content[1].eachAttr()).to.be.false; expect(root.children[3].children[1].eachAttr()).to.be.false;
}); });
}); });
}); });