[CORD-1943] New service graph
- labels
- enforcing service position
- started documentation
- toggling service instances
- toggle fullscreen

Change-Id: I01b71fb2607fb58711d4624f6b5b6479609b2f4f
diff --git a/src/app/service-graph/components/coarse/coarse.component.scss b/src/app/service-graph/components/coarse/coarse.component.scss
deleted file mode 100644
index fabfb91..0000000
--- a/src/app/service-graph/components/coarse/coarse.component.scss
+++ /dev/null
@@ -1,59 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-@import './../../../style/vars.scss';
-@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
-
-xos-coarse-tenancy-graph {
-  display: block;
-  // background: $color-accent;
-
-  svg {
-    height: 400px;
-    width: 100%;
-    background-color: $panel-filled-bg;
-    border-radius: 3px;
-  }
-
-  .node-group {
-
-    .node {
-      cursor: pointer;
-    }
-
-    .node > rect {
-      stroke: $color-accent;
-      fill: $background-color;
-    }
-
-    .node > text {
-      fill: #fff;
-      font-size: 20px;
-    }
-  }
-
-  .link-group {
-    line {
-      stroke: $color-accent;
-    }
-  }
-  .arrow-marker {
-    stroke: $color-accent;
-    fill: $color-accent;
-  }
-}
\ No newline at end of file
diff --git a/src/app/service-graph/components/coarse/coarse.component.ts b/src/app/service-graph/components/coarse/coarse.component.ts
deleted file mode 100644
index c8e2361..0000000
--- a/src/app/service-graph/components/coarse/coarse.component.ts
+++ /dev/null
@@ -1,265 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import './coarse.component.scss';
-import * as d3 from 'd3';
-import * as $ from 'jquery';
-import * as _ from 'lodash';
-import {IXosServiceGraphStore} from '../../services/service-graph.store';
-import {IXosServiceGraph, IXosServiceGraphNode, IXosServiceGraphLink} from '../../interfaces';
-import {XosServiceGraphConfig as config} from '../../graph.config';
-import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
-import {Subscription} from 'rxjs';
-import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
-import {IXosServiceGraphReducer, IXosServiceGraphExtender} from '../../services/graph.extender';
-
-class XosCoarseTenancyGraphCtrl {
-
-  static $inject = [
-    '$log',
-    'XosServiceGraphStore',
-    'XosDebouncer',
-    'XosGraphHelpers',
-    'XosServiceGraphExtender'
-  ];
-
-  public graph: IXosServiceGraph;
-
-  private CoarseGraphSubscription: Subscription;
-  private svg;
-  private forceLayout;
-  private linkGroup;
-  private nodeGroup;
-  private textSize = 20;
-  private textOffset = this.textSize / 4;
-
-  // debounced functions
-  private renderGraph;
-
-  constructor (
-    private $log: ng.ILogService,
-    private XosServiceGraphStore: IXosServiceGraphStore,
-    private XosDebouncer: IXosDebouncer,
-    private XosGraphHelpers: IXosGraphHelpers,
-    private XosServiceGraphExtender: IXosServiceGraphExtender
-  ) {
-
-  }
-
-  $onInit() {
-    this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 500, this);
-
-    this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
-      .subscribe(
-        (graph: IXosServiceGraph) => {
-          this.$log.debug(`[XosCoarseTenancyGraph] Coarse Event and render`, graph);
-
-          // id there are no data, do nothing
-          if (graph.nodes.length === 0) {
-            return;
-          }
-          this.graph = graph;
-
-          _.forEach(this.XosServiceGraphExtender.getCoarse(), (r: IXosServiceGraphReducer) => {
-            graph = r.reducer(graph);
-          });
-          this.renderGraph();
-        },
-        err => {
-          this.$log.error(`[XosCoarseTenancyGraph] Coarse Event error`, err);
-        });
-
-    this.handleSvg();
-    this.setupForceLayout();
-
-    $(window).on('resize', () => {
-      this.setupForceLayout();
-      this.renderGraph();
-    });
-  }
-
-  $onDestroy() {
-    this.CoarseGraphSubscription.unsubscribe();
-  }
-
-  private _renderGraph() {
-    if (!angular.isDefined(this.graph) || !angular.isDefined(this.graph.nodes) || !angular.isDefined(this.graph.links)) {
-      return;
-    }
-    this.addNodeLinksToForceLayout(this.graph);
-    this.renderNodes(this.graph.nodes);
-    this.renderLinks(this.graph.links);
-  }
-
-  private getSvgDimensions(): {width: number, height: number} {
-    return {
-      width: $('xos-coarse-tenancy-graph svg').width() || 0,
-      height: $('xos-coarse-tenancy-graph svg').height() || 0
-    };
-  }
-
-  private handleSvg() {
-    this.svg = d3.select('svg');
-
-    this.svg.append('svg:defs')
-      .selectAll('marker')
-      .data(config.markers)
-      .enter()
-      .append('svg:marker')
-      .attr('id', d => d.id)
-      .attr('viewBox', d => d.viewBox)
-      .attr('refX', d => d.refX)
-      .attr('refY', d => d.refY)
-      .attr('markerWidth', d => d.width)
-      .attr('markerHeight', d => d.height)
-      .attr('orient', 'auto')
-      .attr('class', d => `${d.id}-marker`)
-      .append('svg:path')
-      .attr('d', d => d.path);
-
-    this.linkGroup = this.svg.append('g')
-      .attr({
-        class: 'link-group'
-      });
-
-    this.nodeGroup = this.svg.append('g')
-      .attr({
-        class: 'node-group'
-      });
-  }
-
-  private collide(n: any) {
-    const svgDim = this.getSvgDimensions();
-    const x = Math.max(n.width / 2, Math.min(n.x, svgDim.width - (n.width / 2)));
-    const y = Math.max(n.height / 2, Math.min(n.y, svgDim.height - (n.height / 2)));
-    return `${x}, ${y}`;
-  }
-
-  private setupForceLayout() {
-
-    let svgDim = this.getSvgDimensions();
-
-    const tick = () => {
-
-      this.nodeGroup.selectAll('g.node')
-        .attr({
-          transform: d => `translate(${this.collide(d)})`
-        });
-
-      this.linkGroup.selectAll('line')
-        .attr({
-          x1: l => l.source.x || 0,
-          y1: l => l.source.y || 0,
-          x2: l => l.target.x || 0,
-          y2: l => l.target.y || 0,
-        });
-    };
-
-    this.forceLayout = d3.layout.force()
-      .size([svgDim.width, svgDim.height])
-      .linkDistance(config.force.linkDistance)
-      .charge(config.force.charge)
-      .gravity(config.force.gravity)
-      .on('tick', tick);
-  }
-
-  private addNodeLinksToForceLayout(data: IXosServiceGraph) {
-    this.forceLayout
-      .nodes(data.nodes)
-      .links(data.links)
-      .start();
-  }
-
-  private renderNodes(nodes: IXosServiceGraphNode[]) {
-    const self = this;
-    const node = this.nodeGroup
-      .selectAll('g.node')
-      .data(nodes, n => n.id);
-
-    const svgDim = this.getSvgDimensions();
-    const entering = node.enter()
-      .append('g')
-      .attr({
-        id: n => n.id,
-        class: n => `node ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        transform: `translate(${svgDim.width / 2}, ${svgDim.height / 2})`
-      })
-      .call(this.forceLayout.drag)
-      .on('mousedown', () => {
-        d3.event.stopPropagation();
-      })
-      .on('mouseup', (d) => {
-        d.fixed = true;
-      });
-
-    entering.append('rect')
-      .attr({
-        rx: config.node.radius,
-        ry: config.node.radius
-      });
-
-    entering.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-      // .text(n => `${n.id} - ${n.label}`);
-
-    const existing = node.selectAll('rect');
-
-    // resize node > rect as contained text
-
-    existing.each(function(d: any) {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-      d.width = textBBox.width + config.node.padding;
-      d.height = textBBox.height + config.node.padding;
-    });
-
-  }
-
-  private renderLinks(links: IXosServiceGraphLink[]) {
-    const link = this.linkGroup
-      .selectAll('line')
-      .data(links, l => l.id);
-
-    const entering = link.enter();
-
-    // TODO read classes from graph links
-
-    entering.append('line')
-      .attr({
-        id: n => n.id,
-        class: l => `link ${this.XosGraphHelpers.parseElemClasses(l.d3Class)}`,
-        'marker-start': 'url(#arrow)'
-      });
-  }
-}
-
-export const XosCoarseTenancyGraph: angular.IComponentOptions = {
-  template: require('./coarse.component.html'),
-  controllerAs: 'vm',
-  controller: XosCoarseTenancyGraphCtrl,
-};
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.html b/src/app/service-graph/components/fine-grained/fine-grained.component.html
deleted file mode 100644
index 2e8b92e..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-<!--
-Copyright 2017-present Open Networking Foundation
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-
-<h1>Fine Grained Tenancy Graph</h1>
-
-<svg>
-</svg>
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.scss b/src/app/service-graph/components/fine-grained/fine-grained.component.scss
deleted file mode 100644
index 29ea116..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.scss
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-@import './../../../style/vars.scss';
-@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
-
-xos-fine-grained-tenancy-graph {
-  display: block;
-  height: 100%;
-  // background: $color-accent;
-
-  svg {
-    height: 90%;
-    width: 100%;
-    background-color: $panel-filled-bg;
-    border-radius: 3px;
-  }
-
-  .node-group {
-
-    .node {
-      cursor: pointer;
-    }
-
-    .node .symbol {
-        fill-rule: evenodd;
-        stroke: #bbddff;
-        stroke-width: 4.0px;
-        fill: none;
-    }
-    .node .symbol-bg {
-        fill-rule: evenodd;
-        stroke: none;
-        fill: $background-color;
-    }
-
-    .node {
-      rect, circle {
-        stroke: $color-accent;
-        fill: $background-color;
-      }
-
-      &.network > circle{
-        stroke: blue;
-      }
-
-      &.tenant, &.serviceinstance > rect{
-        stroke: green;
-      }
-
-      &.subscriber > rect,
-      &.tenantroot > rect{
-        stroke: red;
-      }
-    }
-
-    .node > text {
-      fill: #fff;
-      font-size: 20px;
-    }
-  }
-
-  .link-group {
-    line {
-      stroke: $color-accent;
-
-      &.ext-service-instance {
-        stroke: green;
-      }
-
-      &.ext-owner {
-        stroke: green;
-        stroke-dasharray: 5;
-      }
-    }
-  }
-  .arrow-marker {
-    stroke: $color-accent;
-    fill: $color-accent;
-  }
-}
\ No newline at end of file
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.ts b/src/app/service-graph/components/fine-grained/fine-grained.component.ts
deleted file mode 100644
index 7d21131..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.ts
+++ /dev/null
@@ -1,388 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import './fine-grained.component.scss';
-import * as d3 from 'd3';
-import * as $ from 'jquery';
-import * as _ from 'lodash';
-import {Subscription} from 'rxjs';
-import {XosServiceGraphConfig as config} from '../../graph.config';
-import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
-import {IXosServiceGraph, IXosServiceGraphLink, IXosServiceGraphNode} from '../../interfaces';
-import {IXosSidePanelService} from '../../../core/side-panel/side-panel.service';
-import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
-import {IXosServiceGraphExtender, IXosServiceGraphReducer} from '../../services/graph.extender';
-import {IXosServiceInstanceGraphStore} from '../../services/service-instance.graph.store';
-import {IXosModeldefsCache} from '../../../datasources/helpers/modeldefs.service';
-
-class XosFineGrainedTenancyGraphCtrl {
-  static $inject = [
-    '$log',
-    'XosServiceInstanceGraphStore',
-    'XosDebouncer',
-    'XosModelDiscoverer',
-    'XosSidePanel',
-    'XosGraphHelpers',
-    'XosServiceGraphExtender'
-  ];
-
-  public graph: IXosServiceGraph;
-
-  private GraphSubscription: Subscription;
-  private svg;
-  private forceLayout;
-  private linkGroup;
-  private nodeGroup;
-  private defs;
-  private textSize = 20;
-  private textOffset = this.textSize / 4;
-
-  // debounced functions
-  private renderGraph;
-
-  constructor(
-    private $log: ng.ILogService,
-    private XosServiceInstanceGraphStore: IXosServiceInstanceGraphStore,
-    private XosDebouncer: IXosDebouncer,
-    private XosModeldefsCache: IXosModeldefsCache,
-    private XosSidePanel: IXosSidePanelService,
-    private XosGraphHelpers: IXosGraphHelpers,
-    private XosServiceGraphExtender: IXosServiceGraphExtender
-  ) {
-    this.handleSvg();
-    this.loadDefs();
-    this.setupForceLayout();
-    this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 1000, this);
-
-    $(window).on('resize', () => {
-      this.setupForceLayout();
-      this.renderGraph();
-    });
-
-    this.GraphSubscription = this.XosServiceInstanceGraphStore.get()
-      .subscribe(
-        (graph) => {
-          this.$log.debug(`[XosServiceInstanceGraphStore] Fine-Grained Event and render`, graph);
-
-          if (!graph || !graph.nodes || !graph.links) {
-            return;
-          }
-
-          _.forEach(this.XosServiceGraphExtender.getFinegrained(), (r: IXosServiceGraphReducer) => {
-            graph = r.reducer(graph);
-          });
-
-          this.graph = graph;
-          this.renderGraph();
-        },
-        (err) => {
-          this.$log.error(`[XosFineGrainedTenancyGraphCtrl] Error: `, err);
-        }
-      );
-  }
-
-  $onDestroy() {
-    this.GraphSubscription.unsubscribe();
-  }
-
-  private _renderGraph() {
-    if (!angular.isDefined(this.graph) || !angular.isDefined(this.graph.nodes) || !angular.isDefined(this.graph.links)) {
-      return;
-    }
-    this.addNodeLinksToForceLayout(this.graph);
-    this.renderNodes(this.graph.nodes);
-    this.renderLinks(this.graph.links);
-  }
-
-  private getSvgDimensions(): {width: number, height: number} {
-    return {
-      width: $('xos-fine-grained-tenancy-graph svg').width(),
-      height: $('xos-fine-grained-tenancy-graph svg').height()
-    };
-  }
-
-  private handleSvg() {
-    this.svg = d3.select('svg');
-
-    this.defs = this.svg.append('defs');
-
-    this.linkGroup = this.svg.append('g')
-      .attr({
-        class: 'link-group'
-      });
-
-    this.nodeGroup = this.svg.append('g')
-      .attr({
-        class: 'node-group'
-      });
-  }
-
-  private loadDefs() {
-      const cloud = {
-          vbox: '0 0 303.8 185.8',
-          path: `M88.6,44.3c31.7-45.5,102.1-66.7,135-3
-             M37.8,142.9c-22.5,3.5-60.3-32.4-16.3-64.2
-             M101.8,154.2c-15.6,59.7-121.4,18.8-77.3-13
-             M194.6,150c-35.4,51.8-85.7,34.3-98.8-9.5
-             M274.4,116.4c29.4,73.2-81.9,80.3-87.7,44.3
-             M28.5,89.2C3.7,77.4,55.5,4.8,95.3,36.1
-             M216.1,28.9C270.9-13,340.8,91,278.4,131.1`,
-          bgpath: `M22,78.3C21.5,55.1,62.3,10.2,95.2,36
-             h0c31.9-33.4,88.1-50.5,120.6-7.2l0.3,0.2
-             C270.9-13,340.8,91,278.4,131.1v-0.5
-             c10.5,59.8-86.4,63.7-91.8,30.1h-0.4
-             c-30.2,33.6-67.6,24-84.6-6v-0.4
-             c-15.6,59.7-121.4,18.8-77.3-13
-             l-0.2-.2c-20.2-7.9-38.6-36.5-2.8-62.3Z`
-      };
-
-      this.defs.append('symbol')
-          .attr({ id: 'cloud', viewBox: cloud.vbox })
-          .append('path').attr('d', cloud.path);
-
-      this.defs.append('symbol')
-          .attr({ id: 'cloud_bg', viewBox: cloud.vbox })
-          .append('path').attr('d', cloud.bgpath);
-  }
-
-  private setupForceLayout() {
-    this.$log.debug(`[XosFineGrainedTenancyGraphCtrl] Setup Force Layout`);
-    const tick = () => {
-      this.nodeGroup.selectAll('g.node')
-        .attr({
-          transform: d => `translate(${d.x}, ${d.y})`
-        });
-
-      this.linkGroup.selectAll('line')
-        .attr({
-          x1: l => l.source.x || 0,
-          y1: l => l.source.y || 0,
-          x2: l => l.target.x || 0,
-          y2: l => l.target.y || 0,
-        });
-    };
-    const getLinkStrenght = (l: IXosServiceGraphLink) => {
-      return 1;
-    };
-    const svgDim = this.getSvgDimensions();
-    this.forceLayout = d3.layout.force()
-      .size([svgDim.width, svgDim.height])
-      .linkDistance(config.force.linkDistance)
-      .linkStrength(l => getLinkStrenght(l))
-      .charge(config.force.charge)
-      .gravity(config.force.gravity)
-      .on('tick', tick);
-  }
-
-  private addNodeLinksToForceLayout(data: IXosServiceGraph) {
-    this.forceLayout
-      .nodes(data.nodes)
-      .links(data.links)
-      .start();
-  }
-
-  private renderServiceNodes(nodes: any) {
-
-    const self = this;
-
-    nodes.append('rect')
-    .attr({
-      rx: config.node.radius,
-      ry: config.node.radius
-    });
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-    // .text(n => `${n.id} - ${n.label}`);
-
-    const existing = nodes.selectAll('rect');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-    });
-  }
-
-  private renderTenantNodes(nodes: any) {
-    nodes.append('rect')
-      .attr({
-        width: 40,
-        height: 40,
-        x: -20,
-        y: -20,
-        transform: `rotate(45)`
-      });
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-  }
-
-  private renderNetworkNodes(nodes: any) {
-    const self = this;
-
-    nodes.append('use')
-        .attr({
-            class: 'symbol-bg',
-            'xlink:href': '#cloud_bg'
-        });
-
-    nodes.append('use')
-        .attr({
-            class: 'symbol',
-            'xlink:href': '#cloud'
-        });
-
-    nodes.append('text')
-      .attr({
-          'text-anchor': 'middle',
-          'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-
-    const existing = nodes.selectAll('use');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const useElem = d3.select(this);
-      const w = textBBox.width + config.node.padding * 2;
-      const h = w;
-      const xoff = -(w / 2);
-      const yoff = -(h / 2);
-
-      useElem.attr({
-          width: w,
-          height: h,
-          transform: 'translate(' + xoff + ',' + yoff + ')'
-      });
-    });
-  }
-
-  private renderSubscriberNodes(nodes: any) {
-    const self = this;
-    nodes.append('rect');
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-
-    const existing = nodes.selectAll('rect');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-    });
-  }
-
-  private renderNodes(nodes: IXosServiceGraphNode[]) {
-    const node = this.nodeGroup
-      .selectAll('g.node')
-      .data(nodes, n => n.id);
-
-    let mouseEventsTimer, selectedModel;
-    const svgDim = this.getSvgDimensions();
-    const hStep = svgDim.width / (nodes.length - 1);
-    const vStep = svgDim.height / (nodes.length - 1);
-    const entering = node.enter()
-      .append('g')
-      .attr({
-        id: n => n.id,
-        class: n => `node ${n.type} ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        transform: (n, i) => `translate(${hStep * i}, ${vStep * i})`
-      })
-      .call(this.forceLayout.drag)
-      .on('mousedown', () => {
-        mouseEventsTimer = new Date().getTime();
-        d3.event.stopPropagation();
-      })
-      .on('mouseup', (n) => {
-        mouseEventsTimer = new Date().getTime() - mouseEventsTimer;
-        n.fixed = true;
-      })
-      .on('click', (n: IXosServiceGraphNode) => {
-        if (mouseEventsTimer > 100) {
-          // it is a drag
-          return;
-        }
-        if (selectedModel === n.id) {
-          // this model is already selected, so close the panel
-          this.XosSidePanel.removeInjectedComponents();
-          selectedModel = null;
-          return;
-        }
-        selectedModel = n.id;
-        const modelName = n.model['class_names'].split(',')[0];
-        const formConfig = this.XosModeldefsCache.get(modelName).formCfg;
-        const model = angular.copy(n.model);
-        delete model.d3Id;
-        this.XosSidePanel.injectComponent('xosForm', {config: formConfig, ngModel: model});
-      });
-
-    this.renderServiceNodes(entering.filter('.service'));
-    this.renderTenantNodes(entering.filter('.serviceinstance'));
-    this.renderNetworkNodes(entering.filter('.network'));
-    this.renderSubscriberNodes(entering.filter('.subscriber'));
-    // this.renderSubscriberNodes(entering.filter('.tenantroot'));
-  }
-
-  private renderLinks(links: IXosServiceGraphLink[]) {
-    const link = this.linkGroup
-      .selectAll('line')
-      .data(links, l => l.id);
-
-    const entering = link.enter();
-
-    entering.append('line')
-      .attr({
-        id: n => n.id,
-        class: n => `link ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        'marker-start': 'url(#arrow)'
-      });
-  }
-}
-
-export const XosFineGrainedTenancyGraph: angular.IComponentOptions = {
-  template: require('./fine-grained.component.html'),
-  controllerAs: 'vm',
-  controller: XosFineGrainedTenancyGraphCtrl,
-};
diff --git a/src/app/service-graph/components/coarse/coarse.component.html b/src/app/service-graph/components/graph/graph.component.html
similarity index 69%
rename from src/app/service-graph/components/coarse/coarse.component.html
rename to src/app/service-graph/components/graph/graph.component.html
index c0bb4e5..6510a9f 100644
--- a/src/app/service-graph/components/coarse/coarse.component.html
+++ b/src/app/service-graph/components/graph/graph.component.html
@@ -1,4 +1,3 @@
-
 <!--
 Copyright 2017-present Open Networking Foundation
 
@@ -15,8 +14,12 @@
 limitations under the License.
 -->
 
+<h1>Service Graph</h1>
 
-<h1>Service Dependency Graph</h1>
-
-<svg>
-</svg>
+<div class="graph-container">
+  <div class="loader-container" ng-if="vm.loader">
+    <div class="loader"></div>
+  </div>
+  <a ng-click="vm.closeFullscreen()" class="close-btn"><i class="fa fa-times"></i></a>
+  <svg></svg>
+</div>
diff --git a/src/app/service-graph/components/graph/graph.component.scss b/src/app/service-graph/components/graph/graph.component.scss
new file mode 100644
index 0000000..40b6f11
--- /dev/null
+++ b/src/app/service-graph/components/graph/graph.component.scss
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+@import './../../../style/vars.scss';
+@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
+
+$svg-size: 600px;
+
+xos-service-graph {
+  display: block;
+  // background: $color-accent;
+
+  .graph-container {
+    position: relative;
+
+    .loader-container {
+      position: absolute;
+      left: 0;
+      top: 0;
+      height: $svg-size;
+      width: 100%;
+    }
+  }
+
+  svg {
+    height: $svg-size;
+    width: 100%;
+    background-color: $panel-filled-bg;
+    border-radius: 3px;
+  }
+
+  .close-btn {
+    display: none;
+  }
+
+  .fullscreen {
+    svg {
+      z-index: 1040;
+      width: 100%;
+      height: 100%;
+      position: fixed;
+      top: 0;
+      left: 0;
+      background-color: $background-color;
+      transition: all .5s;
+    }
+
+    .close-btn {
+      cursor: pointer;
+      display: block;
+      position: fixed;
+      top: 10px;
+      right: 10px;
+      z-index: 1041;
+    }
+  }
+
+  .node-group {
+
+    .node {
+      cursor: pointer;
+    }
+
+    .node.service {
+      > rect {
+        stroke: $color-accent;
+        fill: $background-color;
+      }
+      > .icon {
+        fill: $color-accent;
+      }
+    }
+
+    .node.serviceinstance {
+      > rect {
+        stroke: green;
+        fill: $background-color;
+      }
+      > .icon {
+        fill: green;
+      }
+    }
+
+    .node {
+      >.label {
+        >text {
+          fill: #fff;
+        }
+        >rect {
+          fill: $background-color;
+          stroke: #fff;
+        }
+      }
+    }
+  }
+
+  .link-group {
+    line {
+      stroke: $color-accent;
+    }
+
+    line.ownership {
+      stroke: green;
+      stroke-dasharray: 5;
+    }
+
+    line.serviceinstancelink {
+      stroke: green;
+    }
+  }
+  .arrow-marker {
+    stroke: $color-accent;
+    fill: $color-accent;
+  }
+}
\ No newline at end of file
diff --git a/src/app/service-graph/components/graph/graph.component.ts b/src/app/service-graph/components/graph/graph.component.ts
new file mode 100644
index 0000000..5769b6f
--- /dev/null
+++ b/src/app/service-graph/components/graph/graph.component.ts
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import './graph.component.scss';
+
+import * as d3 from 'd3';
+import * as $ from 'jquery';
+
+import {IXosGraphStore} from '../../services/graph.store';
+import {Subscription} from 'rxjs/Subscription';
+import {XosServiceGraphConfig as config} from '../../graph.config';
+import {IXosGraphHelpers} from '../../services/d3-helpers/graph-elements.helpers';
+import {IXosServiceGraphIcons} from '../../services/d3-helpers/graph-icons.service';
+import {IXosNodePositioner} from '../../services/node-positioner.service';
+import {IXosNodeRenderer} from '../../services/renderer/node.renderer';
+import {IXosSgNode} from '../../interfaces';
+import {IXosGraphConfig} from '../../services/graph.config';
+
+class XosServiceGraphCtrl {
+  static $inject = [
+    '$log',
+    '$scope',
+    'XosGraphStore',
+    'XosGraphHelpers',
+    'XosServiceGraphIcons',
+    'XosNodePositioner',
+    'XosNodeRenderer',
+    'XosGraphConfig'
+  ];
+
+  public loader: boolean = true;
+
+  private GraphSubscription: Subscription;
+  private graph: any; // this is the Graph instance
+
+  // graph element
+  private svg;
+  private linkGroup;
+  private nodeGroup;
+  private forceLayout;
+
+  constructor (
+    private $log: ng.ILogService,
+    private $scope: ng.IScope,
+    private XosGraphStore: IXosGraphStore,
+    private XosGraphHelpers: IXosGraphHelpers,
+    private XosServiceGraphIcons: IXosServiceGraphIcons,
+    private XosNodePositioner: IXosNodePositioner,
+    private XosNodeRenderer: IXosNodeRenderer,
+    private XosGraphConfig: IXosGraphConfig
+  ) {
+    this.$log.info('[XosServiceGraph] Component setup');
+
+    this.XosGraphConfig.setupKeyboardShortcuts();
+
+    this.setupSvg();
+    this.setupForceLayout();
+
+    this.GraphSubscription = this.XosGraphStore.get()
+      .subscribe(
+        graph => {
+          this.graph = graph;
+          if (this.graph.nodes().length > 0) {
+            this.loader = false;
+            this.renderGraph(this.graph);
+          }
+        },
+        error => {
+          this.$log.error('[XosServiceGraph] XosGraphStore observable error: ', error);
+        }
+      );
+
+    this.$scope.$on('xos.sg.update', () => {
+      this.$log.info(`[XosServiceGraph] Received event: xos.sg.update`);
+      this.renderGraph(this.graph);
+    });
+  }
+
+  $onDestroy() {
+    this.GraphSubscription.unsubscribe();
+  }
+
+  public closeFullscreen() {
+    this.XosGraphConfig.toggleFullscreen();
+  }
+
+  private setupSvg() {
+    this.svg = d3.select('xos-service-graph svg');
+
+    this.linkGroup = this.svg.append('g')
+      .attr({
+        class: 'link-group'
+      });
+
+    this.nodeGroup = this.svg.append('g')
+      .attr({
+        class: 'node-group'
+      });
+  }
+
+  private setupForceLayout() {
+    this.$log.debug(`[XosServiceGraph] Setup Force Layout`);
+    const tick = () => {
+      this.nodeGroup.selectAll('g.node')
+        .attr({
+          transform: d => `translate(${d.x}, ${d.y})`
+        });
+
+      this.linkGroup.selectAll('line')
+        .attr({
+          x1: l => l.source.x || 0,
+          y1: l => l.source.y || 0,
+          x2: l => l.target.x || 0,
+          y2: l => l.target.y || 0,
+        });
+    };
+
+    const svgDim = this.getSvgDimensions();
+
+    this.forceLayout =
+      d3.layout.force()
+      .size([svgDim.width, svgDim.height])
+      .on('tick', tick);
+  }
+
+  private getSvgDimensions(): {width: number, height: number} {
+    return {
+      width: $('xos-service-graph svg').width(),
+      height: $('xos-service-graph svg').height()
+    };
+  }
+
+  private renderGraph(graph: any) {
+    let nodes: IXosSgNode[] = this.XosGraphStore.nodesFromGraph(graph);
+    let links = this.XosGraphStore.linksFromGraph(graph);
+    const svgDim = this.getSvgDimensions();
+
+    this.XosNodePositioner.positionNodes(svgDim, nodes)
+      .then((nodes: IXosSgNode[]) => {
+
+        this.forceLayout
+          .nodes(nodes)
+          .links(links)
+          .size([svgDim.width, svgDim.height])
+          .linkDistance(config.force.linkDistance)
+          .charge(config.force.charge)
+          .gravity(config.force.gravity)
+          .start();
+
+        // render nodes
+        this.XosNodeRenderer.renderNodes(this.forceLayout, this.nodeGroup, nodes);
+        this.renderLinks(links);
+      });
+  }
+
+  private renderLinks(links: any[]) {
+
+    const link = this.linkGroup
+      .selectAll('line')
+      .data(links, l => l.id);
+
+    const entering = link.enter();
+
+    entering.append('line')
+      .attr({
+        id: n => n.id,
+        class: n => n.type
+      });
+
+    link.exit().remove();
+  }
+
+}
+
+export const XosServiceGraph: angular.IComponentOptions = {
+  template: require('./graph.component.html'),
+  controllerAs: 'vm',
+  controller: XosServiceGraphCtrl,
+};