http://git-wip-us.apache.org/repos/asf/ambari/blob/7e00f585/contrib/views/storm/src/main/resources/scripts/components/BarChart.jsx
----------------------------------------------------------------------
diff --git 
a/contrib/views/storm/src/main/resources/scripts/components/BarChart.jsx 
b/contrib/views/storm/src/main/resources/scripts/components/BarChart.jsx
index 7dabed1..ef05422 100644
--- a/contrib/views/storm/src/main/resources/scripts/components/BarChart.jsx
+++ b/contrib/views/storm/src/main/resources/scripts/components/BarChart.jsx
@@ -55,7 +55,7 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
                setUpSVG: function(){
                        this.svg = d3.select(ReactDOM.findDOMNode(this))
                                .attr('width', this.props.width+"px")
-                               .attr('height', this.props.height+30+"px")
+                               .attr('height', this.props.height+50+"px")
                                // .attr("viewBox", "-46 -5 " + 
(this.props.width+82) + " " + (this.props.height+28) );
 
                        this.container = this.svg.append("g")
@@ -136,7 +136,37 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
                        this.xAxisGrp = container['xAxisEl'] = 
container.append("g")
                                .attr("class", "x axis")
                                .attr("transform", "translate(0," + height + 
")")
-                               .call(xAxis);
+                               .call(xAxis)
+                               .selectAll(".tick text")
+                               .call(this.wrap, this.x.rangeBand());
+               },
+               wrap: function(text, width) {
+                       text.each(function() {
+                               var text = d3.select(this),
+                                       words = 
text.text().split(/-+/).reverse(),
+                                       word,
+                                       line = [],
+                                       lineNumber = 0,
+                                       lineHeight = 1.1, // ems
+                                       y = text.attr("y"),
+                                       dy = parseFloat(text.attr("dy")),
+                                       tspan = 
text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
+
+                               //Hack to show hidden div to find 
getComputedTextLength
+                               $('#lag-graph').css({visibility: 'hidden', 
display: 'block', position: 'absolute'});
+
+                               while (word = words.pop()) {
+                                       line.push(word);
+                                       tspan.text(line.join(" "));
+                                       if 
(tspan.node().getComputedTextLength() > width) {
+                                               line.pop();
+                                               tspan.text(line.join(" "));
+                                               line = [word];
+                                               tspan = 
text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * 
lineHeight + dy + "em").text(word);
+                                       }
+                               }
+                               $('#lag-graph').css({visibility: '', display: 
'none', position: ''});
+                       });
                },
                drawYAxis: function(x) {
                        var yAxis = this.yAxis;

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e00f585/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx
----------------------------------------------------------------------
diff --git 
a/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx 
b/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx
index 44afd5d..8679661 100644
--- 
a/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx
+++ 
b/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx
@@ -16,7 +16,7 @@
  limitations under the License.
 */
 
-define(['react', 'react-dom', 'd3', 'd3.tip'], function(React, ReactDOM, d3) {
+define(['react', 'react-dom', 'd3', 'dagreD3', 'd3.tip'], function(React, 
ReactDOM, d3, dagreD3) {
        'use strict';
        return React.createClass({
                displayName: 'TopologyGraph',
@@ -26,39 +26,24 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
             height: React.PropTypes.string
         },
                getInitialState: function(){
-                       this.width = this.props.width || '1100';
-                       this.height = this.props.height || '260';
                        this.syncData(this.props.data);
                        return null;
                },
-        componentWillUpdate: function(){
-
-        },
                componentDidUpdate: function(){
                        this.syncData(this.props.data);
             this.updateGraph();
-            this.force.start();
-            for (var i = 300; i > 0; --i) this.force.tick();
-            this.force.stop();
                },
                componentDidMount: function(){
-                       var width = this.width,
-                height = this.height,
-                nodes = this.nodeArray,
-                links = this.linkArray,
-                radius = this.radius = 20.75;
-
-            var svg = this.svg = d3.select(ReactDOM.findDOMNode(this))
-                .attr('width', width)
-                .attr('height', height);
-
+            var that = this;
+            this.svg = d3.select(ReactDOM.findDOMNode(this))
             //Set up tooltip
-            this.tip = d3.tip()
+            this.tooltip = d3.tip()
                 .attr('class', function() {
-                    return 'd3-tip';
+                    return 'd3-tip testing';
                 })
                 .offset([-10, 0])
-                .html(function(d) {
+                .html(function(data) {
+                    var d = that.g.node(data);
                     var tip = "<ul>";
                     if (d[":capacity"] !== null) tip += "<li>Capacity: " + 
d[":capacity"].toFixed(2) + "</li>";
                     if (d[":latency"] !== null) tip += "<li>Latency: " + 
d[":latency"].toFixed(2) + "</li>";
@@ -66,171 +51,105 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
                     tip += "</ul>";
                     return tip;
                 });
-            svg.call(this.tip);
-
-            // define arrow markers for graph links
-            svg.append('svg:defs').append('svg:marker')
-                .attr('id', 'end-arrow')
-                .attr('viewBox', '0 -5 10 10')
-                .attr('refX', 6)
-                .attr('markerWidth', 6)
-                .attr('markerHeight', 6.5)
-                .attr('orient', 'auto')
-                .append('svg:path')
-                .attr('d', 'M0,-5L10,0L0,5');
-
-            svg.append('svg:defs').append('svg:marker')
-                .attr('id', 'start-arrow')
-                .attr('viewBox', '0 -5 10 10')
-                .attr('refX', 4)
-                .attr('markerWidth', 3)
-                .attr('markerHeight', 3)
-                .attr('orient', 'auto')
-                .append('svg:path')
-                .attr('d', 'M10,-5L0,0L10,5');
-
-            // handles to link and node element groups
-            this.path = svg.append('svg:g').selectAll('path');
-            this.image = svg.append('svg:g').selectAll('g');
-
-            this.selected_node = null;
-                       
-                       // only respond once per keydown
-            this.lastKeyDown = -1;
-            d3.select(window)
-                .on('keydown', this.keydown)
-                .on('keyup', this.keyup);
-            this.updateGraph();
-            this.force.start();
-            this.force.tick();
-            this.force.stop();
-               },
+            //Set up zoom
+            this.zoom = d3.behavior.zoom()
+                .scaleExtent([0, 8])
+                .on("zoom", this.zoomed);
+        },
+        zoomed: function(){
+            this.inner.attr("transform",
+                "translate(" + this.zoom.translate() + ")" +
+                "scale(" + this.zoom.scale() + ")"
+            );
+        },
                // update graph (called when needed)
                updateGraph: function(){
-                       // init D3 force layout
-            this.force = d3.layout.force()
-                .nodes(this.nodeArray)
-                .links(this.linkArray)
-                .size([this.width, this.height])
-                .linkDistance(150)
-                .charge(-500)
-                .on('tick', this.tick);
-
-                       // path (link) group
-            this.path = this.path.data(this.linkArray);
-
-            // update existing links
-            this.path.style('marker-start', function(d) {
-                    return ''; })
-                .style('marker-end', function(d) {
-                    return 'url(#end-arrow)'; });
-
-
-            // add new links
-            this.path.enter().append('svg:path')
-                .attr('class', 'link')
-                .style('marker-start', function(d) {
-                    return ''; })
-                .attr("stroke-dasharray", "5, 5")
-                .attr('stroke-width', '2')
-                .style('marker-end', function(d) {
-                    return 'url(#end-arrow)'; });
-
-            // remove old links
-            this.path.exit().remove();
-
-
-            // image (node) group
-            // NB: the function arg is crucial here! nodes are known by id, 
not by index!
-            this.image = this.image.data(this.nodeArray, function(d) {
-                return d.id; 
-            });
-
-            //update old nodes
-            this.image
-                .on('mouseover', function(d) {
-                    this.tip.show(d);
-                }.bind(this))
-                .on('mouseout', function(d) {
-                    this.tip.hide();
-                }.bind(this));
-
-            // add new nodes
-            var g = this.image.enter().append('svg:g');
+            var that = this;
+            var g = ReactDOM.findDOMNode(this).children[0];
+            if(g){
+                g.remove();
+            }
+            var inner = this.inner = this.svg.append("g");
+            // Create the renderer
+            var render = new dagreD3.render();
+            
+            render.arrows().arrowPoint = function normal(parent, id, edge, 
type) {
+                var marker = parent.append("marker")
+                    .attr("id", id)
+                    .attr("viewBox", "0 0 10 10")
+                    .attr("refX", 5)
+                    .attr("refY", 5)
+                    .attr("markerUnits", "strokeWidth")
+                    .attr("markerWidth", 6)
+                    .attr("markerHeight", 6.5)
+                    .attr("orient", "auto");
+
+                var path = marker.append("path")
+                    .attr("d", "M 0 0 L 10 5 L 0 10 z")
+                    .style("stroke-width", 1)
+                    .style("stroke-dasharray", "1,0")
+                    .style("fill", "grey")
+                    .style("stroke", "grey");
+                dagreD3.util.applyStyle(path, edge[type + "Style"]);
+            };
+
+            render.shapes().img = function circle(parent, bbox, node) {
+                var shapeSvg = parent.insert("image")
+                    .attr("class", "nodeImage")
+                    .attr("xlink:href", function(d) {
+                        if (node) {
+                            if(node.type === 'spout'){
+                                return "images/icon-spout.png";
+                            } else if(node.type === 'bolt'){
+                                return "images/icon-bolt.png";
+                            }
+                        }
+                    }).attr("x", "-12px")
+                    .attr("y", "-12px")
+                    .attr("width", "30px")
+                    .attr("height", "30px");
+                node.intersect = function(point) {
+                    return dagreD3.intersect.circle(node, 20, point);
+                };
+                return shapeSvg;
+            }
+            this.svg.call(this.zoom).call(this.tooltip);
+            // Run the renderer. This is what draws the final graph.
+            render(inner, this.g);
 
-            g.append('svg:image')
-               .attr("xlink:href", function(d){
-                                       if(d.type === 'spout'){
-                                               return "images/icon-spout.png";
-                                       } else if(d.type === 'bolt'){
-                                               return "images/icon-bolt.png";
-                                       }
-                               })
-                               .attr("width", "30px")
-                               .attr("height", "30px")
+            inner.selectAll("g.nodes image")
                 .on('mouseover', function(d) {
-                    this.tip.show(d);
-                }.bind(this))
+                    that.tooltip.show(d);
+                })
                 .on('mouseout', function(d) {
-                    this.tip.hide();
-                }.bind(this));
-
-            g.append("svg:text")
-                .attr("dx", 18)
-                .attr("dy", 38)
-                .text(function(d) {
-                    return d.id; });
-
-            // remove old nodes
-            this.image.exit().remove();
-               },
-               // update force layout (called automatically each iteration)
-               tick: function(){
-                       // draw directed edges with proper padding from node 
centers
-            this.path.attr('d', function(d) {
-                var deltaX = d.target.x - d.source.x,
-                    deltaY = d.target.y - d.source.y,
-                    dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
-                    normX = deltaX / dist,
-                    normY = deltaY / dist,
-                    sourcePadding = 30,
-                    targetPadding = 5,
-                    sourceX = d.source.x + (sourcePadding * normX),
-                    sourceY = d.source.y + (sourcePadding * normY) + 15,
-                    targetX = d.target.x - (targetPadding * normX),
-                    targetY = d.target.y - (targetPadding * normY) + 15;
-                return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + 
targetY;
-            });
-            
-            this.image.attr('transform', function(d) {
-                return 'translate(' + Math.max(this.radius, 
Math.min(this.width - this.radius, d.x)) + ',' + Math.max(this.radius, 
Math.min(this.height - this.radius, d.y)) + ')';
-            }.bind(this));
-               },
-               keydown: function(){
-                       if (this.lastKeyDown !== -1) return;
-            this.lastKeyDown = d3.event.keyCode;
+                    that.tooltip.hide();
+                });
 
-            // ctrl
-            if (d3.event.keyCode === 17) {
-                d3.event.preventDefault();
-                this.tip.hide();
-                this.image.call(this.force.drag);
-                this.svg.classed('ctrl', true);
+            inner.selectAll("g.nodes g.label")
+                    .attr("transform", "translate(2,-30)");
+            // Center the graph
+            var initialScale = 1;
+            var svgWidth = this.svg[0][0].parentNode.clientWidth;
+            var svgHeight = this.svg[0][0].parentNode.clientHeight;
+            if(this.linkArray.length > 0){
+                this.zoom.translate([(svgWidth - this.g.graph().width * 
initialScale) / 2, (svgHeight - this.g.graph().height * initialScale) / 2])
+                    .scale(initialScale)
+                    .event(this.svg);
             }
-               },
-               keyup: function(){
-                       this.lastKeyDown = -1;
-            // ctrl
-            if (d3.event.keyCode === 17) {
-                this.image
-                    .on('mousedown.drag', null)
-                    .on('touchstart.drag', null);
-                this.svg.classed('ctrl', false);
-            }
-               },
+        },
                syncData: function(data){
                        this.nodeArray = [];
             this.linkArray = [];
+            this.g = new dagreD3.graphlib.Graph().setGraph({
+                nodesep: 50,
+                ranksep: 190,
+                rankdir: "LR",
+                marginx: 20,
+                marginy: 20,
+                // transition: function transition(selection) {
+                //     return selection.transition().duration(500);
+                // }
+            });
             if(data){
                var keys = _.keys(data);
                keys.map(function(key){
@@ -243,10 +162,11 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
 
                var spoutObjArr = _.where(this.nodeArray, { "type": "spout" });
                    if (spoutObjArr.length > 1) {
-                       var index = this.nodeArray.length - 1;
-                       this.nodeArray[index].x = 50;
-                       this.nodeArray[index].y = 100;
-                       this.nodeArray[index].fixed = true;
+                    for(var i = 0; i < spoutObjArr.length; i++){
+                        spoutObjArr[i].x = 50;
+                        spoutObjArr[i].y = parseInt(i+'10', 10);
+                        spoutObjArr[i].fixed = true;
+                    }
                    } else if (spoutObjArr.length == 1) {
                        spoutObjArr[0].x = 50;
                        spoutObjArr[0].y = 100;
@@ -257,10 +177,14 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
                        var inputArr = node[":inputs"] || [];
                        inputArr.map(function(input){
                                if(!input[":component"].startsWith("__")){
-                                       this.linkArray.push({
-                                               source: 
_.findWhere(this.nodeArray, {id: input[":component"]}),
+                                       var sourceNode = 
_.findWhere(this.nodeArray, {id: input[":component"]});
+                            this.linkArray.push({
+                                               source: sourceNode,
                                                target: node
                                        });
+                            this.g.setNode(sourceNode.id, _.extend(sourceNode, 
{label: sourceNode.id, shape: 'img'}));
+                            this.g.setNode(node.id, _.extend(node, {label: 
node.id, shape: 'img'}));
+                            this.g.setEdge(sourceNode.id, node.id, 
{"arrowhead": 'arrowPoint'});
                                }
                        }.bind(this));
                    }.bind(this));
@@ -268,7 +192,7 @@ define(['react', 'react-dom', 'd3', 'd3.tip'], 
function(React, ReactDOM, d3) {
                },
                render: function() {
                        return (
-                               <svg className="topology-graph"></svg>
+                               <svg className="topology-graph" width="100%" 
height="300"></svg>
                        );
                },
        });

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e00f585/contrib/views/storm/src/main/resources/scripts/main.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/main.js 
b/contrib/views/storm/src/main/resources/scripts/main.js
index 6b8fe95..7bd2201 100644
--- a/contrib/views/storm/src/main/resources/scripts/main.js
+++ b/contrib/views/storm/src/main/resources/scripts/main.js
@@ -49,6 +49,10 @@ require.config({
     'd3.tip': {
       deps: ['d3']
     },
+    'dagreD3':{
+      deps: ['d3'],
+      exports: 'dagreD3'
+    },
     'x-editable': {
       deps: ['bootstrap']
     }
@@ -70,7 +74,8 @@ require.config({
     'react-dom': '../libs/react/js/react-dom',
     'JSXTransformer': '../libs/jsx/JSXTransformer',
     'jsx': "../libs/jsx/jsx",
-    'x-editable':'../libs/Bootstrap/js/bootstrap-editable.min'
+    'x-editable':'../libs/Bootstrap/js/bootstrap-editable.min',
+    'dagreD3': '../libs/dagre-d3/dagre-d3.min'
   },
   jsx: {
     fileExtension: '.jsx',

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e00f585/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx
----------------------------------------------------------------------
diff --git 
a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx 
b/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx
index 24d3271..136d95f 100644
--- 
a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx
+++ 
b/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx
@@ -50,7 +50,6 @@ define([
                        this.lagCollection = new BaseCollection();
                        this.systemFlag = false;
                        this.windowSize = ':all-time';
-                       this.initializeData();
                        return {
                                model: this.model,
                                graphData: {},
@@ -63,6 +62,7 @@ define([
                },
                componentWillMount: function(){
                        $('.loader').show();
+                       this.initializeData();
                },
                componentDidMount: function(){
                        $(".boot-switch.systemSum").bootstrapSwitch({
@@ -89,7 +89,6 @@ define([
                        $('.loader').hide();
                },
                componentWillUpdate: function(){
-                       $('.loader').show();
                        $('#collapse-spout').off('hidden.bs.collapse');
                        $('#collapse-spout').off('shown.bs.collapse');
                        $('#collapse-bolt').off('hidden.bs.collapse');
@@ -122,7 +121,6 @@ define([
                        if(this.refs.barChart){
                                
ReactDOM.findDOMNode(document.getElementById('lag-graph')).appendChild(this.refs.barChart.legendsEl)
                        }
-                       $('.loader').hide();
                },
                initializeData: function(){
                        this.model.getData({
@@ -147,6 +145,7 @@ define([
                        this.initializeWorkerData();
                },
                initializeGraphData: function(){
+                       $('#graphLoader').show();
                        this.model.getGraphData({
                                id: this.model.get('id'),
                                window: this.windowSize,
@@ -159,6 +158,7 @@ define([
                                                }
                                                this.setState({graphData: 
model});
                                        }
+                                       $('#graphLoader').hide();
                                }.bind(this),
                                error: function(model, response, options){
                                        Utils.notifyError("Error occured in 
fetching topology visualization data.");
@@ -184,23 +184,40 @@ define([
                },
 
                initializeLagData: function(){
+                       $('#kafkaLoader').show();
                        this.model.getTopologyLag({
                                id: this.model.get('id'),
                                success: function(model, response){
                                        if(response.error || model.error){
                                                
Utils.notifyError(response.error || 
model.error+'('+model.errorMessage.split('(')[0]+')');
                                        } else {
-                                               if(model && model.length){
-                                                       var result = 
JSON.parse(model[0].spoutLagResult);
-                                                       for(var i = 0; i < 
result.length; i++){
-                                                               
result[i]['spoutId'] = model[0].spoutId;
-                                                               
result[i]['spoutType'] = model[0].spoutType;
+                                               if(model && 
_.keys(model).length > 0){
+                                                       var keys = 
_.keys(model);
+                                                       var arr = [];
+                                                       for(var i = 0; i < 
keys.length; i++){
+                                                               var data = 
model[keys[i]];
+                                                               var topicKeys = 
_.keys(data.spoutLagResult);
+                                                               for(var j = 0; 
j < topicKeys.length; j++){
+                                                                       var 
topicName = topicKeys[j];
+                                                                       var 
partitionData = data.spoutLagResult[topicName];
+                                                                       var id 
= _.keys(partitionData);
+                                                                       for(var 
k = 0; k < id.length; k++){
+                                                                               
var partitionId = id[k];
+                                                                               
var obj = partitionData[partitionId];
+                                                                               
obj['spoutId'] = data.spoutId;
+                                                                               
obj['spoutType'] = data.spoutType;
+                                                                               
obj['partition'] = partitionId;
+                                                                               
obj['topic'] = topicName;
+                                                                               
arr.push(obj);
+                                                                       }
+                                                               }
                                                        }
-                                                       
this.resetLagCollection(result);
+                                                       
this.resetLagCollection(arr);
                                                } else {
                                                        
this.setState({hideKafkaLagBox : true});
                                                }
                                        }
+                                       $('#kafkaLoader').hide();
                                }.bind(this)
                        })
                },
@@ -788,6 +805,7 @@ define([
                                        </div>
                                        <div className="row">
                                                <div className="col-sm-12">
+                                                       <div 
className="inner-loader" id="graphLoader" />
                                                        <TopologyDetailGraph 
model={this.state.model} graphData={this.state.graphData}/>
                                                </div>
                                        </div>
@@ -811,7 +829,8 @@ define([
                                                                        <div 
className="box-body">
                                                                                
<div className="row">
                                                                                
        <div className="col-sm-12">
-                                                                               
                <div id="lag-graph" className="displayNone">
+                                                                               
                <div className="inner-loader" id="kafkaLoader" />
+                                                                               
                <div id="lag-graph">
                                                                                
                        {this.lagCollection.length > 0 ? 
                                                                                
                        <BarChart
                                                                                
                                ref="barChart"

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e00f585/contrib/views/storm/src/main/resources/styles/style.css
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/styles/style.css 
b/contrib/views/storm/src/main/resources/styles/style.css
index 4c51233..f6b1685 100644
--- a/contrib/views/storm/src/main/resources/styles/style.css
+++ b/contrib/views/storm/src/main/resources/styles/style.css
@@ -425,6 +425,18 @@ REACT
     position: absolute;
     text-align: center;
 }
+.node ellipse {
+  stroke: #333;
+  fill: #fff;
+  stroke-width: 1px;
+}
+.edgePath path {
+  stroke: grey;
+  fill: none;
+  cursor: default;
+  stroke-width: 2;
+  stroke-dasharray: 5, 5;
+}
 
 /* Style northward tooltips differently */
 .d3-tip.n:after {
@@ -533,6 +545,14 @@ ul.legends li.legend{
   background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat 
center center;
   z-index: 9;
 }
+.inner-loader{
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  bottom: 0px;
+  right: 0px;
+  background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat 
center center;
+}
 .searchbar{
   margin-top: 15px;
 }

Reply via email to