diff --git a/lib/svgo/jsAPI.js b/lib/svgo/jsAPI.js index acceb7e3..1894964b 100644 --- a/lib/svgo/jsAPI.js +++ b/lib/svgo/jsAPI.js @@ -296,7 +296,7 @@ JSAPI.prototype.computedAttr = function (name, val) { if (val != null) { return elem ? elem.hasAttr(name, val) : false; } else if (elem && elem.hasAttr(name)) { - return elem.attrs[name].value; + return elem.attributes[name]; } }; diff --git a/plugins/_applyTransforms.js b/plugins/_applyTransforms.js index 63dbc056..307160da 100644 --- a/plugins/_applyTransforms.js +++ b/plugins/_applyTransforms.js @@ -58,9 +58,11 @@ const applyTransforms = (elem, pathData, params) => { let hasStrokeWidth = false; do { - if (idElem.hasAttr('stroke-width')) hasStrokeWidth = true; + if (idElem.attributes['stroke-width']) { + hasStrokeWidth = true; + } } while ( - !idElem.hasAttr('id', id) && + idElem.attributes.id !== id && !hasStrokeWidth && (idElem = idElem.parentNode) ); diff --git a/plugins/collapseGroups.js b/plugins/collapseGroups.js index d75c5615..dfd602c7 100644 --- a/plugins/collapseGroups.js +++ b/plugins/collapseGroups.js @@ -1,22 +1,23 @@ 'use strict'; +const { inheritableAttrs, elemsGroups } = require('./_collections'); + exports.type = 'perItemReverse'; exports.active = true; exports.description = 'collapses useless groups'; -var collections = require('./_collections'), - attrsInheritable = collections.inheritableAttrs, - animationElems = collections.elemsGroups.animation; - -function hasAnimatedAttr(item) { - return ( - (item.isElem(animationElems) && item.hasAttr('attributeName', this)) || - (item.type === 'element' && - item.children.length !== 0 && - item.children.some(hasAnimatedAttr, this)) - ); +function hasAnimatedAttr(item, name) { + if (item.type === 'element') { + return ( + (elemsGroups.animation.includes(item.name) && + item.attributes.attributeName === name) || + (item.children.length !== 0 && + item.children.some((child) => hasAnimatedAttr(child, name))) + ); + } + return false; } /* @@ -51,33 +52,35 @@ exports.fn = function (item) { ) { item.children.forEach(function (g, i) { // non-empty groups - if (g.isElem('g') && g.children.length !== 0) { + if (g.type === 'element' && g.name === 'g' && g.children.length !== 0) { // move group attibutes to the single child element - if (g.hasAttr() && g.children.length === 1) { + if (Object.keys(g.attributes).length !== 0 && g.children.length === 1) { var inner = g.children[0]; if ( inner.type === 'element' && - !inner.hasAttr('id') && - !g.hasAttr('filter') && - !(g.hasAttr('class') && inner.hasAttr('class')) && - ((!g.hasAttr('clip-path') && !g.hasAttr('mask')) || - (inner.isElem('g') && - !g.hasAttr('transform') && - !inner.hasAttr('transform'))) + inner.attributes.id == null && + g.attributes.filter == null && + (g.attributes.class == null || inner.attributes.class == null) && + ((g.attributes['clip-path'] == null && g.attributes.mask == null) || + (inner.type === 'element' && + inner.name === 'g' && + g.attributes.transform == null && + inner.attributes.transform == null)) ) { for (const [name, value] of Object.entries(g.attributes)) { - if (g.children.some(hasAnimatedAttr, name)) return; + if (g.children.some((item) => hasAnimatedAttr(item, name))) + return; - if (!inner.hasAttr(name)) { + if (inner.attributes[name] == null) { inner.attributes[name] = value; } else if (name == 'transform') { inner.attributes[name] = value + ' ' + inner.attributes[name]; - } else if (inner.hasAttr(name, 'inherit')) { + } else if (inner.attributes[name] === 'inherit') { inner.attributes[name] = value; } else if ( - attrsInheritable.includes(name) === false && - !inner.hasAttr(name, value) + inheritableAttrs.includes(name) === false && + inner.attributes[name] !== value ) { return; } @@ -89,10 +92,8 @@ exports.fn = function (item) { // collapse groups without attributes if ( - !g.hasAttr() && - !g.children.some(function (item) { - return item.isElem(animationElems); - }) + Object.keys(g.attributes).length === 0 && + !g.children.some((item) => item.isElem(elemsGroups.animation)) ) { item.spliceContent(i, 1, g.children); } diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js index 780bdd28..3b5ef63c 100644 --- a/plugins/convertPathData.js +++ b/plugins/convertPathData.js @@ -57,7 +57,11 @@ let arcTolerance; * @author Kir Belevich */ exports.fn = function (item, params) { - if (item.isElem(pathElems) && item.hasAttr('d')) { + if ( + item.type === 'element' && + pathElems.includes(item.name) && + item.attributes.d != null + ) { const computedStyle = computeStyle(item); precision = params.floatPrecision; error = diff --git a/plugins/convertTransform.js b/plugins/convertTransform.js index ac346d04..aa33e964 100644 --- a/plugins/convertTransform.js +++ b/plugins/convertTransform.js @@ -46,17 +46,17 @@ var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, exports.fn = function (item, params) { if (item.type === 'element') { // transform - if (item.hasAttr('transform')) { + if (item.attributes.transform != null) { convertTransform(item, 'transform', params); } // gradientTransform - if (item.hasAttr('gradientTransform')) { + if (item.attributes.gradientTransform != null) { convertTransform(item, 'gradientTransform', params); } // patternTransform - if (item.hasAttr('patternTransform')) { + if (item.attributes.patternTransform != null) { convertTransform(item, 'patternTransform', params); } } diff --git a/plugins/moveElemsAttrsToGroup.js b/plugins/moveElemsAttrsToGroup.js index cd8210e3..a2a0c420 100644 --- a/plugins/moveElemsAttrsToGroup.js +++ b/plugins/moveElemsAttrsToGroup.js @@ -1,14 +1,13 @@ 'use strict'; +const { inheritableAttrs, pathElems } = require('./_collections'); + exports.type = 'perItemReverse'; exports.active = true; exports.description = 'moves elements attributes to the existing group wrapper'; -var inheritableAttrs = require('./_collections').inheritableAttrs, - pathElems = require('./_collections.js').pathElems; - /** * Collapse content's intersected and inheritable * attributes to the existing group wrapper. @@ -34,14 +33,22 @@ var inheritableAttrs = require('./_collections').inheritableAttrs, * @author Kir Belevich */ exports.fn = function (item) { - if (item.isElem('g') && item.children.length > 1) { + if ( + item.type === 'element' && + item.name === 'g' && + item.children.length > 1 + ) { var intersection = {}, hasTransform = false, - hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'), + hasClip = + item.attributes['clip-path'] != null || item.attributes.mask != null, intersected = item.children.every(function (inner) { - if (inner.type === 'element' && inner.hasAttr()) { + if ( + inner.type === 'element' && + Object.keys(inner.attributes).length !== 0 + ) { // don't mess with possible styles (hack until CSS parsing is implemented) - if (inner.hasAttr('class')) return false; + if (inner.attributes.class) return false; if (!Object.keys(intersection).length) { intersection = inner.attributes; } else { @@ -68,7 +75,7 @@ exports.fn = function (item) { if (name === 'transform') { if (!hasTransform) { - if (item.hasAttr('transform')) { + if (item.attributes.transform != null) { item.attributes.transform = item.attributes.transform + ' ' + value; } else { diff --git a/plugins/moveGroupAttrsToElems.js b/plugins/moveGroupAttrsToElems.js index ce6f1983..4d798f55 100644 --- a/plugins/moveGroupAttrsToElems.js +++ b/plugins/moveGroupAttrsToElems.js @@ -1,14 +1,14 @@ 'use strict'; +const { pathElems, referencesProps } = require('./_collections.js'); + exports.type = 'perItem'; exports.active = true; exports.description = 'moves some group attributes to the content elements'; -var collections = require('./_collections.js'), - pathElems = collections.pathElems.concat(['g', 'text']), - referencesProps = collections.referencesProps; +const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text']; /** * Move group attrs to the content elements. @@ -41,7 +41,9 @@ exports.fn = function (item) { referencesProps.includes(name) && value.includes('url(') ) === false && item.children.every( - (inner) => inner.isElem(pathElems) && !inner.hasAttr('id') + (inner) => + pathElemsWithGroupsAndText.includes(inner.name) && + inner.attributes.id == null ) ) { for (const inner of item.children) { diff --git a/plugins/removeEmptyContainers.js b/plugins/removeEmptyContainers.js index 4648f71a..230a1266 100644 --- a/plugins/removeEmptyContainers.js +++ b/plugins/removeEmptyContainers.js @@ -1,13 +1,13 @@ 'use strict'; +const { elemsGroups } = require('./_collections'); + exports.type = 'perItemReverse'; exports.active = true; exports.description = 'removes empty container elements'; -var container = require('./_collections').elemsGroups.container; - /** * Remove empty containers. * @@ -25,16 +25,19 @@ var container = require('./_collections').elemsGroups.container; * @author Kir Belevich */ exports.fn = function (item) { - return ( - item.isElem(container) === false || - (item.type === 'element' && item.children.length !== 0) || - item.isElem('svg') || - // empty patterns may contain reusable configuration - (item.isElem('pattern') && Object.keys(item.attributes).length !== 0) || - // The 'g' may not have content, but the filter may cause a rectangle - // to be created and filled with pattern. - (item.isElem('g') && item.hasAttr('filter')) || - // empty hides masked element - (item.isElem('mask') && item.hasAttr('id')) - ); + if (item.type === 'element') { + return ( + item.children.length !== 0 || + elemsGroups.container.includes(item.name) === false || + item.name === 'svg' || + // empty patterns may contain reusable configuration + (item.name === 'pattern' && Object.keys(item.attributes).length !== 0) || + // The 'g' may not have content, but the filter may cause a rectangle + // to be created and filled with pattern. + (item.name === 'g' && item.attributes.filter != null) || + // empty hides masked element + (item.name === 'mask' && item.attributes.id != null) + ); + } + return true; }; diff --git a/plugins/removeEmptyText.js b/plugins/removeEmptyText.js index 89b4644c..30d3e7c2 100644 --- a/plugins/removeEmptyText.js +++ b/plugins/removeEmptyText.js @@ -34,18 +34,24 @@ exports.params = { * @author Kir Belevich */ exports.fn = function (item, params) { - // Remove empty text element - if (params.text && item.isElem('text') && item.children.length === 0) { - return false; - } + if (item.type === 'element') { + // Remove empty text element + if (params.text && item.name === 'text' && item.children.length === 0) { + return false; + } - // Remove empty tspan element - if (params.tspan && item.isElem('tspan') && item.children.length === 0) { - return false; - } + // Remove empty tspan element + if (params.tspan && item.name === 'tspan' && item.children.length === 0) { + return false; + } - // Remove tref with empty xlink:href attribute - if (params.tref && item.isElem('tref') && !item.hasAttrLocal('href')) { - return false; + // Remove tref with empty xlink:href attribute + if ( + params.tref && + item.name === 'tref' && + item.attributes['xlink:href'] == null + ) { + return false; + } } }; diff --git a/plugins/removeOffCanvasPaths.js b/plugins/removeOffCanvasPaths.js index d7743bc7..be363adc 100644 --- a/plugins/removeOffCanvasPaths.js +++ b/plugins/removeOffCanvasPaths.js @@ -25,7 +25,8 @@ var _path = require('./_path.js'), */ exports.fn = function (item) { if ( - item.isElem('path') && + item.type === 'element' && + item.name === 'path' && item.attributes.d != null && typeof viewBox !== 'undefined' ) { @@ -44,7 +45,7 @@ exports.fn = function (item) { return intersects(viewBoxJS, pathJS); } - if (item.isElem('svg')) { + if (item.type === 'element' && item.name === 'svg') { parseViewBox(item); } @@ -59,8 +60,10 @@ exports.fn = function (item) { */ function hasTransform(item) { return ( - item.hasAttr('transform') || - (item.parentNode && hasTransform(item.parentNode)) + item.attributes.transform != null || + (item.parentNode && + item.parentNode.type === 'element' && + hasTransform(item.parentNode)) ); } diff --git a/plugins/removeRasterImages.js b/plugins/removeRasterImages.js index 38c18c66..ec87f8bd 100644 --- a/plugins/removeRasterImages.js +++ b/plugins/removeRasterImages.js @@ -18,8 +18,10 @@ exports.description = 'removes raster images (disabled by default)'; */ exports.fn = function (item) { if ( - item.isElem('image') && - item.hasAttrLocal('href', /(\.|image\/)(jpg|png|gif)/) + item.type === 'element' && + item.name === 'image' && + item.attributes['xlink:href'] != null && + /(\.|image\/)(jpg|png|gif)/.test(item.attributes['xlink:href']) ) { return false; } diff --git a/plugins/removeUselessDefs.js b/plugins/removeUselessDefs.js index 70516364..4beb627f 100644 --- a/plugins/removeUselessDefs.js +++ b/plugins/removeUselessDefs.js @@ -1,13 +1,13 @@ 'use strict'; +const { elemsGroups } = require('./_collections'); + exports.type = 'perItem'; exports.active = true; exports.description = 'removes elements in without id'; -var nonRendering = require('./_collections').elemsGroups.nonRendering; - /** * Removes content of defs and properties that aren't rendered directly without ids. * @@ -17,20 +17,25 @@ var nonRendering = require('./_collections').elemsGroups.nonRendering; * @author Lev Solntsev */ exports.fn = function (item) { - if (item.isElem('defs')) { - item.children = getUsefulItems(item, []); - if (item.children.length === 0) { + if (item.type === 'element') { + if (item.name === 'defs') { + item.children = getUsefulItems(item, []); + if (item.children.length === 0) { + return false; + } + } else if ( + elemsGroups.nonRendering.includes(item.name) && + item.attributes.id == null + ) { return false; } - } else if (item.isElem(nonRendering) && !item.hasAttr('id')) { - return false; } }; function getUsefulItems(item, usefulItems) { for (const child of item.children) { if (child.type === 'element') { - if (child.hasAttr('id') || child.isElem('style')) { + if (child.attributes.id != null || child.name === 'style') { usefulItems.push(child); child.parentNode = item; } else {