123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- 'use strict';
- /**
- * @typedef {import('../lib/types').XastNode} XastNode
- */
- const { inheritableAttrs, elemsGroups } = require('./_collections.js');
- exports.type = 'visitor';
- exports.name = 'collapseGroups';
- exports.active = true;
- exports.description = 'collapses useless groups';
- /**
- * @type {(node: XastNode, name: string) => boolean}
- */
- const hasAnimatedAttr = (node, name) => {
- if (node.type === 'element') {
- if (
- elemsGroups.animation.includes(node.name) &&
- node.attributes.attributeName === name
- ) {
- return true;
- }
- for (const child of node.children) {
- if (hasAnimatedAttr(child, name)) {
- return true;
- }
- }
- }
- return false;
- };
- /**
- * Collapse useless groups.
- *
- * @example
- * <g>
- * <g attr1="val1">
- * <path d="..."/>
- * </g>
- * </g>
- * ⬇
- * <g>
- * <g>
- * <path attr1="val1" d="..."/>
- * </g>
- * </g>
- * ⬇
- * <path attr1="val1" d="..."/>
- *
- * @author Kir Belevich
- *
- * @type {import('../lib/types').Plugin<void>}
- */
- exports.fn = () => {
- return {
- element: {
- exit: (node, parentNode) => {
- if (parentNode.type === 'root' || parentNode.name === 'switch') {
- return;
- }
- // non-empty groups
- if (node.name !== 'g' || node.children.length === 0) {
- return;
- }
- // move group attibutes to the single child element
- if (
- Object.keys(node.attributes).length !== 0 &&
- node.children.length === 1
- ) {
- const firstChild = node.children[0];
- // TODO untangle this mess
- if (
- firstChild.type === 'element' &&
- firstChild.attributes.id == null &&
- node.attributes.filter == null &&
- (node.attributes.class == null ||
- firstChild.attributes.class == null) &&
- ((node.attributes['clip-path'] == null &&
- node.attributes.mask == null) ||
- (firstChild.name === 'g' &&
- node.attributes.transform == null &&
- firstChild.attributes.transform == null))
- ) {
- for (const [name, value] of Object.entries(node.attributes)) {
- // avoid copying to not conflict with animated attribute
- if (hasAnimatedAttr(firstChild, name)) {
- return;
- }
- if (firstChild.attributes[name] == null) {
- firstChild.attributes[name] = value;
- } else if (name === 'transform') {
- firstChild.attributes[name] =
- value + ' ' + firstChild.attributes[name];
- } else if (firstChild.attributes[name] === 'inherit') {
- firstChild.attributes[name] = value;
- } else if (
- inheritableAttrs.includes(name) === false &&
- firstChild.attributes[name] !== value
- ) {
- return;
- }
- delete node.attributes[name];
- }
- }
- }
- // collapse groups without attributes
- if (Object.keys(node.attributes).length === 0) {
- // animation elements "add" attributes to group
- // group should be preserved
- for (const child of node.children) {
- if (
- child.type === 'element' &&
- elemsGroups.animation.includes(child.name)
- ) {
- return;
- }
- }
- // replace current node with all its children
- const index = parentNode.children.indexOf(node);
- parentNode.children.splice(index, 1, ...node.children);
- // TODO remove in v3
- for (const child of node.children) {
- // @ts-ignore parentNode is forbidden for public usage
- // and will be moved in v3
- child.parentNode = parentNode;
- }
- }
- },
- },
- };
- };
|