@cyberalien/svg-utils 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import { ParsedXMLTagElement } from "../xml/types.js";
2
+ type HashCallback = (id: string, content: string, tagName: string) => string;
3
+ /**
4
+ * Change IDs in SVG using a callback function
5
+ */
6
+ declare function changeSVGIDs(root: ParsedXMLTagElement[], callback: HashCallback): void;
7
+ export { changeSVGIDs };
package/lib/svg/ids.js ADDED
@@ -0,0 +1,107 @@
1
+ import { cloneObject } from "../helpers/misc/clone.js";
2
+ import { iterateXMLContent } from "../xml/iterate.js";
3
+ import { stringifyXMLContent } from "../xml/stringify.js";
4
+
5
+ /**
6
+ * Change IDs in SVG using a callback function
7
+ */
8
+ function changeSVGIDs(root, callback) {
9
+ const idMap = /* @__PURE__ */ new Map();
10
+ const idNodes = /* @__PURE__ */ new Map();
11
+ const nestedIDs = /* @__PURE__ */ new Map();
12
+ const usage = [];
13
+ const parse = (replacement) => {
14
+ iterateXMLContent(root, (node, stack) => {
15
+ if (node.type !== "tag") return;
16
+ const attribs = node.attribs;
17
+ const nodeID = attribs.id;
18
+ if (typeof nodeID === "string") {
19
+ if (!replacement) {
20
+ if (idNodes.has(nodeID)) throw new Error(`Duplicate ID found: ${nodeID}`);
21
+ idNodes.set(nodeID, node);
22
+ idMap.set(node, nodeID);
23
+ } else if (nodeID === replacement[0]) attribs.id = replacement[1];
24
+ }
25
+ for (const attrib in attribs) {
26
+ const value = attribs[attrib];
27
+ if (typeof value !== "string") continue;
28
+ const add = (id) => {
29
+ usage.push({
30
+ node,
31
+ attrib,
32
+ id
33
+ });
34
+ [node, ...stack].forEach((node$1) => {
35
+ const parentID = idMap.get(node$1);
36
+ if (parentID) {
37
+ const nested = nestedIDs.get(parentID) || [];
38
+ if (!nested.includes(id)) {
39
+ nested.push(id);
40
+ nestedIDs.set(parentID, nested);
41
+ }
42
+ }
43
+ });
44
+ };
45
+ switch (attrib) {
46
+ case "id": break;
47
+ case "begin":
48
+ case "end": {
49
+ const newValue = value.split(";").map((part) => {
50
+ const chunks = part.trim().split(".");
51
+ if (chunks.length < 2) return part;
52
+ const time = chunks[1];
53
+ if (time?.startsWith("begin") || time?.startsWith("end")) {
54
+ const id = chunks.shift();
55
+ if (!replacement) add(id);
56
+ else if (id === replacement[0]) return `${replacement[1]}.${chunks.join(".")}`;
57
+ }
58
+ return part;
59
+ }).join(";");
60
+ if (replacement) attribs[attrib] = newValue;
61
+ break;
62
+ }
63
+ case "href":
64
+ case "xlink:href":
65
+ if (value.startsWith("#")) {
66
+ const id = value.slice(1);
67
+ if (!replacement) add(id);
68
+ else if (id === replacement[0]) attribs[attrib] = `#${replacement[1]}`;
69
+ }
70
+ break;
71
+ default: if (value.startsWith("url(#")) {
72
+ const id = value.slice(5, -1);
73
+ if (!replacement) add(id);
74
+ else if (id === replacement[0]) attribs[attrib] = `url(#${replacement[1]})`;
75
+ }
76
+ }
77
+ }
78
+ });
79
+ };
80
+ parse();
81
+ if (!idMap.size) return;
82
+ const allIDs = new Set(idMap.values());
83
+ const parseIDs = (parseAll = false) => {
84
+ const oldSize = allIDs.size;
85
+ for (const id of allIDs) {
86
+ const nested = nestedIDs.get(id)?.filter((nestedID) => nestedID !== id && allIDs.has(nestedID)) ?? [];
87
+ if (parseAll || !nested.length) {
88
+ const node = idNodes.get(id);
89
+ const newNode = cloneObject(node);
90
+ delete newNode.attribs.id;
91
+ for (const usageItem of usage) if (usageItem.node === node && usageItem.id === id) delete newNode.attribs[usageItem.attrib];
92
+ const content = stringifyXMLContent([newNode]);
93
+ if (!content) throw new Error(`Failed to stringify node with ID: ${id}`);
94
+ const newID = callback(id, content, node.tag);
95
+ if (newID !== id) parse([id, newID]);
96
+ allIDs.delete(id);
97
+ }
98
+ }
99
+ return allIDs.size !== oldSize;
100
+ };
101
+ while (allIDs.size) if (!parseIDs()) {
102
+ parseIDs(true);
103
+ return;
104
+ }
105
+ }
106
+
107
+ export { changeSVGIDs };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Common functions for working with SVG used by various packages.",
5
5
  "author": "Vjacheslav Trushkin",
6
- "version": "0.0.2",
6
+ "version": "0.0.3",
7
7
  "license": "MIT",
8
8
  "bugs": "https://github.com/cyberalien/svg-utils/issues",
9
9
  "homepage": "https://cyberalien.dev/",