|
|
/**
|
|
|
* @license
|
|
|
* The MIT License
|
|
|
*
|
|
|
* Copyright © 2012–2016 Kir Belevich
|
|
|
*
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
* obtaining a copy of this software and associated documentation
|
|
|
* files (the "Software"), to deal in the Software without
|
|
|
* restriction, including without limitation the rights to use,
|
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
* copies of the Software, and to permit persons to whom the
|
|
|
* Software is furnished to do so, subject to the following
|
|
|
* conditions:
|
|
|
*
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
*
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
|
*
|
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
*
|
|
|
* Лицензия MIT
|
|
|
*
|
|
|
* Copyright © 2012–2016 Кир Белевич
|
|
|
*
|
|
|
* Данная лицензия разрешает лицам, получившим копию
|
|
|
* данного
|
|
|
* программного обеспечения и сопутствующей
|
|
|
* документации
|
|
|
* (в дальнейшем именуемыми «Программное Обеспечение»),
|
|
|
* безвозмездно
|
|
|
* использовать Программное Обеспечение без
|
|
|
* ограничений, включая
|
|
|
* неограниченное право на использование, копирование,
|
|
|
* изменение,
|
|
|
* добавление, публикацию, распространение,
|
|
|
* сублицензирование
|
|
|
* и/или продажу копий Программного Обеспечения, также
|
|
|
* как и лицам,
|
|
|
* которым предоставляется данное Программное
|
|
|
* Обеспечение,
|
|
|
* при соблюдении следующих условий:
|
|
|
*
|
|
|
* Указанное выше уведомление об авторском праве и
|
|
|
* данные условия
|
|
|
* должны быть включены во все копии или значимые части
|
|
|
* данного
|
|
|
* Программного Обеспечения.
|
|
|
*
|
|
|
* ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК
|
|
|
* ЕСТЬ»,
|
|
|
* БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ
|
|
|
* ПОДРАЗУМЕВАЕМЫХ,
|
|
|
* ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ
|
|
|
* ПРИГОДНОСТИ,
|
|
|
* СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
|
|
|
* ОТСУТСТВИЯ НАРУШЕНИЙ
|
|
|
* ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ
|
|
|
* НЕСУТ
|
|
|
* ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ
|
|
|
* ИЛИ ДРУГИХ
|
|
|
* ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ
|
|
|
* ИНОМУ,
|
|
|
* ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С
|
|
|
* ПРОГРАММНЫМ
|
|
|
* ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО
|
|
|
* ОБЕСПЕЧЕНИЯ
|
|
|
* ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
|
|
|
*/
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
var JSAPI = require('../lib/svgo/jsAPI');
|
|
|
|
|
|
exports.type = 'full';
|
|
|
|
|
|
exports.active = false;
|
|
|
|
|
|
exports.description = 'Finds <path> elements with the same d, fill, and ' +
|
|
|
'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
|
|
|
* <use> elements referencing a single <path> def.
|
|
|
*
|
|
|
* @author Jacob Howcroft
|
|
|
*/
|
|
|
exports.fn = function(data) {
|
|
|
const seen = new Map();
|
|
|
let count = 0;
|
|
|
const defs = [];
|
|
|
traverse(data, item => {
|
|
|
if (!item.isElem('path') || !item.hasAttr('d')) {
|
|
|
return;
|
|
|
}
|
|
|
const d = item.attr('d').value;
|
|
|
const fill = (item.hasAttr('fill') && item.attr('fill').value) || '';
|
|
|
const stroke = (item.hasAttr('stroke') && item.attr('stroke').value) || '';
|
|
|
const key = d + ';s:' + stroke + ';f:' + fill;
|
|
|
const hasSeen = seen.get(key);
|
|
|
if (!hasSeen) {
|
|
|
seen.set(key, {elem: item, reused: false});
|
|
|
return;
|
|
|
}
|
|
|
if (!hasSeen.reused) {
|
|
|
hasSeen.reused = true;
|
|
|
if (!hasSeen.elem.hasAttr('id')) {
|
|
|
hasSeen.elem.addAttr({name: 'id', local: 'id',
|
|
|
prefix: '', value: 'reuse-' + (count++)});
|
|
|
}
|
|
|
defs.push(hasSeen.elem);
|
|
|
}
|
|
|
item = convertToUse(item, hasSeen.elem.attr('id').value);
|
|
|
});
|
|
|
const defsTag = new JSAPI({
|
|
|
elem: 'defs', prefix: '', local: 'defs', content: [], attrs: []}, data);
|
|
|
data.content[0].spliceContent(0, 0, defsTag);
|
|
|
for (let def of defs) {
|
|
|
// Remove class and style before copying to avoid circular refs in
|
|
|
// JSON.stringify. This is fine because we don't actually want class or
|
|
|
// style information to be copied.
|
|
|
const style = def.style;
|
|
|
const defClass = def.class;
|
|
|
delete def.style;
|
|
|
delete def.class;
|
|
|
const defClone = def.clone();
|
|
|
def.style = style;
|
|
|
def.class = defClass;
|
|
|
defClone.removeAttr('transform');
|
|
|
defsTag.spliceContent(0, 0, defClone);
|
|
|
// Convert the original def to a use so the first usage isn't duplicated.
|
|
|
def = convertToUse(def, defClone.attr('id').value);
|
|
|
def.removeAttr('id');
|
|
|
}
|
|
|
return data;
|
|
|
};
|
|
|
|
|
|
/** */
|
|
|
function convertToUse(item, href) {
|
|
|
item.renameElem('use');
|
|
|
item.removeAttr('d');
|
|
|
item.removeAttr('stroke');
|
|
|
item.removeAttr('fill');
|
|
|
item.addAttr({name: 'xlink:href', local: 'xlink:href',
|
|
|
prefix: 'none', value: '#' + href});
|
|
|
delete item.pathJS;
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
/** */
|
|
|
function traverse(parent, callback) {
|
|
|
if (parent.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
for (let child of parent.content) {
|
|
|
callback(child);
|
|
|
traverse(child, callback);
|
|
|
}
|
|
|
}
|