blob: 5f84384a5cc5656dd18e71f0a6bf12e9a2a2a980 [file] [log] [blame]
Matteo Scandolo8cf33a32017-11-14 15:52:29 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import './graph.component.scss';
18
19import * as d3 from 'd3';
20import * as $ from 'jquery';
21
22import {IXosGraphStore} from '../../services/graph.store';
23import {Subscription} from 'rxjs/Subscription';
24import {XosServiceGraphConfig as config} from '../../graph.config';
25import {IXosGraphHelpers} from '../../services/d3-helpers/graph-elements.helpers';
26import {IXosServiceGraphIcons} from '../../services/d3-helpers/graph-icons.service';
27import {IXosNodePositioner} from '../../services/node-positioner.service';
28import {IXosNodeRenderer} from '../../services/renderer/node.renderer';
29import {IXosSgNode} from '../../interfaces';
30import {IXosGraphConfig} from '../../services/graph.config';
31
32class XosServiceGraphCtrl {
33 static $inject = [
34 '$log',
35 '$scope',
36 'XosGraphStore',
37 'XosGraphHelpers',
38 'XosServiceGraphIcons',
39 'XosNodePositioner',
40 'XosNodeRenderer',
41 'XosGraphConfig'
42 ];
43
44 public loader: boolean = true;
45
46 private GraphSubscription: Subscription;
47 private graph: any; // this is the Graph instance
48
49 // graph element
50 private svg;
51 private linkGroup;
52 private nodeGroup;
53 private forceLayout;
54
55 constructor (
56 private $log: ng.ILogService,
57 private $scope: ng.IScope,
58 private XosGraphStore: IXosGraphStore,
59 private XosGraphHelpers: IXosGraphHelpers,
60 private XosServiceGraphIcons: IXosServiceGraphIcons,
61 private XosNodePositioner: IXosNodePositioner,
62 private XosNodeRenderer: IXosNodeRenderer,
63 private XosGraphConfig: IXosGraphConfig
64 ) {
65 this.$log.info('[XosServiceGraph] Component setup');
66
67 this.XosGraphConfig.setupKeyboardShortcuts();
68
69 this.setupSvg();
70 this.setupForceLayout();
71
72 this.GraphSubscription = this.XosGraphStore.get()
73 .subscribe(
74 graph => {
75 this.graph = graph;
76 if (this.graph.nodes().length > 0) {
77 this.loader = false;
78 this.renderGraph(this.graph);
79 }
80 },
81 error => {
82 this.$log.error('[XosServiceGraph] XosGraphStore observable error: ', error);
83 }
84 );
85
86 this.$scope.$on('xos.sg.update', () => {
87 this.$log.info(`[XosServiceGraph] Received event: xos.sg.update`);
88 this.renderGraph(this.graph);
89 });
Matteo Scandolo35fdf242017-11-30 12:29:45 -080090
91 $(window).resize(() => {
92 this.renderGraph(this.graph);
93 });
Matteo Scandolo8cf33a32017-11-14 15:52:29 -080094 }
95
96 $onDestroy() {
97 this.GraphSubscription.unsubscribe();
98 }
99
100 public closeFullscreen() {
101 this.XosGraphConfig.toggleFullscreen();
102 }
103
104 private setupSvg() {
105 this.svg = d3.select('xos-service-graph svg');
106
107 this.linkGroup = this.svg.append('g')
108 .attr({
109 class: 'link-group'
110 });
111
112 this.nodeGroup = this.svg.append('g')
113 .attr({
114 class: 'node-group'
115 });
116 }
117
118 private setupForceLayout() {
119 this.$log.debug(`[XosServiceGraph] Setup Force Layout`);
120 const tick = () => {
121 this.nodeGroup.selectAll('g.node')
122 .attr({
123 transform: d => `translate(${d.x}, ${d.y})`
124 });
125
126 this.linkGroup.selectAll('line')
127 .attr({
128 x1: l => l.source.x || 0,
129 y1: l => l.source.y || 0,
130 x2: l => l.target.x || 0,
131 y2: l => l.target.y || 0,
132 });
133 };
134
135 const svgDim = this.getSvgDimensions();
136
137 this.forceLayout =
138 d3.layout.force()
139 .size([svgDim.width, svgDim.height])
140 .on('tick', tick);
141 }
142
143 private getSvgDimensions(): {width: number, height: number} {
144 return {
145 width: $('xos-service-graph svg').width(),
146 height: $('xos-service-graph svg').height()
147 };
148 }
149
150 private renderGraph(graph: any) {
151 let nodes: IXosSgNode[] = this.XosGraphStore.nodesFromGraph(graph);
152 let links = this.XosGraphStore.linksFromGraph(graph);
153 const svgDim = this.getSvgDimensions();
154
155 this.XosNodePositioner.positionNodes(svgDim, nodes)
156 .then((nodes: IXosSgNode[]) => {
157
158 this.forceLayout
159 .nodes(nodes)
160 .links(links)
161 .size([svgDim.width, svgDim.height])
162 .linkDistance(config.force.linkDistance)
163 .charge(config.force.charge)
164 .gravity(config.force.gravity)
165 .start();
166
167 // render nodes
168 this.XosNodeRenderer.renderNodes(this.forceLayout, this.nodeGroup, nodes);
169 this.renderLinks(links);
170 });
171 }
172
173 private renderLinks(links: any[]) {
174
175 const link = this.linkGroup
176 .selectAll('line')
177 .data(links, l => l.id);
178
179 const entering = link.enter();
180
181 entering.append('line')
182 .attr({
183 id: n => n.id,
184 class: n => n.type
185 });
186
187 link.exit().remove();
188 }
189
190}
191
192export const XosServiceGraph: angular.IComponentOptions = {
193 template: require('./graph.component.html'),
194 controllerAs: 'vm',
195 controller: XosServiceGraphCtrl,
196};