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

Format all plugins with prettier

This commit is contained in:
Bogdan Chadkin
2021-03-09 19:58:00 +03:00
parent 1be0d0dba1
commit 00ec0f71fe
41 changed files with 2053 additions and 1992 deletions

View File

@ -43,47 +43,46 @@ plugins: [
} }
} }
] ]
` `;
/** /**
* Add attributes to an outer <svg> element. Example config: * Add attributes to an outer <svg> element. Example config:
* *
* @author April Arcus * @author April Arcus
*/ */
exports.fn = function(data, params) { exports.fn = function (data, params) {
if (!params || !(Array.isArray(params.attributes) || params.attribute)) { if (!params || !(Array.isArray(params.attributes) || params.attribute)) {
console.error(ENOCLS); console.error(ENOCLS);
return data;
}
var attributes = params.attributes || [ params.attribute ],
svg = data.content[0];
if (svg.isElem('svg')) {
attributes.forEach(function (attribute) {
if (typeof attribute === 'string') {
if (!svg.hasAttr(attribute)) {
svg.addAttr({
name: attribute,
prefix: '',
local: attribute
});
}
} else if (typeof attribute === 'object') {
Object.keys(attribute).forEach(function (key) {
if (!svg.hasAttr(key)) {
svg.addAttr({
name: key,
value: attribute[key],
prefix: '',
local: key
});
}
});
}
});
}
return data; return data;
}
var attributes = params.attributes || [params.attribute],
svg = data.content[0];
if (svg.isElem('svg')) {
attributes.forEach(function (attribute) {
if (typeof attribute === 'string') {
if (!svg.hasAttr(attribute)) {
svg.addAttr({
name: attribute,
prefix: '',
local: attribute,
});
}
} else if (typeof attribute === 'object') {
Object.keys(attribute).forEach(function (key) {
if (!svg.hasAttr(key)) {
svg.addAttr({
name: key,
value: attribute[key],
prefix: '',
local: key,
});
}
});
}
});
}
return data;
}; };

View File

@ -32,19 +32,24 @@ plugins:
* *
* @author April Arcus * @author April Arcus
*/ */
exports.fn = function(data, params) { exports.fn = function (data, params) {
if (!params || !(Array.isArray(params.classNames) && params.classNames.some(String) || params.className)) { if (
console.error(ENOCLS); !params ||
return data; !(
} (Array.isArray(params.classNames) && params.classNames.some(String)) ||
params.className
var classNames = params.classNames || [ params.className ], )
svg = data.content[0]; ) {
console.error(ENOCLS);
if (svg.isElem('svg')) {
svg.class.add.apply(svg.class, classNames);
}
return data; return data;
}
var classNames = params.classNames || [params.className],
svg = data.content[0];
if (svg.isElem('svg')) {
svg.class.add.apply(svg.class, classNames);
}
return data;
}; };

View File

@ -4,17 +4,18 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.description = 'cleanups attributes from newlines, trailing and repeating spaces'; exports.description =
'cleanups attributes from newlines, trailing and repeating spaces';
exports.params = { exports.params = {
newlines: true, newlines: true,
trim: true, trim: true,
spaces: true spaces: true,
}; };
var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g, var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g,
regNewlines = /\r?\n/g, regNewlines = /\r?\n/g,
regSpaces = /\s{2,}/g; regSpaces = /\s{2,}/g;
/** /**
* Cleanup attributes values from newlines, trailing and repeating spaces. * Cleanup attributes values from newlines, trailing and repeating spaces.
@ -25,32 +26,29 @@ var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.isElem()) {
item.eachAttr(function (attr) {
if (params.newlines) {
// new line which requires a space instead of themselve
attr.value = attr.value.replace(
regNewlinesNeedSpace,
function (match, p1, p2) {
return p1 + ' ' + p2;
}
);
if (item.isElem()) { // simple new line
attr.value = attr.value.replace(regNewlines, '');
}
item.eachAttr(function(attr) { if (params.trim) {
attr.value = attr.value.trim();
if (params.newlines) { }
// new line which requires a space instead of themselve
attr.value = attr.value.replace(regNewlinesNeedSpace, function(match, p1, p2) {
return p1 + ' ' + p2;
});
// simple new line
attr.value = attr.value.replace(regNewlines, '');
}
if (params.trim) {
attr.value = attr.value.trim();
}
if (params.spaces) {
attr.value = attr.value.replace(regSpaces, ' ');
}
});
}
if (params.spaces) {
attr.value = attr.value.replace(regSpaces, ' ');
}
});
}
}; };

View File

@ -4,7 +4,8 @@ exports.type = 'full';
exports.active = true; exports.active = true;
exports.description = 'remove or cleanup enable-background attribute when possible'; exports.description =
'remove or cleanup enable-background attribute when possible';
/** /**
* Remove or cleanup enable-background attr which coincides with a width/height box. * Remove or cleanup enable-background attr which coincides with a width/height box.
@ -21,64 +22,65 @@ exports.description = 'remove or cleanup enable-background attribute when possib
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(data) { exports.fn = function (data) {
var regEnableBackground = /^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/,
hasFilter = false,
elems = ['svg', 'mask', 'pattern'];
var regEnableBackground = /^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/, function checkEnableBackground(item) {
hasFilter = false, if (
elems = ['svg', 'mask', 'pattern']; item.isElem(elems) &&
item.hasAttr('enable-background') &&
item.hasAttr('width') &&
item.hasAttr('height')
) {
var match = item
.attr('enable-background')
.value.match(regEnableBackground);
function checkEnableBackground(item) { if (match) {
if ( if (
item.isElem(elems) && item.attr('width').value === match[1] &&
item.hasAttr('enable-background') && item.attr('height').value === match[3]
item.hasAttr('width') &&
item.hasAttr('height')
) { ) {
if (item.isElem('svg')) {
var match = item.attr('enable-background').value.match(regEnableBackground);
if (match) {
if (
item.attr('width').value === match[1] &&
item.attr('height').value === match[3]
) {
if (item.isElem('svg')) {
item.removeAttr('enable-background');
} else {
item.attr('enable-background').value = 'new';
}
}
}
}
}
function checkForFilter(item) {
if (item.isElem('filter')) {
hasFilter = true;
}
}
function monkeys(items, fn) {
items.content.forEach(function(item) {
fn(item);
if (item.content) {
monkeys(item, fn);
}
});
return items;
}
var firstStep = monkeys(data, function(item) {
checkEnableBackground(item);
if (!hasFilter) {
checkForFilter(item);
}
});
return hasFilter ? firstStep : monkeys(firstStep, function(item) {
//we don't need 'enable-background' if we have no filters
item.removeAttr('enable-background'); item.removeAttr('enable-background');
}); } else {
item.attr('enable-background').value = 'new';
}
}
}
}
}
function checkForFilter(item) {
if (item.isElem('filter')) {
hasFilter = true;
}
}
function monkeys(items, fn) {
items.content.forEach(function (item) {
fn(item);
if (item.content) {
monkeys(item, fn);
}
});
return items;
}
var firstStep = monkeys(data, function (item) {
checkEnableBackground(item);
if (!hasFilter) {
checkForFilter(item);
}
});
return hasFilter
? firstStep
: monkeys(firstStep, function (item) {
//we don't need 'enable-background' if we have no filters
item.removeAttr('enable-background');
});
}; };

View File

@ -7,24 +7,74 @@ exports.active = true;
exports.description = 'removes unused IDs and minifies used'; exports.description = 'removes unused IDs and minifies used';
exports.params = { exports.params = {
remove: true, remove: true,
minify: true, minify: true,
prefix: '', prefix: '',
preserve: [], preserve: [],
preservePrefixes: [], preservePrefixes: [],
force: false force: false,
}; };
var referencesProps = new Set(require('./_collections').referencesProps), var referencesProps = new Set(require('./_collections').referencesProps),
regReferencesUrl = /\burl\(("|')?#(.+?)\1\)/, regReferencesUrl = /\burl\(("|')?#(.+?)\1\)/,
regReferencesHref = /^#(.+?)$/, regReferencesHref = /^#(.+?)$/,
regReferencesBegin = /(\w+)\./, regReferencesBegin = /(\w+)\./,
styleOrScript = ['style', 'script'], styleOrScript = ['style', 'script'],
generateIDchars = [ generateIDchars = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 'b',
], 'c',
maxIDindex = generateIDchars.length - 1; 'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
],
maxIDindex = generateIDchars.length - 1;
/** /**
* Remove unused and minify used IDs * Remove unused and minify used IDs
@ -35,128 +85,155 @@ var referencesProps = new Set(require('./_collections').referencesProps),
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(data, params) { exports.fn = function (data, params) {
var currentID, var currentID,
currentIDstring, currentIDstring,
IDs = new Map(), IDs = new Map(),
referencesIDs = new Map(), referencesIDs = new Map(),
hasStyleOrScript = false, hasStyleOrScript = false,
preserveIDs = new Set(Array.isArray(params.preserve) ? params.preserve : params.preserve ? [params.preserve] : []), preserveIDs = new Set(
preserveIDPrefixes = new Set(Array.isArray(params.preservePrefixes) ? params.preservePrefixes : (params.preservePrefixes ? [params.preservePrefixes] : [])), Array.isArray(params.preserve)
idValuePrefix = '#', ? params.preserve
idValuePostfix = '.'; : params.preserve
? [params.preserve]
: []
),
preserveIDPrefixes = new Set(
Array.isArray(params.preservePrefixes)
? params.preservePrefixes
: params.preservePrefixes
? [params.preservePrefixes]
: []
),
idValuePrefix = '#',
idValuePostfix = '.';
/** /**
* Bananas! * Bananas!
* *
* @param {Array} items input items * @param {Array} items input items
* @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.content.length && !hasStyleOrScript; i++) {
var item = items.content[i]; var item = items.content[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 = Boolean(item.content); var isNotEmpty = Boolean(item.content);
if (item.isElem(styleOrScript) && isNotEmpty) { if (item.isElem(styleOrScript) && isNotEmpty) {
hasStyleOrScript = true; hasStyleOrScript = true;
continue; continue;
}
// Don't remove IDs if the whole SVG consists only of defs.
if (item.isElem('svg') && item.content) {
var hasDefsOnly = true;
for (var j = 0; j < item.content.length; j++) {
if (!item.content[j].isElem('defs')) {
hasDefsOnly = false;
break;
}
}
if (hasDefsOnly) {
break;
}
}
}
// …and don't remove any ID if yes
if (item.isElem()) {
item.eachAttr(function(attr) {
var key, match;
// save IDs
if (attr.name === 'id') {
key = attr.value;
if (IDs.has(key)) {
item.removeAttr('id'); // remove repeated id
} else {
IDs.set(key, item);
}
return;
}
// save references
if (referencesProps.has(attr.name) && (match = attr.value.match(regReferencesUrl))) {
key = match[2]; // url() reference
} else if (
attr.local === 'href' && (match = attr.value.match(regReferencesHref)) ||
attr.name === 'begin' && (match = attr.value.match(regReferencesBegin))
) {
key = match[1]; // href reference
}
if (key) {
var ref = referencesIDs.get(key) || [];
ref.push(attr);
referencesIDs.set(key, ref);
}
});
}
// go deeper
if (item.content) {
monkeys(item);
}
} }
return items;
}
data = monkeys(data); // Don't remove IDs if the whole SVG consists only of defs.
if (item.isElem('svg') && item.content) {
var hasDefsOnly = true;
if (hasStyleOrScript) { for (var j = 0; j < item.content.length; j++) {
return data; if (!item.content[j].isElem('defs')) {
} hasDefsOnly = false;
break;
const idPreserved = id => preserveIDs.has(id) || idMatchesPrefix(preserveIDPrefixes, id);
for (var ref of referencesIDs) {
var key = ref[0];
if (IDs.has(key)) {
// replace referenced IDs with the minified ones
if (params.minify && !idPreserved(key)) {
do {
currentIDstring = getIDstring(currentID = generateID(currentID), params);
} while (idPreserved(currentIDstring));
IDs.get(key).attr('id').value = currentIDstring;
for (var attr of ref[1]) {
attr.value = attr.value.includes(idValuePrefix) ?
attr.value.replace(idValuePrefix + key, idValuePrefix + currentIDstring) :
attr.value.replace(key + idValuePostfix, currentIDstring + idValuePostfix);
}
} }
// don't remove referenced IDs }
IDs.delete(key); if (hasDefsOnly) {
break;
}
} }
} }
// remove non-referenced IDs attributes from elements // …and don't remove any ID if yes
if (params.remove) { if (item.isElem()) {
for(var keyElem of IDs) { item.eachAttr(function (attr) {
if (!idPreserved(keyElem[0])) { var key, match;
keyElem[1].removeAttr('id');
// save IDs
if (attr.name === 'id') {
key = attr.value;
if (IDs.has(key)) {
item.removeAttr('id'); // remove repeated id
} else {
IDs.set(key, item);
} }
} return;
}
// save references
if (
referencesProps.has(attr.name) &&
(match = attr.value.match(regReferencesUrl))
) {
key = match[2]; // url() reference
} else if (
(attr.local === 'href' &&
(match = attr.value.match(regReferencesHref))) ||
(attr.name === 'begin' &&
(match = attr.value.match(regReferencesBegin)))
) {
key = match[1]; // href reference
}
if (key) {
var ref = referencesIDs.get(key) || [];
ref.push(attr);
referencesIDs.set(key, ref);
}
});
}
// go deeper
if (item.content) {
monkeys(item);
}
} }
return items;
}
data = monkeys(data);
if (hasStyleOrScript) {
return data; return data;
}
const idPreserved = (id) =>
preserveIDs.has(id) || idMatchesPrefix(preserveIDPrefixes, id);
for (var ref of referencesIDs) {
var key = ref[0];
if (IDs.has(key)) {
// replace referenced IDs with the minified ones
if (params.minify && !idPreserved(key)) {
do {
currentIDstring = getIDstring(
(currentID = generateID(currentID)),
params
);
} while (idPreserved(currentIDstring));
IDs.get(key).attr('id').value = currentIDstring;
for (var attr of ref[1]) {
attr.value = attr.value.includes(idValuePrefix)
? attr.value.replace(
idValuePrefix + key,
idValuePrefix + currentIDstring
)
: attr.value.replace(
key + idValuePostfix,
currentIDstring + idValuePostfix
);
}
}
// don't remove referenced IDs
IDs.delete(key);
}
}
// remove non-referenced IDs attributes from elements
if (params.remove) {
for (var keyElem of IDs) {
if (!idPreserved(keyElem[0])) {
keyElem[1].removeAttr('id');
}
}
}
return data;
}; };
/** /**
@ -167,10 +244,10 @@ exports.fn = function(data, params) {
* @return {Boolean} if currentID starts with one of the strings in prefixArray * @return {Boolean} if currentID starts with one of the strings in prefixArray
*/ */
function idMatchesPrefix(prefixArray, currentID) { function idMatchesPrefix(prefixArray, currentID) {
if (!currentID) return false; if (!currentID) return false;
for (var prefix of prefixArray) if (currentID.startsWith(prefix)) return true; for (var prefix of prefixArray) if (currentID.startsWith(prefix)) return true;
return false; return false;
} }
/** /**
@ -180,24 +257,24 @@ function idMatchesPrefix(prefixArray, currentID) {
* @return {Array} generated ID array * @return {Array} generated ID array
*/ */
function generateID(currentID) { function generateID(currentID) {
if (!currentID) return [0]; if (!currentID) return [0];
currentID[currentID.length - 1]++; currentID[currentID.length - 1]++;
for(var i = currentID.length - 1; i > 0; i--) { for (var i = currentID.length - 1; i > 0; i--) {
if (currentID[i] > maxIDindex) { if (currentID[i] > maxIDindex) {
currentID[i] = 0; currentID[i] = 0;
if (currentID[i - 1] !== undefined) { if (currentID[i - 1] !== undefined) {
currentID[i - 1]++; currentID[i - 1]++;
} }
}
} }
if (currentID[0] > maxIDindex) { }
currentID[0] = 0; if (currentID[0] > maxIDindex) {
currentID.unshift(0); currentID[0] = 0;
} currentID.unshift(0);
return currentID; }
return currentID;
} }
/** /**
@ -207,6 +284,6 @@ function generateID(currentID) {
* @return {String} output ID string * @return {String} output ID string
*/ */
function getIDstring(arr, params) { function getIDstring(arr, params) {
var str = params.prefix; var str = params.prefix;
return str + arr.map(i => generateIDchars[i]).join(''); return str + arr.map((i) => generateIDchars[i]).join('');
} }

View File

@ -7,22 +7,23 @@ exports.active = false;
exports.description = 'rounds list of values to the fixed precision'; exports.description = 'rounds list of values to the fixed precision';
exports.params = { exports.params = {
floatPrecision: 3, floatPrecision: 3,
leadingZero: true, leadingZero: true,
defaultPx: true, defaultPx: true,
convertToPx: true convertToPx: true,
}; };
var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/, var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
regSeparator = /\s+,?\s*|,\s*/, regSeparator = /\s+,?\s*|,\s*/,
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero, removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
absoluteLengths = { // relative to px absoluteLengths = {
cm: 96/2.54, // relative to px
mm: 96/25.4, cm: 96 / 2.54,
in: 96, mm: 96 / 25.4,
pt: 4/3, in: 96,
pc: 16 pt: 4 / 3,
}; pc: 16,
};
/** /**
* Round list of values to the fixed precision. * Round list of values to the fixed precision.
@ -44,96 +45,90 @@ var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|
* *
* @author kiyopikko * @author kiyopikko
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.hasAttr('points')) {
roundValues(item.attrs.points);
}
if (item.hasAttr('enable-background')) {
roundValues(item.attrs['enable-background']);
}
if ( item.hasAttr('points') ) { if (item.hasAttr('viewBox')) {
roundValues(item.attrs.points); roundValues(item.attrs.viewBox);
} }
if ( item.hasAttr('enable-background') ) { if (item.hasAttr('stroke-dasharray')) {
roundValues(item.attrs['enable-background']); roundValues(item.attrs['stroke-dasharray']);
} }
if ( item.hasAttr('viewBox') ) { if (item.hasAttr('dx')) {
roundValues(item.attrs.viewBox); roundValues(item.attrs.dx);
} }
if ( item.hasAttr('stroke-dasharray') ) { if (item.hasAttr('dy')) {
roundValues(item.attrs['stroke-dasharray']); roundValues(item.attrs.dy);
} }
if ( item.hasAttr('dx') ) { if (item.hasAttr('x')) {
roundValues(item.attrs.dx); roundValues(item.attrs.x);
} }
if ( item.hasAttr('dy') ) { if (item.hasAttr('y')) {
roundValues(item.attrs.dy); roundValues(item.attrs.y);
} }
if ( item.hasAttr('x') ) { function roundValues($prop) {
roundValues(item.attrs.x); var num,
} units,
match,
matchNew,
lists = $prop.value,
listsArr = lists.split(regSeparator),
roundedListArr = [],
roundedList;
if ( item.hasAttr('y') ) { listsArr.forEach(function (elem) {
roundValues(item.attrs.y); match = elem.match(regNumericValues);
} matchNew = elem.match(/new/);
// if attribute value matches regNumericValues
if (match) {
// round it to the fixed precision
(num = +(+match[1]).toFixed(params.floatPrecision)),
(units = match[3] || '');
function roundValues($prop){ // convert absolute values to pixels
if (params.convertToPx && units && units in absoluteLengths) {
var pxNum = +(absoluteLengths[units] * match[1]).toFixed(
params.floatPrecision
);
var num, units, if (String(pxNum).length < match[0].length)
match, (num = pxNum), (units = 'px');
matchNew, }
lists = $prop.value,
listsArr = lists.split(regSeparator),
roundedListArr = [],
roundedList;
listsArr.forEach(function(elem){ // and remove leading zero
if (params.leadingZero) {
num = removeLeadingZero(num);
}
match = elem.match(regNumericValues); // remove default 'px' units
matchNew = elem.match(/new/); if (params.defaultPx && units === 'px') {
units = '';
}
// if attribute value matches regNumericValues roundedListArr.push(num + units);
if (match) { }
// round it to the fixed precision // if attribute value is "new"(only enable-background).
num = +(+match[1]).toFixed(params.floatPrecision), else if (matchNew) {
units = match[3] || ''; roundedListArr.push('new');
} else if (elem) {
// convert absolute values to pixels roundedListArr.push(elem);
if (params.convertToPx && units && (units in absoluteLengths)) { }
var pxNum = +(absoluteLengths[units] * match[1]).toFixed(params.floatPrecision); });
if (String(pxNum).length < match[0].length)
num = pxNum,
units = 'px';
}
// and remove leading zero
if (params.leadingZero) {
num = removeLeadingZero(num);
}
// remove default 'px' units
if (params.defaultPx && units === 'px') {
units = '';
}
roundedListArr.push(num+units);
}
// if attribute value is "new"(only enable-background).
else if (matchNew) {
roundedListArr.push('new');
} else if (elem) {
roundedListArr.push(elem);
}
});
roundedList = roundedListArr.join(' ');
$prop.value = roundedList;
}
roundedList = roundedListArr.join(' ');
$prop.value = roundedList;
}
}; };

View File

@ -4,24 +4,26 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.description = 'rounds numeric values to the fixed precision, removes default px units'; exports.description =
'rounds numeric values to the fixed precision, removes default px units';
exports.params = { exports.params = {
floatPrecision: 3, floatPrecision: 3,
leadingZero: true, leadingZero: true,
defaultPx: true, defaultPx: true,
convertToPx: true convertToPx: true,
}; };
var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/, var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero, removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
absoluteLengths = { // relative to px absoluteLengths = {
cm: 96/2.54, // relative to px
mm: 96/25.4, cm: 96 / 2.54,
in: 96, mm: 96 / 25.4,
pt: 4/3, in: 96,
pc: 16 pt: 4 / 3,
}; pc: 16,
};
/** /**
* Round numeric values to the fixed precision, * Round numeric values to the fixed precision,
@ -33,56 +35,58 @@ var regNumericValues = /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.isElem()) {
if (item.isElem()) { var floatPrecision = params.floatPrecision;
var floatPrecision = params.floatPrecision;
if (item.hasAttr('viewBox')) {
var nums = item.attr('viewBox').value.split(/\s,?\s*|,\s*/g);
item.attr('viewBox').value = nums.map(function(value) {
var num = +value;
return isNaN(num) ? value : +num.toFixed(floatPrecision);
}).join(' ');
}
item.eachAttr(function(attr) {
// The `version` attribute is a text string and cannot be rounded
if (attr.name === 'version') { return }
var match = attr.value.match(regNumericValues);
// if attribute value matches regNumericValues
if (match) {
// round it to the fixed precision
var num = +(+match[1]).toFixed(floatPrecision),
units = match[3] || '';
// convert absolute values to pixels
if (params.convertToPx && units && (units in absoluteLengths)) {
var pxNum = +(absoluteLengths[units] * match[1]).toFixed(floatPrecision);
if (String(pxNum).length < match[0].length) {
num = pxNum;
units = 'px';
}
}
// and remove leading zero
if (params.leadingZero) {
num = removeLeadingZero(num);
}
// remove default 'px' units
if (params.defaultPx && units === 'px') {
units = '';
}
attr.value = num + units;
}
});
if (item.hasAttr('viewBox')) {
var nums = item.attr('viewBox').value.split(/\s,?\s*|,\s*/g);
item.attr('viewBox').value = nums
.map(function (value) {
var num = +value;
return isNaN(num) ? value : +num.toFixed(floatPrecision);
})
.join(' ');
} }
item.eachAttr(function (attr) {
// The `version` attribute is a text string and cannot be rounded
if (attr.name === 'version') {
return;
}
var match = attr.value.match(regNumericValues);
// if attribute value matches regNumericValues
if (match) {
// round it to the fixed precision
var num = +(+match[1]).toFixed(floatPrecision),
units = match[3] || '';
// convert absolute values to pixels
if (params.convertToPx && units && units in absoluteLengths) {
var pxNum = +(absoluteLengths[units] * match[1]).toFixed(
floatPrecision
);
if (String(pxNum).length < match[0].length) {
num = pxNum;
units = 'px';
}
}
// and remove leading zero
if (params.leadingZero) {
num = removeLeadingZero(num);
}
// remove default 'px' units
if (params.defaultPx && units === 'px') {
units = '';
}
attr.value = num + units;
}
});
}
}; };

View File

@ -7,12 +7,14 @@ exports.active = true;
exports.description = 'collapses useless groups'; exports.description = 'collapses useless groups';
var collections = require('./_collections'), var collections = require('./_collections'),
attrsInheritable = collections.inheritableAttrs, attrsInheritable = collections.inheritableAttrs,
animationElems = collections.elemsGroups.animation; animationElems = collections.elemsGroups.animation;
function hasAnimatedAttr(item) { function hasAnimatedAttr(item) {
return item.isElem(animationElems) && item.hasAttr('attributeName', this) || return (
!item.isEmpty() && item.content.some(hasAnimatedAttr, this); (item.isElem(animationElems) && item.hasAttr('attributeName', this)) ||
(!item.isEmpty() && item.content.some(hasAnimatedAttr, this))
);
} }
/* /*
@ -38,49 +40,58 @@ function hasAnimatedAttr(item) {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
// non-empty elements
if (item.isElem() && !item.isElem('switch') && !item.isEmpty()) {
item.content.forEach(function (g, i) {
// non-empty groups
if (g.isElem('g') && !g.isEmpty()) {
// move group attibutes to the single content element
if (g.hasAttr() && g.content.length === 1) {
var inner = g.content[0];
// non-empty elements if (
if (item.isElem() && !item.isElem('switch') && !item.isEmpty()) { inner.isElem() &&
item.content.forEach(function(g, i) { !inner.hasAttr('id') &&
// non-empty groups !g.hasAttr('filter') &&
if (g.isElem('g') && !g.isEmpty()) { !(g.hasAttr('class') && inner.hasAttr('class')) &&
// move group attibutes to the single content element ((!g.hasAttr('clip-path') && !g.hasAttr('mask')) ||
if (g.hasAttr() && g.content.length === 1) { (inner.isElem('g') &&
var inner = g.content[0]; !g.hasAttr('transform') &&
!inner.hasAttr('transform')))
) {
g.eachAttr(function (attr) {
if (g.content.some(hasAnimatedAttr, attr.name)) return;
if (inner.isElem() && !inner.hasAttr('id') && !g.hasAttr('filter') && if (!inner.hasAttr(attr.name)) {
!(g.hasAttr('class') && inner.hasAttr('class')) && ( inner.addAttr(attr);
!g.hasAttr('clip-path') && !g.hasAttr('mask') || } else if (attr.name == 'transform') {
inner.isElem('g') && !g.hasAttr('transform') && !inner.hasAttr('transform') inner.attr(attr.name).value =
) attr.value + ' ' + inner.attr(attr.name).value;
) { } else if (inner.hasAttr(attr.name, 'inherit')) {
g.eachAttr(function(attr) { inner.attr(attr.name).value = attr.value;
if (g.content.some(hasAnimatedAttr, attr.name)) return; } else if (
attrsInheritable.indexOf(attr.name) < 0 &&
!inner.hasAttr(attr.name, attr.value)
) {
return;
}
if (!inner.hasAttr(attr.name)) { g.removeAttr(attr.name);
inner.addAttr(attr); });
} else if (attr.name == 'transform') { }
inner.attr(attr.name).value = attr.value + ' ' + inner.attr(attr.name).value; }
} else if (inner.hasAttr(attr.name, 'inherit')) {
inner.attr(attr.name).value = attr.value;
} else if (
attrsInheritable.indexOf(attr.name) < 0 &&
!inner.hasAttr(attr.name, attr.value)
) {
return;
}
g.removeAttr(attr.name); // collapse groups without attributes
}); if (
} !g.hasAttr() &&
} !g.content.some(function (item) {
return item.isElem(animationElems);
// collapse groups without attributes })
if (!g.hasAttr() && !g.content.some(function(item) { return item.isElem(animationElems) })) { ) {
item.spliceContent(i, 1, g.content); item.spliceContent(i, 1, g.content);
} }
} }
}); });
} }
}; };

View File

@ -7,19 +7,21 @@ exports.active = true;
exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb'; exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
exports.params = { exports.params = {
currentColor: false, currentColor: false,
names2hex: true, names2hex: true,
rgb2hex: true, rgb2hex: true,
shorthex: true, shorthex: true,
shortname: true shortname: true,
}; };
var collections = require('./_collections'), var collections = require('./_collections'),
rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)', rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)',
rComma = '\\s*,\\s*', rComma = '\\s*,\\s*',
regRGB = new RegExp('^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'), regRGB = new RegExp(
regHEX = /^#(([a-fA-F0-9])\2){3}$/, '^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'
none = /\bnone\b/i; ),
regHEX = /^#(([a-fA-F0-9])\2){3}$/,
none = /\bnone\b/i;
/** /**
* Convert different colors formats in element attributes to hex. * Convert different colors formats in element attributes to hex.
@ -47,69 +49,60 @@ var collections = require('./_collections'),
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.elem) {
item.eachAttr(function (attr) {
if (collections.colorsProps.indexOf(attr.name) > -1) {
var val = attr.value,
match;
if (item.elem) { // Convert colors to currentColor
if (params.currentColor) {
if (typeof params.currentColor === 'string') {
match = val === params.currentColor;
} else if (params.currentColor.exec) {
match = params.currentColor.exec(val);
} else {
match = !val.match(none);
}
if (match) {
val = 'currentColor';
}
}
item.eachAttr(function(attr) { // Convert color name keyword to long hex
if (params.names2hex && val.toLowerCase() in collections.colorsNames) {
val = collections.colorsNames[val.toLowerCase()];
}
if (collections.colorsProps.indexOf(attr.name) > -1) { // Convert rgb() to long hex
if (params.rgb2hex && (match = val.match(regRGB))) {
match = match.slice(1, 4).map(function (m) {
if (m.indexOf('%') > -1) m = Math.round(parseFloat(m) * 2.55);
var val = attr.value, return Math.max(0, Math.min(m, 255));
match; });
// Convert colors to currentColor val = rgb2hex(match);
if (params.currentColor) { }
if (typeof params.currentColor === 'string') {
match = val === params.currentColor;
} else if (params.currentColor.exec) {
match = params.currentColor.exec(val);
} else {
match = !val.match(none);
}
if (match) {
val = 'currentColor';
}
}
// Convert color name keyword to long hex // Convert long hex to short hex
if (params.names2hex && val.toLowerCase() in collections.colorsNames) { if (params.shorthex && (match = val.match(regHEX))) {
val = collections.colorsNames[val.toLowerCase()]; val = '#' + match[0][1] + match[0][3] + match[0][5];
} }
// Convert rgb() to long hex // Convert hex to short name
if (params.rgb2hex && (match = val.match(regRGB))) { if (params.shortname) {
match = match.slice(1, 4).map(function(m) { var lowerVal = val.toLowerCase();
if (m.indexOf('%') > -1) if (lowerVal in collections.colorsShortNames) {
m = Math.round(parseFloat(m) * 2.55); val = collections.colorsShortNames[lowerVal];
}
return Math.max(0, Math.min(m, 255)); }
});
val = rgb2hex(match);
}
// Convert long hex to short hex
if (params.shorthex && (match = val.match(regHEX))) {
val = '#' + match[0][1] + match[0][3] + match[0][5];
}
// Convert hex to short name
if (params.shortname) {
var lowerVal = val.toLowerCase();
if (lowerVal in collections.colorsShortNames) {
val = collections.colorsShortNames[lowerVal];
}
}
attr.value = val;
}
});
}
attr.value = val;
}
});
}
}; };
/** /**
@ -126,5 +119,10 @@ exports.fn = function(item, params) {
* @author Jed Schmidt * @author Jed Schmidt
*/ */
function rgb2hex(rgb) { function rgb2hex(rgb) {
return '#' + ('00000' + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6).toUpperCase(); return (
'#' +
('00000' + ((rgb[0] << 16) | (rgb[1] << 8) | rgb[2]).toString(16))
.slice(-6)
.toUpperCase()
);
} }

View File

@ -16,24 +16,26 @@ exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
* *
* @author Taylor Hunt * @author Taylor Hunt
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.isElem('ellipse')) { if (item.isElem('ellipse')) {
var rx = item.hasAttr('rx') && item.attr('rx').value || 0; var rx = (item.hasAttr('rx') && item.attr('rx').value) || 0;
var ry = item.hasAttr('ry') && item.attr('ry').value || 0; var ry = (item.hasAttr('ry') && item.attr('ry').value) || 0;
if (rx === ry || if (
rx === 'auto' || ry === 'auto' // SVG2 rx === ry ||
) { rx === 'auto' ||
var radius = rx !== 'auto' ? rx : ry; ry === 'auto' // SVG2
item.renameElem('circle'); ) {
item.removeAttr(['rx', 'ry']); var radius = rx !== 'auto' ? rx : ry;
item.addAttr({ item.renameElem('circle');
name: 'r', item.removeAttr(['rx', 'ry']);
value: radius, item.addAttr({
prefix: '', name: 'r',
local: 'r', value: radius,
}); prefix: '',
} local: 'r',
});
}
} }
return; return;
}; };

View File

@ -7,34 +7,46 @@ exports.active = false;
exports.description = 'converts style to attributes'; exports.description = 'converts style to attributes';
exports.params = { exports.params = {
keepImportant: false keepImportant: false,
}; };
var stylingProps = require('./_collections').attrsGroups.presentation, var stylingProps = require('./_collections').attrsGroups.presentation,
rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space. rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like fill rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like fill
rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth' rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth" rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'), rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
// Parentheses, E.g.: url(data:image/png;base64,iVBO...).
// Parentheses, E.g.: url(data:image/png;base64,iVBO...). // ':' and ';' inside of it should be threated as is. (Just like in strings.)
// ':' and ';' inside of it should be threated as is. (Just like in strings.) rParenthesis =
rParenthesis = '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)', '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input. rValue =
rValue = '\\s*(' + g('[^!\'"();\\\\]+?', rEscape, rSingleQuotes, rQuotes, rParenthesis, '[^;]*?') + '*?' + ')', '\\s*(' +
g(
// End of declaration. Spaces outside of capturing groups help to do natural trimming. '[^!\'"();\\\\]+?',
rDeclEnd = '\\s*(?:;\\s*|$)', rEscape,
rSingleQuotes,
// Important rule rQuotes,
rImportant = '(\\s*!important(?![-(\\w]))?', rParenthesis,
'[^;]*?'
// Final RegExp to parse CSS declarations. ) +
regDeclarationBlock = new RegExp(rAttr + ':' + rValue + rImportant + rDeclEnd, 'ig'), '*?' +
')',
// Comments expression. Honors escape sequences and strings. // End of declaration. Spaces outside of capturing groups help to do natural trimming.
regStripComments = new RegExp(g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'), 'ig'); rDeclEnd = '\\s*(?:;\\s*|$)',
// Important rule
rImportant = '(\\s*!important(?![-(\\w]))?',
// Final RegExp to parse CSS declarations.
regDeclarationBlock = new RegExp(
rAttr + ':' + rValue + rImportant + rDeclEnd,
'ig'
),
// Comments expression. Honors escape sequences and strings.
regStripComments = new RegExp(
g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
'ig'
);
/** /**
* Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect. * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
@ -54,71 +66,70 @@ var stylingProps = require('./_collections').attrsGroups.presentation,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.elem && item.hasAttr('style')) {
// ['opacity: 1', 'color: #000']
var styleValue = item.attr('style').value,
styles = [],
attrs = {};
if (item.elem && item.hasAttr('style')) { // Strip CSS comments preserving escape sequences and strings.
// ['opacity: 1', 'color: #000'] styleValue = styleValue.replace(regStripComments, function (match) {
var styleValue = item.attr('style').value, return match[0] == '/'
styles = [], ? ''
attrs = {}; : match[0] == '\\' && /[-g-z]/i.test(match[1])
? match[1]
// Strip CSS comments preserving escape sequences and strings. : match;
styleValue = styleValue.replace(regStripComments, function(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
for (var rule; rule = regDeclarationBlock.exec(styleValue);) {
if (!params.keepImportant || !rule[3]) {
styles.push([rule[1], rule[2]]);
}
}
if (styles.length) {
styles = styles.filter(function(style) {
if (style[0]) {
var prop = style[0].toLowerCase(),
val = style[1];
if (rQuotedString.test(val)) {
val = val.slice(1, -1);
}
if (stylingProps.indexOf(prop) > -1) {
attrs[prop] = {
name: prop,
value: val,
local: prop,
prefix: ''
};
return false;
}
}
return true;
});
Object.assign(item.attrs, attrs);
if (styles.length) {
item.attr('style').value = styles
.map(function(declaration) { return declaration.join(':') })
.join(';');
} else {
item.removeAttr('style');
}
}
regDeclarationBlock.lastIndex = 0;
// eslint-disable-next-line no-cond-assign
for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
if (!params.keepImportant || !rule[3]) {
styles.push([rule[1], rule[2]]);
}
} }
if (styles.length) {
styles = styles.filter(function (style) {
if (style[0]) {
var prop = style[0].toLowerCase(),
val = style[1];
if (rQuotedString.test(val)) {
val = val.slice(1, -1);
}
if (stylingProps.indexOf(prop) > -1) {
attrs[prop] = {
name: prop,
value: val,
local: prop,
prefix: '',
};
return false;
}
}
return true;
});
Object.assign(item.attrs, attrs);
if (styles.length) {
item.attr('style').value = styles
.map(function (declaration) {
return declaration.join(':');
})
.join(';');
} else {
item.removeAttr('style');
}
}
}
}; };
function g() { function g() {
return '(?:' + Array.prototype.join.call(arguments, '|') + ')'; return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
} }

View File

@ -7,27 +7,27 @@ exports.active = true;
exports.description = 'collapses multiple transformations and optimizes it'; exports.description = 'collapses multiple transformations and optimizes it';
exports.params = { exports.params = {
convertToShorts: true, convertToShorts: true,
// degPrecision: 3, // transformPrecision (or matrix precision) - 2 by default // degPrecision: 3, // transformPrecision (or matrix precision) - 2 by default
floatPrecision: 3, floatPrecision: 3,
transformPrecision: 5, transformPrecision: 5,
matrixToTransform: true, matrixToTransform: true,
shortTranslate: true, shortTranslate: true,
shortScale: true, shortScale: true,
shortRotate: true, shortRotate: true,
removeUseless: true, removeUseless: true,
collapseIntoOne: true, collapseIntoOne: true,
leadingZero: true, leadingZero: true,
negativeExtraSpace: false negativeExtraSpace: false,
}; };
var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
transform2js = require('./_transforms.js').transform2js, transform2js = require('./_transforms.js').transform2js,
transformsMultiply = require('./_transforms.js').transformsMultiply, transformsMultiply = require('./_transforms.js').transformsMultiply,
matrixToTransform = require('./_transforms.js').matrixToTransform, matrixToTransform = require('./_transforms.js').matrixToTransform,
degRound, degRound,
floatRound, floatRound,
transformRound; transformRound;
/** /**
* Convert matrices to the short aliases, * Convert matrices to the short aliases,
@ -43,27 +43,23 @@ var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.elem) {
if (item.elem) { // transform
if (item.hasAttr('transform')) {
// transform convertTransform(item, 'transform', params);
if (item.hasAttr('transform')) {
convertTransform(item, 'transform', params);
}
// gradientTransform
if (item.hasAttr('gradientTransform')) {
convertTransform(item, 'gradientTransform', params);
}
// patternTransform
if (item.hasAttr('patternTransform')) {
convertTransform(item, 'patternTransform', params);
}
} }
// gradientTransform
if (item.hasAttr('gradientTransform')) {
convertTransform(item, 'gradientTransform', params);
}
// patternTransform
if (item.hasAttr('patternTransform')) {
convertTransform(item, 'patternTransform', params);
}
}
}; };
/** /**
@ -74,28 +70,28 @@ exports.fn = function(item, params) {
* @param {Object} params plugin params * @param {Object} params plugin params
*/ */
function convertTransform(item, attrName, params) { function convertTransform(item, attrName, params) {
var data = transform2js(item.attr(attrName).value); var data = transform2js(item.attr(attrName).value);
params = definePrecision(data, params); params = definePrecision(data, params);
if (params.collapseIntoOne && data.length > 1) { if (params.collapseIntoOne && data.length > 1) {
data = [transformsMultiply(data)]; data = [transformsMultiply(data)];
} }
if (params.convertToShorts) { if (params.convertToShorts) {
data = convertToShorts(data, params); data = convertToShorts(data, params);
} else { } else {
data.forEach(roundTransform); data.forEach(roundTransform);
} }
if (params.removeUseless) { if (params.removeUseless) {
data = removeUseless(data); data = removeUseless(data);
} }
if (data.length) { if (data.length) {
item.attr(attrName).value = js2transform(data, params); item.attr(attrName).value = js2transform(data, params);
} else { } else {
item.removeAttr(attrName); item.removeAttr(attrName);
} }
} }
/** /**
@ -110,37 +106,49 @@ function convertTransform(item, attrName, params) {
* @return {Array} output array * @return {Array} output array
*/ */
function definePrecision(data, params) { function definePrecision(data, params) {
var matrixData = data.reduce(getMatrixData, []), var matrixData = data.reduce(getMatrixData, []),
significantDigits = params.transformPrecision; significantDigits = params.transformPrecision;
// Clone params so it don't affect other elements transformations. // Clone params so it don't affect other elements transformations.
params = Object.assign({}, params); params = Object.assign({}, params);
// Limit transform precision with matrix one. Calculating with larger precision doesn't add any value. // Limit transform precision with matrix one. Calculating with larger precision doesn't add any value.
if (matrixData.length) { if (matrixData.length) {
params.transformPrecision = Math.min(params.transformPrecision, params.transformPrecision = Math.min(
Math.max.apply(Math, matrixData.map(floatDigits)) || params.transformPrecision); params.transformPrecision,
Math.max.apply(Math, matrixData.map(floatDigits)) ||
params.transformPrecision
);
significantDigits = Math.max.apply(Math, matrixData.map(function(n) { significantDigits = Math.max.apply(
return String(n).replace(/\D+/g, '').length; // Number of digits in a number. 123.45 → 5 Math,
})); matrixData.map(function (n) {
} return String(n).replace(/\D+/g, '').length; // Number of digits in a number. 123.45 → 5
// No sense in angle precision more then number of significant digits in matrix. })
if (!('degPrecision' in params)) { );
params.degPrecision = Math.max(0, Math.min(params.floatPrecision, significantDigits - 2)); }
} // No sense in angle precision more then number of significant digits in matrix.
if (!('degPrecision' in params)) {
params.degPrecision = Math.max(
0,
Math.min(params.floatPrecision, significantDigits - 2)
);
}
floatRound = params.floatPrecision >= 1 && params.floatPrecision < 20 ? floatRound =
smartRound.bind(this, params.floatPrecision) : params.floatPrecision >= 1 && params.floatPrecision < 20
round; ? smartRound.bind(this, params.floatPrecision)
degRound = params.degPrecision >= 1 && params.floatPrecision < 20 ? : round;
smartRound.bind(this, params.degPrecision) : degRound =
round; params.degPrecision >= 1 && params.floatPrecision < 20
transformRound = params.transformPrecision >= 1 && params.floatPrecision < 20 ? ? smartRound.bind(this, params.degPrecision)
smartRound.bind(this, params.transformPrecision) : : round;
round; transformRound =
params.transformPrecision >= 1 && params.floatPrecision < 20
? smartRound.bind(this, params.transformPrecision)
: round;
return params; return params;
} }
/** /**
@ -151,14 +159,14 @@ function definePrecision(data, params) {
* @return {Array} output array * @return {Array} output array
*/ */
function getMatrixData(a, b) { function getMatrixData(a, b) {
return b.name == 'matrix' ? a.concat(b.data.slice(0, 4)) : a; return b.name == 'matrix' ? a.concat(b.data.slice(0, 4)) : a;
} }
/** /**
* Returns number of digits after the point. 0.125 → 3 * Returns number of digits after the point. 0.125 → 3
*/ */
function floatDigits(n) { function floatDigits(n) {
return (n = String(n)).slice(n.indexOf('.')).length - 1; return (n = String(n)).slice(n.indexOf('.')).length - 1;
} }
/** /**
@ -169,79 +177,74 @@ function floatDigits(n) {
* @return {Array} output array * @return {Array} output array
*/ */
function convertToShorts(transforms, params) { function convertToShorts(transforms, params) {
for (var i = 0; i < transforms.length; i++) {
var transform = transforms[i];
for(var i = 0; i < transforms.length; i++) { // convert matrix to the short aliases
if (params.matrixToTransform && transform.name === 'matrix') {
var transform = transforms[i]; var decomposed = matrixToTransform(transform, params);
if (
// convert matrix to the short aliases decomposed != transform &&
if ( js2transform(decomposed, params).length <=
params.matrixToTransform && js2transform([transform], params).length
transform.name === 'matrix' ) {
) { transforms.splice.apply(transforms, [i, 1].concat(decomposed));
var decomposed = matrixToTransform(transform, params); }
if (decomposed != transform && transform = transforms[i];
js2transform(decomposed, params).length <= js2transform([transform], params).length) {
transforms.splice.apply(transforms, [i, 1].concat(decomposed));
}
transform = transforms[i];
}
// fixed-point numbers
// 12.754997 → 12.755
roundTransform(transform);
// convert long translate transform notation to the shorts one
// translate(10 0) → translate(10)
if (
params.shortTranslate &&
transform.name === 'translate' &&
transform.data.length === 2 &&
!transform.data[1]
) {
transform.data.pop();
}
// convert long scale transform notation to the shorts one
// scale(2 2) → scale(2)
if (
params.shortScale &&
transform.name === 'scale' &&
transform.data.length === 2 &&
transform.data[0] === transform.data[1]
) {
transform.data.pop();
}
// convert long rotate transform notation to the short one
// translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
if (
params.shortRotate &&
transforms[i - 2] &&
transforms[i - 2].name === 'translate' &&
transforms[i - 1].name === 'rotate' &&
transforms[i].name === 'translate' &&
transforms[i - 2].data[0] === -transforms[i].data[0] &&
transforms[i - 2].data[1] === -transforms[i].data[1]
) {
transforms.splice(i - 2, 3, {
name: 'rotate',
data: [
transforms[i - 1].data[0],
transforms[i - 2].data[0],
transforms[i - 2].data[1]
]
});
// splice compensation
i -= 2;
}
} }
return transforms; // fixed-point numbers
// 12.754997 → 12.755
roundTransform(transform);
// convert long translate transform notation to the shorts one
// translate(10 0) → translate(10)
if (
params.shortTranslate &&
transform.name === 'translate' &&
transform.data.length === 2 &&
!transform.data[1]
) {
transform.data.pop();
}
// convert long scale transform notation to the shorts one
// scale(2 2) → scale(2)
if (
params.shortScale &&
transform.name === 'scale' &&
transform.data.length === 2 &&
transform.data[0] === transform.data[1]
) {
transform.data.pop();
}
// convert long rotate transform notation to the short one
// translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
if (
params.shortRotate &&
transforms[i - 2] &&
transforms[i - 2].name === 'translate' &&
transforms[i - 1].name === 'rotate' &&
transforms[i].name === 'translate' &&
transforms[i - 2].data[0] === -transforms[i].data[0] &&
transforms[i - 2].data[1] === -transforms[i].data[1]
) {
transforms.splice(i - 2, 3, {
name: 'rotate',
data: [
transforms[i - 1].data[0],
transforms[i - 2].data[0],
transforms[i - 2].data[1],
],
});
// splice compensation
i -= 2;
}
}
return transforms;
} }
/** /**
@ -251,38 +254,36 @@ function convertToShorts(transforms, params) {
* @return {Array} output array * @return {Array} output array
*/ */
function removeUseless(transforms) { function removeUseless(transforms) {
return transforms.filter(function (transform) {
// translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0)
if (
(['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 &&
(transform.data.length == 1 || transform.name == 'rotate') &&
!transform.data[0]) ||
// translate(0, 0)
(transform.name == 'translate' &&
!transform.data[0] &&
!transform.data[1]) ||
// scale(1)
(transform.name == 'scale' &&
transform.data[0] == 1 &&
(transform.data.length < 2 || transform.data[1] == 1)) ||
// matrix(1 0 0 1 0 0)
(transform.name == 'matrix' &&
transform.data[0] == 1 &&
transform.data[3] == 1 &&
!(
transform.data[1] ||
transform.data[2] ||
transform.data[4] ||
transform.data[5]
))
) {
return false;
}
return transforms.filter(function(transform) { return true;
});
// translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0)
if (
['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 &&
(transform.data.length == 1 || transform.name == 'rotate') &&
!transform.data[0] ||
// translate(0, 0)
transform.name == 'translate' &&
!transform.data[0] &&
!transform.data[1] ||
// scale(1)
transform.name == 'scale' &&
transform.data[0] == 1 &&
(transform.data.length < 2 || transform.data[1] == 1) ||
// matrix(1 0 0 1 0 0)
transform.name == 'matrix' &&
transform.data[0] == 1 &&
transform.data[3] == 1 &&
!(transform.data[1] || transform.data[2] || transform.data[4] || transform.data[5])
) {
return false;
}
return true;
});
} }
/** /**
@ -293,39 +294,46 @@ function removeUseless(transforms) {
* @return {String} output string * @return {String} output string
*/ */
function js2transform(transformJS, params) { function js2transform(transformJS, params) {
var transformString = '';
var transformString = ''; // collect output value string
transformJS.forEach(function (transform) {
// collect output value string roundTransform(transform);
transformJS.forEach(function(transform) { transformString +=
roundTransform(transform); (transformString && ' ') +
transformString += (transformString && ' ') + transform.name + '(' + cleanupOutData(transform.data, params) + ')'; transform.name +
}); '(' +
cleanupOutData(transform.data, params) +
return transformString; ')';
});
return transformString;
} }
function roundTransform(transform) { function roundTransform(transform) {
switch (transform.name) { switch (transform.name) {
case 'translate': case 'translate':
transform.data = floatRound(transform.data); transform.data = floatRound(transform.data);
break; break;
case 'rotate': case 'rotate':
transform.data = degRound(transform.data.slice(0, 1)).concat(floatRound(transform.data.slice(1))); transform.data = degRound(transform.data.slice(0, 1)).concat(
break; floatRound(transform.data.slice(1))
case 'skewX': );
case 'skewY': break;
transform.data = degRound(transform.data); case 'skewX':
break; case 'skewY':
case 'scale': transform.data = degRound(transform.data);
transform.data = transformRound(transform.data); break;
break; case 'scale':
case 'matrix': transform.data = transformRound(transform.data);
transform.data = transformRound(transform.data.slice(0, 4)).concat(floatRound(transform.data.slice(4))); break;
break; case 'matrix':
} transform.data = transformRound(transform.data.slice(0, 4)).concat(
return transform; floatRound(transform.data.slice(4))
);
break;
}
return transform;
} }
/** /**
@ -335,7 +343,7 @@ function roundTransform(transform) {
* @return {Array} output data array * @return {Array} output data array
*/ */
function round(data) { function round(data) {
return data.map(Math.round); return data.map(Math.round);
} }
/** /**
@ -348,13 +356,19 @@ function round(data) {
* @return {Array} output data array * @return {Array} output data array
*/ */
function smartRound(precision, data) { function smartRound(precision, data) {
for (var i = data.length, tolerance = +Math.pow(.1, precision).toFixed(precision); i--;) { for (
if (data[i].toFixed(precision) != data[i]) { var i = data.length,
var rounded = +data[i].toFixed(precision - 1); tolerance = +Math.pow(0.1, precision).toFixed(precision);
data[i] = +Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance ? i--;
+data[i].toFixed(precision) :
rounded; ) {
} if (data[i].toFixed(precision) != data[i]) {
var rounded = +data[i].toFixed(precision - 1);
data[i] =
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance
? +data[i].toFixed(precision)
: rounded;
} }
return data; }
return data;
} }

View File

@ -4,18 +4,19 @@ exports.type = 'full';
exports.active = true; exports.active = true;
exports.description = 'minifies styles and removes unused styles based on usage data'; exports.description =
'minifies styles and removes unused styles based on usage data';
exports.params = { exports.params = {
// ... CSSO options goes here // ... CSSO options goes here
// additional // additional
usage: { usage: {
force: false, // force to use usage data even if it unsafe (document contains <script> or on* attributes) force: false, // force to use usage data even if it unsafe (document contains <script> or on* attributes)
ids: true, ids: true,
classes: true, classes: true,
tags: true tags: true,
} },
}; };
var csso = require('csso'); var csso = require('csso');
@ -25,130 +26,146 @@ var csso = require('csso');
* *
* @author strarsis <strarsis@gmail.com> * @author strarsis <strarsis@gmail.com>
*/ */
exports.fn = function(ast, options) { exports.fn = function (ast, options) {
options = options || {}; options = options || {};
var minifyOptionsForStylesheet = cloneObject(options); var minifyOptionsForStylesheet = cloneObject(options);
var minifyOptionsForAttribute = cloneObject(options); var minifyOptionsForAttribute = cloneObject(options);
var elems = findStyleElems(ast); var elems = findStyleElems(ast);
minifyOptionsForStylesheet.usage = collectUsageData(ast, options); minifyOptionsForStylesheet.usage = collectUsageData(ast, options);
minifyOptionsForAttribute.usage = null; minifyOptionsForAttribute.usage = null;
elems.forEach(function(elem) { elems.forEach(function (elem) {
if (elem.isElem('style')) { if (elem.isElem('style')) {
// <style> element // <style> element
var styleCss = elem.content[0].text || elem.content[0].cdata || []; var styleCss = elem.content[0].text || elem.content[0].cdata || [];
var DATA = styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0 ? 'cdata' : 'text'; var DATA =
styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0
? 'cdata'
: 'text';
elem.content[0][DATA] = csso.minify(styleCss, minifyOptionsForStylesheet).css; elem.content[0][DATA] = csso.minify(
} else { styleCss,
// style attribute minifyOptionsForStylesheet
var elemStyle = elem.attr('style').value; ).css;
} else {
// style attribute
var elemStyle = elem.attr('style').value;
elem.attr('style').value = csso.minifyBlock(elemStyle, minifyOptionsForAttribute).css; elem.attr('style').value = csso.minifyBlock(
} elemStyle,
}); minifyOptionsForAttribute
).css;
}
});
return ast; return ast;
}; };
function cloneObject(obj) { function cloneObject(obj) {
return {...obj}; return { ...obj };
} }
function findStyleElems(ast) { function findStyleElems(ast) {
function walk(items, styles) {
for (var i = 0; i < items.content.length; i++) {
var item = items.content[i];
function walk(items, styles) { // go deeper
for (var i = 0; i < items.content.length; i++) { if (item.content) {
var item = items.content[i]; walk(item, styles);
}
// go deeper if (item.isElem('style') && !item.isEmpty()) {
if (item.content) { styles.push(item);
walk(item, styles); } else if (item.isElem() && item.hasAttr('style')) {
} styles.push(item);
}
if (item.isElem('style') && !item.isEmpty()) {
styles.push(item);
} else if (item.isElem() && item.hasAttr('style')) {
styles.push(item);
}
}
return styles;
} }
return walk(ast, []); return styles;
}
return walk(ast, []);
} }
function shouldFilter(options, name) { function shouldFilter(options, name) {
if ('usage' in options === false) { if ('usage' in options === false) {
return true; return true;
} }
if (options.usage && name in options.usage === false) { if (options.usage && name in options.usage === false) {
return true; return true;
} }
return Boolean(options.usage && options.usage[name]); return Boolean(options.usage && options.usage[name]);
} }
function collectUsageData(ast, options) { function collectUsageData(ast, options) {
function walk(items, usageData) {
for (var i = 0; i < items.content.length; i++) {
var item = items.content[i];
function walk(items, usageData) { // go deeper
for (var i = 0; i < items.content.length; i++) { if (item.content) {
var item = items.content[i]; walk(item, usageData);
}
// go deeper if (item.isElem('script')) {
if (item.content) { safe = false;
walk(item, usageData); }
}
if (item.isElem('script')) { if (item.isElem()) {
safe = false; usageData.tags[item.elem] = true;
}
if (item.isElem()) { if (item.hasAttr('id')) {
usageData.tags[item.elem] = true; usageData.ids[item.attr('id').value] = true;
if (item.hasAttr('id')) {
usageData.ids[item.attr('id').value] = true;
}
if (item.hasAttr('class')) {
item.attr('class').value.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); })) {
safe = false;
}
}
} }
return usageData; if (item.hasAttr('class')) {
} item
.attr('class')
var safe = true; .value.replace(/^\s+|\s+$/g, '')
var usageData = {}; .split(/\s+/)
var hasData = false; .forEach(function (className) {
var rawData = walk(ast, { usageData.classes[className] = true;
ids: Object.create(null), });
classes: Object.create(null),
tags: Object.create(null)
});
if (!safe && options.usage && options.usage.force) {
safe = true;
}
for (const [key, data] of Object.entries(rawData)) {
if (shouldFilter(options, key)) {
usageData[key] = Object.keys(data);
hasData = true;
} }
if (
item.attrs &&
Object.keys(item.attrs).some(function (name) {
return /^on/i.test(name);
})
) {
safe = false;
}
}
} }
return safe && hasData ? usageData : null; return usageData;
}
var safe = true;
var usageData = {};
var hasData = false;
var rawData = walk(ast, {
ids: Object.create(null),
classes: Object.create(null),
tags: Object.create(null),
});
if (!safe && options.usage && options.usage.force) {
safe = true;
}
for (const [key, data] of Object.entries(rawData)) {
if (shouldFilter(options, key)) {
usageData[key] = Object.keys(data);
hasData = true;
}
}
return safe && hasData ? usageData : null;
} }

View File

@ -7,7 +7,7 @@ exports.active = true;
exports.description = 'moves elements attributes to the existing group wrapper'; exports.description = 'moves elements attributes to the existing group wrapper';
var inheritableAttrs = require('./_collections').inheritableAttrs, var inheritableAttrs = require('./_collections').inheritableAttrs,
pathElems = require('./_collections.js').pathElems; pathElems = require('./_collections.js').pathElems;
/** /**
* Collapse content's intersected and inheritable * Collapse content's intersected and inheritable
@ -33,65 +33,54 @@ 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) {
var intersection = {},
hasTransform = false,
hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'),
intersected = item.content.every(function (inner) {
if (inner.isElem() && 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) {
intersection = inner.attrs;
} else {
intersection = intersectInheritableAttrs(intersection, inner.attrs);
if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) { if (!intersection) return false;
}
var intersection = {},
hasTransform = false,
hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'),
intersected = item.content.every(function(inner) {
if (inner.isElem() && 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) {
intersection = inner.attrs;
} else {
intersection = intersectInheritableAttrs(intersection, inner.attrs);
if (!intersection) return false;
}
return true;
}
}),
allPath = item.content.every(function(inner) {
return inner.isElem(pathElems);
});
if (intersected) {
item.content.forEach(function(g) {
for (const [name, attr] of Object.entries(intersection)) {
if (!allPath && !hasClip || name !== 'transform') {
g.removeAttr(name);
if (name === 'transform') {
if (!hasTransform) {
if (item.hasAttr('transform')) {
item.attr('transform').value += ' ' + attr.value;
} else {
item.addAttr(attr);
}
hasTransform = true;
}
} else {
item.addAttr(attr);
}
}
}
});
return true;
} }
}),
allPath = item.content.every(function (inner) {
return inner.isElem(pathElems);
});
if (intersected) {
item.content.forEach(function (g) {
for (const [name, attr] of Object.entries(intersection)) {
if ((!allPath && !hasClip) || name !== 'transform') {
g.removeAttr(name);
if (name === 'transform') {
if (!hasTransform) {
if (item.hasAttr('transform')) {
item.attr('transform').value += ' ' + attr.value;
} else {
item.addAttr(attr);
}
hasTransform = true;
}
} else {
item.addAttr(attr);
}
}
}
});
} }
}
}; };
/** /**
@ -103,25 +92,23 @@ exports.fn = function(item) {
* @return {Object} intersected attrs object * @return {Object} intersected attrs object
*/ */
function intersectInheritableAttrs(a, b) { function intersectInheritableAttrs(a, b) {
var c = {};
var c = {}; for (const [n, attr] of Object.entries(a)) {
if (
for (const [n, attr] of Object.entries(a)) { // eslint-disable-next-line no-prototype-builtins
if ( b.hasOwnProperty(n) &&
// eslint-disable-next-line no-prototype-builtins inheritableAttrs.indexOf(n) > -1 &&
b.hasOwnProperty(n) && attr.name === b[n].name &&
inheritableAttrs.indexOf(n) > -1 && attr.value === b[n].value &&
attr.name === b[n].name && attr.prefix === b[n].prefix &&
attr.value === b[n].value && attr.local === b[n].local
attr.prefix === b[n].prefix && ) {
attr.local === b[n].local c[n] = attr;
) {
c[n] = attr;
}
} }
}
if (!Object.keys(c).length) return false; if (!Object.keys(c).length) return false;
return c;
return c;
} }

View File

@ -7,8 +7,8 @@ exports.active = true;
exports.description = 'moves some group attributes to the content elements'; exports.description = 'moves some group attributes to the content elements';
var collections = require('./_collections.js'), var collections = require('./_collections.js'),
pathElems = collections.pathElems.concat(['g', 'text']), pathElems = collections.pathElems.concat(['g', 'text']),
referencesProps = collections.referencesProps; referencesProps = collections.referencesProps;
/** /**
* Move group attrs to the content elements. * Move group attrs to the content elements.
@ -29,35 +29,34 @@ var collections = require('./_collections.js'),
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { 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.hasAttr('transform') &&
item.hasAttr('transform') && !item.isEmpty() &&
!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.content.every(function(inner) { return inner.isElem(pathElems) && !inner.hasAttr('id');
return inner.isElem(pathElems) && !inner.hasAttr('id'); })
}) ) {
) { item.content.forEach(function (inner) {
item.content.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 = attr.value + ' ' + inner.attr('transform').value; attr.value + ' ' + inner.attr('transform').value;
} else { } else {
inner.addAttr({ inner.addAttr({
'name': attr.name, name: attr.name,
'local': attr.local, local: attr.local,
'prefix': attr.prefix, prefix: attr.prefix,
'value': attr.value value: attr.value,
});
}
}); });
}
});
item.removeAttr('transform'); item.removeAttr('transform');
} }
}; };

View File

@ -5,148 +5,147 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.params = { exports.params = {
delim: '__', delim: '__',
prefixIds: true, prefixIds: true,
prefixClassNames: true, prefixClassNames: true,
}; };
exports.description = 'prefix IDs'; exports.description = 'prefix IDs';
var csstree = require('css-tree'), var csstree = require('css-tree'),
collections = require('./_collections.js'), collections = require('./_collections.js'),
referencesProps = collections.referencesProps, referencesProps = collections.referencesProps,
rxId = /^#(.*)$/, // regular expression for matching an ID + extracing its name rxId = /^#(.*)$/, // regular expression for matching an ID + extracing its name
addPrefix = null; addPrefix = null;
const unquote = (string) => { const unquote = (string) => {
const first = string.charAt(0) const first = string.charAt(0);
if (first === "'" || first === '"') { if (first === "'" || first === '"') {
if (first === string.charAt(string.length - 1)) { if (first === string.charAt(string.length - 1)) {
return string.slice(1, -1); return string.slice(1, -1);
} }
} }
return string; return string;
} };
// Escapes a string for being used as ID // Escapes a string for being used as ID
var escapeIdentifierName = function(str) { var escapeIdentifierName = function (str) {
return str.replace(/[. ]/g, '_'); return str.replace(/[. ]/g, '_');
}; };
// Matches an #ID value, captures the ID name // Matches an #ID value, captures the ID name
var matchId = function(urlVal) { var matchId = function (urlVal) {
var idUrlMatches = urlVal.match(rxId); var idUrlMatches = urlVal.match(rxId);
if (idUrlMatches === null) { if (idUrlMatches === null) {
return false; return false;
} }
return idUrlMatches[1]; return idUrlMatches[1];
}; };
// Matches an url(...) value, captures the URL // Matches an url(...) value, captures the URL
var matchUrl = function(val) { var matchUrl = function (val) {
var urlMatches = /url\((.*?)\)/gi.exec(val); var urlMatches = /url\((.*?)\)/gi.exec(val);
if (urlMatches === null) { if (urlMatches === null) {
return false; return false;
} }
return urlMatches[1]; return urlMatches[1];
}; };
// Checks if attribute is empty // Checks if attribute is empty
var attrNotEmpty = function(attr) { var attrNotEmpty = function (attr) {
return (attr && attr.value && attr.value.length > 0); return attr && attr.value && attr.value.length > 0;
}; };
// prefixes an #ID // prefixes an #ID
var prefixId = function(val) { var prefixId = function (val) {
var idName = matchId(val); var idName = matchId(val);
if (!idName) { if (!idName) {
return false; return false;
} }
return '#' + addPrefix(idName); return '#' + addPrefix(idName);
}; };
// attr.value helper methods // attr.value helper methods
// prefixes a class attribute value // prefixes a class attribute value
var addPrefixToClassAttr = function(attr) { var addPrefixToClassAttr = function (attr) {
if (!attrNotEmpty(attr)) { if (!attrNotEmpty(attr)) {
return; return;
} }
attr.value = attr.value.split(/\s+/).map(addPrefix).join(' '); attr.value = attr.value.split(/\s+/).map(addPrefix).join(' ');
}; };
// prefixes an ID attribute value // prefixes an ID attribute value
var addPrefixToIdAttr = function(attr) { var addPrefixToIdAttr = function (attr) {
if (!attrNotEmpty(attr)) { if (!attrNotEmpty(attr)) {
return; return;
} }
attr.value = addPrefix(attr.value); attr.value = addPrefix(attr.value);
}; };
// prefixes a href attribute value // prefixes a href attribute value
var addPrefixToHrefAttr = function(attr) { var addPrefixToHrefAttr = function (attr) {
if (!attrNotEmpty(attr)) { if (!attrNotEmpty(attr)) {
return; return;
} }
var idPrefixed = prefixId(attr.value); var idPrefixed = prefixId(attr.value);
if (!idPrefixed) { if (!idPrefixed) {
return; return;
} }
attr.value = idPrefixed; attr.value = idPrefixed;
}; };
// prefixes an URL attribute value // prefixes an URL attribute value
var addPrefixToUrlAttr = function(attr) { var addPrefixToUrlAttr = function (attr) {
if (!attrNotEmpty(attr)) { if (!attrNotEmpty(attr)) {
return; return;
} }
// url(...) in value // url(...) in value
var urlVal = matchUrl(attr.value); var urlVal = matchUrl(attr.value);
if (!urlVal) { if (!urlVal) {
return; return;
} }
var idPrefixed = prefixId(urlVal); var idPrefixed = prefixId(urlVal);
if (!idPrefixed) { if (!idPrefixed) {
return; return;
} }
attr.value = 'url(' + idPrefixed + ')'; attr.value = 'url(' + idPrefixed + ')';
}; };
// prefixes begin/end attribute value // prefixes begin/end attribute value
var addPrefixToBeginEndAttr = function(attr) { var addPrefixToBeginEndAttr = function (attr) {
if (!attrNotEmpty(attr)) { if (!attrNotEmpty(attr)) {
return; return;
}
var parts = attr.value.split('; ').map(function (val) {
val = val.trim();
if (val.endsWith('.end') || val.endsWith('.start')) {
var idPostfix = val.split('.'),
id = idPostfix[0],
postfix = idPostfix[1];
var idPrefixed = prefixId(`#${id}`);
if (!idPrefixed) {
return val;
}
idPrefixed = idPrefixed.slice(1);
return `${idPrefixed}.${postfix}`;
} else {
return val;
} }
});
var parts = attr.value.split('; ').map(function(val) { attr.value = parts.join('; ');
val = val.trim();
if (val.endsWith('.end') || val.endsWith('.start')) {
var idPostfix = val.split('.'),
id = idPostfix[0],
postfix = idPostfix[1];
var idPrefixed = prefixId(`#${id}`);
if (!idPrefixed) {
return val;
}
idPrefixed = idPrefixed.slice(1);
return `${idPrefixed}.${postfix}`;
} else {
return val;
}
});
attr.value = parts.join('; ');
}; };
const getBasename = (path) => { const getBasename = (path) => {
@ -167,123 +166,123 @@ const getBasename = (path) => {
* *
* @author strarsis <strarsis@gmail.com> * @author strarsis <strarsis@gmail.com>
*/ */
exports.fn = function(node, opts, extra) { exports.fn = function (node, opts, extra) {
// skip subsequent passes when multipass is used
// skip subsequent passes when multipass is used if (extra.multipassCount && extra.multipassCount > 0) {
if(extra.multipassCount && extra.multipassCount > 0) {
return node;
}
// prefix, from file name or option
var prefix = 'prefix';
if (opts.prefix) {
if (typeof opts.prefix === 'function') {
prefix = opts.prefix(node, extra);
} else {
prefix = opts.prefix;
}
} else if (opts.prefix === false) {
prefix = false;
} else if (extra && extra.path && extra.path.length > 0) {
var filename = getBasename(extra.path);
prefix = filename;
}
// prefixes a normal value
addPrefix = function(name) {
if(prefix === false){
return escapeIdentifierName(name);
}
return escapeIdentifierName(prefix + opts.delim + name);
};
// <style/> property values
if (node.elem === 'style') {
if (node.isEmpty()) {
// skip empty <style/>s
return node;
}
var cssStr = node.content[0].text || node.content[0].cdata || [];
var cssAst = {};
try {
cssAst = csstree.parse(cssStr, {
parseValue: true,
parseCustomProperty: false
});
} catch (parseError) {
console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError);
return node;
}
var idPrefixed = '';
csstree.walk(cssAst, function(node) {
// #ID, .class
if (((opts.prefixIds && node.type === 'IdSelector') ||
(opts.prefixClassNames && node.type === 'ClassSelector')) &&
node.name) {
node.name = addPrefix(node.name);
return;
}
// url(...) in value
if (node.type === 'Url' &&
node.value.value && node.value.value.length > 0) {
idPrefixed = prefixId(unquote(node.value.value));
if (!idPrefixed) {
return;
}
node.value.value = idPrefixed;
}
});
// update <style>s
node.content[0].text = csstree.generate(cssAst);
return node;
}
// element attributes
if (!node.attrs) {
return node;
}
// Nodes
if(opts.prefixIds) {
// ID
addPrefixToIdAttr(node.attrs.id);
}
if(opts.prefixClassNames) {
// Class
addPrefixToClassAttr(node.attrs.class);
}
// References
// href
addPrefixToHrefAttr(node.attrs.href);
// (xlink:)href (deprecated, must be still supported)
addPrefixToHrefAttr(node.attrs['xlink:href']);
// (referenceable) properties
for (var referencesProp of referencesProps) {
addPrefixToUrlAttr(node.attrs[referencesProp]);
}
addPrefixToBeginEndAttr(node.attrs.begin);
addPrefixToBeginEndAttr(node.attrs.end);
return node; return node;
}
// prefix, from file name or option
var prefix = 'prefix';
if (opts.prefix) {
if (typeof opts.prefix === 'function') {
prefix = opts.prefix(node, extra);
} else {
prefix = opts.prefix;
}
} else if (opts.prefix === false) {
prefix = false;
} else if (extra && extra.path && extra.path.length > 0) {
var filename = getBasename(extra.path);
prefix = filename;
}
// prefixes a normal value
addPrefix = function (name) {
if (prefix === false) {
return escapeIdentifierName(name);
}
return escapeIdentifierName(prefix + opts.delim + name);
};
// <style/> property values
if (node.elem === 'style') {
if (node.isEmpty()) {
// skip empty <style/>s
return node;
}
var cssStr = node.content[0].text || node.content[0].cdata || [];
var cssAst = {};
try {
cssAst = csstree.parse(cssStr, {
parseValue: true,
parseCustomProperty: false,
});
} catch (parseError) {
console.warn(
'Warning: Parse error of styles of <style/> element, skipped. Error details: ' +
parseError
);
return node;
}
var idPrefixed = '';
csstree.walk(cssAst, function (node) {
// #ID, .class
if (
((opts.prefixIds && node.type === 'IdSelector') ||
(opts.prefixClassNames && node.type === 'ClassSelector')) &&
node.name
) {
node.name = addPrefix(node.name);
return;
}
// url(...) in value
if (
node.type === 'Url' &&
node.value.value &&
node.value.value.length > 0
) {
idPrefixed = prefixId(unquote(node.value.value));
if (!idPrefixed) {
return;
}
node.value.value = idPrefixed;
}
});
// update <style>s
node.content[0].text = csstree.generate(cssAst);
return node;
}
// element attributes
if (!node.attrs) {
return node;
}
// Nodes
if (opts.prefixIds) {
// ID
addPrefixToIdAttr(node.attrs.id);
}
if (opts.prefixClassNames) {
// Class
addPrefixToClassAttr(node.attrs.class);
}
// References
// href
addPrefixToHrefAttr(node.attrs.href);
// (xlink:)href (deprecated, must be still supported)
addPrefixToHrefAttr(node.attrs['xlink:href']);
// (referenceable) properties
for (var referencesProp of referencesProps) {
addPrefixToUrlAttr(node.attrs[referencesProp]);
}
addPrefixToBeginEndAttr(node.attrs.begin);
addPrefixToBeginEndAttr(node.attrs.end);
return node;
}; };

View File

@ -4,8 +4,8 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.description = 'removes attributes of elements that match a css selector'; exports.description =
'removes attributes of elements that match a css selector';
/** /**
* Removes attributes of elements that match a css selector. * Removes attributes of elements that match a css selector.
@ -23,7 +23,7 @@ exports.description = 'removes attributes of elements that match a css selector'
* *
* <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/> * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
* ↓ * ↓
* <rect x="0" y="0" width="100" height="100" stroke="#00ff00"/> * <rect x="0" y="0" width="100" height="100" stroke="#00ff00"/>
* *
* <caption>A selector removing multiple attributes</caption> * <caption>A selector removing multiple attributes</caption>
* plugins: * plugins:
@ -35,7 +35,7 @@ exports.description = 'removes attributes of elements that match a css selector'
* *
* <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/> * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
* ↓ * ↓
* <rect x="0" y="0" width="100" height="100"/> * <rect x="0" y="0" width="100" height="100"/>
* *
* <caption>Multiple selectors removing attributes</caption> * <caption>Multiple selectors removing attributes</caption>
* plugins: * plugins:
@ -57,14 +57,12 @@ exports.description = 'removes attributes of elements that match a css selector'
* *
* @author Bradley Mease * @author Bradley Mease
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
var selectors = Array.isArray(params.selectors) ? params.selectors : [params];
var selectors = Array.isArray(params.selectors) ? params.selectors : [params];
selectors.map(function(i) {
if (item.matches(i.selector)) {
item.removeAttr(i.attributes);
}
});
selectors.map(function (i) {
if (item.matches(i.selector)) {
item.removeAttr(i.attributes);
}
});
}; };

View File

@ -9,9 +9,9 @@ exports.active = false;
exports.description = 'removes specified attributes'; exports.description = 'removes specified attributes';
exports.params = { exports.params = {
elemSeparator: DEFAULT_SEPARATOR, elemSeparator: DEFAULT_SEPARATOR,
preserveCurrentColor: false, preserveCurrentColor: false,
attrs: [] attrs: [],
}; };
/** /**
@ -81,70 +81,68 @@ exports.params = {
* *
* @author Benny Schudel * @author Benny Schudel
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
// wrap into an array if params is not // wrap into an array if params is not
if (!Array.isArray(params.attrs)) { if (!Array.isArray(params.attrs)) {
params.attrs = [params.attrs]; params.attrs = [params.attrs];
} }
if (item.isElem()) { if (item.isElem()) {
var elemSeparator = typeof params.elemSeparator == 'string' ? params.elemSeparator : DEFAULT_SEPARATOR; var elemSeparator =
var preserveCurrentColor = typeof params.preserveCurrentColor == 'boolean' ? params.preserveCurrentColor : false; typeof params.elemSeparator == 'string'
? params.elemSeparator
: DEFAULT_SEPARATOR;
var preserveCurrentColor =
typeof params.preserveCurrentColor == 'boolean'
? params.preserveCurrentColor
: false;
// prepare patterns // prepare patterns
var patterns = params.attrs.map(function(pattern) { var patterns = params.attrs.map(function (pattern) {
// if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value*
if (pattern.indexOf(elemSeparator) === -1) {
pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join('');
// if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value* // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value
if (pattern.indexOf(elemSeparator) === -1) { } else if (pattern.split(elemSeparator).length < 3) {
pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join(''); pattern = [pattern, elemSeparator, '.*'].join('');
}
// if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value // create regexps for element, attribute name, and attribute value
} else if (pattern.split(elemSeparator).length < 3) { return pattern.split(elemSeparator).map(function (value) {
pattern = [pattern, elemSeparator, '.*'].join(''); // adjust single * to match anything
if (value === '*') {
value = '.*';
}
return new RegExp(['^', value, '$'].join(''), 'i');
});
});
// loop patterns
patterns.forEach(function (pattern) {
// matches element
if (pattern[0].test(item.elem)) {
// loop attributes
item.eachAttr(function (attr) {
var name = attr.name;
var value = attr.value;
var isFillCurrentColor =
preserveCurrentColor && name == 'fill' && value == 'currentColor';
var isStrokeCurrentColor =
preserveCurrentColor && name == 'stroke' && value == 'currentColor';
if (!(isFillCurrentColor || isStrokeCurrentColor)) {
// matches attribute name
if (pattern[1].test(name)) {
// matches attribute value
if (pattern[2].test(attr.value)) {
item.removeAttr(name);
}
} }
}
// create regexps for element, attribute name, and attribute value
return pattern.split(elemSeparator)
.map(function(value) {
// adjust single * to match anything
if (value === '*') { value = '.*'; }
return new RegExp(['^', value, '$'].join(''), 'i');
});
}); });
}
// loop patterns });
patterns.forEach(function(pattern) { }
// matches element
if (pattern[0].test(item.elem)) {
// loop attributes
item.eachAttr(function(attr) {
var name = attr.name;
var value = attr.value;
var isFillCurrentColor = preserveCurrentColor && name == 'fill' && value == 'currentColor';
var isStrokeCurrentColor = preserveCurrentColor && name == 'stroke' && value == 'currentColor';
if (!(isFillCurrentColor || isStrokeCurrentColor)) {
// matches attribute name
if (pattern[1].test(name)) {
// matches attribute value
if (pattern[2].test(attr.value)) {
item.removeAttr(name);
}
}
}
});
}
});
}
}; };

View File

@ -18,10 +18,8 @@ exports.description = 'removes comments';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.comment && item.comment.charAt(0) !== '!') {
if (item.comment && item.comment.charAt(0) !== '!') { return false;
return false; }
}
}; };

View File

@ -5,7 +5,7 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.params = { exports.params = {
removeAny: true removeAny: true,
}; };
exports.description = 'removes <desc>'; exports.description = 'removes <desc>';
@ -24,9 +24,13 @@ var standardDescs = /^(Created with|Created using)/;
* *
* @author Daniel Wabyick * @author Daniel Wabyick
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
return (
return !item.isElem('desc') || !(params.removeAny || item.isEmpty() || !item.isElem('desc') ||
standardDescs.test(item.content[0].text)); !(
params.removeAny ||
item.isEmpty() ||
standardDescs.test(item.content[0].text)
)
);
}; };

View File

@ -4,7 +4,8 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.description = 'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)'; exports.description =
'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)';
/** /**
* Remove width/height attributes and add the viewBox attribute if it's missing * Remove width/height attributes and add the viewBox attribute if it's missing
@ -19,31 +20,29 @@ exports.description = 'removes width and height in presence of viewBox (opposite
* *
* @author Benny Schudel * @author Benny Schudel
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.isElem('svg')) {
if (item.isElem('svg')) { if (item.hasAttr('viewBox')) {
if (item.hasAttr('viewBox')) { item.removeAttr('width');
item.removeAttr('width'); item.removeAttr('height');
item.removeAttr('height'); } else if (
} else if ( item.hasAttr('width') &&
item.hasAttr('width') && item.hasAttr('height') &&
item.hasAttr('height') && !isNaN(Number(item.attr('width').value)) &&
!isNaN(Number(item.attr('width').value)) && !isNaN(Number(item.attr('height').value))
!isNaN(Number(item.attr('height').value)) ) {
) { item.addAttr({
item.addAttr({ name: 'viewBox',
name: 'viewBox', value:
value: '0 0 ' +
'0 0 ' + Number(item.attr('width').value) +
Number(item.attr('width').value) + ' ' +
' ' + Number(item.attr('height').value),
Number(item.attr('height').value), prefix: '',
prefix: '', local: 'viewBox',
local: 'viewBox' });
}); item.removeAttr('width');
item.removeAttr('width'); item.removeAttr('height');
item.removeAttr('height');
}
} }
}
}; };

View File

@ -31,10 +31,8 @@ exports.description = 'removes doctype declaration';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.doctype) {
if (item.doctype) { return false;
return false; }
}
}; };

View File

@ -7,10 +7,10 @@ exports.active = true;
exports.description = 'removes editors namespaces, elements and attributes'; exports.description = 'removes editors namespaces, elements and attributes';
var editorNamespaces = require('./_collections').editorNamespaces, var editorNamespaces = require('./_collections').editorNamespaces,
prefixes = []; prefixes = [];
exports.params = { exports.params = {
additionalNamespaces: [] additionalNamespaces: [],
}; };
/** /**
@ -27,39 +27,36 @@ exports.params = {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (Array.isArray(params.additionalNamespaces)) {
editorNamespaces = editorNamespaces.concat(params.additionalNamespaces);
}
if (Array.isArray(params.additionalNamespaces)) { if (item.elem) {
editorNamespaces = editorNamespaces.concat(params.additionalNamespaces); if (item.isElem('svg')) {
item.eachAttr(function (attr) {
if (
attr.prefix === 'xmlns' &&
editorNamespaces.indexOf(attr.value) > -1
) {
prefixes.push(attr.local);
// <svg xmlns:sodipodi="">
item.removeAttr(attr.name);
}
});
} }
if (item.elem) { // <* sodipodi:*="">
item.eachAttr(function (attr) {
if (item.isElem('svg')) { if (prefixes.indexOf(attr.prefix) > -1) {
item.removeAttr(attr.name);
item.eachAttr(function(attr) { }
if (attr.prefix === 'xmlns' && editorNamespaces.indexOf(attr.value) > -1) { });
prefixes.push(attr.local);
// <svg xmlns:sodipodi="">
item.removeAttr(attr.name);
}
});
}
// <* sodipodi:*="">
item.eachAttr(function(attr) {
if (prefixes.indexOf(attr.prefix) > -1) {
item.removeAttr(attr.name);
}
});
// <sodipodi:*>
if (prefixes.indexOf(item.prefix) > -1) {
return false;
}
// <sodipodi:*>
if (prefixes.indexOf(item.prefix) > -1) {
return false;
} }
}
}; };

View File

@ -4,11 +4,12 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.description = 'removes arbitrary elements by ID or className (disabled by default)'; exports.description =
'removes arbitrary elements by ID or className (disabled by default)';
exports.params = { exports.params = {
id: [], id: [],
class: [] class: [],
}; };
/** /**
@ -50,11 +51,11 @@ exports.params = {
* *
* @author Eli Dupuis (@elidupuis) * @author Eli Dupuis (@elidupuis)
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
// wrap params in an array if not already // wrap params in an array if not already
['id', 'class'].forEach(function(key) { ['id', 'class'].forEach(function (key) {
if (!Array.isArray(params[key])) { if (!Array.isArray(params[key])) {
params[key] = [ params[key] ]; params[key] = [params[key]];
} }
}); });
@ -73,6 +74,6 @@ exports.fn = function(item, params) {
const elemClass = item.attr('class'); const elemClass = item.attr('class');
if (elemClass && params.class.length !== 0) { if (elemClass && params.class.length !== 0) {
const classList = elemClass.value.split(' '); const classList = elemClass.value.split(' ');
return params.class.some(item => classList.includes(item)) === false; return params.class.some((item) => classList.includes(item)) === false;
} }
}; };

View File

@ -7,9 +7,9 @@ exports.active = true;
exports.description = 'removes empty <text> elements'; exports.description = 'removes empty <text> elements';
exports.params = { exports.params = {
text: true, text: true,
tspan: true, tspan: true,
tref: true tref: true,
}; };
/** /**
@ -33,27 +33,14 @@ exports.params = {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
// Remove empty text element
if (params.text && item.isElem('text') && item.isEmpty()) return false;
// Remove empty text element // Remove empty tspan element
if ( if (params.tspan && item.isElem('tspan') && item.isEmpty()) return false;
params.text &&
item.isElem('text') &&
item.isEmpty()
) return false;
// Remove empty tspan element
if (
params.tspan &&
item.isElem('tspan') &&
item.isEmpty()
) return false;
// Remove tref with empty xlink:href attribute
if (
params.tref &&
item.isElem('tref') &&
!item.hasAttrLocal('href')
) return false;
// Remove tref with empty xlink:href attribute
if (params.tref && item.isElem('tref') && !item.hasAttrLocal('href'))
return false;
}; };

View File

@ -16,8 +16,6 @@ exports.description = 'removes <metadata>';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
return !item.isElem('metadata');
return !item.isElem('metadata');
}; };

View File

@ -4,11 +4,12 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.description = 'removes non-inheritable groups presentational attributes'; exports.description =
'removes non-inheritable groups presentational attributes';
var inheritableAttrs = require('./_collections').inheritableAttrs, var inheritableAttrs = require('./_collections').inheritableAttrs,
attrsGroups = require('./_collections').attrsGroups, attrsGroups = require('./_collections').attrsGroups,
applyGroups = require('./_collections').presentationNonInheritableGroupAttrs; applyGroups = require('./_collections').presentationNonInheritableGroupAttrs;
/** /**
* Remove non-inheritable group's "presentation" attributes. * Remove non-inheritable group's "presentation" attributes.
@ -18,20 +19,16 @@ var inheritableAttrs = require('./_collections').inheritableAttrs,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.isElem('g')) {
if (item.isElem('g')) { item.eachAttr(function (attr) {
if (
item.eachAttr(function(attr) { ~attrsGroups.presentation.indexOf(attr.name) &&
if ( !~inheritableAttrs.indexOf(attr.name) &&
~attrsGroups.presentation.indexOf(attr.name) && !~applyGroups.indexOf(attr.name)
!~inheritableAttrs.indexOf(attr.name) && ) {
!~applyGroups.indexOf(attr.name) item.removeAttr(attr.name);
) { }
item.removeAttr(attr.name); });
} }
});
}
}; };

View File

@ -4,15 +4,16 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.description = 'removes elements that are drawn outside of the viewbox (disabled by default)'; exports.description =
'removes elements that are drawn outside of the viewbox (disabled by default)';
const JSAPI = require('../lib/svgo/jsAPI.js'); const JSAPI = require('../lib/svgo/jsAPI.js');
var _path = require('./_path.js'), var _path = require('./_path.js'),
intersects = _path.intersects, intersects = _path.intersects,
path2js = _path.path2js, path2js = _path.path2js,
viewBox, viewBox,
viewBoxJS; viewBoxJS;
/** /**
* Remove elements that are drawn outside of the viewbox. * Remove elements that are drawn outside of the viewbox.
@ -22,33 +23,32 @@ var _path = require('./_path.js'),
* *
* @author JoshyPHP * @author JoshyPHP
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (
item.isElem('path') &&
item.hasAttr('d') &&
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)) {
return true;
}
if (item.isElem('path') && item.hasAttr('d') && typeof viewBox !== 'undefined') var pathJS = path2js(item);
{ if (pathJS.length === 2) {
// Consider that any item with a transform attribute or a M instruction // Use a closed clone of the path if it's too short for intersects()
// within the viewBox is visible pathJS = JSON.parse(JSON.stringify(pathJS));
if (hasTransform(item) || pathMovesWithinViewBox(item.attr('d').value)) pathJS.push({ instruction: 'z' });
{ }
return true;
}
var pathJS = path2js(item); return intersects(viewBoxJS, pathJS);
if (pathJS.length === 2) }
{ if (item.isElem('svg')) {
// Use a closed clone of the path if it's too short for intersects() parseViewBox(item);
pathJS = JSON.parse(JSON.stringify(pathJS)); }
pathJS.push({ instruction: 'z' });
}
return intersects(viewBoxJS, pathJS); return true;
}
if (item.isElem('svg'))
{
parseViewBox(item);
}
return true;
}; };
/** /**
@ -57,9 +57,11 @@ exports.fn = function(item) {
* @param {String} path * @param {String} path
* @return {Boolean} * @return {Boolean}
*/ */
function hasTransform(item) function hasTransform(item) {
{ return (
return item.hasAttr('transform') || (item.parentNode && hasTransform(item.parentNode)); item.hasAttr('transform') ||
(item.parentNode && hasTransform(item.parentNode))
);
} }
/** /**
@ -67,50 +69,51 @@ function hasTransform(item)
* *
* @param {Object} svg svg element item * @param {Object} svg svg element item
*/ */
function parseViewBox(svg) function parseViewBox(svg) {
{ var viewBoxData = '';
var viewBoxData = ''; if (svg.hasAttr('viewBox')) {
if (svg.hasAttr('viewBox')) // Remove commas and plus signs, normalize and trim whitespace
{ viewBoxData = svg.attr('viewBox').value;
// Remove commas and plus signs, normalize and trim whitespace } else if (svg.hasAttr('height') && svg.hasAttr('width')) {
viewBoxData = svg.attr('viewBox').value; viewBoxData =
} '0 0 ' + svg.attr('width').value + ' ' + svg.attr('height').value;
else if (svg.hasAttr('height') && svg.hasAttr('width')) }
{
viewBoxData = '0 0 ' + svg.attr('width').value + ' ' + svg.attr('height').value;
}
// Remove commas and plus signs, normalize and trim whitespace // Remove commas and plus signs, normalize and trim whitespace
viewBoxData = viewBoxData.replace(/[,+]|px/g, ' ').replace(/\s+/g, ' ').replace(/^\s*|\s*$/g, ''); viewBoxData = viewBoxData
.replace(/[,+]|px/g, ' ')
.replace(/\s+/g, ' ')
.replace(/^\s*|\s*$/g, '');
// Ensure that the dimensions are 4 values separated by space // Ensure that the dimensions are 4 values separated by space
var m = /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(viewBoxData); var m = /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
if (!m) viewBoxData
{ );
return; if (!m) {
} return;
}
// Store the viewBox boundaries // Store the viewBox boundaries
viewBox = { viewBox = {
left: parseFloat(m[1]), left: parseFloat(m[1]),
top: parseFloat(m[2]), top: parseFloat(m[2]),
right: parseFloat(m[1]) + parseFloat(m[3]), right: parseFloat(m[1]) + parseFloat(m[3]),
bottom: parseFloat(m[2]) + parseFloat(m[4]) bottom: parseFloat(m[2]) + parseFloat(m[4]),
}; };
var path = new JSAPI({ var path = new JSAPI({
elem: 'path', elem: 'path',
prefix: '', prefix: '',
local: 'path' local: 'path',
}); });
path.addAttr({ path.addAttr({
name: 'd', name: 'd',
prefix: '', prefix: '',
local: 'd', local: 'd',
value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z' value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z',
}); });
viewBoxJS = path2js(path); viewBoxJS = path2js(path);
} }
/** /**
@ -119,16 +122,19 @@ function parseViewBox(svg)
* @param {String} path * @param {String} path
* @return {Boolean} * @return {Boolean}
*/ */
function pathMovesWithinViewBox(path) function pathMovesWithinViewBox(path) {
{ var regexp = /M\s*(-?\d*\.?\d+)(?!\d)\s*(-?\d*\.?\d+)/g,
var regexp = /M\s*(-?\d*\.?\d+)(?!\d)\s*(-?\d*\.?\d+)/g, m; m;
while (null !== (m = regexp.exec(path))) while (null !== (m = regexp.exec(path))) {
{ if (
if (m[1] >= viewBox.left && m[1] <= viewBox.right && m[2] >= viewBox.top && m[2] <= viewBox.bottom) m[1] >= viewBox.left &&
{ m[1] <= viewBox.right &&
return true; m[2] >= viewBox.top &&
} m[2] <= viewBox.bottom
} ) {
return true;
}
}
return false; return false;
} }

View File

@ -16,13 +16,11 @@ exports.description = 'removes raster images (disabled by default)';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (
if ( item.isElem('image') &&
item.isElem('image') && item.hasAttrLocal('href', /(\.|image\/)(jpg|png|gif)/)
item.hasAttrLocal('href', /(\.|image\/)(jpg|png|gif)/) ) {
) { return false;
return false; }
}
}; };

View File

@ -16,8 +16,6 @@ exports.description = 'removes <script> elements (disabled by default)';
* *
* @author Patrick Klingemann * @author Patrick Klingemann
*/ */
exports.fn = function(item) { exports.fn = function (item) {
return !item.isElem('script');
return !item.isElem('script');
}; };

View File

@ -16,8 +16,6 @@ exports.description = 'removes <style> element (disabled by default)';
* *
* @author Betsy Dupuis * @author Betsy Dupuis
*/ */
exports.fn = function(item) { exports.fn = function (item) {
return !item.isElem('style');
return !item.isElem('style');
}; };

View File

@ -16,8 +16,6 @@ exports.description = 'removes <title>';
* *
* @author Igor Kalashnikov * @author Igor Kalashnikov
*/ */
exports.fn = function(item) { exports.fn = function (item) {
return !item.isElem('title');
return !item.isElem('title');
}; };

View File

@ -4,54 +4,54 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.description = 'removes unknown elements content and attributes, removes attrs with default values'; exports.description =
'removes unknown elements content and attributes, removes attrs with default values';
exports.params = { exports.params = {
unknownContent: true, unknownContent: true,
unknownAttrs: true, unknownAttrs: true,
defaultAttrs: true, defaultAttrs: true,
uselessOverrides: true, uselessOverrides: true,
keepDataAttrs: true, keepDataAttrs: true,
keepAriaAttrs: true, keepAriaAttrs: true,
keepRoleAttr: false keepRoleAttr: false,
}; };
var collections = require('./_collections'), var collections = require('./_collections'),
elems = collections.elems, elems = collections.elems,
attrsGroups = collections.attrsGroups, attrsGroups = collections.attrsGroups,
elemsGroups = collections.elemsGroups, elemsGroups = collections.elemsGroups,
attrsGroupsDefaults = collections.attrsGroupsDefaults, attrsGroupsDefaults = collections.attrsGroupsDefaults,
attrsInheritable = collections.inheritableAttrs, attrsInheritable = collections.inheritableAttrs,
applyGroups = collections.presentationNonInheritableGroupAttrs; applyGroups = collections.presentationNonInheritableGroupAttrs;
// collect and extend all references // collect and extend all references
for (const elem of Object.values(elems)) { for (const elem of Object.values(elems)) {
if (elem.attrsGroups) { if (elem.attrsGroups) {
elem.attrs = elem.attrs || []; elem.attrs = elem.attrs || [];
elem.attrsGroups.forEach(function(attrsGroupName) { elem.attrsGroups.forEach(function (attrsGroupName) {
elem.attrs = elem.attrs.concat(attrsGroups[attrsGroupName]); elem.attrs = elem.attrs.concat(attrsGroups[attrsGroupName]);
var groupDefaults = attrsGroupsDefaults[attrsGroupName]; var groupDefaults = attrsGroupsDefaults[attrsGroupName];
if (groupDefaults) { if (groupDefaults) {
elem.defaults = elem.defaults || {}; elem.defaults = elem.defaults || {};
for (const [attrName, attr] of Object.entries(groupDefaults)) { for (const [attrName, attr] of Object.entries(groupDefaults)) {
elem.defaults[attrName] = attr; elem.defaults[attrName] = attr;
} }
} }
}); });
}
} if (elem.contentGroups) {
elem.content = elem.content || [];
if (elem.contentGroups) { elem.contentGroups.forEach(function (contentGroupName) {
elem.content = elem.content || []; elem.content = elem.content.concat(elemsGroups[contentGroupName]);
});
elem.contentGroups.forEach(function(contentGroupName) { }
elem.content = elem.content.concat(elemsGroups[contentGroupName]);
});
}
} }
/** /**
@ -64,85 +64,64 @@ for (const elem of Object.values(elems)) {
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
// elems w/o namespace prefix
if (item.isElem() && !item.prefix) {
var elem = item.elem;
// elems w/o namespace prefix // remove unknown element's content
if (item.isElem() && !item.prefix) { if (
params.unknownContent &&
var elem = item.elem; !item.isEmpty() &&
elems[elem] && // make sure we know of this element before checking its children
// remove unknown element's content elem !== 'foreignObject' // Don't check foreignObject
) {
item.content.forEach(function (content, i) {
if ( if (
params.unknownContent && content.isElem() &&
!item.isEmpty() && !content.prefix &&
elems[elem] && // make sure we know of this element before checking its children ((elems[elem].content && // Do we have a record of its permitted content?
elem !== 'foreignObject' // Don't check foreignObject elems[elem].content.indexOf(content.elem) === -1) ||
(!elems[elem].content && // we dont know about its permitted content
!elems[content.elem])) // check that we know about the element at all
) { ) {
item.content.forEach(function(content, i) { item.content.splice(i, 1);
if (
content.isElem() &&
!content.prefix &&
(
(
elems[elem].content && // Do we have a record of its permitted content?
elems[elem].content.indexOf(content.elem) === -1
) ||
(
!elems[elem].content && // we dont know about its permitted content
!elems[content.elem] // check that we know about the element at all
)
)
) {
item.content.splice(i, 1);
}
});
} }
});
// remove element's unknown attrs and attrs with default values
if (elems[elem] && elems[elem].attrs) {
item.eachAttr(function(attr) {
if (
attr.name !== 'xmlns' &&
(attr.prefix === 'xml' || !attr.prefix) &&
(!params.keepDataAttrs || attr.name.indexOf('data-') != 0) &&
(!params.keepAriaAttrs || attr.name.indexOf('aria-') != 0) &&
(!params.keepRoleAttr || attr.name != 'role')
) {
if (
// unknown attrs
(
params.unknownAttrs &&
elems[elem].attrs.indexOf(attr.name) === -1
) ||
// attrs with default values
(
params.defaultAttrs &&
!item.hasAttr('id') &&
elems[elem].defaults &&
elems[elem].defaults[attr.name] === attr.value && (
attrsInheritable.indexOf(attr.name) < 0 ||
!item.parentNode.computedAttr(attr.name)
)
) ||
// useless overrides
(
params.uselessOverrides &&
!item.hasAttr('id') &&
applyGroups.indexOf(attr.name) < 0 &&
attrsInheritable.indexOf(attr.name) > -1 &&
item.parentNode.computedAttr(attr.name, attr.value)
)
) {
item.removeAttr(attr.name);
}
}
});
}
} }
// remove element's unknown attrs and attrs with default values
if (elems[elem] && elems[elem].attrs) {
item.eachAttr(function (attr) {
if (
attr.name !== 'xmlns' &&
(attr.prefix === 'xml' || !attr.prefix) &&
(!params.keepDataAttrs || attr.name.indexOf('data-') != 0) &&
(!params.keepAriaAttrs || attr.name.indexOf('aria-') != 0) &&
(!params.keepRoleAttr || attr.name != 'role')
) {
if (
// unknown attrs
(params.unknownAttrs &&
elems[elem].attrs.indexOf(attr.name) === -1) ||
// attrs with default values
(params.defaultAttrs &&
!item.hasAttr('id') &&
elems[elem].defaults &&
elems[elem].defaults[attr.name] === attr.value &&
(attrsInheritable.indexOf(attr.name) < 0 ||
!item.parentNode.computedAttr(attr.name))) ||
// useless overrides
(params.uselessOverrides &&
!item.hasAttr('id') &&
applyGroups.indexOf(attr.name) < 0 &&
attrsInheritable.indexOf(attr.name) > -1 &&
item.parentNode.computedAttr(attr.name, attr.value))
) {
item.removeAttr(attr.name);
}
}
});
}
}
}; };

View File

@ -14,96 +14,84 @@ exports.description = 'removes unused namespaces declaration';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(data) { exports.fn = function (data) {
var svgElem,
xmlnsCollection = [];
var svgElem, /**
xmlnsCollection = []; * Remove namespace from collection.
*
/** * @param {String} ns namescape name
* Remove namespace from collection. */
* function removeNSfromCollection(ns) {
* @param {String} ns namescape name var pos = xmlnsCollection.indexOf(ns);
*/
function removeNSfromCollection(ns) {
var pos = xmlnsCollection.indexOf(ns);
// if found - remove ns from the namespaces collection
if (pos > -1) {
xmlnsCollection.splice(pos, 1);
}
// if found - remove ns from the namespaces collection
if (pos > -1) {
xmlnsCollection.splice(pos, 1);
} }
}
/** /**
* Bananas! * Bananas!
* *
* @param {Array} items input items * @param {Array} items input items
* *
* @return {Array} output items * @return {Array} output items
*/ */
function monkeys(items) { function monkeys(items) {
var i = 0,
length = items.content.length;
var i = 0, while (i < length) {
length = items.content.length; var item = items.content[i];
while(i < length) { if (item.isElem('svg')) {
item.eachAttr(function (attr) {
var item = items.content[i]; // collect namespaces
if (attr.prefix === 'xmlns' && attr.local) {
if (item.isElem('svg')) { xmlnsCollection.push(attr.local);
}
item.eachAttr(function(attr) {
// collect namespaces
if (attr.prefix === 'xmlns' && attr.local) {
xmlnsCollection.push(attr.local);
}
});
// if svg element has ns-attr
if (xmlnsCollection.length) {
// save svg element
svgElem = item;
}
}
if (xmlnsCollection.length) {
// check item for the ns-attrs
if (item.prefix) {
removeNSfromCollection(item.prefix);
}
// check each attr for the ns-attrs
item.eachAttr(function(attr) {
removeNSfromCollection(attr.prefix);
});
}
// if nothing is found - go deeper
if (xmlnsCollection.length && item.content) {
monkeys(item);
}
i++;
}
return items;
}
data = monkeys(data);
// remove svg element ns-attributes if they are not used even once
if (xmlnsCollection.length) {
xmlnsCollection.forEach(function(name) {
svgElem.removeAttr('xmlns:' + name);
}); });
// if svg element has ns-attr
if (xmlnsCollection.length) {
// save svg element
svgElem = item;
}
}
if (xmlnsCollection.length) {
// check item for the ns-attrs
if (item.prefix) {
removeNSfromCollection(item.prefix);
}
// check each attr for the ns-attrs
item.eachAttr(function (attr) {
removeNSfromCollection(attr.prefix);
});
}
// if nothing is found - go deeper
if (xmlnsCollection.length && item.content) {
monkeys(item);
}
i++;
} }
return data; return items;
}
data = monkeys(data);
// remove svg element ns-attributes if they are not used even once
if (xmlnsCollection.length) {
xmlnsCollection.forEach(function (name) {
svgElem.removeAttr('xmlns:' + name);
});
}
return data;
}; };

View File

@ -16,38 +16,27 @@ var nonRendering = require('./_collections').elemsGroups.nonRendering;
* *
* @author Lev Solntsev * @author Lev Solntsev
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.isElem('defs')) {
if (item.isElem('defs')) { if (item.content) {
item.content = getUsefulItems(item, []);
if (item.content) {
item.content = getUsefulItems(item, []);
}
if (item.isEmpty()) return false;
} else if (item.isElem(nonRendering) && !item.hasAttr('id')) {
return false;
} }
if (item.isEmpty()) return false;
} else if (item.isElem(nonRendering) && !item.hasAttr('id')) {
return false;
}
}; };
function getUsefulItems(item, usefulItems) { function getUsefulItems(item, usefulItems) {
item.content.forEach(function (child) {
if (child.hasAttr('id') || child.isElem('style')) {
usefulItems.push(child);
child.parentNode = item;
} else if (!child.isEmpty()) {
child.content = getUsefulItems(child, usefulItems);
}
});
item.content.forEach(function(child) { return usefulItems;
if (child.hasAttr('id') || child.isElem('style')) {
usefulItems.push(child);
child.parentNode = item;
} else if (!child.isEmpty()) {
child.content = getUsefulItems(child, usefulItems);
}
});
return usefulItems;
} }

View File

@ -7,16 +7,16 @@ exports.active = true;
exports.description = 'removes useless stroke and fill attributes'; exports.description = 'removes useless stroke and fill attributes';
exports.params = { exports.params = {
stroke: true, stroke: true,
fill: true, fill: true,
removeNone: false, removeNone: false,
hasStyleOrScript: false hasStyleOrScript: false,
}; };
var shape = require('./_collections').elemsGroups.shape, var shape = require('./_collections').elemsGroups.shape,
regStrokeProps = /^stroke/, regStrokeProps = /^stroke/,
regFillProps = /^fill-/, regFillProps = /^fill-/,
styleOrScript = ['style', 'script']; styleOrScript = ['style', 'script'];
/** /**
* Remove useless stroke and fill attrs. * Remove useless stroke and fill attrs.
@ -27,81 +27,78 @@ var shape = require('./_collections').elemsGroups.shape,
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
if (item.isElem(styleOrScript)) {
if (item.isElem(styleOrScript)) { params.hasStyleOrScript = true;
params.hasStyleOrScript = true; }
if (
!params.hasStyleOrScript &&
item.isElem(shape) &&
!item.computedAttr('id')
) {
var stroke = params.stroke && item.computedAttr('stroke'),
fill = params.fill && !item.computedAttr('fill', 'none');
// remove stroke*
if (
params.stroke &&
(!stroke ||
stroke == 'none' ||
item.computedAttr('stroke-opacity', '0') ||
item.computedAttr('stroke-width', '0'))
) {
// stroke-width may affect the size of marker-end
if (
item.computedAttr('stroke-width', '0') === true ||
item.computedAttr('marker-end') == null
) {
var parentStroke = item.parentNode.computedAttr('stroke'),
declineStroke = parentStroke && parentStroke != 'none';
item.eachAttr(function (attr) {
if (regStrokeProps.test(attr.name)) {
item.removeAttr(attr.name);
}
});
if (declineStroke)
item.addAttr({
name: 'stroke',
value: 'none',
prefix: '',
local: 'stroke',
});
}
} }
if (!params.hasStyleOrScript && item.isElem(shape) && !item.computedAttr('id')) { // remove fill*
if (params.fill && (!fill || item.computedAttr('fill-opacity', '0'))) {
var stroke = params.stroke && item.computedAttr('stroke'), item.eachAttr(function (attr) {
fill = params.fill && !item.computedAttr('fill', 'none'); if (regFillProps.test(attr.name)) {
item.removeAttr(attr.name);
// remove stroke*
if (
params.stroke &&
(
!stroke ||
stroke == 'none' ||
item.computedAttr('stroke-opacity', '0') ||
item.computedAttr('stroke-width', '0')
)
) {
// stroke-width may affect the size of marker-end
if (
item.computedAttr('stroke-width', '0') === true ||
item.computedAttr('marker-end') == null
) {
var parentStroke = item.parentNode.computedAttr('stroke'),
declineStroke = parentStroke && parentStroke != 'none';
item.eachAttr(function(attr) {
if (regStrokeProps.test(attr.name)) {
item.removeAttr(attr.name);
}
});
if (declineStroke) item.addAttr({
name: 'stroke',
value: 'none',
prefix: '',
local: 'stroke'
});
}
}
// remove fill*
if (
params.fill &&
(!fill || item.computedAttr('fill-opacity', '0'))
) {
item.eachAttr(function(attr) {
if (regFillProps.test(attr.name)) {
item.removeAttr(attr.name);
}
});
if (fill) {
if (item.hasAttr('fill'))
item.attr('fill').value = 'none';
else
item.addAttr({
name: 'fill',
value: 'none',
prefix: '',
local: 'fill'
});
}
}
if (params.removeNone &&
(!stroke || item.hasAttr('stroke') && item.attr('stroke').value=='none') &&
(!fill || item.hasAttr('fill') && item.attr('fill').value=='none')) {
return false;
} }
});
if (fill) {
if (item.hasAttr('fill')) item.attr('fill').value = 'none';
else
item.addAttr({
name: 'fill',
value: 'none',
prefix: '',
local: 'fill',
});
}
} }
if (
params.removeNone &&
(!stroke ||
(item.hasAttr('stroke') && item.attr('stroke').value == 'none')) &&
(!fill || (item.hasAttr('fill') && item.attr('fill').value == 'none'))
) {
return false;
}
}
}; };

View File

@ -4,7 +4,8 @@ exports.type = 'perItem';
exports.active = false; exports.active = false;
exports.description = 'removes xmlns attribute (for inline svg, disabled by default)'; exports.description =
'removes xmlns attribute (for inline svg, disabled by default)';
/** /**
* Remove the xmlns attribute when present. * Remove the xmlns attribute when present.
@ -19,10 +20,8 @@ exports.description = 'removes xmlns attribute (for inline svg, disabled by defa
* *
* @author Ricardo Tomasi * @author Ricardo Tomasi
*/ */
exports.fn = function(item) { exports.fn = function (item) {
if (item.isElem('svg') && item.hasAttr('xmlns')) {
if (item.isElem('svg') && item.hasAttr('xmlns')) { item.removeAttr('xmlns');
item.removeAttr('xmlns'); }
} };
};

View File

@ -17,8 +17,8 @@ exports.description = 'removes XML processing instructions';
* *
* @author Kir Belevich * @author Kir Belevich
*/ */
exports.fn = function(item) { exports.fn = function (item) {
return !(
return !(item.processinginstruction && item.processinginstruction.name === 'xml'); item.processinginstruction && item.processinginstruction.name === 'xml'
);
}; };

View File

@ -84,9 +84,10 @@ exports.type = 'full';
exports.active = false; exports.active = false;
exports.description = 'Finds <path> elements with the same d, fill, and ' + exports.description =
'stroke, and converts them to <use> elements ' + 'Finds <path> elements with the same d, fill, and ' +
'referencing a single <path> def.'; 'stroke, and converts them to <use> elements ' +
'referencing a single <path> def.';
/** /**
* Finds <path> elements with the same d, fill, and stroke, and converts them to * Finds <path> elements with the same d, fill, and stroke, and converts them to
@ -94,11 +95,11 @@ exports.description = 'Finds <path> elements with the same d, fill, and ' +
* *
* @author Jacob Howcroft * @author Jacob Howcroft
*/ */
exports.fn = function(data) { exports.fn = function (data) {
const seen = new Map(); const seen = new Map();
let count = 0; let count = 0;
const defs = []; const defs = [];
traverse(data, item => { traverse(data, (item) => {
if (!item.isElem('path') || !item.hasAttr('d')) { if (!item.isElem('path') || !item.hasAttr('d')) {
return; return;
} }
@ -108,22 +109,34 @@ exports.fn = function(data) {
const key = d + ';s:' + stroke + ';f:' + fill; const key = d + ';s:' + stroke + ';f:' + fill;
const hasSeen = seen.get(key); const hasSeen = seen.get(key);
if (!hasSeen) { if (!hasSeen) {
seen.set(key, {elem: item, reused: false}); seen.set(key, { elem: item, reused: false });
return; return;
} }
if (!hasSeen.reused) { if (!hasSeen.reused) {
hasSeen.reused = true; hasSeen.reused = true;
if (!hasSeen.elem.hasAttr('id')) { if (!hasSeen.elem.hasAttr('id')) {
hasSeen.elem.addAttr({name: 'id', local: 'id', hasSeen.elem.addAttr({
prefix: '', value: 'reuse-' + (count++)}); name: 'id',
local: 'id',
prefix: '',
value: 'reuse-' + count++,
});
} }
defs.push(hasSeen.elem); defs.push(hasSeen.elem);
} }
convertToUse(item, hasSeen.elem.attr('id').value); convertToUse(item, hasSeen.elem.attr('id').value);
}); });
if (defs.length > 0) { if (defs.length > 0) {
const defsTag = new JSAPI({ const defsTag = new JSAPI(
elem: 'defs', prefix: '', local: 'defs', content: [], attrs: []}, data); {
elem: 'defs',
prefix: '',
local: 'defs',
content: [],
attrs: [],
},
data
);
data.content[0].spliceContent(0, 0, defsTag); data.content[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
@ -152,8 +165,12 @@ function convertToUse(item, href) {
item.removeAttr('d'); item.removeAttr('d');
item.removeAttr('stroke'); item.removeAttr('stroke');
item.removeAttr('fill'); item.removeAttr('fill');
item.addAttr({name: 'xlink:href', local: 'xlink:href', item.addAttr({
prefix: 'none', value: '#' + href}); name: 'xlink:href',
local: 'xlink:href',
prefix: 'none',
value: '#' + href,
});
delete item.pathJS; delete item.pathJS;
return item; return item;
} }

View File

@ -7,15 +7,25 @@ exports.active = false;
exports.description = 'sorts element attributes (disabled by default)'; exports.description = 'sorts element attributes (disabled by default)';
exports.params = { exports.params = {
order: [ order: [
'id', 'id',
'width', 'height', 'width',
'x', 'x1', 'x2', 'height',
'y', 'y1', 'y2', 'x',
'cx', 'cy', 'r', 'x1',
'fill', 'stroke', 'marker', 'x2',
'd', 'points' 'y',
] 'y1',
'y2',
'cx',
'cy',
'r',
'fill',
'stroke',
'marker',
'd',
'points',
],
}; };
/** /**
@ -26,59 +36,53 @@ exports.params = {
* *
* @author Nikolay Frantsev * @author Nikolay Frantsev
*/ */
exports.fn = function(item, params) { exports.fn = function (item, params) {
var attrs = [],
sorted = {},
orderlen = params.order.length + 1,
xmlnsOrder = params.xmlnsOrder || 'front';
var attrs = [], if (item.elem) {
sorted = {}, item.eachAttr(function (attr) {
orderlen = params.order.length + 1, attrs.push(attr);
xmlnsOrder = params.xmlnsOrder || 'front'; });
if (item.elem) { attrs.sort(function (a, b) {
if (a.prefix != b.prefix) {
// xmlns attributes implicitly have the prefix xmlns
if (xmlnsOrder == 'front') {
if (a.prefix == 'xmlns') return -1;
if (b.prefix == 'xmlns') return 1;
}
return a.prefix < b.prefix ? -1 : 1;
}
item.eachAttr(function(attr) { var aindex = orderlen;
attrs.push(attr); var bindex = orderlen;
});
attrs.sort(function(a, b) { for (var i = 0; i < params.order.length; i++) {
if (a.prefix != b.prefix) { if (a.name == params.order[i]) {
// xmlns attributes implicitly have the prefix xmlns aindex = i;
if (xmlnsOrder == 'front') { } else if (a.name.indexOf(params.order[i] + '-') === 0) {
if (a.prefix == 'xmlns') aindex = i + 0.5;
return -1; }
if (b.prefix == 'xmlns') if (b.name == params.order[i]) {
return 1; bindex = i;
} } else if (b.name.indexOf(params.order[i] + '-') === 0) {
return a.prefix < b.prefix ? -1 : 1; bindex = i + 0.5;
} }
}
var aindex = orderlen; if (aindex != bindex) {
var bindex = orderlen; return aindex - bindex;
}
return a.name < b.name ? -1 : 1;
});
for (var i = 0; i < params.order.length; i++) { attrs.forEach(function (attr) {
if (a.name == params.order[i]) { sorted[attr.name] = attr;
aindex = i; });
} else if (a.name.indexOf(params.order[i] + '-') === 0) {
aindex = i + .5;
}
if (b.name == params.order[i]) {
bindex = i;
} else if (b.name.indexOf(params.order[i] + '-') === 0) {
bindex = i + .5;
}
}
if (aindex != bindex) {
return aindex - bindex;
}
return a.name < b.name ? -1 : 1;
});
attrs.forEach(function (attr) {
sorted[attr.name] = attr;
});
item.attrs = sorted;
}
item.attrs = sorted;
}
}; };

View File

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