ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yus...@apache.org
Subject [08/23] ambari git commit: Revert "AMBARI-21955. Update React version to 15.6.2 to get MIT license. (Sanket Shah via yusaku)"
Date Fri, 20 Oct 2017 21:00:34 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/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
new file mode 100644
index 0000000..8679661
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/components/TopologyGraph.jsx
@@ -0,0 +1,199 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react', 'react-dom', 'd3', 'dagreD3', 'd3.tip'], function(React, ReactDOM, d3, dagreD3) {
+	'use strict';
+	return React.createClass({
+		displayName: 'TopologyGraph',
+        propTypes: {
+            data: React.PropTypes.object.isRequired,
+            width: React.PropTypes.string,
+            height: React.PropTypes.string
+        },
+		getInitialState: function(){
+			this.syncData(this.props.data);
+			return null;
+		},
+		componentDidUpdate: function(){
+			this.syncData(this.props.data);
+            this.updateGraph();
+		},
+		componentDidMount: function(){
+            var that = this;
+            this.svg = d3.select(ReactDOM.findDOMNode(this))
+            //Set up tooltip
+            this.tooltip = d3.tip()
+                .attr('class', function() {
+                    return 'd3-tip testing';
+                })
+                .offset([-10, 0])
+                .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>";
+                    if (d[":transferred"] !== null) tip += "<li>Transferred: " + d[":transferred"].toFixed(2) + "</li>";
+                    tip += "</ul>";
+                    return tip;
+                });
+            //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(){
+            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);
+
+            inner.selectAll("g.nodes image")
+                .on('mouseover', function(d) {
+                    that.tooltip.show(d);
+                })
+                .on('mouseout', function(d) {
+                    that.tooltip.hide();
+                });
+
+            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);
+            }
+        },
+		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){
+            		if(!key.startsWith('__')){
+            			data[key].id = key;
+                    	data[key].type = data[key][":type"];
+                    	this.nodeArray.push(data[key]);
+            		}
+            	}.bind(this));
+
+            	var spoutObjArr = _.where(this.nodeArray, { "type": "spout" });
+	            if (spoutObjArr.length > 1) {
+                    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;
+	                spoutObjArr[0].fixed = true;
+	            }
+
+	            this.nodeArray.map(function(node){
+	            	var inputArr = node[":inputs"] || [];
+	            	inputArr.map(function(input){
+	            		if(!input[":component"].startsWith("__")){
+	            			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));
+            }
+		},
+		render: function() {
+			return (
+				<svg className="topology-graph" width="100%" height="300"></svg>
+			);
+		},
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/ClusterSummary.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/ClusterSummary.jsx b/contrib/views/storm/src/main/resources/scripts/containers/ClusterSummary.jsx
new file mode 100644
index 0000000..ad0d9d7
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/ClusterSummary.jsx
@@ -0,0 +1,122 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'utils/Utils',
+	'jsx!components/RadialChart',
+	'models/VCluster',
+	'jsx!containers/NimbusSummary',
+	], function(React, ReactDOM, Utils, RadialChart, VCluster, NimbusSummary){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'ClusterSummary',
+		getInitialState: function(){
+			this.initializeData();
+			return {
+				executorsTotal: 0,
+				tasksTotal: 0,
+				supervisors: 0,
+				slotsUsed: 0,
+				slotsTotal:0
+			};
+		},
+		initializeData: function(){
+			this.model = new VCluster();
+			this.model.fetch({
+				success: function(model, response){
+					if(response.error || model.error){
+						Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')');
+					} else {
+						this.setState(model.attributes);
+					}
+				}.bind(this),
+				error: function(model, response, options){
+					Utils.notifyError("Error occured in fetching cluster summary data.");
+				}
+			});
+		},
+		componentDidMount: function(){
+			$('[data-rel="tooltip1"]').tooltip({
+				placement: 'bottom'
+			});
+		},
+		render: function(){
+			return (
+				<div className="col-sm-5">
+					<div className="row">
+				        <div className="col-sm-6">
+				            <div className="tile primary" title="Executors are threads in a Worker process." data-rel="tooltip1">
+				                <div className="tile-header">Executor</div>
+				                <div className="tile-body">
+				                    <i className="fa fa-play-circle-o"></i>
+				                    <span className="count">{this.state.executorsTotal}</span>
+				                </div>
+				            </div>
+				        </div>
+				        <div className="col-sm-6">
+				            <div className="tile warning" title="A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors." data-rel="tooltip1">
+				                <div className="tile-header">Tasks</div>
+				                <div className="tile-body">
+				                    <i className="fa fa-tasks"></i>
+				                    <span className="count">{this.state.tasksTotal}</span>
+				                </div>
+				            </div>
+				        </div>
+				    </div>
+				    <div className="row">
+			            <div className="col-sm-6">
+			                <div className="tile success" title="The number of nodes in the cluster currently." data-rel="tooltip1">
+			                    <div className="tile-header" style={{textAlign:"center"}}>Supervisor</div>
+			                    <div className="tile-body" style={{textAlign:"center"}}>
+			                        <div id="supervisorCount">
+			                            <RadialChart width="100" height="100" innerRadius="46" outerRadius="50" 
+			                            	color={["rgba(255,255,255,0.6)", "rgba(255,255,255,1)"]} 
+			                            	data={[this.state.supervisors, this.state.supervisors]}
+			                            	labels={['Used','Total']}
+			                            />
+			                        </div>
+			                    </div>
+			                </div>
+			            </div>
+			            <div className="col-sm-6">
+			                <div className="tile danger" title="Slots are Workers (processes)." data-rel="tooltip1">
+			                    <div className="tile-header" style={{textAlign:"center"}}>Slots</div>
+			                    <div className="tile-body" style={{textAlign:"center"}}>
+			                        <div id="slotsCount">
+			                            <RadialChart width="100" height="100" innerRadius="46" outerRadius="50" 
+			                            	color={["rgba(255,255,255,0.6)", "rgba(255,255,255,1)"]} 
+			                            	data={[this.state.slotsUsed, this.state.slotsTotal]}
+			                            	labels={['Used','Total']}
+			                            />
+			                        </div>
+			                    </div>
+			                </div>
+			            </div>
+			        </div>
+			        <div className="row">
+			        	<div className="col-sm-12">
+			        		<NimbusSummary fromDashboard={true} />
+			        	</div>
+			        </div>
+			    </div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/NimbusConfigSummary.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/NimbusConfigSummary.jsx b/contrib/views/storm/src/main/resources/scripts/containers/NimbusConfigSummary.jsx
new file mode 100644
index 0000000..19846c8
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/NimbusConfigSummary.jsx
@@ -0,0 +1,103 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/Table',
+	'utils/Utils',
+	'jsx!modules/Table/Pagination',
+	'collections/VNimbusConfigList',
+	'models/VNimbusConfig',
+	'bootstrap'
+	], function(React, ReactDOM, Table, Utils, Pagination, VNimbusConfigList, VNimbusConfig){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'NimbusConfigSummary',
+		getInitialState: function(){
+			this.initializeCollection();
+			return null;
+		},
+		initializeCollection: function(){
+			this.collection = new VNimbusConfigList();
+			this.collection.comparator = "key";
+			this.collection.fetch({
+				success: function(model, response){
+					if(response.error || model.error){
+						Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')');
+					} else {
+						var result = [];
+						var keys = _.keys(response);
+						for(var k in keys){
+							result.push(new VNimbusConfig({
+								key: keys[k],
+								value: String(response[keys[k]])
+							}));
+						}
+						this.collection.getFirstPage().fullCollection.reset(result);
+					}
+				}.bind(this),
+				error: function(model, response, options){
+					Utils.notifyError("Error occured in fetching nimbus configuration data.");
+				}
+			});
+		},
+		componentDidMount: function() {
+			$('#collapseBody').on('hidden.bs.collapse', function () {
+				$("#collapseTable").toggleClass("fa-compress fa-expand");
+			}).on('shown.bs.collapse', function() {
+				$("#collapseTable").toggleClass("fa-compress fa-expand");
+			});
+		},
+		getColumns: function(){
+			return [
+				{name: 'key', title: 'Key'},
+				{name: 'value', title: 'Value'}
+			];
+		},
+		handleFilter: function(e){
+			var value = e.currentTarget.value;
+			this.collection.search(value);
+		},
+		handleCollapseClick: function(e){
+			$("#collapseBody").collapse('toggle');
+  		},
+		render: function(){
+			return (
+				<div className="box node-accordian">
+		            <div className="box-header" data-toggle="collapse" data-target="#collapseBody" aria-expanded="false" aria-controls="collapseBody">
+		                <h4>Nimbus Configuration</h4>
+		                <div className="box-control">
+		                	<a href="javascript:void(0);" className="primary"><i className="fa fa-expand" id="collapseTable" onClick={this.handleCollapseClick}></i></a>
+		                </div>
+		            </div>
+		            <div className="box-body collapse" id="collapseBody">
+		                	<div className="input-group col-sm-4">
+								<input type="text"  onKeyUp={this.handleFilter} className="form-control" placeholder="Search By Key" />
+								<span className="input-group-btn">
+								<button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button>
+								</span>
+							</div>
+						<Table className="table no-margin" collection={this.collection} emptyText="No nimbus configuration found !" columns={this.getColumns()}/>
+						<Pagination collection={this.collection} />
+		            </div>
+		        </div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/NimbusSummary.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/NimbusSummary.jsx b/contrib/views/storm/src/main/resources/scripts/containers/NimbusSummary.jsx
new file mode 100644
index 0000000..be2f18d
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/NimbusSummary.jsx
@@ -0,0 +1,139 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/Table',
+	'utils/Utils',
+	'jsx!modules/Table/Pagination',
+	'collections/VNimbusList',
+	'models/VNimbus'
+	], function(React, ReactDOM, Table, Utils, Pagination, VNimbusList, VNimbus){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'NimbusSummary',
+		propTypes: {
+			fromDashboard: React.PropTypes.bool
+		},
+		getInitialState: function(){
+			this.initializeCollection();
+			return null;
+		},
+		initializeCollection: function(){
+			this.collection = new VNimbusList();
+			this.collection.fetch({
+				success: function(model, response){
+					if(response.error || model.error){
+						Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')');
+					} else {
+						var result = [];
+						if(!_.isArray(response.nimbuses)){
+							response.nimbuses = new Array(response.nimbuses);
+						}
+						response.nimbuses.map(function(n){
+							n['host:port'] = n.host+':'+n.port;
+							result.push(new VNimbus(n));
+						});
+						this.collection.getFirstPage().fullCollection.reset(result);
+					}
+				}.bind(this),
+				error: function(model, response, options){
+					Utils.notifyError("Error occured in fetching nimbus summary data.");
+				}
+			});
+		},
+		getColumns: function(){
+			return [
+				{name: 'host', title: 'Host:Port', tooltip: 'Nimbus hostname and port number', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return ( <a href={this.props.model.get('nimbusLogLink')} target="_blank"> {this.props.model.get('host:port')} </a> );
+					}
+				})},
+				{name: 'status', title: 'Status', tooltip: 'Leader if this host is leader, Not a Leader for all other live hosts, note that these hosts may or may not be in leader lock queue, and Dead for hosts that are part of nimbus.seeds list but are not alive.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						var classname="label ";
+						switch(this.props.model.get("status")){
+							case 'Leader':
+								classname += "label-success";
+							break;
+							// case 'Follower':
+							// 	classname += "label-warning";
+							// break;
+							default:
+								classname += "label-warning";
+							break;
+						}
+						return (<span className={classname}>{this.props.model.get('status')}</span>);
+					}
+				})},
+				{name: 'nimbusUpTime', title: 'Uptime', tooltip: 'Time since this nimbus host has been running.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return (<small>{this.props.model.get('nimbusUpTime')}</small>);
+					}
+				})}
+			];
+		},
+		handleFilter: function(e){
+			var value = e.currentTarget.value;
+			this.collection.search(value);
+		},
+		render: function(){
+			var elemI = null,
+				pagination = null,
+				elemBox = null;
+			if(this.props.fromDashboard){
+				elemI = ( <div className="box-control">
+		                    <a className="primary" href="#!/nimbus"><i className="fa fa-external-link"></i></a>
+		                </div> )
+			} else {				
+		        pagination = ( <Pagination collection={this.collection} /> );
+		        elemBox = (
+		        		<div className="input-group col-sm-4">
+								<input type="text"  onKeyUp={this.handleFilter} className="form-control" placeholder="Search By Host Name" />
+								<span className="input-group-btn">
+								<button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button>
+								</span>
+						</div>
+		        	);
+			}
+			return (
+				<div className="box">
+		            <div className="box-header">
+		                <h4>Nimbus Summary</h4>
+		                {elemI}
+		            </div>
+		            <div className={this.props.fromDashboard ? "box-body paddless" : "box-body"}>
+		                {elemBox}
+		            	<Table className="table no-margin" collection={this.collection} emptyText="No nimbus found !" columns={this.getColumns()} limitRows={this.props.fromDashboard ? "6" : undefined}/>
+		            	{pagination}
+		            </div>
+		        </div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/SupervisorSummary.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/SupervisorSummary.jsx b/contrib/views/storm/src/main/resources/scripts/containers/SupervisorSummary.jsx
new file mode 100644
index 0000000..15fe53a
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/SupervisorSummary.jsx
@@ -0,0 +1,155 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/Table',
+	'utils/Utils',
+	'jsx!modules/Table/Pagination',
+	'jsx!components/RadialChart',
+	'collections/VSupervisorList',
+	'models/VSupervisor'
+	], function(React, ReactDOM, Table, Utils, Pagination, RadialChart, VSupervisorList, VSupervisor){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'SupervisorSummary',
+		propTypes: {
+			fromDashboard: React.PropTypes.bool
+		},
+		getInitialState: function(){
+			this.initializeCollection();
+			return null;
+		},
+		initializeCollection: function(){
+			this.collection = new VSupervisorList();
+			this.collection.fetch({
+				success: function(model, response){
+					if(response.error || model.error){
+						Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')');
+					} else {
+						var result = [];
+						if(!_.isArray(response.supervisors)){
+							response.supervisors = new Array(response.supervisors);
+						}
+						response.supervisors.map(function(s){
+							result.push(new VSupervisor(s));
+						});
+						this.collection.getFirstPage().fullCollection.reset(result);
+					}
+				}.bind(this),
+				error: function(model, response, options){
+					Utils.notifyError("Error occured in fetching supervisor summary data.");
+				}
+			});
+		},
+		getColumns: function(){
+			return [
+				{name: 'host', title: 'Host', tooltip:'The hostname reported by the remote host. (Note that this hostname is not the result of a reverse lookup at the Nimbus node.)', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return ( <a href={this.props.model.get('logLink')} target="_blank"> {this.props.model.get('host')} </a> );
+					}
+				})},
+				{name: 'slotsTotal', title: 'Slots', tooltip:'Slots are Workers (processes).', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return (<RadialChart innerRadius="19" outerRadius="21" 
+							color={["#bcbcbc", "#235693"]} 
+							data={[this.props.model.get('slotsUsed'), this.props.model.get('slotsTotal')]}
+							labels={['Used','Total']}/>
+						);
+					}
+				})},
+				{name: 'totalCpu', title: 'CPU', tooltip:'CPU that has been allocated.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return (<RadialChart innerRadius="19" outerRadius="21" 
+							color={["#bcbcbc", "#235693"]} 
+							data={[this.props.model.get('usedCpu'), this.props.model.get('totalCpu')]}
+							labels={['Used','Total']}/>
+						);
+					}
+				})},
+				{name: 'totalMem', title: 'Memory', tooltip:'Memory that has been allocated.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return (<RadialChart innerRadius="19" outerRadius="21" 
+							color={["#bcbcbc", "#235693"]} 
+							data={[this.props.model.get('usedMem'), this.props.model.get('totalMem')]}
+							labels={['Used','Total']}/>
+						);
+					}
+				})},
+				{name: 'uptime', title: 'Uptime', tooltip:'The length of time a Supervisor has been registered to the cluster.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return (<small>{this.props.model.get('uptime')}</small>);
+					}
+				})}
+			];
+		},
+		handleFilter: function(e){
+			var value = e.currentTarget.value;
+			this.collection.search(value);
+		},
+		render: function(){
+			var elemI = null,
+				pagination = null,
+				elemBox = null;
+			if(this.props.fromDashboard){
+				elemI = ( <div className="box-control">
+		                    <a className="primary" href="#!/supervisor"><i className="fa fa-external-link"></i></a>
+		                </div> )
+			} else {				
+		        pagination = ( <Pagination collection={this.collection} /> );
+		        elemBox = (
+		        		<div className="input-group col-sm-4">
+								<input type="text"  onKeyUp={this.handleFilter} className="form-control" placeholder="Search By Host" />
+								<span className="input-group-btn">
+								<button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button>
+								</span>
+						</div>
+		        	);
+			}
+			return (
+				<div className="box">
+		            <div className="box-header">
+		                <h4>Supervisor Summary</h4>
+		                {elemI}
+		            </div>
+		            <div className={this.props.fromDashboard ? "box-body paddless" : "box-body"}>
+		            	{elemBox}
+		            	<Table className="table no-margin supervisor-table" collection={this.collection} emptyText="No supervisor found !" columns={this.getColumns()} limitRows={this.props.fromDashboard ? "3" : undefined}/>
+		            	{pagination}
+		            </div>
+		        </div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/TopologyConfiguration.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/TopologyConfiguration.jsx b/contrib/views/storm/src/main/resources/scripts/containers/TopologyConfiguration.jsx
new file mode 100644
index 0000000..21dc4ff
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/TopologyConfiguration.jsx
@@ -0,0 +1,93 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/Table',
+	'jsx!modules/Table/Pagination',
+	'collections/VTopologyConfigList',
+	'models/VTopologyConfig',
+	'bootstrap'
+	], function(React, ReactDOM, Table, Pagination, VTopologyConfigList, VTopologyConfig){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'TopologyConfiguration',
+		propTypes: {
+			configArr: React.PropTypes.object.isRequired
+		},
+		getInitialState: function(){
+			this.collection = new VTopologyConfigList();
+			this.collection.comparator = "key";
+			return null;
+		},
+		componentDidMount: function() {
+			$('#collapseBody').on('hidden.bs.collapse', function () {
+				$("#collapseTable").toggleClass("fa-compress fa-expand");
+			}).on('shown.bs.collapse', function() {
+				$("#collapseTable").toggleClass("fa-compress fa-expand");
+			});
+		},
+		componentDidUpdate: function(){
+			var keys = _.keys(this.props.configArr);
+			var results = [];
+			for(var k in keys){
+				results.push(new VTopologyConfig({
+					key: keys[k],
+					value: String(this.props.configArr[keys[k]])
+				}));
+			}
+			this.collection.getFirstPage().fullCollection.reset(results);
+		},
+		getColumns: function(){
+			return [
+				{name: 'key', title: 'Key'},
+				{name: 'value', title: 'Value'}
+			];
+		},
+		handleFilter: function(e){
+			var value = e.currentTarget.value;
+			this.collection.search(value);
+		},
+		handleCollapseClick: function(e){
+			$("#collapseBody").collapse('toggle');
+  		},
+		render: function(){
+			return (
+				<div className="box">
+		            <div className="box-header" data-toggle="collapse" data-target="#collapseBody" aria-expanded="false" aria-controls="collapseBody">
+		                <h4>Topology Configuration</h4>
+		                <div className="box-control">
+		                	<a href="javascript:void(0);" className="primary"><i className="fa fa-expand" id="collapseTable" onClick={this.handleCollapseClick}></i></a>
+		                </div>
+		            </div>
+		            <div className="box-body collapse" id="collapseBody">
+		                	<div className="input-group col-sm-4">
+								<input type="text"  onKeyUp={this.handleFilter} className="form-control" placeholder="Search By Key" />
+								<span className="input-group-btn">
+								<button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button>
+								</span>
+							</div>
+		                <Table className="table no-margin" collection={this.collection} emptyText="No topology configuration found !" columns={this.getColumns()}/>
+		                <Pagination collection={this.collection} /> 
+		            </div>
+		        </div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/TopologyDetailGraph.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/TopologyDetailGraph.jsx b/contrib/views/storm/src/main/resources/scripts/containers/TopologyDetailGraph.jsx
new file mode 100644
index 0000000..e19cb30
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/TopologyDetailGraph.jsx
@@ -0,0 +1,66 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/TopologyGraph'
+	], function(React, ReactDOM, TopologyGraph){
+	'use strict';
+	return React.createClass({
+		displayName: 'TopologyDetailGraph',
+		propTypes: {
+			model: React.PropTypes.object.isRequired,
+			graphData: React.PropTypes.object.isRequired
+		},
+		getInitialState: function(){
+			return null;
+		},
+		componentWillUpdate: function(){
+			$('#collapse-graph').off('hidden.bs.collapse').off('shown.bs.collapse');
+		},
+		componentDidUpdate: function(){
+			$('#collapse-graph').on('hidden.bs.collapse', function () {
+				$("#graph-icon").toggleClass("fa-compress fa-expand");
+			}).on('shown.bs.collapse', function() {
+				$("#graph-icon").toggleClass("fa-compress fa-expand");
+			});
+		},
+		toggleAccordionIcon: function(){
+			$("#collapse-graph").collapse('toggle');
+		},
+		render: function(){
+			return (
+				<div className="box">
+					<div className="box-header" data-toggle="collapse" data-target="#collapse-graph" aria-expanded="false" aria-controls="collapse-graph">
+						<h4>{this.props.model.get('name')}</h4>
+						<h4 className="box-control">
+							<a href="javascript:void(0);" className="primary">
+								<i className="fa fa-compress" id="graph-icon" onClick={this.toggleAccordionIcon}></i>
+							</a>
+						</h4>
+					</div>
+					<div className="box-body graph-bg collapse in" id="collapse-graph">
+						<div className="col-sm-12 text-center">
+							<TopologyGraph data={this.props.graphData}/>
+						</div>
+					</div>
+				</div>
+			);
+		},
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/containers/TopologyListing.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/containers/TopologyListing.jsx b/contrib/views/storm/src/main/resources/scripts/containers/TopologyListing.jsx
new file mode 100644
index 0000000..4c624cb
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/containers/TopologyListing.jsx
@@ -0,0 +1,188 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react',
+	'react-dom',
+	'jsx!components/Table',
+	'utils/Utils',
+	'collections/VTopologyList',
+	'models/VTopology',
+	'jsx!components/RadialChart',
+	'jsx!modules/Table/Pagination',
+	'jsx!containers/SupervisorSummary'
+	], function(React, ReactDOM, Table, Utils, VTopologyList, VTopology, RadialChart, Pagination, SupervisorSummary){
+	'use strict';
+
+	return React.createClass({
+		displayName: 'TopologyListing',
+		propTypes: {
+			fromDashboard: React.PropTypes.bool
+		},
+		getInitialState: function(){
+			this.initializeCollection();
+			return null;
+		},
+		initializeCollection: function(){
+			this.collection = new VTopologyList();
+			this.collection.fetch({
+				success: function(model, response){
+					if(response.error || model.error){
+						Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')');
+					} else {
+						var result = [];
+						if(!_.isArray(response.topologies)){
+							response.topologies = new Array(response.topologies);
+						}
+						response.topologies.map(function(t){
+							result.push(new VTopology(t));
+						});
+						this.collection.getFirstPage().fullCollection.reset(result);
+					}
+				}.bind(this),
+				error: function(model, response, options){
+					Utils.notifyError("Error occured in fetching topology listing data.");
+				}
+			});
+		},
+		getColumns: function(){
+			var columns = [
+				{name: 'name', title: 'Topology Name', tooltip:'The name given to the topology by when it was submitted. Click the name to view the Topology\'s information.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						return ( <a href={"#!/topology/"+this.props.model.get('id')}> {this.props.model.get('name')} </a>);
+					}
+				})},
+				{name: 'status', title: 'Status', tooltip:'The status can be one of ACTIVE, INACTIVE, KILLED, or REBALANCING.', component: React.createClass({
+					propTypes: {
+						model: React.PropTypes.object.isRequired
+					},
+					render: function(){
+						var classname="label ";
+						switch(this.props.model.get("status")){
+							case 'ACTIVE':
+								classname += "label-success";
+							break;
+							case 'INACTIVE':
+								classname += "label-default";
+							break;
+							case 'REBALANCING':
+								classname += "label-warning";
+							break;
+							case 'KILLED':
+								classname += "label-danger";
+							break;
+							default:
+								classname += "label-primary";
+							break;
+						}
+						return ( <span className={classname}> {this.props.model.get("status")} </span> );
+					}
+				})}
+			];
+			if(!this.props.fromDashboard){
+				var additionalColumns = [
+					{name: 'assignedTotalMem', title: 'Memory Assigned (MB)', tooltip:'Assigned Total Memory by Scheduler.'},
+					{name: 'workersTotal', title: 'Workers', tooltip:'The number of Workers (processes).'},
+					{name: 'executorsTotal', title: 'Executors', tooltip:'Executors are threads in a Worker process.'},
+					{name: 'tasksTotal', title: 'Tasks', tooltip:'A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.'},
+					{name: 'owner', title: 'Owner', tooltip:'The user that submitted the Topology, if authentication is enabled.'}
+				];
+				Array.prototype.push.apply(columns, additionalColumns);
+			}
+			columns.push({name: 'uptime', title: 'Uptime', tooltip:'The time since the Topology was submitted.', component: React.createClass({
+				propTypes: {
+					model: React.PropTypes.object.isRequired
+				},
+				render: function(){
+					return (<small>{this.props.model.get('uptime')}</small>);
+				}
+			})})
+			return columns;
+		},
+		handleFilter: function(e){
+			var value = e.currentTarget.value;
+			this.collection.search(value);
+		},
+		render: function(){
+			var completeElem = null,
+				className = null;
+
+			if(this.props.fromDashboard){
+				var topologyListingElem = (
+					<div className="row">
+						<div className="col-sm-12">
+							<div className="box">
+					            <div className="box-header">
+					                <h4>Topology Listing</h4>
+					                <div className="box-control">
+					                    <a className="primary" href="#!/topology"><i className="fa fa-external-link"></i></a>
+					                </div>
+					            </div>
+					            <div className="box-body paddless">
+					            	<Table className="table no-margin" collection={this.collection} emptyText="No topology found !" columns={this.getColumns()} limitRows={this.props.fromDashboard ? "5" : undefined}/>
+					            </div>
+					        </div>
+						</div>
+					</div>
+				);
+				var supervisorSummaryELem = (
+					<div className="row">
+						<div className="col-sm-12">
+							<SupervisorSummary fromDashboard={true} />
+						</div>
+					</div>
+				);
+				completeElem = (
+					<div>
+						{topologyListingElem}{supervisorSummaryELem}
+					</div>
+				);
+				className = "col-sm-7";
+			} else {
+				var headerELem = (
+					<div className="box-header">
+		                <h4>Topology Listing</h4>		                
+		            </div>);
+		        var bodyElem = (
+		        	<div className="box-body">
+		        		<div className="input-group col-sm-4">
+								<input type="text"  onKeyUp={this.handleFilter} className="form-control" placeholder="Search By Topology Name" />
+								<span className="input-group-btn">
+								<button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button>
+								</span>
+						</div>
+		            	<Table className="table no-margin" collection={this.collection} emptyText="No topology found !" columns={this.getColumns()} limitRows={this.props.fromDashboard ? "5" : undefined}/>
+		            	<Pagination collection={this.collection} />
+		            </div>);
+				completeElem = (
+					<div>
+		            	{headerELem}{bodyElem}
+		            </div>
+				);
+				className = "box";
+			}
+			return (
+				<div className={className}>
+					{completeElem}
+				</div>
+			);
+		}
+	});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/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
new file mode 100644
index 0000000..7bd2201
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/main.js
@@ -0,0 +1,98 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+require.config({
+  deps: [],
+  waitSeconds: 30,
+  shim: {
+    backbone: {
+      deps: ['underscore', 'jquery'],
+      exports: 'Backbone'
+    },
+    react: {
+      exports: 'React'
+    },
+    bootstrap: {
+      deps: ['jquery'],
+      exports: 'jquery'
+    },
+    'bootstrap-switch': {
+      deps: ['bootstrap']
+    },
+    'bootstrap-slider': {
+      deps: ['bootstrap']
+    },
+    'bootstrap-notify': {
+      deps: ['bootstrap']
+    },
+    underscore: {
+      exports: '_'
+    },
+    JSXTransformer: {
+        exports: "JSXTransformer"
+    },
+    'd3.tip': {
+      deps: ['d3']
+    },
+    'dagreD3':{
+      deps: ['d3'],
+      exports: 'dagreD3'
+    },
+    'x-editable': {
+      deps: ['bootstrap']
+    }
+  },
+  paths: {
+    'jquery': '../libs/jQuery/js/jquery-2.2.3.min',
+    'underscore': '../libs/Underscore/js/Underscore',
+    'backbone': '../libs/Backbone/js/Backbone',
+    'backbone.paginator': '../libs/Backbone-Paginator/js/backbone-paginator.min',
+    'bootstrap': '../libs/Bootstrap/js/bootstrap.min',
+    'bootstrap-switch': '../libs/Bootstrap/js/bootstrap-switch.min',
+    'bootstrap-slider': '../libs/Bootstrap/js/bootstrap-slider.min',
+    'bootstrap-notify': '../libs/Bootstrap/js/bootstrap-notify.min',
+    'bootbox': '../libs/bootbox/js/bootbox.min',
+    'd3': '../libs/d3/js/d3.min',
+    'd3.tip': '../libs/d3/js/d3-tip.min',
+    'text': '../libs/require-text/js/text',
+    'react':'../libs/react/js/react-with-addons',
+    'react-dom': '../libs/react/js/react-dom',
+    'JSXTransformer': '../libs/jsx/JSXTransformer',
+    'jsx': "../libs/jsx/jsx",
+    'x-editable':'../libs/Bootstrap/js/bootstrap-editable.min',
+    'dagreD3': '../libs/dagre-d3/dagre-d3.min'
+  },
+  jsx: {
+    fileExtension: '.jsx',
+  }
+});
+
+require([
+  "jquery",
+  "backbone",
+  "utils/Overrides",
+  "router/Router"
+  ], function($, Backbone, Overrides, Router) {
+      window.App = {};
+
+      App.Container = document.getElementById('container');
+      App.Footer = document.getElementById('footer');
+
+      App.appRouter = new Router();
+      Backbone.history.start();
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/BaseModel.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/BaseModel.js b/contrib/views/storm/src/main/resources/scripts/models/BaseModel.js
new file mode 100644
index 0000000..225788e
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/models/BaseModel.js
@@ -0,0 +1,83 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['require', 'backbone'], function (require, Backbone) {
+  'use strict';
+
+var BaseModel = Backbone.Model.extend(
+	/** @lends BaseModel.prototype */
+	{
+		/**
+		 * BaseModel's initialize function
+		 * @augments Backbone.Model
+		 * @constructs
+		 */
+		initialize : function() {
+			
+		},
+		bindErrorEvents :function(){
+			this.bind("error", function(model, error) {
+			    if (error.status == 401) {
+			      throw new Error("ERROR 401 occured.\n");
+			    }
+			  });
+		},
+		/**
+		 * toString for a model. Every model should implement this function.
+		 */
+		toString : function() {},
+
+		/**
+		 * Silent'ly set the attributes. ( do not trigger events )
+		 */
+		silent_set: function(attrs) {
+			return this.set(attrs, {
+				silent: true
+			});
+		},
+		parse:function(resp, options){
+			return this.parseRecords(resp, options);
+		},
+		parseRecords: function(resp, options) {
+			if (this.modelAttrName) {
+				return Globalize.byString(resp, this.modelAttrName);
+			}else{
+				return resp;
+			}
+		},
+	},
+	/** BaseModel's Static Attributes */
+	{
+
+		/**
+		 * [nonCrudOperation description]
+		 * @param  {[type]} url           [description]
+		 * @param  {[type]} requestMethod [description]
+		 * @param  {[type]} options       [description]
+		 * @return {[type]}               [description]
+		 */
+		nonCrudOperation : function(url, requestMethod, options){
+			return Backbone.sync.call(this, null, this, _.extend({
+				url: url,
+				type: requestMethod
+			}, options));
+		}
+	});
+
+	return BaseModel;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VCluster.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VCluster.js b/contrib/views/storm/src/main/resources/scripts/models/VCluster.js
index 432ca5b..46bcf9d 100644
--- a/contrib/views/storm/src/main/resources/scripts/models/VCluster.js
+++ b/contrib/views/storm/src/main/resources/scripts/models/VCluster.js
@@ -14,24 +14,29 @@
  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 React, {Component}from 'react';
-import { render } from 'react-dom';
-import ReactToastr, {ToastMessage, ToastContainer} from "react-toastr";
-import CustomToastContainer from './CustomToastContainer';
-var {animation}  = ToastMessage;
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var vCluster = vBaseModel.extend({
+    urlRoot: Globals.baseURL + '/api/v1/cluster/summary',
 
-var ToastMessageFactory = React.createFactory(animation);
+    defaults: {},
 
-var container = document.createElement('div');
-var body = document.getElementsByTagName('body').item(0);
-body.appendChild(container);
+    serverSchema: {},
 
-const FSReactToastr = render(
-    <CustomToastContainer
-                  toastMessageFactory={ToastMessageFactory}
-                  className="toast-top-right" />, container
-);
+    idAttribute: 'id',
 
-export default FSReactToastr;
+    initialize: function() {
+      this.modelName = 'VCluster';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    }
+  }, {});
+  return vCluster;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VNimbus.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VNimbus.js b/contrib/views/storm/src/main/resources/scripts/models/VNimbus.js
index 04456cb..02bbc1f 100644
--- a/contrib/views/storm/src/main/resources/scripts/models/VNimbus.js
+++ b/contrib/views/storm/src/main/resources/scripts/models/VNimbus.js
@@ -14,28 +14,29 @@
  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 React, {Component}from 'react';
-import { render } from 'react-dom';
-import ReactToastr, {ToastMessage, ToastContainer} from "react-toastr";
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var vNimbus = vBaseModel.extend({
+    urlRoot: Globals.baseURL + '/api/v1/nimbus/summary',
 
-class CustomToastContainer extends ToastContainer{
-  success(msg, title, opts){
-    super.success(msg.props.children, msg, opts);
-  }
+    defaults: {},
 
-  error(msg, title, opts){
-    super.error(msg.props.children, msg, opts);
-  }
+    serverSchema: {},
 
-  info(msg, title, opts){
-    super.info(msg.props.children, msg, opts);
-  }
+    idAttribute: 'id',
 
-  warning(msg, title, opts){
-    super.warning(msg.props.children, msg, opts);
-  }
-}
-
-export default CustomToastContainer;
+    initialize: function() {
+      this.modelName = 'VNimbus';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    }
+  }, {});
+  return vNimbus;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VNimbusConfig.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VNimbusConfig.js b/contrib/views/storm/src/main/resources/scripts/models/VNimbusConfig.js
index c9536ea..ce8997b 100644
--- a/contrib/views/storm/src/main/resources/scripts/models/VNimbusConfig.js
+++ b/contrib/views/storm/src/main/resources/scripts/models/VNimbusConfig.js
@@ -14,32 +14,29 @@
  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 TopologyREST from '../rest/TopologyREST';
-// const baseUrl = '/api/v1/';
-const baseUrl = location.pathname+'proxy?url=/api/v1/';
-const toastOpt = {
-  timeOut: 0,
-  closeButton: true,
-  tapToDismiss: false,
-  extendedTimeOut: 0,
-  preventDuplicates:true
-};
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var vNimbusConfig = vBaseModel.extend({
+    urlRoot: Globals.baseURL + '/api/v1/cluster/configuration',
 
-const pageSize = 25;
+    defaults: {},
 
-let stormVersion = '';
-function getStormVersion(){
-  return TopologyREST.getSummary('cluster').then((res) => {
-    stormVersion = res.stormVersion;
-  });
-}
+    serverSchema: {},
 
-export {
-  baseUrl,
-  toastOpt,
-  pageSize,
-  getStormVersion,
-  stormVersion
-};
+    idAttribute: 'key',
+
+    initialize: function() {
+      this.modelName = 'VNimbusConfig';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    }
+  }, {});
+  return vNimbusConfig;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VSupervisor.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VSupervisor.js b/contrib/views/storm/src/main/resources/scripts/models/VSupervisor.js
index 41a7fa9..fa8ebbb 100644
--- a/contrib/views/storm/src/main/resources/scripts/models/VSupervisor.js
+++ b/contrib/views/storm/src/main/resources/scripts/models/VSupervisor.js
@@ -14,27 +14,29 @@
  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 React, {Component} from 'react';
-import routes from './routers/routes';
-import {render} from 'react-dom';
-import {Router, browserHistory, hashHistory} from 'react-router';
-import {getStormVersion} from './utils/Constants';
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var VSupervisor = vBaseModel.extend({
+    urlRoot: Globals.baseURL + '/api/v1/supervisor/summary',
 
-class App extends Component {
-  constructor() {
-    super();
-    this.fetchVersion();
-  }
-  fetchVersion(){
-    getStormVersion().then((res) => {
-      this.forceUpdate();
-    });
-  }
-  render() {
-    return (<Router ref="router" history={hashHistory} routes={routes}/>);
-  }
-}
+    defaults: {},
 
-export default App;
+    serverSchema: {},
+
+    idAttribute: 'id',
+
+    initialize: function() {
+      this.modelName = 'VSupervisor';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    }
+  }, {});
+  return VSupervisor;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VTopology.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VTopology.js b/contrib/views/storm/src/main/resources/scripts/models/VTopology.js
new file mode 100644
index 0000000..c746935
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/models/VTopology.js
@@ -0,0 +1,90 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var VTopology = vBaseModel.extend({
+    urlRoot: Globals.baseURL + '/api/v1/topology',
+
+    defaults: {},
+
+    serverSchema: {},
+
+    idAttribute: 'id',
+
+    initialize: function() {
+      this.modelName = 'VTopology';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    },
+    getData: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '?window='+options.window + '&sys=' + options.sys, 'GET', options);
+    },
+    getGraphData: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/visualization?window='+options.window, 'GET', options);
+    },
+    getLogConfig: function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/logconfig', 'GET', options);
+    },
+    saveLogConfig:function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/logconfig', 'POST', options);
+    },
+    activateTopology: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/activate', 'POST', options);
+    },
+    deactivateTopology: function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/deactivate', 'POST', options);
+    },
+    rebalanceTopology: function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/rebalance/' + options.waitTime, 'POST', options);
+    },
+    killTopology: function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/kill/' + options.waitTime, 'POST', options);
+    },
+    getComponent: function(options) {
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/component/' + options.name + '?window='+options.window + '&sys=' + options.sys, 'GET', options);
+    },
+    debugTopology: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id + '/debug/' + options.debugType + '/' + options.percent, 'POST', options);
+    },
+    debugComponent: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id  + '/component/' + options.name + '/debug/' + options.debugType + '/' + options.percent, 'POST', options);
+    },
+    profileJStack: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id  + '/profiling/dumpjstack/' + options.hostPort, 'GET', options);
+    },
+    profileRestartWorker: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id  + '/profiling/restartworker/' + options.hostPort, 'GET', options);
+    },
+    profileHeap: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id  + '/profiling/dumpheap/' + options.hostPort, 'GET', options);
+    },
+    getTopologyLag: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology/' + options.id  + '/lag', 'GET', options);
+    },
+    getWorkerHost: function(options){
+      return this.constructor.nonCrudOperation.call(this, Globals.baseURL + '/api/v1/topology-workers/' + options.id, 'GET', options);
+    },
+  }, {});
+  return VTopology;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/models/VTopologyConfig.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/models/VTopologyConfig.js b/contrib/views/storm/src/main/resources/scripts/models/VTopologyConfig.js
index 558d2a2..c4dd46c 100644
--- a/contrib/views/storm/src/main/resources/scripts/models/VTopologyConfig.js
+++ b/contrib/views/storm/src/main/resources/scripts/models/VTopologyConfig.js
@@ -14,17 +14,21 @@
  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 React, {Component} from 'react';
-
-const CommonExpanded = (props) => {
-  const {expandFlag} = props;
-  return (
-    <div className="box-control pull-right" style={{marginLeft : '17px',marginTop : '-2px'}}>
-      <span className="primary"><i className={`fa ${expandFlag ? 'fa-compress' : 'fa-expand'}`}></i></span>
-    </div>
-  );
-};
-
-export default CommonExpanded;
+define(['require',
+  'utils/Globals',
+  'models/BaseModel'
+], function(require, Globals, vBaseModel) {
+  'use strict';
+  var vTopologyConfig = vBaseModel.extend({
+    initialize: function() {
+      this.modelName = 'vTopologyConfig';
+      this.bindErrorEvents();
+    },
+    toString: function() {
+      return this.get('name');
+    }
+  }, {});
+  return vTopologyConfig;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/modules/Table/PageableTable.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/modules/Table/PageableTable.jsx b/contrib/views/storm/src/main/resources/scripts/modules/Table/PageableTable.jsx
new file mode 100644
index 0000000..7d39e25
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/modules/Table/PageableTable.jsx
@@ -0,0 +1,47 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+var React = require('react');
+var Table = require('jsx!components/Table');
+var Pagination = require('jsx!modules/Table/Pagination');
+
+
+module.exports = React.createClass({
+  displayName: 'PageableTable',
+  propTypes: {
+    nextPageCallback: React.PropTypes.func.isRequired,
+    previousPageCallback: React.PropTypes.func.isRequired,
+    pageCallback: React.PropTypes.func.isRequired,
+    totalPages: React.PropTypes.number.isRequired,
+    currentPage: React.PropTypes.number.isRequired,
+    maximumPages: React.PropTypes.number,
+    collection: React.PropTypes.array.isRequired,
+    columns: React.PropTypes.object.isRequired,
+    sortingCallback: React.PropTypes.func.isRequired,
+    sortKey: React.PropTypes.string,
+    sortOrder: React.PropTypes.number
+  },
+  render: function () {
+    var maximumPages = this.props.maximumPages  ? this.props.maximumPages : 10;
+    return(
+      <div>
+        <Table {...this.props} />
+        <Pagination {...this.props} maximumPages={maximumPages}/>
+     </div>);
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/modules/Table/Pagination.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/modules/Table/Pagination.jsx b/contrib/views/storm/src/main/resources/scripts/modules/Table/Pagination.jsx
new file mode 100644
index 0000000..985a760
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/modules/Table/Pagination.jsx
@@ -0,0 +1,161 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['react', 'utils/Globals'], function(React, Globals){
+  'use strict';
+  return React.createClass({
+    displayName: 'Pagination',
+    propTypes: {
+      collection: React.PropTypes.object.isRequired,
+      maximumPages: React.PropTypes.number
+    },
+    getInitialState: function(){
+      this.props.collection.on('reset', function(data){
+        this.setState({'collection': data});
+      }.bind(this));
+      return {
+        collection: this.props.collection
+      };
+    },
+    /**
+     * Next page button being clicked
+     */
+    nextPage: function(e) {
+      e.preventDefault();
+      this.props.collection.getNextPage(this.props.collection.state.currentPage);
+    },
+    /**
+     * Previous page button being clicked
+     */
+    previousPage: function(e) {
+      e.preventDefault();
+      this.props.collection.getPreviousPage(this.props.collection.state.currentPage);
+    },
+    /**
+     * Change page being clicked
+     * @param {Event} e Event of the page number being clicked
+     */
+    changePage: function(e) {
+      e.preventDefault();
+      var pageNumber = +e.currentTarget.getAttribute('data-page');
+      this.props.collection.getParticularPage(pageNumber);
+    },
+    /**
+     * Render function for the next page button.
+     * If the current page is the last then it shouldn't render a clickable next page
+     */
+    renderNext: function() {
+      if(this.props.collection.state.currentPage < this.props.collection.state.totalPages){
+        return (<li><a href="javascript: void(0);" ref="nextPage" onClick={this.nextPage}>&raquo;</a></li>);
+      } else {
+        return (<li className="disabled"><a href="javascript: void 0;">&raquo;</a></li>);
+      }
+    },
+    /**
+     * Render functon for the pages
+     * If the number of maximumPages is exceeded by the number of pages then that must be handled with an ellipsis
+     * If the page is active then it should have the active class
+     *
+     */
+    renderPages: function(){
+      var pages = [];
+      var starterPage = 1;
+      if(this.props.collection.state.currentPage >= 4) {
+        starterPage = this.props.collection.state.currentPage - 1;
+      }
+      var page = 1;
+      if(!this.props.maximumPages || this.props.maximumPages > this.props.collection.state.totalPages) {
+        for(page = 1; page <= this.props.collection.state.totalPages; page++){
+          if(page !== this.props.collection.state.currentPage) {
+            pages.push(<li key={page}><a href="javascript: void 0;" onClick={this.changePage} data-page={page}>{page}</a></li>);
+          } else {
+            pages.push(<li key={page} className="active"><a href="javascript: void 0;" >{page}</a></li>);
+
+          }
+        }
+      } else {
+        if(this.props.collection.state.currentPage >= 4) {
+          pages.push(<li key={1}><a href="javascript: void 0;" onClick={this.changePage} data-page={1} >{1}</a></li>);
+          pages.push(<li  key="leftellips" className="disabled"><a href="javascript: void 0;">&hellip;</a></li>);
+
+        }
+        for(page = starterPage; page <= this.props.collection.state.totalPages; ++page) {
+          if((starterPage + this.props.maximumPages) < page && (page + this.props.maximumPages) < this.props.collection.state.totalPages) {
+            pages.push(<li key={'ellips'} className="disabled"><a href="javascript: void 0;">&hellip;</a></li>);
+            pages.push(<li key={'collection.state.totalPages'}><a href="javascript: void 0;" onClick={this.changePage} data-page={this.props.collection.state.totalPages} className="">{this.props.collection.state.totalPages}</a></li>);
+            break;
+          } else if (page !== this.props.collection.state.currentPage){
+            pages.push(<li key={page}><a href="javascript: void 0;" onClick={this.changePage} data-page={page} className="">{page}</a></li>);
+          } else {
+            pages.push(<li key={page} className="active"><a href="javascript: void 0;" >{page}</a></li>);
+
+          }
+        }
+      }
+      return pages;
+
+    },
+    /**
+     * Render function for the previous page button.
+     * If the current page is the first then it shouldn't render a clickable previous page
+     */
+    renderPrevious : function() {
+      if(this.props.collection.state.currentPage > 1){
+        return (<li className=""><a href="javascript: void 0;"  ref="prevPage" onClick={this.previousPage}>&laquo;</a></li>);
+      }else {
+        return (<li className="disabled"><a href="javascript: void 0;" >&laquo;</a></li>);
+      }
+    },
+    renderNumber: function(){
+      var startNumber, endNumber;
+      if(this.props.collection.state.currentPage > 1){
+        startNumber = ((this.props.collection.state.currentPage - 1) * Globals.settings.PAGE_SIZE) + 1;
+        endNumber = startNumber + Globals.settings.PAGE_SIZE - 1;
+        if(endNumber > this.props.collection.state.totalRecords){
+          endNumber = this.props.collection.state.totalRecords;
+        }
+      } else {
+        startNumber = 1;
+        if(this.props.collection.state.totalRecords){
+          endNumber = (this.props.collection.state.totalRecords > Globals.settings.PAGE_SIZE ? Globals.settings.PAGE_SIZE : this.props.collection.state.totalRecords);
+        } else {
+          startNumber = 0;
+          endNumber = 0;
+        }
+      }
+      return (
+        <span className="pull-left">Showing {startNumber} to {endNumber} of {this.props.collection.state.totalRecords || 0} entries.</span>
+      );
+    },
+
+    render: function () {
+      var next = this.renderNext();
+      var pages = this.renderPages();
+      var previous = this.renderPrevious();
+      var number = this.renderNumber();
+      return(<div className="clearfix">
+        {number}
+        <ul className="pagination pagination-sm pull-right no-margin">
+          {previous}
+          {pages}
+          {next}
+        </ul>
+      </div>);
+    }
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/router/Router.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/router/Router.js b/contrib/views/storm/src/main/resources/scripts/router/Router.js
new file mode 100644
index 0000000..be6943e
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/router/Router.js
@@ -0,0 +1,123 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define([
+	'require',
+	'backbone',
+	'react',
+	'react-dom',
+	'utils/Utils'
+], function(require, Backbone, React, ReactDOM, Utils) {
+	'use strict';
+	var rRender;
+	var AppRouter = Backbone.Router.extend({
+		routes: {
+			'' 													: 'dashboardAction',
+			'!/dashboard' 										: 'dashboardAction',
+			'!/topology' 										: 'topologyAction',
+			'!/topology/:id'									: 'topologyDetailsAction',
+			'!/topology/:id/component/:name'					: 'componentDetailsAction',
+			'!/nimbus' 											: 'nimbusAction',
+			'!/supervisor' 										: 'supervisorAction',
+			'*actions'											: 'defaultAction'
+		},
+		initialize: function() {
+                        App.baseURL = location.pathname+'proxy?url=';
+			this.showRegions();
+			this.listenTo(this, "route", this.postRouteExecute, this);
+		},
+		showRegions: function() {
+			this.renderFooter();
+			if(window != window.parent){
+				var viewPath = this.getParameterByName("viewpath");
+				location.hash = viewPath ? viewPath : '';
+			}
+		},
+		renderFooter: function(){
+			require(['jsx!views/Footer'], function(Footer){
+				ReactDOM.render(React.createElement(Footer), App.Footer);
+			});
+		},
+		execute: function(callback, args) {
+			this.preRouteExecute();
+			if (callback) callback.apply(this, args);
+			this.postRouteExecute();
+		},
+		preRouteExecute: function() {},
+		postRouteExecute: function(name, args) {
+			this.shareUrl();
+		},
+
+		getParameterByName: function(name) {
+			name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+			var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+				results = regex.exec(location.search);
+			return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+		},
+
+		shareUrl : function(){
+			if(window != window.parent){
+				var parentWindow = window.parent;
+				var parentHash = parentWindow.location.hash.split("?")[0];
+				var newurl = parentWindow.location.protocol + "//" + parentWindow.location.host + parentHash + '?viewpath='+encodeURIComponent(location.hash);
+				parentWindow.history.replaceState({path:newurl},'',newurl);
+			}
+		},
+
+		/**
+		 * Define route handlers here
+		 */
+
+		dashboardAction: function(){
+			require(['jsx!views/Dashboard'], function(DashboardView){
+				ReactDOM.render(React.createElement(DashboardView), App.Container);
+			});
+		},
+		topologyAction: function(){
+			require(['jsx!views/TopologyListingView'], function(TopologyListingView){
+				ReactDOM.render(React.createElement(TopologyListingView), App.Container);
+			});
+		},
+		topologyDetailsAction: function(id){
+			require(['jsx!views/TopologyDetailView'], function(TopologyDetailView){
+				ReactDOM.render(React.createElement(TopologyDetailView, _.extend({}, this.props, {id: id})), App.Container);
+			}.bind(this));
+		},
+		componentDetailsAction: function(id, name){
+			require(['jsx!views/ComponentDetailView'], function(ComponentDetailView){
+				ReactDOM.render(React.createElement(ComponentDetailView, _.extend({}, this.props, {id: id, name: name})), App.Container);
+			}.bind(this));
+		},
+		nimbusAction: function(){
+			require(['jsx!views/NimbusSummaryView'], function(NimbusSummaryView){
+				ReactDOM.render(React.createElement(NimbusSummaryView), App.Container);
+			});
+		},
+		supervisorAction: function(){
+			require(['jsx!views/SupervisorSummaryView'], function(SupervisorSummaryView){
+				ReactDOM.render(React.createElement(SupervisorSummaryView), App.Container);
+			});
+		},
+		defaultAction: function(actions) {
+			throw new Error("No such route found in the application: "+actions);
+		},
+	});
+
+	return AppRouter;
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/utils/Globals.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/utils/Globals.js b/contrib/views/storm/src/main/resources/scripts/utils/Globals.js
index 59064d2..c28d45f 100644
--- a/contrib/views/storm/src/main/resources/scripts/utils/Globals.js
+++ b/contrib/views/storm/src/main/resources/scripts/utils/Globals.js
@@ -14,15 +14,17 @@
  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 React, {Component} from 'react';
-import {stormVersion} from '../utils/Constants';
+define(['require'], function (require) {
+  'use strict';
+	var Globals = {};
 
-const Footer = () =>{
-  return(
-    <p className="text-center">Apache Storm - {stormVersion}</p>
-  );
-};
+	Globals.baseURL = App.baseURL;
 
-export default Footer;
+	Globals.settings = {
+		PAGE_SIZE: 25
+	};
+
+	return Globals;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/utils/Overrides.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/utils/Overrides.js b/contrib/views/storm/src/main/resources/scripts/utils/Overrides.js
new file mode 100644
index 0000000..1170e04
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/utils/Overrides.js
@@ -0,0 +1,30 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['backbone'], function(Backbone){
+	'use strict';
+	Backbone.ajax = function() {
+		if(!arguments[0].data){
+			var urlPart = arguments[0].url.split('url=')[0];
+		    var stormUrlPart = arguments[0].url.split('url=')[1];
+		    urlPart += 'url=' + encodeURIComponent(stormUrlPart);
+		    arguments[0].url = urlPart;
+		}
+	    return Backbone.$.ajax.apply(Backbone.$, arguments);
+	};
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/utils/Utils.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/scripts/utils/Utils.js b/contrib/views/storm/src/main/resources/scripts/utils/Utils.js
new file mode 100644
index 0000000..d9a9dd6
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/scripts/utils/Utils.js
@@ -0,0 +1,113 @@
+/**
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+*/
+
+define(['require',
+    'react',
+    'react-dom',
+    'bootbox',
+    'bootstrap',
+    'bootstrap-notify'], function(require, React, ReactDOM, bootbox) {
+    'use strict';
+    var Utils = {};
+
+    Utils.ArrayToCollection = function(array, collection){
+        if(array.length){
+            array.map(function(obj){
+                collection.add(new Backbone.Model(obj));
+            });
+        }
+        return collection;
+    };
+
+    Utils.ConfirmDialog = function(message, title, successCallback, cancelCallback) {
+        bootbox.dialog({
+            message: message,
+            title: title,
+            className: 'confirmation-dialog',
+            buttons: {
+                cancel: {
+                    label: 'No',
+                    className: 'btn-default btn-small',
+                    callback: cancelCallback ? cancelCallback : function(){}
+                },
+                success: {
+                    label: 'Yes',
+                    className: 'btn-success btn-small',
+                    callback: successCallback
+                }
+            }
+        });
+    };
+
+    Utils.notifyError = function(message) {
+        $.notify({
+            icon: 'fa fa-warning',
+            message: message
+        },{
+            type: 'danger',
+            allow_dismiss: true,
+            animate: {
+                enter: 'animated fadeInDown',
+                exit: 'animated fadeOutUp'
+            }
+        });
+    };
+    Utils.notifySuccess = function(message) {
+        $.notify({
+            icon: 'fa fa-check',
+            message: message
+        },{
+            type: 'success',
+            allow_dismiss: true,
+            animate: {
+                enter: 'animated fadeInDown',
+                exit: 'animated fadeOutUp'
+            }
+        });
+    };
+
+    Utils.notifyInfo = function(message) {
+        $.notify({
+            icon: 'fa fa-info',
+            message: message
+        },{
+            type: 'info',
+            allow_dismiss: true,
+            animate: {
+                enter: 'animated fadeInDown',
+                exit: 'animated fadeOutUp'
+            }
+        });
+    };
+
+    Utils.notifyWarning = function(message) {
+        $.notify({
+            icon: 'fa fa-warning',
+            message: message
+        },{
+            type: 'warning',
+            allow_dismiss: true,
+            animate: {
+                enter: 'animated fadeInDown',
+                exit: 'animated fadeOutUp'
+            }
+        });
+    };
+
+    return Utils;
+});


Mime
View raw message