'use strict'; const collections = require('./_collections.js'); exports.type = 'visitor'; exports.name = 'convertColors'; exports.active = true; exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb'; const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)'; const rComma = '\\s*,\\s*'; const regRGB = new RegExp( '^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$' ); const regHEX = /^#(([a-fA-F0-9])\2){3}$/; /** * Convert [r, g, b] to #rrggbb. * * @see https://gist.github.com/983535 * * @example * rgb2hex([255, 255, 255]) // '#ffffff' * * @author Jed Schmidt * * @type {(rgb: Array) => string} */ const convertRgbToHex = ([r, g, b]) => { // combine the octets into a 32-bit integer as: [1][r][g][b] const hexNumber = // operator precedence is (+) > (<<) > (|) ((((256 + // [1][0] r) << // [1][r] 8) | // [1][r][0] g) << // [1][r][g] 8) | // [1][r][g][0] b; // serialize [1][r][g][b] to a hex string, and // remove the 1 to get the number with 0s intact return '#' + hexNumber.toString(16).slice(1).toUpperCase(); }; /** * Convert different colors formats in element attributes to hex. * * @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor * @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords * * @example * Convert color name keyword to long hex: * fuchsia ➡ #ff00ff * * Convert rgb() to long hex: * rgb(255, 0, 255) ➡ #ff00ff * rgb(50%, 100, 100%) ➡ #7f64ff * * Convert long hex to short hex: * #aabbcc ➡ #abc * * Convert hex to short name * #000080 ➡ navy * * @author Kir Belevich * * @type {import('../lib/types').Plugin<{ * currentColor?: boolean | string | RegExp, * names2hex?: boolean, * rgb2hex?: boolean, * shorthex?: boolean, * shortname?: boolean, * }>} */ exports.fn = (_root, params) => { const { currentColor = false, names2hex = true, rgb2hex = true, shorthex = true, shortname = true, } = params; return { element: { enter: (node) => { for (const [name, value] of Object.entries(node.attributes)) { if (collections.colorsProps.includes(name)) { let val = value; // convert colors to currentColor if (currentColor) { let matched; if (typeof currentColor === 'string') { matched = val === currentColor; } else if (currentColor instanceof RegExp) { matched = currentColor.exec(val) != null; } else { matched = val !== 'none'; } if (matched) { val = 'currentColor'; } } // convert color name keyword to long hex if (names2hex) { const colorName = val.toLowerCase(); if (collections.colorsNames[colorName] != null) { val = collections.colorsNames[colorName]; } } // convert rgb() to long hex if (rgb2hex) { let match = val.match(regRGB); if (match != null) { let nums = match.slice(1, 4).map((m) => { let n; if (m.indexOf('%') > -1) { n = Math.round(parseFloat(m) * 2.55); } else { n = Number(m); } return Math.max(0, Math.min(n, 255)); }); val = convertRgbToHex(nums); } } // convert long hex to short hex if (shorthex) { let match = val.match(regHEX); if (match != null) { val = '#' + match[0][1] + match[0][3] + match[0][5]; } } // convert hex to short name if (shortname) { const colorName = val.toLowerCase(); if (collections.colorsShortNames[colorName] != null) { val = collections.colorsShortNames[colorName]; } } node.attributes[name] = val; } } }, }, }; };