mirror of
https://github.com/svg/svgo.git
synced 2025-07-29 20:21:14 +03:00
Prepare root and element nodes for xast
Ref https://github.com/syntax-tree/xast - added type: root | element - renamed elem to name - replaced "elem" property checks with check for correct type
This commit is contained in:
@ -168,7 +168,7 @@ const computeStyle = (node) => {
|
||||
// collect inherited styles
|
||||
const computedStyles = computeOwnStyle(node, stylesheet);
|
||||
let parent = node;
|
||||
while (parent.parentNode && parent.parentNode.elem !== '#document') {
|
||||
while (parent.parentNode && parent.parentNode.type !== 'root') {
|
||||
const inheritedStyles = computeOwnStyle(parent.parentNode, stylesheet);
|
||||
for (const [name, computed] of Object.entries(inheritedStyles)) {
|
||||
if (
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @return {node is any}
|
||||
*/
|
||||
const isTag = (node) => {
|
||||
return node.isElem();
|
||||
return node.type === 'element';
|
||||
};
|
||||
|
||||
const existsOne = (test, elems) => {
|
||||
@ -27,7 +27,7 @@ const getChildren = (node) => {
|
||||
};
|
||||
|
||||
const getName = (elemAst) => {
|
||||
return elemAst.elem;
|
||||
return elemAst.name;
|
||||
};
|
||||
|
||||
const getParent = (node) => {
|
||||
|
@ -97,17 +97,22 @@ JS2SVG.prototype.convert = function (data) {
|
||||
this.indentLevel++;
|
||||
|
||||
data.content.forEach(function (item) {
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
svg += this.createElem(item);
|
||||
} else if (item.type === 'text') {
|
||||
}
|
||||
if (item.type === 'text') {
|
||||
svg += this.createText(item);
|
||||
} else if (item.type === 'doctype') {
|
||||
}
|
||||
if (item.type === 'doctype') {
|
||||
svg += this.createDoctype(item);
|
||||
} else if (item.type === 'instruction') {
|
||||
}
|
||||
if (item.type === 'instruction') {
|
||||
svg += this.createProcInst(item);
|
||||
} else if (item.type === 'comment') {
|
||||
}
|
||||
if (item.type === 'comment') {
|
||||
svg += this.createComment(item);
|
||||
} else if (item.type === 'cdata') {
|
||||
}
|
||||
if (item.type === 'cdata') {
|
||||
svg += this.createCDATA(item);
|
||||
}
|
||||
}, this);
|
||||
@ -211,7 +216,7 @@ JS2SVG.prototype.createElem = function (data) {
|
||||
return (
|
||||
this.createIndent() +
|
||||
this.config.tagShortStart +
|
||||
data.elem +
|
||||
data.name +
|
||||
this.createAttrs(data) +
|
||||
this.config.tagShortEnd
|
||||
);
|
||||
@ -219,11 +224,11 @@ JS2SVG.prototype.createElem = function (data) {
|
||||
return (
|
||||
this.createIndent() +
|
||||
this.config.tagShortStart +
|
||||
data.elem +
|
||||
data.name +
|
||||
this.createAttrs(data) +
|
||||
this.config.tagOpenEnd +
|
||||
this.config.tagCloseStart +
|
||||
data.elem +
|
||||
data.name +
|
||||
this.config.tagCloseEnd
|
||||
);
|
||||
}
|
||||
@ -260,14 +265,14 @@ JS2SVG.prototype.createElem = function (data) {
|
||||
return (
|
||||
openIndent +
|
||||
tagOpenStart +
|
||||
data.elem +
|
||||
data.name +
|
||||
this.createAttrs(data) +
|
||||
tagOpenEnd +
|
||||
processedData +
|
||||
dataEnd +
|
||||
closeIndent +
|
||||
tagCloseStart +
|
||||
data.elem +
|
||||
data.name +
|
||||
tagCloseEnd
|
||||
);
|
||||
}
|
||||
|
@ -68,11 +68,16 @@ JSAPI.prototype.clone = function () {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.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;
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (param == null) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(param)) {
|
||||
return param.includes(this.name);
|
||||
}
|
||||
return this.name === param;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -82,7 +87,7 @@ JSAPI.prototype.isElem = function (param) {
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.renameElem = function (name) {
|
||||
if (name && typeof name === 'string') this.elem = name;
|
||||
if (name && typeof name === 'string') this.name = name;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -23,16 +23,14 @@ var config = {
|
||||
*/
|
||||
module.exports = function (data) {
|
||||
var sax = SAX.parser(config.strict, config),
|
||||
root = new JSAPI({ elem: '#document', content: [] }),
|
||||
root = new JSAPI({ type: 'root', content: [] }),
|
||||
current = root,
|
||||
stack = [root];
|
||||
|
||||
function pushToContent(content) {
|
||||
content = new JSAPI(content, current);
|
||||
|
||||
(current.content = current.content || []).push(content);
|
||||
|
||||
return content;
|
||||
const wrapped = new JSAPI(content, current);
|
||||
current.content.push(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
sax.ondoctype = function (doctype) {
|
||||
@ -80,42 +78,44 @@ module.exports = function (data) {
|
||||
};
|
||||
|
||||
sax.onopentag = function (data) {
|
||||
var elem = {
|
||||
elem: data.name,
|
||||
var element = {
|
||||
type: 'element',
|
||||
name: data.name,
|
||||
attrs: {},
|
||||
content: [],
|
||||
};
|
||||
|
||||
elem.class = new CSSClassList(elem);
|
||||
elem.style = new CSSStyleDeclaration(elem);
|
||||
element.class = new CSSClassList(element);
|
||||
element.style = new CSSStyleDeclaration(element);
|
||||
|
||||
if (Object.keys(data.attributes).length) {
|
||||
for (const [name, attr] of Object.entries(data.attributes)) {
|
||||
if (name === 'class') {
|
||||
// has class attribute
|
||||
elem.class.hasClass();
|
||||
element.class.hasClass();
|
||||
}
|
||||
|
||||
if (name === 'style') {
|
||||
// has style attribute
|
||||
elem.style.hasStyle();
|
||||
element.style.hasStyle();
|
||||
}
|
||||
|
||||
elem.attrs[name] = {
|
||||
element.attrs[name] = {
|
||||
name: name,
|
||||
value: attr.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
elem = pushToContent(elem);
|
||||
current = elem;
|
||||
element = pushToContent(element);
|
||||
current = element;
|
||||
|
||||
stack.push(elem);
|
||||
stack.push(element);
|
||||
};
|
||||
|
||||
sax.ontext = function (text) {
|
||||
// prevent trimming of meaningful whitespace inside textual tags
|
||||
if (textElems.includes(current.elem) && !data.prefix) {
|
||||
if (textElems.includes(current.name) && !data.prefix) {
|
||||
pushToContent({
|
||||
type: 'text',
|
||||
value: text,
|
||||
|
@ -27,7 +27,7 @@ var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g,
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.isElem()) {
|
||||
if (item.type === 'element') {
|
||||
item.eachAttr(function (attr) {
|
||||
if (params.newlines) {
|
||||
// new line which requires a space instead of themselve
|
||||
|
@ -122,14 +122,14 @@ exports.fn = function (data, params) {
|
||||
|
||||
// quit if <style> or <script> present ('force' param prevents quitting)
|
||||
if (!params.force) {
|
||||
var isNotEmpty = Boolean(item.content);
|
||||
var isNotEmpty = item.isEmpty() === false;
|
||||
if (item.isElem(styleOrScript) && isNotEmpty) {
|
||||
hasStyleOrScript = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't remove IDs if the whole SVG consists only of defs.
|
||||
if (item.isElem('svg') && item.content) {
|
||||
if (item.isElem('svg')) {
|
||||
var hasDefsOnly = true;
|
||||
|
||||
for (var j = 0; j < item.content.length; j++) {
|
||||
@ -144,7 +144,7 @@ exports.fn = function (data, params) {
|
||||
}
|
||||
}
|
||||
// …and don't remove any ID if yes
|
||||
if (item.isElem()) {
|
||||
if (item.type === 'element') {
|
||||
item.eachAttr(function (attr) {
|
||||
var key, match;
|
||||
|
||||
|
@ -36,7 +36,7 @@ var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.isElem()) {
|
||||
if (item.type === 'element') {
|
||||
var floatPrecision = params.floatPrecision;
|
||||
|
||||
if (item.hasAttr('viewBox')) {
|
||||
|
@ -42,7 +42,7 @@ function hasAnimatedAttr(item) {
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
// non-empty elements
|
||||
if (item.isElem() && !item.isElem('switch') && !item.isEmpty()) {
|
||||
if (item.type === 'element' && !item.isElem('switch') && !item.isEmpty()) {
|
||||
item.content.forEach(function (g, i) {
|
||||
// non-empty groups
|
||||
if (g.isElem('g') && !g.isEmpty()) {
|
||||
@ -51,7 +51,7 @@ exports.fn = function (item) {
|
||||
var inner = g.content[0];
|
||||
|
||||
if (
|
||||
inner.isElem() &&
|
||||
inner.type === 'element' &&
|
||||
!inner.hasAttr('id') &&
|
||||
!g.hasAttr('filter') &&
|
||||
!(g.hasAttr('class') && inner.hasAttr('class')) &&
|
||||
|
@ -50,7 +50,7 @@ var collections = require('./_collections'),
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
item.eachAttr(function (attr) {
|
||||
if (collections.colorsProps.indexOf(attr.name) > -1) {
|
||||
var val = attr.value,
|
||||
|
@ -67,7 +67,7 @@ var stylingProps = require('./_collections').attrsGroups.presentation,
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.elem && item.hasAttr('style')) {
|
||||
if (item.type === 'element' && item.hasAttr('style')) {
|
||||
// ['opacity: 1', 'color: #000']
|
||||
var styleValue = item.attr('style').value,
|
||||
styles = [],
|
||||
|
@ -44,7 +44,7 @@ var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
// transform
|
||||
if (item.hasAttr('transform')) {
|
||||
convertTransform(item, 'transform', params);
|
||||
|
@ -248,7 +248,7 @@ exports.fn = function (document, opts) {
|
||||
1
|
||||
);
|
||||
|
||||
if (styleParentEl.elem === 'defs' && styleParentEl.content.length === 0) {
|
||||
if (styleParentEl.name === 'defs' && styleParentEl.content.length === 0) {
|
||||
// also clean up now empty <def/>s
|
||||
var defsParentEl = styleParentEl.parentNode;
|
||||
defsParentEl.spliceContent(
|
||||
|
@ -26,7 +26,7 @@ exports.params = {
|
||||
* @author Kir Belevich, Lev Solntsev
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (!item.isElem() || item.isEmpty()) return;
|
||||
if (item.type !== 'element' || item.isEmpty()) return;
|
||||
|
||||
var prevContentItem = null,
|
||||
prevContentItemKeys = null;
|
||||
|
@ -81,7 +81,7 @@ function findStyleElems(ast) {
|
||||
|
||||
if (item.isElem('style') && !item.isEmpty()) {
|
||||
styles.push(item);
|
||||
} else if (item.isElem() && item.hasAttr('style')) {
|
||||
} else if (item.type === 'element' && item.hasAttr('style')) {
|
||||
styles.push(item);
|
||||
}
|
||||
}
|
||||
@ -118,8 +118,8 @@ function collectUsageData(ast, options) {
|
||||
safe = false;
|
||||
}
|
||||
|
||||
if (item.isElem()) {
|
||||
usageData.tags[item.elem] = true;
|
||||
if (item.type === 'element') {
|
||||
usageData.tags[item.name] = true;
|
||||
|
||||
if (item.hasAttr('id')) {
|
||||
usageData.ids[item.attr('id').value] = true;
|
||||
|
@ -39,7 +39,7 @@ exports.fn = function (item) {
|
||||
hasTransform = false,
|
||||
hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'),
|
||||
intersected = item.content.every(function (inner) {
|
||||
if (inner.isElem() && inner.hasAttr()) {
|
||||
if (inner.type === 'element' && inner.hasAttr()) {
|
||||
// don't mess with possible styles (hack until CSS parsing is implemented)
|
||||
if (inner.hasAttr('class')) return false;
|
||||
if (!Object.keys(intersection).length) {
|
||||
|
@ -197,7 +197,7 @@ exports.fn = function (node, opts, extra) {
|
||||
|
||||
// <style/> property values
|
||||
|
||||
if (node.elem === 'style') {
|
||||
if (node.name === 'style') {
|
||||
if (node.isEmpty()) {
|
||||
// skip empty <style/>s
|
||||
return node;
|
||||
|
@ -87,7 +87,7 @@ exports.fn = function (item, params) {
|
||||
params.attrs = [params.attrs];
|
||||
}
|
||||
|
||||
if (item.isElem()) {
|
||||
if (item.type === 'element') {
|
||||
var elemSeparator =
|
||||
typeof params.elemSeparator == 'string'
|
||||
? params.elemSeparator
|
||||
@ -122,7 +122,7 @@ exports.fn = function (item, params) {
|
||||
// loop patterns
|
||||
patterns.forEach(function (pattern) {
|
||||
// matches element
|
||||
if (pattern[0].test(item.elem)) {
|
||||
if (pattern[0].test(item.name)) {
|
||||
// loop attributes
|
||||
item.eachAttr(function (attr) {
|
||||
var name = attr.name;
|
||||
|
@ -34,7 +34,7 @@ exports.fn = function (item, params) {
|
||||
editorNamespaces = editorNamespaces.concat(params.additionalNamespaces);
|
||||
}
|
||||
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
if (item.isElem('svg')) {
|
||||
item.eachAttr(function (attr) {
|
||||
const { prefix, local } = parseName(attr.name);
|
||||
@ -56,7 +56,7 @@ exports.fn = function (item, params) {
|
||||
});
|
||||
|
||||
// <sodipodi:*>
|
||||
const { prefix } = parseName(item.elem);
|
||||
const { prefix } = parseName(item.name);
|
||||
if (prefixes.includes(prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ exports.fn = function (item, params) {
|
||||
});
|
||||
|
||||
// abort if current item is no an element
|
||||
if (!item.isElem()) {
|
||||
if (item.type !== 'element') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ exports.description = 'removes empty attributes';
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
item.eachAttr(function (attr) {
|
||||
if (
|
||||
attr.value === '' &&
|
||||
|
@ -48,7 +48,7 @@ exports.params = {
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
// Removes hidden elements
|
||||
// https://www.w3schools.com/cssref/pr_class_visibility.asp
|
||||
const computedStyle = computeStyle(item);
|
||||
|
@ -102,7 +102,9 @@ function parseViewBox(svg) {
|
||||
};
|
||||
|
||||
var path = new JSAPI({
|
||||
elem: 'path',
|
||||
type: 'element',
|
||||
name: 'path',
|
||||
content: []
|
||||
});
|
||||
path.addAttr({
|
||||
name: 'd',
|
||||
|
@ -68,8 +68,8 @@ for (const elem of Object.values(elems)) {
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
// elems w/o namespace prefix
|
||||
if (item.isElem() && !parseName(item.elem).prefix) {
|
||||
var elem = item.elem;
|
||||
if (item.type === 'element' && !parseName(item.name).prefix) {
|
||||
var elem = item.name;
|
||||
|
||||
// remove unknown element's content
|
||||
if (
|
||||
@ -80,12 +80,12 @@ exports.fn = function (item, params) {
|
||||
) {
|
||||
item.content.forEach(function (content, i) {
|
||||
if (
|
||||
content.isElem() &&
|
||||
!parseName(content.elem).prefix &&
|
||||
content.type === 'element' &&
|
||||
!parseName(content.name).prefix &&
|
||||
((elems[elem].content && // Do we have a record of its permitted content?
|
||||
elems[elem].content.indexOf(content.elem) === -1) ||
|
||||
elems[elem].content.indexOf(content.name) === -1) ||
|
||||
(!elems[elem].content && // we dont know about its permitted content
|
||||
!elems[content.elem])) // 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);
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ exports.fn = function (data) {
|
||||
}
|
||||
|
||||
if (xmlnsCollection.length) {
|
||||
const { prefix } = parseName(item.elem);
|
||||
const { prefix } = parseName(item.name);
|
||||
// check item for the ns-attrs
|
||||
if (prefix) {
|
||||
removeNSfromCollection(prefix);
|
||||
|
@ -49,7 +49,8 @@ exports.fn = function (data) {
|
||||
if (defs.length > 0) {
|
||||
const defsTag = new JSAPI(
|
||||
{
|
||||
elem: 'defs',
|
||||
type: 'element',
|
||||
name: 'defs',
|
||||
content: [],
|
||||
attrs: [],
|
||||
},
|
||||
|
@ -44,7 +44,7 @@ exports.fn = function (item, params) {
|
||||
orderlen = params.order.length + 1,
|
||||
xmlnsOrder = params.xmlnsOrder || 'front';
|
||||
|
||||
if (item.elem) {
|
||||
if (item.type === 'element') {
|
||||
item.eachAttr(function (attr) {
|
||||
attrs.push(attr);
|
||||
});
|
||||
|
@ -19,23 +19,23 @@ exports.fn = function (item) {
|
||||
if (item.isElem('defs')) {
|
||||
if (item.content) {
|
||||
var frequency = item.content.reduce(function (frequency, child) {
|
||||
if (child.elem in frequency) {
|
||||
frequency[child.elem]++;
|
||||
if (child.name in frequency) {
|
||||
frequency[child.name]++;
|
||||
} else {
|
||||
frequency[child.elem] = 1;
|
||||
frequency[child.name] = 1;
|
||||
}
|
||||
return frequency;
|
||||
}, {});
|
||||
item.content.sort(function (a, b) {
|
||||
var frequencyComparison = frequency[b.elem] - frequency[a.elem];
|
||||
var frequencyComparison = frequency[b.name] - frequency[a.name];
|
||||
if (frequencyComparison !== 0) {
|
||||
return frequencyComparison;
|
||||
}
|
||||
var lengthComparison = b.elem.length - a.elem.length;
|
||||
var lengthComparison = b.name.length - a.name.length;
|
||||
if (lengthComparison !== 0) {
|
||||
return lengthComparison;
|
||||
}
|
||||
return a.elem != b.elem ? (a.elem > b.elem ? -1 : 1) : 0;
|
||||
return a.name != b.name ? (a.name > b.name ? -1 : 1) : 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -76,9 +76,11 @@ describe('svg2js', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('elem', function () {
|
||||
it('should have property elem: "svg"', function () {
|
||||
expect(root.content[3]).to.have.property('elem', 'svg');
|
||||
describe('name', function () {
|
||||
it('should have property name: "svg"', function () {
|
||||
expect(root.content[3]).to.include({
|
||||
name: 'svg',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user