21700: Install Bundler system-wide in Rails postinst
[arvados.git] / services / workbench2 / src / lib / cwl-svg / utils / svg-dumper.ts
1 export class SvgDumper {
2
3     private containerElements = ["svg", "g"];
4     private embeddableStyles  = {
5         "rect": ["fill", "stroke", "stroke-width"],
6         "path": ["fill", "stroke", "stroke-width"],
7         "circle": ["fill", "stroke", "stroke-width"],
8         "line": ["stroke", "stroke-width"],
9         "text": ["fill", "font-size", "text-anchor", "font-family"],
10         "polygon": ["stroke", "fill"]
11     };
12
13     constructor(private svg: SVGSVGElement) {
14         this.svg = svg
15     }
16
17     dump({padding} = {padding: 50}): string {
18         this.adaptViewbox(this.svg, padding);
19         const clone = this.svg.cloneNode(true) as SVGSVGElement;
20
21         const portLabels: any = clone.querySelectorAll(".port .label");
22
23
24         for (const label of portLabels) {
25             label.parentNode.removeChild(label);
26         }
27
28         this.treeShakeStyles(clone, this.svg);
29
30         // Remove panning handle so we don't have to align it
31         const panHandle = clone.querySelector(".pan-handle");
32         if (panHandle) {
33             clone.removeChild(panHandle);
34         }
35
36         return new XMLSerializer().serializeToString(clone);
37
38     }
39
40     private adaptViewbox(svg: SVGSVGElement, padding = 50) {
41         const workflow = svg.querySelector(".workflow");
42         const rect     = workflow!.getBoundingClientRect();
43
44         const origin = this.getPointOnSVG(rect.left, rect.top);
45
46         const viewBox  = this.svg.viewBox.baseVal;
47         viewBox.x      = origin.x - padding / 2;
48         viewBox.y      = origin.y - padding / 2;
49         viewBox.height = rect.height + padding;
50         viewBox.width  = rect.width + padding;
51
52     }
53
54     private getPointOnSVG(x: number, y: number): SVGPoint {
55         const svgCTM = this.svg.getScreenCTM();
56         const point  = this.svg.createSVGPoint();
57         point.x      = x;
58         point.y      = y;
59
60         return point.matrixTransform(svgCTM!.inverse());
61
62     }
63
64     private treeShakeStyles(clone: SVGElement, original: SVGElement) {
65
66         const children             = clone.childNodes;
67         const originalChildrenData = original.childNodes as NodeListOf<SVGElement>;
68
69
70         for (let childIndex = 0; childIndex < children.length; childIndex++) {
71
72             const child   = children[childIndex] as SVGElement;
73             const tagName = child.tagName;
74
75             if (this.containerElements.indexOf(tagName) !== -1) {
76                 this.treeShakeStyles(child, originalChildrenData[childIndex]);
77             } else if (tagName in this.embeddableStyles) {
78
79                 const styleDefinition = window.getComputedStyle(originalChildrenData[childIndex]);
80
81                 let styleString = "";
82                 for (let st = 0; st < this.embeddableStyles[tagName].length; st++) {
83                     styleString +=
84                         this.embeddableStyles[tagName][st]
85                         + ":"
86                         + styleDefinition.getPropertyValue(this.embeddableStyles[tagName][st])
87                         + "; ";
88                 }
89
90                 child.setAttribute("style", styleString);
91             }
92         }
93     }
94 }
95