| /** |
| * @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); |
| } |
| } |