Opal-Estate-Pro/node_modules/svgo/plugins/cleanupIDs.js
2019-09-13 09:44:33 +07:00

209 lines
5.4 KiB
JavaScript

'use strict';
exports.type = 'full';
exports.active = true;
exports.description = 'removes unused IDs and minifies used';
exports.params = {
remove: true,
minify: true,
prefix: ''
};
var referencesProps = require('./_collections').referencesProps,
regReferencesUrl = /\burl\(("|')?#(.+?)\1\)/,
regReferencesHref = /^#(.+?)$/,
regReferencesBegin = /^(\w+?)\./,
styleOrScript = ['style', 'script'],
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', '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
* (only if there are no any <style> or <script>).
*
* @param {Object} item current iteration item
* @param {Object} params plugin params
*
* @author Kir Belevich
*/
exports.fn = function(data, params) {
var currentID,
currentIDstring,
IDs = Object.create(null),
referencesIDs = Object.create(null),
idPrefix = 'id-', // prefix IDs so that values like '__proto__' don't break the work
hasStyleOrScript = false;
/**
* Bananas!
*
* @param {Array} items input items
* @return {Array} output items
*/
function monkeys(items) {
for (var i = 0; i < items.content.length && !hasStyleOrScript; i++) {
var item = items.content[i],
match;
// check if <style> of <script> presents
if (item.isElem(styleOrScript)) {
hasStyleOrScript = true;
continue;
}
// …and don't remove any ID if yes
if (item.isElem()) {
item.eachAttr(function(attr) {
var key;
// save IDs
if (attr.name === 'id') {
key = idPrefix + attr.value;
if (key in IDs) {
item.removeAttr('id');
} else {
IDs[key] = item;
}
}
// save IDs url() references
else if (referencesProps.indexOf(attr.name) > -1) {
match = attr.value.match(regReferencesUrl);
if (match) {
key = idPrefix + match[2];
if (referencesIDs[key]) {
referencesIDs[key].push(attr);
} else {
referencesIDs[key] = [attr];
}
}
}
// save IDs href references
else if (
attr.local === 'href' && (match = attr.value.match(regReferencesHref)) ||
attr.name === 'begin' && (match = attr.value.match(regReferencesBegin))
) {
key = idPrefix + match[1];
if (referencesIDs[key]) {
referencesIDs[key].push(attr);
} else {
referencesIDs[key] = [attr];
}
}
});
}
// go deeper
if (item.content) {
monkeys(item);
}
}
return items;
}
data = monkeys(data);
if (hasStyleOrScript) {
return data;
}
var idKey;
for (var k in referencesIDs) {
if (IDs[k]) {
idKey = k;
k = k.replace(idPrefix, '');
// replace referenced IDs with the minified ones
if (params.minify) {
currentIDstring = getIDstring(currentID = generateID(currentID), params);
IDs[idKey].attr('id').value = currentIDstring;
referencesIDs[idKey].forEach(function(attr) {
attr.value = attr.value
.replace('#' + k, '#' + currentIDstring)
.replace(k + '.', currentIDstring + '.');
});
idKey = idPrefix + k;
}
// don't remove referenced IDs
delete IDs[idKey];
}
}
// remove non-referenced IDs attributes from elements
if (params.remove) {
for(var ID in IDs) {
IDs[ID].removeAttr('id');
}
}
return data;
};
/**
* Generate unique minimal ID.
*
* @param {Array} [currentID] current ID
* @return {Array} generated ID array
*/
function generateID(currentID) {
if (!currentID) return [0];
currentID[currentID.length - 1]++;
for(var i = currentID.length - 1; i > 0; i--) {
if (currentID[i] > maxIDindex) {
currentID[i] = 0;
if (currentID[i - 1] !== undefined) {
currentID[i - 1]++;
}
}
}
if (currentID[0] > maxIDindex) {
currentID[0] = 0;
currentID.unshift(0);
}
return currentID;
}
/**
* Get string from generated ID array.
*
* @param {Array} arr input ID array
* @return {String} output ID string
*/
function getIDstring(arr, params) {
var str = params.prefix;
arr.forEach(function(i) {
str += generateIDchars[i];
});
return str;
}