Add radix cwl graph visualization
[arvados.git] / src / lib / cwl-svg / plugins / validate / validate.ts
1 import {Edge}          from "cwlts/models";
2 import {PluginBase}    from "../plugin-base";
3 import { Workflow } from "~/lib/cwl-svg";
4
5 export class SVGValidatePlugin extends PluginBase {
6
7     private modelDisposers: any[] = [];
8
9     /** Map of CSS classes attached by this plugin */
10     private css = {
11         plugin: "__plugin-validate",
12         invalid: "__validate-invalid"
13     };
14
15     registerWorkflow(workflow: Workflow): void {
16         super.registerWorkflow(workflow);
17
18         // add plugin specific class to the svgRoot for scoping
19         this.workflow.svgRoot.classList.add(this.css.plugin);
20     }
21
22     afterModelChange(): void {
23
24         this.disposeModelListeners();
25
26         // add listener for all subsequent edge validation
27         const update = this.workflow.model.on("connections.updated", this.renderEdgeValidation.bind(this));
28         const create = this.workflow.model.on("connection.create", this.renderEdgeValidation.bind(this));
29
30         this.modelDisposers.concat([update.dispose, create.dispose]);
31     }
32
33     destroy(): void {
34         this.disposeModelListeners();
35     }
36
37     afterRender(): void {
38         // do initial validation rendering for edges
39         this.renderEdgeValidation();
40     }
41
42     onEditableStateChange(enabled: boolean): void {
43
44         if (enabled) {
45             // only show validation if workflow is editable
46             this.renderEdgeValidation();
47         } else {
48             this.removeClasses(this.workflow.workflow.querySelectorAll(".edge"))
49         }
50     }
51
52     private disposeModelListeners(): void {
53         for (let disposeListener of this.modelDisposers) {
54             disposeListener();
55         }
56         this.modelDisposers = [];
57     }
58
59     private removeClasses(edges: NodeListOf<Element>): void {
60         // remove validity class on all edges
61         for (const e of (edges as any)) {
62             e.classList.remove(this.css.invalid);
63         }
64     }
65
66     private renderEdgeValidation(): void {
67         const graphEdges: any = this.workflow.workflow.querySelectorAll(".edge") as NodeListOf<Element>;
68
69         this.removeClasses(graphEdges);
70
71         // iterate through all modal connections
72         this.workflow.model.connections.forEach((e: Edge) => {
73             // if the connection isn't valid (should be colored on graph)
74             if (!e.isValid) {
75
76                 // iterate through edges on the svg
77                 for (const ge of graphEdges) {
78                     const sourceNodeID      = ge.getAttribute("data-source-connection");
79                     const destinationNodeID = ge.getAttribute("data-destination-connection");
80
81                     // compare invalid edge source/destination with svg edge
82                     if (e.source.id === sourceNodeID && e.destination.id === destinationNodeID) {
83                         // if its a match, tag it with the appropriate class and break from the loop
84                         ge.classList.add(this.css.invalid);
85                         break;
86                     }
87                 }
88             }
89         });
90     }
91 }