diff --git a/plugins/addAttributesToSVGElement.js b/plugins/addAttributesToSVGElement.js index e89a904d..4997513b 100644 --- a/plugins/addAttributesToSVGElement.js +++ b/plugins/addAttributesToSVGElement.js @@ -1,13 +1,8 @@ 'use strict'; -const { closestByName } = require('../lib/xast.js'); - exports.name = 'addAttributesToSVGElement'; - -exports.type = 'perItem'; - +exports.type = 'visitor'; exports.active = false; - exports.description = 'adds attributes to an outer element'; var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters. @@ -54,32 +49,32 @@ plugins: [ * * @author April Arcus */ -exports.fn = (node, params) => { - if ( - node.type === 'element' && - node.name === 'svg' && - closestByName(node.parentNode, 'svg') == null - ) { - if (!params || !(Array.isArray(params.attributes) || params.attribute)) { - console.error(ENOCLS); - return; - } - - const attributes = params.attributes || [params.attribute]; - - for (const attribute of attributes) { - if (typeof attribute === 'string') { - if (node.attributes[attribute] == null) { - node.attributes[attribute] = undefined; - } - } - if (typeof attribute === 'object') { - for (const key of Object.keys(attribute)) { - if (node.attributes[key] == null) { - node.attributes[key] = attribute[key]; +exports.fn = (root, params) => { + if (!Array.isArray(params.attributes) && !params.attribute) { + console.error(ENOCLS); + return; + } + const attributes = params.attributes || [params.attribute]; + return { + element: { + enter: (node, parentNode) => { + if (node.name === 'svg' && parentNode.type === 'root') { + for (const attribute of attributes) { + if (typeof attribute === 'string') { + if (node.attributes[attribute] == null) { + node.attributes[attribute] = undefined; + } + } + if (typeof attribute === 'object') { + for (const key of Object.keys(attribute)) { + if (node.attributes[key] == null) { + node.attributes[key] = attribute[key]; + } + } + } } } - } - } - } + }, + }, + }; }; diff --git a/plugins/addClassesToSVGElement.js b/plugins/addClassesToSVGElement.js index dc3f176f..e13ca4ec 100644 --- a/plugins/addClassesToSVGElement.js +++ b/plugins/addClassesToSVGElement.js @@ -1,57 +1,72 @@ 'use strict'; exports.name = 'addClassesToSVGElement'; - -exports.type = 'full'; - +exports.type = 'visitor'; exports.active = false; - exports.description = 'adds classnames to an outer element'; var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters. It should have a list of classes in "classNames" or one "className". Config example: -plugins: -- addClassesToSVGElement: - className: "mySvg" +plugins: [ + { + name: "addClassesToSVGElement", + params: { + className: "mySvg" + } + } +] -plugins: -- addClassesToSVGElement: - classNames: ["mySvg", "size-big"] +plugins: [ + { + name: "addClassesToSVGElement", + params: { + classNames: ["mySvg", "size-big"] + } + } +] `; /** * Add classnames to an outer element. Example config: * - * plugins: - * - addClassesToSVGElement: - * className: 'mySvg' + * plugins: [ + * { + * name: "addClassesToSVGElement", + * params: { + * className: "mySvg" + * } + * } + * ] * - * plugins: - * - addClassesToSVGElement: - * classNames: ['mySvg', 'size-big'] + * plugins: [ + * { + * name: "addClassesToSVGElement", + * params: { + * classNames: ["mySvg", "size-big"] + * } + * } + * ] * * @author April Arcus */ -exports.fn = function (data, params) { +exports.fn = (root, params) => { if ( - !params || - !( - (Array.isArray(params.classNames) && params.classNames.some(String)) || - params.className - ) + !(Array.isArray(params.classNames) && params.classNames.some(String)) && + !params.className ) { console.error(ENOCLS); - return data; + return; } - - var classNames = params.classNames || [params.className], - svg = data.children[0]; - - if (svg.isElem('svg')) { - svg.class.add.apply(svg.class, classNames); - } - - return data; + const classNames = params.classNames || [params.className]; + return { + element: { + enter: (node, parentNode) => { + if (node.name === 'svg' && parentNode.type === 'root') { + node.class.add(...classNames); + } + }, + }, + }; }; diff --git a/plugins/removeAttributesBySelector.js b/plugins/removeAttributesBySelector.js index 9674a425..60c19b38 100644 --- a/plugins/removeAttributesBySelector.js +++ b/plugins/removeAttributesBySelector.js @@ -1,55 +1,71 @@ 'use strict'; +const { querySelectorAll } = require('../lib/xast.js'); + exports.name = 'removeAttributesBySelector'; - -exports.type = 'perItem'; - +exports.type = 'visitor'; exports.active = false; - exports.description = 'removes attributes of elements that match a css selector'; /** * Removes attributes of elements that match a css selector. * - * @param {Object} item current iteration item - * @param {Object} params plugin params - * @return {Boolean} if false, item will be filtered out - * * @example * A selector removing a single attribute - * plugins: - * - removeAttributesBySelector: + * plugins: [ + * { + * name: "removeAttributesBySelector", + * params: { * selector: "[fill='#00ff00']" * attributes: "fill" + * } + * } + * ] * * * ↓ * * * A selector removing multiple attributes - * plugins: - * - removeAttributesBySelector: - * selector: "[fill='#00ff00']" - * attributes: - * - fill - * - stroke + * plugins: [ + * { + * name: "removeAttributesBySelector", + * params: { + * selector: "[fill='#00ff00']", + * attributes: [ + * "fill", + * "stroke" + * ] + * } + * } + * ] * * * ↓ * * * Multiple selectors removing attributes - * plugins: - * - removeAttributesBySelector: - * selectors: - * - selector: "[fill='#00ff00']" + * plugins: [ + * { + * name: "removeAttributesBySelector", + * params: { + * selectors: [ + * { + * selector: "[fill='#00ff00']", * attributes: "fill" - * - * - selector: "#remove" - * attributes: - * - stroke - * - id + * }, + * { + * selector: "#remove", + * attributes: [ + * "stroke", + * "id" + * ] + * } + * ] + * } + * } + * ] * * * ↓ @@ -59,18 +75,21 @@ exports.description = * * @author Bradley Mease */ -exports.fn = function (item, params) { - var selectors = Array.isArray(params.selectors) ? params.selectors : [params]; - - selectors.map(({ selector, attributes }) => { - if (item.matches(selector)) { +exports.fn = (root, params) => { + const selectors = Array.isArray(params.selectors) + ? params.selectors + : [params]; + for (const { selector, attributes } of selectors) { + const nodes = querySelectorAll(root, selector); + for (const node of nodes) { if (Array.isArray(attributes)) { for (const name of attributes) { - delete item.attributes[name]; + delete node.attributes[name]; } } else { - delete item.attributes[attributes]; + delete node.attributes[attributes]; } } - }); + } + return {}; }; diff --git a/plugins/removeAttrs.js b/plugins/removeAttrs.js index 561b511d..54f37363 100644 --- a/plugins/removeAttrs.js +++ b/plugins/removeAttrs.js @@ -1,20 +1,11 @@ 'use strict'; -var DEFAULT_SEPARATOR = ':'; - exports.name = 'removeAttrs'; - -exports.type = 'perItem'; - +exports.type = 'visitor'; exports.active = false; - exports.description = 'removes specified attributes'; -exports.params = { - elemSeparator: DEFAULT_SEPARATOR, - preserveCurrentColor: false, - attrs: [], -}; +const DEFAULT_SEPARATOR = ':'; /** * Remove attributes @@ -77,72 +68,69 @@ exports.params = { * attrs: 'stroke.*' * * - * @param {Object} item current iteration item - * @param {Object} params plugin params - * @return {Boolean} if false, item will be filtered out - * * @author Benny Schudel */ -exports.fn = function (item, params) { +exports.fn = (root, params) => { // wrap into an array if params is not - if (!Array.isArray(params.attrs)) { - params.attrs = [params.attrs]; - } + const elemSeparator = + typeof params.elemSeparator == 'string' + ? params.elemSeparator + : DEFAULT_SEPARATOR; + const preserveCurrentColor = + typeof params.preserveCurrentColor == 'boolean' + ? params.preserveCurrentColor + : false; + const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs]; - if (item.type === 'element') { - var elemSeparator = - typeof params.elemSeparator == 'string' - ? params.elemSeparator - : DEFAULT_SEPARATOR; - var preserveCurrentColor = - typeof params.preserveCurrentColor == 'boolean' - ? params.preserveCurrentColor - : false; + return { + element: { + enter: (node) => { + for (let pattern of attrs) { + // if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value* + if (pattern.includes(elemSeparator) === false) { + pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join( + '' + ); + // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value + } else if (pattern.split(elemSeparator).length < 3) { + pattern = [pattern, elemSeparator, '.*'].join(''); + } - // prepare patterns - 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(''); + // create regexps for element, attribute name, and attribute value + const list = pattern.split(elemSeparator).map((value) => { + // adjust single * to match anything + if (value === '*') { + value = '.*'; + } + return new RegExp(['^', value, '$'].join(''), 'i'); + }); - // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value - } else if (pattern.split(elemSeparator).length < 3) { - pattern = [pattern, elemSeparator, '.*'].join(''); - } - - // 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.name)) { - // loop attributes - for (const [name, value] of Object.entries(item.attributes)) { - 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(value)) { - delete item.attributes[name]; + // matches element + if (list[0].test(node.name)) { + // loop attributes + for (const [name, value] of Object.entries(node.attributes)) { + const isFillCurrentColor = + preserveCurrentColor && + name == 'fill' && + value == 'currentColor'; + const isStrokeCurrentColor = + preserveCurrentColor && + name == 'stroke' && + value == 'currentColor'; + if ( + !isFillCurrentColor && + !isStrokeCurrentColor && + // matches attribute name + list[1].test(name) && + // matches attribute value + list[2].test(value) + ) { + delete node.attributes[name]; } } } } - } - }); - } + }, + }, + }; }; diff --git a/plugins/removeElementsByAttr.js b/plugins/removeElementsByAttr.js index a262182c..03e2c2df 100644 --- a/plugins/removeElementsByAttr.js +++ b/plugins/removeElementsByAttr.js @@ -1,19 +1,13 @@ 'use strict'; +const { detachNodeFromParent } = require('../lib/xast.js'); + exports.name = 'removeElementsByAttr'; - -exports.type = 'perItem'; - +exports.type = 'visitor'; exports.active = false; - exports.description = 'removes arbitrary elements by ID or className (disabled by default)'; -exports.params = { - id: [], - class: [], -}; - /** * Remove arbitrary SVG elements by ID or className. * @@ -47,33 +41,37 @@ exports.params = { * - 'elementClass' * - 'anotherClass' * - * @param {Object} item current iteration item - * @param {Object} params plugin params - * @return {Boolean} if false, item will be filtered out - * * @author Eli Dupuis (@elidupuis) */ -exports.fn = function (item, params) { - // wrap params in an array if not already - ['id', 'class'].forEach(function (key) { - if (!Array.isArray(params[key])) { - params[key] = [params[key]]; - } - }); - - // abort if current item is no an element - if (item.type !== 'element') { - return; - } - - // remove element if it's `id` matches configured `id` params - if (item.attributes.id != null && params.id.length !== 0) { - return params.id.includes(item.attributes.id) === false; - } - - // remove element if it's `class` contains any of the configured `class` params - if (item.attributes.class && params.class.length !== 0) { - const classList = item.attributes.class.split(' '); - return params.class.some((item) => classList.includes(item)) === false; - } +exports.fn = (root, params) => { + const ids = + params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id]; + const classes = + params.class == null + ? [] + : Array.isArray(params.class) + ? params.class + : [params.class]; + return { + element: { + enter: (node, parentNode) => { + // remove element if it's `id` matches configured `id` params + if (node.attributes.id != null && ids.length !== 0) { + if (ids.includes(node.attributes.id)) { + detachNodeFromParent(node, parentNode); + } + } + // remove element if it's `class` contains any of the configured `class` params + if (node.attributes.class && classes.length !== 0) { + const classList = node.attributes.class.split(' '); + for (const item of classes) { + if (classList.includes(item)) { + detachNodeFromParent(node, parentNode); + break; + } + } + } + }, + }, + }; };