UNPKG

@mapcss/preset-svg

Version:
406 lines (405 loc) 12.6 kB
import { isClean, my } from "./symbols.js"; import Declaration from "./declaration.js"; import Comment from "./comment.js"; import Node from "./node.js"; let parse, Rule, AtRule; function cleanSource(nodes) { return nodes.map((i) => { if (i.nodes) i.nodes = cleanSource(i.nodes); delete i.source; return i; }); } function markDirtyUp(node) { node[isClean] = false; if (node.proxyOf.nodes) { for (let i of node.proxyOf.nodes) { markDirtyUp(i); } } } class Container extends Node { push(child) { child.parent = this; this.proxyOf.nodes.push(child); return this; } each(callback) { if (!this.proxyOf.nodes) return undefined; let iterator = this.getIterator(); let index, result; while (this.indexes[iterator] < this.proxyOf.nodes.length) { index = this.indexes[iterator]; result = callback(this.proxyOf.nodes[index], index); if (result === false) break; this.indexes[iterator] += 1; } delete this.indexes[iterator]; return result; } walk(callback) { return this.each((child, i) => { let result; try { result = callback(child, i); } catch (e) { throw child.addToError(e); } if (result !== false && child.walk) { result = child.walk(callback); } return result; }); } walkDecls(prop, callback) { if (!callback) { callback = prop; return this.walk((child, i) => { if (child.type === "decl") { return callback(child, i); } }); } if (prop instanceof RegExp) { return this.walk((child, i) => { if (child.type === "decl" && prop.test(child.prop)) { return callback(child, i); } }); } return this.walk((child, i) => { if (child.type === "decl" && child.prop === prop) { return callback(child, i); } }); } walkRules(selector, callback) { if (!callback) { callback = selector; return this.walk((child, i) => { if (child.type === "rule") { return callback(child, i); } }); } if (selector instanceof RegExp) { return this.walk((child, i) => { if (child.type === "rule" && selector.test(child.selector)) { return callback(child, i); } }); } return this.walk((child, i) => { if (child.type === "rule" && child.selector === selector) { return callback(child, i); } }); } walkAtRules(name, callback) { if (!callback) { callback = name; return this.walk((child, i) => { if (child.type === "atrule") { return callback(child, i); } }); } if (name instanceof RegExp) { return this.walk((child, i) => { if (child.type === "atrule" && name.test(child.name)) { return callback(child, i); } }); } return this.walk((child, i) => { if (child.type === "atrule" && child.name === name) { return callback(child, i); } }); } walkComments(callback) { return this.walk((child, i) => { if (child.type === "comment") { return callback(child, i); } }); } append(...children) { for (let child of children) { let nodes = this.normalize(child, this.last); for (let node of nodes) this.proxyOf.nodes.push(node); } this.markDirty(); return this; } prepend(...children) { children = children.reverse(); for (let child of children) { let nodes = this.normalize(child, this.first, "prepend").reverse(); for (let node of nodes) this.proxyOf.nodes.unshift(node); for (let id in this.indexes) { this.indexes[id] = this.indexes[id] + nodes.length; } } this.markDirty(); return this; } cleanRaws(keepBetween) { super.cleanRaws(keepBetween); if (this.nodes) { for (let node of this.nodes) node.cleanRaws(keepBetween); } } insertBefore(exist, add) { exist = this.index(exist); let type = exist === 0 ? "prepend" : false; let nodes = this.normalize(add, this.proxyOf.nodes[exist], type).reverse(); for (let node of nodes) this.proxyOf.nodes.splice(exist, 0, node); let index; for (let id in this.indexes) { index = this.indexes[id]; if (exist <= index) { this.indexes[id] = index + nodes.length; } } this.markDirty(); return this; } insertAfter(exist, add) { exist = this.index(exist); let nodes = this.normalize(add, this.proxyOf.nodes[exist]).reverse(); for (let node of nodes) this.proxyOf.nodes.splice(exist + 1, 0, node); let index; for (let id in this.indexes) { index = this.indexes[id]; if (exist < index) { this.indexes[id] = index + nodes.length; } } this.markDirty(); return this; } removeChild(child) { child = this.index(child); this.proxyOf.nodes[child].parent = undefined; this.proxyOf.nodes.splice(child, 1); let index; for (let id in this.indexes) { index = this.indexes[id]; if (index >= child) { this.indexes[id] = index - 1; } } this.markDirty(); return this; } removeAll() { for (let node of this.proxyOf.nodes) node.parent = undefined; this.proxyOf.nodes = []; this.markDirty(); return this; } replaceValues(pattern, opts, callback) { if (!callback) { callback = opts; opts = {}; } this.walkDecls((decl) => { if (opts.props && !opts.props.includes(decl.prop)) return; if (opts.fast && !decl.value.includes(opts.fast)) return; decl.value = decl.value.replace(pattern, callback); }); this.markDirty(); return this; } every(condition) { return this.nodes.every(condition); } some(condition) { return this.nodes.some(condition); } index(child) { if (typeof child === "number") return child; if (child.proxyOf) child = child.proxyOf; return this.proxyOf.nodes.indexOf(child); } get first() { if (!this.proxyOf.nodes) return undefined; return this.proxyOf.nodes[0]; } get last() { if (!this.proxyOf.nodes) return undefined; return this.proxyOf.nodes[this.proxyOf.nodes.length - 1]; } normalize(nodes, sample) { if (typeof nodes === "string") { nodes = cleanSource(parse(nodes).nodes); } else if (Array.isArray(nodes)) { nodes = nodes.slice(0); for (let i of nodes) { if (i.parent) i.parent.removeChild(i, "ignore"); } } else if (nodes.type === "root" && this.type !== "document") { nodes = nodes.nodes.slice(0); for (let i of nodes) { if (i.parent) i.parent.removeChild(i, "ignore"); } } else if (nodes.type) { nodes = [nodes]; } else if (nodes.prop) { if (typeof nodes.value === "undefined") { throw new Error("Value field is missed in node creation"); } else if (typeof nodes.value !== "string") { nodes.value = String(nodes.value); } nodes = [new Declaration(nodes)]; } else if (nodes.selector) { nodes = [new Rule(nodes)]; } else if (nodes.name) { nodes = [new AtRule(nodes)]; } else if (nodes.text) { nodes = [new Comment(nodes)]; } else { throw new Error("Unknown node type in node creation"); } let processed = nodes.map((i) => { /* c8 ignore next */ if (!i[my]) Container.rebuild(i); i = i.proxyOf; if (i.parent) i.parent.removeChild(i); if (i[isClean]) markDirtyUp(i); if (typeof i.raws.before === "undefined") { if (sample && typeof sample.raws.before !== "undefined") { i.raws.before = sample.raws.before.replace(/\S/g, ""); } } i.parent = this; return i; }); return processed; } getProxyProcessor() { return { set(node, prop, value) { if (node[prop] === value) return true; node[prop] = value; if (prop === "name" || prop === "params" || prop === "selector") { node.markDirty(); } return true; }, get(node, prop) { if (prop === "proxyOf") { return node; } else if (!node[prop]) { return node[prop]; } else if (prop === "each" || (typeof prop === "string" && prop.startsWith("walk"))) { return (...args) => { return node[prop](...args.map((i) => { if (typeof i === "function") { return (child, index) => i(child.toProxy(), index); } else { return i; } })); }; } else if (prop === "every" || prop === "some") { return (cb) => { return node[prop]((child, ...other) => cb(child.toProxy(), ...other)); }; } else if (prop === "root") { return () => node.root().toProxy(); } else if (prop === "nodes") { return node.nodes.map((i) => i.toProxy()); } else if (prop === "first" || prop === "last") { return node[prop].toProxy(); } else { return node[prop]; } }, }; } getIterator() { if (!this.lastEach) this.lastEach = 0; if (!this.indexes) this.indexes = {}; this.lastEach += 1; let iterator = this.lastEach; this.indexes[iterator] = 0; return iterator; } } Container.registerParse = (dependant) => { parse = dependant; }; Container.registerRule = (dependant) => { Rule = dependant; }; Container.registerAtRule = (dependant) => { AtRule = dependant; }; export default Container; Container.default = Container; /* c8 ignore start */ Container.rebuild = (node) => { if (node.type === "atrule") { Object.setPrototypeOf(node, AtRule.prototype); } else if (node.type === "rule") { Object.setPrototypeOf(node, Rule.prototype); } else if (node.type === "decl") { Object.setPrototypeOf(node, Declaration.prototype); } else if (node.type === "comment") { Object.setPrototypeOf(node, Comment.prototype); } node[my] = true; if (node.nodes) { node.nodes.forEach((child) => { Container.rebuild(child); }); } }; /* c8 ignore stop */