mirror of
https://github.com/svg/svgo.git
synced 2025-08-01 18:46:52 +03:00
Access attributes directly (#1433)
Got rid from `.attrs`, `.attr()` and `.addAttr()` usages
This commit is contained in:
@ -19,7 +19,7 @@ const existsOne = (test, elems) => {
|
||||
};
|
||||
|
||||
const getAttributeValue = (elem, name) => {
|
||||
return elem.hasAttr(name) ? elem.attr(name).value : undefined;
|
||||
return elem.attributes[name];
|
||||
};
|
||||
|
||||
const getChildren = (node) => {
|
||||
@ -47,7 +47,7 @@ const getText = (node) => {
|
||||
};
|
||||
|
||||
const hasAttrib = (elem, name) => {
|
||||
return getAttributeValue(elem, name) !== undefined;
|
||||
return elem.attributes[name] !== undefined;
|
||||
};
|
||||
|
||||
const removeSubsets = (nodes) => {
|
||||
|
@ -203,9 +203,13 @@ JS2SVG.prototype.createCDATA = function (node) {
|
||||
*/
|
||||
JS2SVG.prototype.createElem = function (data) {
|
||||
// beautiful injection for obtaining SVG information :)
|
||||
if (data.isElem('svg') && data.hasAttr('width') && data.hasAttr('height')) {
|
||||
this.width = data.attr('width').value;
|
||||
this.height = data.attr('height').value;
|
||||
if (
|
||||
data.name === 'svg' &&
|
||||
data.attributes.width != null &&
|
||||
data.attributes.height != null
|
||||
) {
|
||||
this.width = data.attributes.width;
|
||||
this.height = data.attributes.height;
|
||||
}
|
||||
|
||||
// empty element and short tag
|
||||
|
@ -289,7 +289,7 @@ JSAPI.prototype.computedAttr = function (name, val) {
|
||||
|
||||
for (
|
||||
var elem = this;
|
||||
elem && (!elem.hasAttr(name) || !elem.attr(name).value);
|
||||
elem && (!elem.hasAttr(name) || !elem.attributes[name]);
|
||||
elem = elem.parentNode
|
||||
);
|
||||
|
||||
|
@ -38,7 +38,7 @@ const applyTransforms = (elem, pathData, params) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const matrix = transformsMultiply(transform2js(elem.attr('transform').value));
|
||||
const matrix = transformsMultiply(transform2js(elem.attributes.transform));
|
||||
const stroke = elem.computedAttr('stroke');
|
||||
const id = elem.computedAttr('id');
|
||||
const transformPrecision = params.transformPrecision;
|
||||
@ -77,34 +77,33 @@ const applyTransforms = (elem, pathData, params) => {
|
||||
elem.computedAttr('stroke-width') || defaultStrokeWidth;
|
||||
|
||||
if (
|
||||
!elem.hasAttr('vector-effect') ||
|
||||
elem.attr('vector-effect').value !== 'non-scaling-stroke'
|
||||
elem.attributes['vector-effect'] == null ||
|
||||
elem.attributes['vector-effect'] !== 'non-scaling-stroke'
|
||||
) {
|
||||
if (elem.hasAttr('stroke-width')) {
|
||||
elem.attrs['stroke-width'].value = elem.attrs['stroke-width'].value
|
||||
if (elem.attributes['stroke-width'] != null) {
|
||||
elem.attributes['stroke-width'] = elem.attributes['stroke-width']
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
} else {
|
||||
elem.addAttr({
|
||||
name: 'stroke-width',
|
||||
value: strokeWidth.replace(regNumericValues, (num) =>
|
||||
elem.attributes[
|
||||
'stroke-width'
|
||||
] = strokeWidth.replace(regNumericValues, (num) =>
|
||||
removeLeadingZero(num * scale)
|
||||
),
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
if (elem.hasAttr('stroke-dashoffset')) {
|
||||
elem.attrs['stroke-dashoffset'].value = elem.attrs[
|
||||
if (elem.attributes['stroke-dashoffset'] != null) {
|
||||
elem.attributes['stroke-dashoffset'] = elem.attributes[
|
||||
'stroke-dashoffset'
|
||||
].value
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
}
|
||||
|
||||
if (elem.hasAttr('stroke-dasharray')) {
|
||||
elem.attrs['stroke-dasharray'].value = elem.attrs[
|
||||
if (elem.attributes['stroke-dasharray'] != null) {
|
||||
elem.attributes['stroke-dasharray'] = elem.attributes[
|
||||
'stroke-dasharray'
|
||||
].value
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ var prevCtrlPoint;
|
||||
exports.path2js = function (path) {
|
||||
if (path.pathJS) return path.pathJS;
|
||||
const pathData = []; // JS representation of the path data
|
||||
const newPathData = parsePathData(path.attr('d').value);
|
||||
const newPathData = parsePathData(path.attributes.d);
|
||||
for (const { command, args } of newPathData) {
|
||||
if (command === 'Z' || command === 'z') {
|
||||
pathData.push({ instruction: 'z' });
|
||||
@ -343,7 +343,7 @@ exports.js2path = function (path, data, params) {
|
||||
});
|
||||
}
|
||||
|
||||
path.attr('d').value = stringifyPathData({
|
||||
path.attributes.d = stringifyPathData({
|
||||
pathData,
|
||||
precision: params.floatPrecision,
|
||||
disableSpaceAfterFlags: params.noSpaceAfterFlags,
|
||||
|
@ -62,18 +62,13 @@ exports.fn = function (data, params) {
|
||||
if (svg.isElem('svg')) {
|
||||
attributes.forEach(function (attribute) {
|
||||
if (typeof attribute === 'string') {
|
||||
if (!svg.hasAttr(attribute)) {
|
||||
svg.addAttr({
|
||||
name: attribute,
|
||||
});
|
||||
if (svg.attributes[attribute] == null) {
|
||||
svg.attributes[attribute] = undefined;
|
||||
}
|
||||
} else if (typeof attribute === 'object') {
|
||||
Object.keys(attribute).forEach(function (key) {
|
||||
if (!svg.hasAttr(key)) {
|
||||
svg.addAttr({
|
||||
name: key,
|
||||
value: attribute[key],
|
||||
});
|
||||
if (svg.attributes[key] == null) {
|
||||
svg.attributes[key] = attribute[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ exports.fn = function (data, params) {
|
||||
);
|
||||
} while (idPreserved(currentIDstring));
|
||||
|
||||
IDs.get(key).attr('id').value = currentIDstring;
|
||||
IDs.get(key).attributes.id = currentIDstring;
|
||||
|
||||
for (const { element, name, value } of refs) {
|
||||
element.attributes[name] = value.includes(idValuePrefix)
|
||||
|
@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { removeLeadingZero } = require('../lib/svgo/tools.js');
|
||||
|
||||
exports.type = 'perItem';
|
||||
|
||||
exports.active = false;
|
||||
@ -13,17 +15,16 @@ exports.params = {
|
||||
convertToPx: true,
|
||||
};
|
||||
|
||||
var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
|
||||
regSeparator = /\s+,?\s*|,\s*/,
|
||||
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
|
||||
absoluteLengths = {
|
||||
const regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
|
||||
const regSeparator = /\s+,?\s*|,\s*/;
|
||||
const absoluteLengths = {
|
||||
// relative to px
|
||||
cm: 96 / 2.54,
|
||||
mm: 96 / 25.4,
|
||||
in: 96,
|
||||
pt: 4 / 3,
|
||||
pc: 16,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Round list of values to the fixed precision.
|
||||
@ -46,49 +47,55 @@ var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|
|
||||
* @author kiyopikko
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.hasAttr('points')) {
|
||||
roundValues(item.attrs.points);
|
||||
if (item.type !== 'element') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.hasAttr('enable-background')) {
|
||||
roundValues(item.attrs['enable-background']);
|
||||
if (item.attributes.points != null) {
|
||||
item.attributes.points = roundValues(item.attributes.points);
|
||||
}
|
||||
|
||||
if (item.hasAttr('viewBox')) {
|
||||
roundValues(item.attrs.viewBox);
|
||||
if (item.attributes['enable-background'] != null) {
|
||||
item.attributes['enable-background'] = roundValues(
|
||||
item.attributes['enable-background']
|
||||
);
|
||||
}
|
||||
|
||||
if (item.hasAttr('stroke-dasharray')) {
|
||||
roundValues(item.attrs['stroke-dasharray']);
|
||||
if (item.attributes.viewBox != null) {
|
||||
item.attributes.viewBox = roundValues(item.attributes.viewBox);
|
||||
}
|
||||
|
||||
if (item.hasAttr('dx')) {
|
||||
roundValues(item.attrs.dx);
|
||||
if (item.attributes['stroke-dasharray'] != null) {
|
||||
item.attributes['stroke-dasharray'] = roundValues(
|
||||
item.attributes['stroke-dasharray']
|
||||
);
|
||||
}
|
||||
|
||||
if (item.hasAttr('dy')) {
|
||||
roundValues(item.attrs.dy);
|
||||
if (item.attributes.dx != null) {
|
||||
item.attributes.dx = roundValues(item.attributes.dx);
|
||||
}
|
||||
|
||||
if (item.hasAttr('x')) {
|
||||
roundValues(item.attrs.x);
|
||||
if (item.attributes.dy != null) {
|
||||
item.attributes.dy = roundValues(item.attributes.dy);
|
||||
}
|
||||
|
||||
if (item.hasAttr('y')) {
|
||||
roundValues(item.attrs.y);
|
||||
if (item.attributes.x != null) {
|
||||
item.attributes.x = roundValues(item.attributes.x);
|
||||
}
|
||||
|
||||
function roundValues($prop) {
|
||||
if (item.attributes.y != null) {
|
||||
item.attributes.y = roundValues(item.attributes.y);
|
||||
}
|
||||
|
||||
function roundValues(lists) {
|
||||
var num,
|
||||
units,
|
||||
match,
|
||||
matchNew,
|
||||
lists = $prop.value,
|
||||
listsArr = lists.split(regSeparator),
|
||||
roundedListArr = [],
|
||||
roundedList;
|
||||
roundedList = [];
|
||||
|
||||
listsArr.forEach(function (elem) {
|
||||
for (const elem of listsArr) {
|
||||
match = elem.match(regNumericValues);
|
||||
matchNew = elem.match(/new/);
|
||||
|
||||
@ -118,17 +125,16 @@ exports.fn = function (item, params) {
|
||||
units = '';
|
||||
}
|
||||
|
||||
roundedListArr.push(num + units);
|
||||
roundedList.push(num + units);
|
||||
}
|
||||
// if attribute value is "new"(only enable-background).
|
||||
else if (matchNew) {
|
||||
roundedListArr.push('new');
|
||||
roundedList.push('new');
|
||||
} else if (elem) {
|
||||
roundedListArr.push(elem);
|
||||
roundedList.push(elem);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
roundedList = roundedListArr.join(' ');
|
||||
$prop.value = roundedList;
|
||||
return roundedList.join(' ');
|
||||
}
|
||||
};
|
||||
|
@ -39,9 +39,9 @@ exports.fn = function (item, params) {
|
||||
if (item.type === 'element') {
|
||||
var floatPrecision = params.floatPrecision;
|
||||
|
||||
if (item.hasAttr('viewBox')) {
|
||||
var nums = item.attr('viewBox').value.split(/\s,?\s*|,\s*/g);
|
||||
item.attr('viewBox').value = nums
|
||||
if (item.attributes.viewBox != null) {
|
||||
var nums = item.attributes.viewBox.split(/\s,?\s*|,\s*/g);
|
||||
item.attributes.viewBox = nums
|
||||
.map(function (value) {
|
||||
var num = +value;
|
||||
return isNaN(num) ? value : +num.toFixed(floatPrecision);
|
||||
|
@ -13,7 +13,6 @@ exports.params = {
|
||||
floatPrecision: null,
|
||||
};
|
||||
|
||||
const none = { value: 0 };
|
||||
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
||||
|
||||
/**
|
||||
@ -35,15 +34,15 @@ exports.fn = function (item, params) {
|
||||
|
||||
if (
|
||||
item.isElem('rect') &&
|
||||
item.hasAttr('width') &&
|
||||
item.hasAttr('height') &&
|
||||
!item.hasAttr('rx') &&
|
||||
!item.hasAttr('ry')
|
||||
item.attributes.width != null &&
|
||||
item.attributes.height != null &&
|
||||
item.attributes.rx == null &&
|
||||
item.attributes.ry == null
|
||||
) {
|
||||
var x = +(item.attr('x') || none).value,
|
||||
y = +(item.attr('y') || none).value,
|
||||
width = +item.attr('width').value,
|
||||
height = +item.attr('height').value;
|
||||
const x = Number(item.attributes.x || '0');
|
||||
const y = Number(item.attributes.y || '0');
|
||||
const width = Number(item.attributes.width);
|
||||
const height = Number(item.attributes.height);
|
||||
// Values like '100%' compute to NaN, thus running after
|
||||
// cleanupNumericValues when 'px' units has already been removed.
|
||||
// TODO: Calculate sizes from % and non-px units if possible.
|
||||
@ -55,10 +54,7 @@ exports.fn = function (item, params) {
|
||||
{ command: 'H', args: [x] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
item.addAttr({
|
||||
name: 'd',
|
||||
value: stringifyPathData({ pathData, precision }),
|
||||
});
|
||||
item.attributes.d = stringifyPathData({ pathData, precision });
|
||||
item.renameElem('path');
|
||||
delete item.attributes.x;
|
||||
delete item.attributes.y;
|
||||
@ -67,19 +63,16 @@ exports.fn = function (item, params) {
|
||||
}
|
||||
|
||||
if (item.isElem('line')) {
|
||||
var x1 = +(item.attr('x1') || none).value,
|
||||
y1 = +(item.attr('y1') || none).value,
|
||||
x2 = +(item.attr('x2') || none).value,
|
||||
y2 = +(item.attr('y2') || none).value;
|
||||
const x1 = Number(item.attributes.x1 || '0');
|
||||
const y1 = Number(item.attributes.y1 || '0');
|
||||
const x2 = Number(item.attributes.x2 || '0');
|
||||
const y2 = Number(item.attributes.y2 || '0');
|
||||
if (isNaN(x1 - y1 + x2 - y2)) return;
|
||||
const pathData = [
|
||||
{ command: 'M', args: [x1, y1] },
|
||||
{ command: 'L', args: [x2, y2] },
|
||||
];
|
||||
item.addAttr({
|
||||
name: 'd',
|
||||
value: stringifyPathData({ pathData, precision }),
|
||||
});
|
||||
item.attributes.d = stringifyPathData({ pathData, precision });
|
||||
item.renameElem('path');
|
||||
delete item.attributes.x1;
|
||||
delete item.attributes.y1;
|
||||
@ -89,9 +82,9 @@ exports.fn = function (item, params) {
|
||||
|
||||
if (
|
||||
(item.isElem('polyline') || item.isElem('polygon')) &&
|
||||
item.hasAttr('points')
|
||||
item.attributes.points != null
|
||||
) {
|
||||
var coords = (item.attr('points').value.match(regNumber) || []).map(Number);
|
||||
const coords = (item.attributes.points.match(regNumber) || []).map(Number);
|
||||
if (coords.length < 4) return false;
|
||||
const pathData = [];
|
||||
for (let i = 0; i < coords.length; i += 2) {
|
||||
@ -103,18 +96,15 @@ exports.fn = function (item, params) {
|
||||
if (item.isElem('polygon')) {
|
||||
pathData.push({ command: 'z', args: [] });
|
||||
}
|
||||
item.addAttr({
|
||||
name: 'd',
|
||||
value: stringifyPathData({ pathData, precision }),
|
||||
});
|
||||
item.attributes.d = stringifyPathData({ pathData, precision });
|
||||
item.renameElem('path');
|
||||
delete item.attributes.points;
|
||||
}
|
||||
|
||||
if (item.isElem('circle') && convertArcs) {
|
||||
var cx = +(item.attr('cx') || none).value;
|
||||
var cy = +(item.attr('cy') || none).value;
|
||||
var r = +(item.attr('r') || none).value;
|
||||
const cx = Number(item.attributes.cx || '0');
|
||||
const cy = Number(item.attributes.cy || '0');
|
||||
const r = Number(item.attributes.r || '0');
|
||||
if (isNaN(cx - cy + r)) {
|
||||
return;
|
||||
}
|
||||
@ -124,10 +114,7 @@ exports.fn = function (item, params) {
|
||||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
item.addAttr({
|
||||
name: 'd',
|
||||
value: stringifyPathData({ pathData, precision }),
|
||||
});
|
||||
item.attributes.d = stringifyPathData({ pathData, precision });
|
||||
item.renameElem('path');
|
||||
delete item.attributes.cx;
|
||||
delete item.attributes.cy;
|
||||
@ -135,10 +122,10 @@ exports.fn = function (item, params) {
|
||||
}
|
||||
|
||||
if (item.isElem('ellipse') && convertArcs) {
|
||||
var ecx = +(item.attr('cx') || none).value;
|
||||
var ecy = +(item.attr('cy') || none).value;
|
||||
var rx = +(item.attr('rx') || none).value;
|
||||
var ry = +(item.attr('ry') || none).value;
|
||||
const ecx = Number(item.attributes.cx || '0');
|
||||
const ecy = Number(item.attributes.cy || '0');
|
||||
const rx = Number(item.attributes.rx || '0');
|
||||
const ry = Number(item.attributes.ry || '0');
|
||||
if (isNaN(ecx - ecy + rx - ry)) {
|
||||
return;
|
||||
}
|
||||
@ -148,10 +135,7 @@ exports.fn = function (item, params) {
|
||||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
item.addAttr({
|
||||
name: 'd',
|
||||
value: stringifyPathData({ pathData, precision }),
|
||||
});
|
||||
item.attributes.d = stringifyPathData({ pathData, precision });
|
||||
item.renameElem('path');
|
||||
delete item.attributes.cx;
|
||||
delete item.attributes.cy;
|
||||
|
@ -67,20 +67,22 @@ var stylingProps = require('./_collections').attrsGroups.presentation,
|
||||
* @author Kir Belevich
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.type === 'element' && item.hasAttr('style')) {
|
||||
if (item.type === 'element' && item.attributes.style != null) {
|
||||
// ['opacity: 1', 'color: #000']
|
||||
var styleValue = item.attr('style').value,
|
||||
styles = [],
|
||||
attrs = {};
|
||||
let styles = [];
|
||||
const newAttributes = {};
|
||||
|
||||
// Strip CSS comments preserving escape sequences and strings.
|
||||
styleValue = styleValue.replace(regStripComments, function (match) {
|
||||
const styleValue = item.attributes.style.replace(
|
||||
regStripComments,
|
||||
(match) => {
|
||||
return match[0] == '/'
|
||||
? ''
|
||||
: match[0] == '\\' && /[-g-z]/i.test(match[1])
|
||||
? match[1]
|
||||
: match;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
regDeclarationBlock.lastIndex = 0;
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
@ -100,11 +102,8 @@ exports.fn = function (item, params) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
|
||||
if (stylingProps.indexOf(prop) > -1) {
|
||||
attrs[prop] = {
|
||||
name: prop,
|
||||
value: val,
|
||||
};
|
||||
if (stylingProps.includes(prop)) {
|
||||
newAttributes[prop] = val;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -113,13 +112,11 @@ exports.fn = function (item, params) {
|
||||
return true;
|
||||
});
|
||||
|
||||
Object.assign(item.attrs, attrs);
|
||||
Object.assign(item.attributes, newAttributes);
|
||||
|
||||
if (styles.length) {
|
||||
item.attr('style').value = styles
|
||||
.map(function (declaration) {
|
||||
return declaration.join(':');
|
||||
})
|
||||
item.attributes.style = styles
|
||||
.map((declaration) => declaration.join(':'))
|
||||
.join(';');
|
||||
} else {
|
||||
delete item.attributes.style;
|
||||
|
@ -70,7 +70,7 @@ exports.fn = function (item, params) {
|
||||
* @param {Object} params plugin params
|
||||
*/
|
||||
function convertTransform(item, attrName, params) {
|
||||
var data = transform2js(item.attr(attrName).value);
|
||||
let data = transform2js(item.attributes[attrName]);
|
||||
params = definePrecision(data, params);
|
||||
|
||||
if (params.collapseIntoOne && data.length > 1) {
|
||||
|
@ -55,9 +55,9 @@ exports.fn = function (document, opts) {
|
||||
for (var styleEl of styleEls) {
|
||||
// values other than the empty string or text/css are not used
|
||||
if (
|
||||
styleEl.hasAttr('type') &&
|
||||
styleEl.attr('type').value !== '' &&
|
||||
styleEl.attr('type').value !== 'text/css'
|
||||
styleEl.attributes.type != null &&
|
||||
styleEl.attributes.type !== '' &&
|
||||
styleEl.attributes.type !== 'text/css'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
@ -28,18 +28,18 @@ exports.params = {
|
||||
exports.fn = function (item, params) {
|
||||
if (item.type !== 'element' || item.children.length === 0) return;
|
||||
|
||||
var prevContentItem = null,
|
||||
prevContentItemKeys = null;
|
||||
let prevContentItem = null;
|
||||
let prevContentItemKeys = null;
|
||||
|
||||
item.children = item.children.filter(function (contentItem) {
|
||||
if (
|
||||
prevContentItem &&
|
||||
prevContentItem.isElem('path') &&
|
||||
prevContentItem.children.length === 0 &&
|
||||
prevContentItem.hasAttr('d') &&
|
||||
prevContentItem.attributes.d != null &&
|
||||
contentItem.isElem('path') &&
|
||||
contentItem.children.length === 0 &&
|
||||
contentItem.hasAttr('d')
|
||||
contentItem.attributes.d != null
|
||||
) {
|
||||
const computedStyle = computeStyle(contentItem);
|
||||
// keep path to not break markers
|
||||
@ -51,21 +51,21 @@ exports.fn = function (item, params) {
|
||||
return true;
|
||||
}
|
||||
if (!prevContentItemKeys) {
|
||||
prevContentItemKeys = Object.keys(prevContentItem.attrs);
|
||||
prevContentItemKeys = Object.keys(prevContentItem.attributes);
|
||||
}
|
||||
|
||||
var contentItemAttrs = Object.keys(contentItem.attrs),
|
||||
equalData =
|
||||
const contentItemAttrs = Object.keys(contentItem.attributes);
|
||||
const equalData =
|
||||
prevContentItemKeys.length == contentItemAttrs.length &&
|
||||
contentItemAttrs.every(function (key) {
|
||||
return (
|
||||
key == 'd' ||
|
||||
(prevContentItem.hasAttr(key) &&
|
||||
prevContentItem.attr(key).value == contentItem.attr(key).value)
|
||||
(prevContentItem.attributes[key] != null &&
|
||||
prevContentItem.attributes[key] == contentItem.attributes[key])
|
||||
);
|
||||
}),
|
||||
prevPathJS = path2js(prevContentItem),
|
||||
curPathJS = path2js(contentItem);
|
||||
});
|
||||
const prevPathJS = path2js(prevContentItem);
|
||||
const curPathJS = path2js(contentItem);
|
||||
|
||||
if (equalData && (params.force || !intersects(prevPathJS, curPathJS))) {
|
||||
js2path(prevContentItem, prevPathJS.concat(curPathJS), params);
|
||||
|
@ -56,9 +56,9 @@ exports.fn = function (ast, options) {
|
||||
}
|
||||
} else {
|
||||
// style attribute
|
||||
var elemStyle = elem.attr('style').value;
|
||||
var elemStyle = elem.attributes.style;
|
||||
|
||||
elem.attr('style').value = csso.minifyBlock(
|
||||
elem.attributes.style = csso.minifyBlock(
|
||||
elemStyle,
|
||||
minifyOptionsForAttribute
|
||||
).css;
|
||||
@ -84,7 +84,7 @@ function findStyleElems(ast) {
|
||||
|
||||
if (item.isElem('style') && item.children.length !== 0) {
|
||||
styles.push(item);
|
||||
} else if (item.type === 'element' && item.hasAttr('style')) {
|
||||
} else if (item.type === 'element' && item.attributes.style != null) {
|
||||
styles.push(item);
|
||||
}
|
||||
}
|
||||
@ -109,41 +109,33 @@ function shouldFilter(options, name) {
|
||||
|
||||
function collectUsageData(ast, options) {
|
||||
function walk(items, usageData) {
|
||||
for (var i = 0; i < items.children.length; i++) {
|
||||
var item = items.children[i];
|
||||
|
||||
for (const item of items.children) {
|
||||
// go deeper
|
||||
if (item.children) {
|
||||
if (item.type === 'root' || item.type === 'element') {
|
||||
walk(item, usageData);
|
||||
}
|
||||
|
||||
if (item.isElem('script')) {
|
||||
if (item.type === 'element') {
|
||||
if (item.name === 'script') {
|
||||
safe = false;
|
||||
}
|
||||
|
||||
if (item.type === 'element') {
|
||||
usageData.tags[item.name] = true;
|
||||
|
||||
if (item.hasAttr('id')) {
|
||||
usageData.ids[item.attr('id').value] = true;
|
||||
if (item.attributes.id != null) {
|
||||
usageData.ids[item.attributes.id] = true;
|
||||
}
|
||||
|
||||
if (item.hasAttr('class')) {
|
||||
item
|
||||
.attr('class')
|
||||
.value.replace(/^\s+|\s+$/g, '')
|
||||
if (item.attributes.class != null) {
|
||||
item.attributes.class
|
||||
.replace(/^\s+|\s+$/g, '')
|
||||
.split(/\s+/)
|
||||
.forEach(function (className) {
|
||||
usageData.classes[className] = true;
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
item.attrs &&
|
||||
Object.keys(item.attrs).some(function (name) {
|
||||
return /^on/i.test(name);
|
||||
})
|
||||
) {
|
||||
if (Object.keys(item.attributes).some((name) => /^on/i.test(name))) {
|
||||
safe = false;
|
||||
}
|
||||
}
|
||||
|
@ -69,15 +69,16 @@ exports.fn = function (item) {
|
||||
if (name === 'transform') {
|
||||
if (!hasTransform) {
|
||||
if (item.hasAttr('transform')) {
|
||||
item.attr('transform').value += ' ' + value;
|
||||
item.attributes.transform =
|
||||
item.attributes.transform + ' ' + value;
|
||||
} else {
|
||||
item.addAttr({ name, value });
|
||||
item.attributes.transform = value;
|
||||
}
|
||||
|
||||
hasTransform = true;
|
||||
}
|
||||
} else {
|
||||
item.addAttr({ name, value });
|
||||
item.attributes[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,11 +51,6 @@ var matchUrl = function (val) {
|
||||
return urlMatches[1];
|
||||
};
|
||||
|
||||
// Checks if attribute is empty
|
||||
var attrNotEmpty = function (attr) {
|
||||
return attr && attr.value && attr.value.length > 0;
|
||||
};
|
||||
|
||||
// prefixes an #ID
|
||||
var prefixId = function (val) {
|
||||
var idName = matchId(val);
|
||||
@ -65,74 +60,88 @@ var prefixId = function (val) {
|
||||
return '#' + addPrefix(idName);
|
||||
};
|
||||
|
||||
// attr.value helper methods
|
||||
|
||||
// prefixes a class attribute value
|
||||
var addPrefixToClassAttr = function (attr) {
|
||||
if (!attrNotEmpty(attr)) {
|
||||
const addPrefixToClassAttr = (element, name) => {
|
||||
if (
|
||||
element.attributes[name] == null ||
|
||||
element.attributes[name].length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
attr.value = attr.value.split(/\s+/).map(addPrefix).join(' ');
|
||||
element.attributes[name] = element.attributes[name]
|
||||
.split(/\s+/)
|
||||
.map(addPrefix)
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
// prefixes an ID attribute value
|
||||
var addPrefixToIdAttr = function (attr) {
|
||||
if (!attrNotEmpty(attr)) {
|
||||
const addPrefixToIdAttr = (element, name) => {
|
||||
if (
|
||||
element.attributes[name] == null ||
|
||||
element.attributes[name].length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
attr.value = addPrefix(attr.value);
|
||||
element.attributes[name] = addPrefix(element.attributes[name]);
|
||||
};
|
||||
|
||||
// prefixes a href attribute value
|
||||
var addPrefixToHrefAttr = function (attr) {
|
||||
if (!attrNotEmpty(attr)) {
|
||||
const addPrefixToHrefAttr = (element, name) => {
|
||||
if (
|
||||
element.attributes[name] == null ||
|
||||
element.attributes[name].length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
var idPrefixed = prefixId(attr.value);
|
||||
const idPrefixed = prefixId(element.attributes[name]);
|
||||
if (!idPrefixed) {
|
||||
return;
|
||||
}
|
||||
attr.value = idPrefixed;
|
||||
element.attributes[name] = idPrefixed;
|
||||
};
|
||||
|
||||
// prefixes an URL attribute value
|
||||
var addPrefixToUrlAttr = function (attr) {
|
||||
if (!attrNotEmpty(attr)) {
|
||||
const addPrefixToUrlAttr = (element, name) => {
|
||||
if (
|
||||
element.attributes[name] == null ||
|
||||
element.attributes[name].length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// url(...) in value
|
||||
var urlVal = matchUrl(attr.value);
|
||||
const urlVal = matchUrl(element.attributes[name]);
|
||||
if (!urlVal) {
|
||||
return;
|
||||
}
|
||||
|
||||
var idPrefixed = prefixId(urlVal);
|
||||
const idPrefixed = prefixId(urlVal);
|
||||
if (!idPrefixed) {
|
||||
return;
|
||||
}
|
||||
|
||||
attr.value = 'url(' + idPrefixed + ')';
|
||||
element.attributes[name] = 'url(' + idPrefixed + ')';
|
||||
};
|
||||
|
||||
// prefixes begin/end attribute value
|
||||
var addPrefixToBeginEndAttr = function (attr) {
|
||||
if (!attrNotEmpty(attr)) {
|
||||
const addPrefixToBeginEndAttr = (element, name) => {
|
||||
if (
|
||||
element.attributes[name] == null ||
|
||||
element.attributes[name].length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = attr.value.split('; ').map(function (val) {
|
||||
const parts = element.attributes[name].split('; ').map((val) => {
|
||||
val = val.trim();
|
||||
|
||||
if (val.endsWith('.end') || val.endsWith('.start')) {
|
||||
var idPostfix = val.split('.'),
|
||||
id = idPostfix[0],
|
||||
postfix = idPostfix[1];
|
||||
const [id, postfix] = val.split('.');
|
||||
|
||||
var idPrefixed = prefixId(`#${id}`);
|
||||
let idPrefixed = prefixId(`#${id}`);
|
||||
|
||||
if (!idPrefixed) {
|
||||
return val;
|
||||
@ -145,7 +154,7 @@ var addPrefixToBeginEndAttr = function (attr) {
|
||||
}
|
||||
});
|
||||
|
||||
attr.value = parts.join('; ');
|
||||
element.attributes[name] = parts.join('; ');
|
||||
};
|
||||
|
||||
const getBasename = (path) => {
|
||||
@ -169,7 +178,7 @@ const getBasename = (path) => {
|
||||
exports.fn = function (node, opts, extra) {
|
||||
// skip subsequent passes when multipass is used
|
||||
if (extra.multipassCount && extra.multipassCount > 0) {
|
||||
return node;
|
||||
return;
|
||||
}
|
||||
|
||||
// prefix, from file name or option
|
||||
@ -200,7 +209,7 @@ exports.fn = function (node, opts, extra) {
|
||||
if (node.type === 'element' && node.name === 'style') {
|
||||
if (node.children.length === 0) {
|
||||
// skip empty <style/>s
|
||||
return node;
|
||||
return;
|
||||
}
|
||||
|
||||
var cssStr = '';
|
||||
@ -219,7 +228,7 @@ exports.fn = function (node, opts, extra) {
|
||||
'Warning: Parse error of styles of <style/> element, skipped. Error details: ' +
|
||||
parseError
|
||||
);
|
||||
return node;
|
||||
return;
|
||||
}
|
||||
|
||||
var idPrefixed = '';
|
||||
@ -250,42 +259,40 @@ exports.fn = function (node, opts, extra) {
|
||||
|
||||
// update <style>s
|
||||
node.children[0].value = csstree.generate(cssAst);
|
||||
return node;
|
||||
return;
|
||||
}
|
||||
|
||||
// element attributes
|
||||
|
||||
if (!node.attrs) {
|
||||
return node;
|
||||
if (node.type !== 'element') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Nodes
|
||||
|
||||
if (opts.prefixIds) {
|
||||
// ID
|
||||
addPrefixToIdAttr(node.attrs.id);
|
||||
addPrefixToIdAttr(node, 'id');
|
||||
}
|
||||
|
||||
if (opts.prefixClassNames) {
|
||||
// Class
|
||||
addPrefixToClassAttr(node.attrs.class);
|
||||
addPrefixToClassAttr(node, 'class');
|
||||
}
|
||||
|
||||
// References
|
||||
|
||||
// href
|
||||
addPrefixToHrefAttr(node.attrs.href);
|
||||
addPrefixToHrefAttr(node, 'href');
|
||||
|
||||
// (xlink:)href (deprecated, must be still supported)
|
||||
addPrefixToHrefAttr(node.attrs['xlink:href']);
|
||||
addPrefixToHrefAttr(node, 'xlink:href');
|
||||
|
||||
// (referenceable) properties
|
||||
for (var referencesProp of referencesProps) {
|
||||
addPrefixToUrlAttr(node.attrs[referencesProp]);
|
||||
addPrefixToUrlAttr(node, referencesProp);
|
||||
}
|
||||
|
||||
addPrefixToBeginEndAttr(node.attrs.begin);
|
||||
addPrefixToBeginEndAttr(node.attrs.end);
|
||||
|
||||
return node;
|
||||
addPrefixToBeginEndAttr(node, 'begin');
|
||||
addPrefixToBeginEndAttr(node, 'end');
|
||||
};
|
||||
|
@ -65,15 +65,13 @@ exports.fn = function (item, params) {
|
||||
}
|
||||
|
||||
// remove element if it's `id` matches configured `id` params
|
||||
const elemId = item.attr('id');
|
||||
if (elemId && params.id.length !== 0) {
|
||||
return params.id.includes(elemId.value) === false;
|
||||
if (item.attributes.id != null && params.id.length !== 0) {
|
||||
return params.id.includes(item.attributes.id) === false;
|
||||
}
|
||||
|
||||
// remove element if it's `class` contains any of the configured `class` params
|
||||
const elemClass = item.attr('class');
|
||||
if (elemClass && params.class.length !== 0) {
|
||||
const classList = elemClass.value.split(' ');
|
||||
if (item.attributes.class && params.class.length !== 0) {
|
||||
const classList = item.attributes.class.split(' ');
|
||||
return params.class.some((item) => classList.includes(item)) === false;
|
||||
}
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ exports.fn = function (item) {
|
||||
(item.type === 'element' && item.children.length !== 0) ||
|
||||
item.isElem('svg') ||
|
||||
// empty patterns may contain reusable configuration
|
||||
(item.isElem('pattern') && Object.keys(item.attrs).length !== 0) ||
|
||||
(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')) ||
|
||||
|
@ -103,7 +103,7 @@ exports.fn = function (item, params) {
|
||||
params.circleR0 &&
|
||||
item.isElem('circle') &&
|
||||
item.children.length === 0 &&
|
||||
item.hasAttr('r', '0')
|
||||
item.attributes.r === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -118,7 +118,7 @@ exports.fn = function (item, params) {
|
||||
params.ellipseRX0 &&
|
||||
item.isElem('ellipse') &&
|
||||
item.children.length === 0 &&
|
||||
item.hasAttr('rx', '0')
|
||||
item.attributes.rx === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -133,7 +133,7 @@ exports.fn = function (item, params) {
|
||||
params.ellipseRY0 &&
|
||||
item.isElem('ellipse') &&
|
||||
item.children.length === 0 &&
|
||||
item.hasAttr('ry', '0')
|
||||
item.attributes.ry === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -148,7 +148,7 @@ exports.fn = function (item, params) {
|
||||
params.rectWidth0 &&
|
||||
item.isElem('rect') &&
|
||||
item.children.length === 0 &&
|
||||
item.hasAttr('width', '0')
|
||||
item.attributes.width === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -164,7 +164,7 @@ exports.fn = function (item, params) {
|
||||
params.rectWidth0 &&
|
||||
item.isElem('rect') &&
|
||||
item.children.length === 0 &&
|
||||
item.hasAttr('height', '0')
|
||||
item.attributes.height === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -178,7 +178,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.patternWidth0 &&
|
||||
item.isElem('pattern') &&
|
||||
item.hasAttr('width', '0')
|
||||
item.attributes.width === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -192,7 +192,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.patternHeight0 &&
|
||||
item.isElem('pattern') &&
|
||||
item.hasAttr('height', '0')
|
||||
item.attributes.height === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -206,7 +206,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.imageWidth0 &&
|
||||
item.isElem('image') &&
|
||||
item.hasAttr('width', '0')
|
||||
item.attributes.width === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -220,7 +220,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.imageHeight0 &&
|
||||
item.isElem('image') &&
|
||||
item.hasAttr('height', '0')
|
||||
item.attributes.height === '0'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -231,10 +231,10 @@ exports.fn = function (item, params) {
|
||||
//
|
||||
// <path d=""/>
|
||||
if (params.pathEmptyD && item.isElem('path')) {
|
||||
if (item.hasAttr('d') === false) {
|
||||
if (item.attributes.d == null) {
|
||||
return false;
|
||||
}
|
||||
const pathData = parsePathData(item.attr('d').value);
|
||||
const pathData = parsePathData(item.attributes.d);
|
||||
if (pathData.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@ -257,7 +257,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.polylineEmptyPoints &&
|
||||
item.isElem('polyline') &&
|
||||
!item.hasAttr('points')
|
||||
item.attributes.points == null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -270,7 +270,7 @@ exports.fn = function (item, params) {
|
||||
if (
|
||||
params.polygonEmptyPoints &&
|
||||
item.isElem('polygon') &&
|
||||
!item.hasAttr('points')
|
||||
item.attributes.points == null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -26,12 +26,12 @@ var _path = require('./_path.js'),
|
||||
exports.fn = function (item) {
|
||||
if (
|
||||
item.isElem('path') &&
|
||||
item.hasAttr('d') &&
|
||||
item.attributes.d != null &&
|
||||
typeof viewBox !== 'undefined'
|
||||
) {
|
||||
// Consider that any item with a transform attribute or a M instruction
|
||||
// within the viewBox is visible
|
||||
if (hasTransform(item) || pathMovesWithinViewBox(item.attr('d').value)) {
|
||||
if (hasTransform(item) || pathMovesWithinViewBox(item.attributes.d)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -71,12 +71,11 @@ function hasTransform(item) {
|
||||
*/
|
||||
function parseViewBox(svg) {
|
||||
var viewBoxData = '';
|
||||
if (svg.hasAttr('viewBox')) {
|
||||
if (svg.attributes.viewBox != null) {
|
||||
// Remove commas and plus signs, normalize and trim whitespace
|
||||
viewBoxData = svg.attr('viewBox').value;
|
||||
} else if (svg.hasAttr('height') && svg.hasAttr('width')) {
|
||||
viewBoxData =
|
||||
'0 0 ' + svg.attr('width').value + ' ' + svg.attr('height').value;
|
||||
viewBoxData = svg.attributes.viewBox;
|
||||
} else if (svg.attributes.height != null && svg.attributes.width != null) {
|
||||
viewBoxData = `0 0 ${svg.attributes.width} ${svg.attributes.height}`;
|
||||
}
|
||||
|
||||
// Remove commas and plus signs, normalize and trim whitespace
|
||||
@ -104,12 +103,11 @@ function parseViewBox(svg) {
|
||||
var path = new JSAPI({
|
||||
type: 'element',
|
||||
name: 'path',
|
||||
attributes: {
|
||||
d: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z',
|
||||
},
|
||||
content: [],
|
||||
});
|
||||
path.addAttr({
|
||||
name: 'd',
|
||||
value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z',
|
||||
});
|
||||
|
||||
viewBoxJS = path2js(path);
|
||||
}
|
||||
|
@ -83,9 +83,8 @@ exports.fn = function (item, params) {
|
||||
|
||||
if (
|
||||
params.removeNone &&
|
||||
(!stroke ||
|
||||
(item.hasAttr('stroke') && item.attr('stroke').value == 'none')) &&
|
||||
(!fill || (item.hasAttr('fill') && item.attr('fill').value == 'none'))
|
||||
(!stroke || item.attributes.stroke == 'none') &&
|
||||
(!fill || item.attributes.fill == 'none')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -22,12 +22,16 @@ exports.fn = function (data) {
|
||||
let count = 0;
|
||||
const defs = [];
|
||||
traverse(data, (item) => {
|
||||
if (!item.isElem('path') || !item.hasAttr('d')) {
|
||||
if (
|
||||
item.type !== 'element' ||
|
||||
item.name !== 'path' ||
|
||||
item.attributes.d == null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const d = item.attr('d').value;
|
||||
const fill = (item.hasAttr('fill') && item.attr('fill').value) || '';
|
||||
const stroke = (item.hasAttr('stroke') && item.attr('stroke').value) || '';
|
||||
const d = item.attributes.d;
|
||||
const fill = item.attributes.fill || '';
|
||||
const stroke = item.attributes.stroke || '';
|
||||
const key = d + ';s:' + stroke + ';f:' + fill;
|
||||
const hasSeen = seen.get(key);
|
||||
if (!hasSeen) {
|
||||
@ -36,23 +40,20 @@ exports.fn = function (data) {
|
||||
}
|
||||
if (!hasSeen.reused) {
|
||||
hasSeen.reused = true;
|
||||
if (!hasSeen.elem.hasAttr('id')) {
|
||||
hasSeen.elem.addAttr({
|
||||
name: 'id',
|
||||
value: 'reuse-' + count++,
|
||||
});
|
||||
if (hasSeen.elem.attributes.id == null) {
|
||||
hasSeen.elem.attributes.id = 'reuse-' + count++;
|
||||
}
|
||||
defs.push(hasSeen.elem);
|
||||
}
|
||||
convertToUse(item, hasSeen.elem.attr('id').value);
|
||||
convertToUse(item, hasSeen.elem.attributes.id);
|
||||
});
|
||||
if (defs.length > 0) {
|
||||
const defsTag = new JSAPI(
|
||||
{
|
||||
type: 'element',
|
||||
name: 'defs',
|
||||
attributes: {},
|
||||
children: [],
|
||||
attrs: {},
|
||||
},
|
||||
data
|
||||
);
|
||||
@ -71,7 +72,7 @@ exports.fn = function (data) {
|
||||
delete defClone.attributes.transform;
|
||||
defsTag.spliceContent(0, 0, defClone);
|
||||
// Convert the original def to a use so the first usage isn't duplicated.
|
||||
def = convertToUse(def, defClone.attr('id').value);
|
||||
def = convertToUse(def, defClone.attributes.id);
|
||||
delete def.attributes.id;
|
||||
}
|
||||
}
|
||||
@ -84,10 +85,7 @@ function convertToUse(item, href) {
|
||||
delete item.attributes.d;
|
||||
delete item.attributes.stroke;
|
||||
delete item.attributes.fill;
|
||||
item.addAttr({
|
||||
name: 'xlink:href',
|
||||
value: '#' + href,
|
||||
});
|
||||
item.attributes['xlink:href'] = '#' + href;
|
||||
delete item.pathJS;
|
||||
return item;
|
||||
}
|
||||
|
Reference in New Issue
Block a user