Browse Source

AMBARI-11954. Ambari storm view fix for html page and added loading gif (Sriharsha Chintalapani via srimanth)

Srimanth Gunturi 10 years ago
parent
commit
41fe3e89f3
20 changed files with 1028 additions and 212 deletions
  1. 1 0
      contrib/views/pom.xml
  2. 0 0
      contrib/views/storm/src/main/resources/libs/bower/bootstrap/css/bootstrap.min.css
  3. 0 0
      contrib/views/storm/src/main/resources/libs/bower/jquery-ui/css/jquery-ui.min.css
  4. 655 0
      contrib/views/storm/src/main/resources/libs/bower/jquery-ui/js/jquery-ui-slider.js
  5. 2 1
      contrib/views/storm/src/main/resources/scripts/globalize/message/en.js
  6. 8 1
      contrib/views/storm/src/main/resources/scripts/main.js
  7. 7 6
      contrib/views/storm/src/main/resources/scripts/router/Router.js
  8. 137 56
      contrib/views/storm/src/main/resources/scripts/utils/Overrides.js
  9. 10 38
      contrib/views/storm/src/main/resources/scripts/views/Cluster/ClusterSummary.js
  10. 54 9
      contrib/views/storm/src/main/resources/scripts/views/Topology/RebalanceForm.js
  11. 25 23
      contrib/views/storm/src/main/resources/scripts/views/Topology/TopologyDetail.js
  12. 20 19
      contrib/views/storm/src/main/resources/scripts/views/Topology/TopologySummary.js
  13. 27 7
      contrib/views/storm/src/main/resources/scripts/views/site/Header.js
  14. 34 18
      contrib/views/storm/src/main/resources/styles/default.css
  15. 4 4
      contrib/views/storm/src/main/resources/templates/cluster/clusterSummary.html
  16. 6 0
      contrib/views/storm/src/main/resources/templates/site/header.html
  17. 26 0
      contrib/views/storm/src/main/resources/templates/topology/rebalanceForm.html
  18. 6 6
      contrib/views/storm/src/main/resources/templates/topology/topologyDetail.html
  19. 4 22
      contrib/views/storm/src/main/resources/templates/topology/topologyForm.html
  20. 2 2
      contrib/views/storm/src/main/resources/templates/topology/topologySummary.html

+ 1 - 0
contrib/views/pom.xml

@@ -40,6 +40,7 @@
     <module>capacity-scheduler</module>
     <module>tez</module>
     <module>utils</module>
+    <module>storm</module>
   </modules>
   <build>
     <pluginManagement>

File diff suppressed because it is too large
+ 0 - 0
contrib/views/storm/src/main/resources/libs/bower/bootstrap/css/bootstrap.min.css


File diff suppressed because it is too large
+ 0 - 0
contrib/views/storm/src/main/resources/libs/bower/jquery-ui/css/jquery-ui.min.css


+ 655 - 0
contrib/views/storm/src/main/resources/libs/bower/jquery-ui/js/jquery-ui-slider.js

@@ -0,0 +1,655 @@
+(function( $, undefined ) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget( "ui.slider", $.ui.mouse, {
+	version: "1.10.3",
+	widgetEventPrefix: "slide",
+
+	options: {
+		animate: false,
+		distance: 0,
+		max: 100,
+		min: 0,
+		orientation: "horizontal",
+		range: false,
+		step: 1,
+		value: 0,
+		values: null,
+
+		// callbacks
+		change: null,
+		slide: null,
+		start: null,
+		stop: null
+	},
+
+	_create: function() {
+		this._keySliding = false;
+		this._mouseSliding = false;
+		this._animateOff = true;
+		this._handleIndex = null;
+		this._detectOrientation();
+		this._mouseInit();
+
+		this.element
+			.addClass( "ui-slider" +
+				" ui-slider-" + this.orientation +
+				" ui-widget" +
+				" ui-widget-content" +
+				" ui-corner-all");
+
+		this._refresh();
+		this._setOption( "disabled", this.options.disabled );
+
+		this._animateOff = false;
+	},
+
+	_refresh: function() {
+		this._createRange();
+		this._createHandles();
+		this._setupEvents();
+		this._refreshValue();
+	},
+
+	_createHandles: function() {
+		var i, handleCount,
+			options = this.options,
+			existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
+			handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
+			handles = [];
+
+		handleCount = ( options.values && options.values.length ) || 1;
+
+		if ( existingHandles.length > handleCount ) {
+			existingHandles.slice( handleCount ).remove();
+			existingHandles = existingHandles.slice( 0, handleCount );
+		}
+
+		for ( i = existingHandles.length; i < handleCount; i++ ) {
+			handles.push( handle );
+		}
+
+		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
+
+		this.handle = this.handles.eq( 0 );
+
+		this.handles.each(function( i ) {
+			$( this ).data( "ui-slider-handle-index", i );
+		});
+	},
+
+	_createRange: function() {
+		var options = this.options,
+			classes = "";
+
+		if ( options.range ) {
+			if ( options.range === true ) {
+				if ( !options.values ) {
+					options.values = [ this._valueMin(), this._valueMin() ];
+				} else if ( options.values.length && options.values.length !== 2 ) {
+					options.values = [ options.values[0], options.values[0] ];
+				} else if ( $.isArray( options.values ) ) {
+					options.values = options.values.slice(0);
+				}
+			}
+
+			if ( !this.range || !this.range.length ) {
+				this.range = $( "<div></div>" )
+					.appendTo( this.element );
+
+				classes = "ui-slider-range" +
+				// note: this isn't the most fittingly semantic framework class for this element,
+				// but worked best visually with a variety of themes
+				" ui-widget-header ui-corner-all";
+			} else {
+				this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
+					// Handle range switching from true to min/max
+					.css({
+						"left": "",
+						"bottom": ""
+					});
+			}
+
+			this.range.addClass( classes +
+				( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
+		} else {
+			this.range = $([]);
+		}
+	},
+
+	_setupEvents: function() {
+		var elements = this.handles.add( this.range ).filter( "a" );
+		this._off( elements );
+		this._on( elements, this._handleEvents );
+		this._hoverable( elements );
+		this._focusable( elements );
+	},
+
+	_destroy: function() {
+		this.handles.remove();
+		this.range.remove();
+
+		this.element
+			.removeClass( "ui-slider" +
+				" ui-slider-horizontal" +
+				" ui-slider-vertical" +
+				" ui-widget" +
+				" ui-widget-content" +
+				" ui-corner-all" );
+
+		this._mouseDestroy();
+	},
+
+	_mouseCapture: function( event ) {
+		var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
+			that = this,
+			o = this.options;
+
+		if ( o.disabled ) {
+			return false;
+		}
+
+		this.elementSize = {
+			width: this.element.outerWidth(),
+			height: this.element.outerHeight()
+		};
+		this.elementOffset = this.element.offset();
+
+		position = { x: event.pageX, y: event.pageY };
+		normValue = this._normValueFromMouse( position );
+		distance = this._valueMax() - this._valueMin() + 1;
+		this.handles.each(function( i ) {
+			var thisDistance = Math.abs( normValue - that.values(i) );
+			if (( distance > thisDistance ) ||
+				( distance === thisDistance &&
+					(i === that._lastChangedValue || that.values(i) === o.min ))) {
+				distance = thisDistance;
+				closestHandle = $( this );
+				index = i;
+			}
+		});
+
+		allowed = this._start( event, index );
+		if ( allowed === false ) {
+			return false;
+		}
+		this._mouseSliding = true;
+
+		this._handleIndex = index;
+
+		closestHandle
+			.addClass( "ui-state-active" )
+			.focus();
+
+		offset = closestHandle.offset();
+		mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
+		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
+			top: event.pageY - offset.top -
+				( closestHandle.height() / 2 ) -
+				( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
+				( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
+				( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
+		};
+
+		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
+			this._slide( event, index, normValue );
+		}
+		this._animateOff = true;
+		return true;
+	},
+
+	_mouseStart: function() {
+		return true;
+	},
+
+	_mouseDrag: function( event ) {
+		var position = { x: event.pageX, y: event.pageY },
+			normValue = this._normValueFromMouse( position );
+
+		this._slide( event, this._handleIndex, normValue );
+
+		return false;
+	},
+
+	_mouseStop: function( event ) {
+		this.handles.removeClass( "ui-state-active" );
+		this._mouseSliding = false;
+
+		this._stop( event, this._handleIndex );
+		this._change( event, this._handleIndex );
+
+		this._handleIndex = null;
+		this._clickOffset = null;
+		this._animateOff = false;
+
+		return false;
+	},
+
+	_detectOrientation: function() {
+		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
+	},
+
+	_normValueFromMouse: function( position ) {
+		var pixelTotal,
+			pixelMouse,
+			percentMouse,
+			valueTotal,
+			valueMouse;
+
+		if ( this.orientation === "horizontal" ) {
+			pixelTotal = this.elementSize.width;
+			pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
+		} else {
+			pixelTotal = this.elementSize.height;
+			pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
+		}
+
+		percentMouse = ( pixelMouse / pixelTotal );
+		if ( percentMouse > 1 ) {
+			percentMouse = 1;
+		}
+		if ( percentMouse < 0 ) {
+			percentMouse = 0;
+		}
+		if ( this.orientation === "vertical" ) {
+			percentMouse = 1 - percentMouse;
+		}
+
+		valueTotal = this._valueMax() - this._valueMin();
+		valueMouse = this._valueMin() + percentMouse * valueTotal;
+
+		return this._trimAlignValue( valueMouse );
+	},
+
+	_start: function( event, index ) {
+		var uiHash = {
+			handle: this.handles[ index ],
+			value: this.value()
+		};
+		if ( this.options.values && this.options.values.length ) {
+			uiHash.value = this.values( index );
+			uiHash.values = this.values();
+		}
+		return this._trigger( "start", event, uiHash );
+	},
+
+	_slide: function( event, index, newVal ) {
+		var otherVal,
+			newValues,
+			allowed;
+
+		if ( this.options.values && this.options.values.length ) {
+			otherVal = this.values( index ? 0 : 1 );
+
+			if ( ( this.options.values.length === 2 && this.options.range === true ) &&
+					( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
+				) {
+				newVal = otherVal;
+			}
+
+			if ( newVal !== this.values( index ) ) {
+				newValues = this.values();
+				newValues[ index ] = newVal;
+				// A slide can be canceled by returning false from the slide callback
+				allowed = this._trigger( "slide", event, {
+					handle: this.handles[ index ],
+					value: newVal,
+					values: newValues
+				} );
+				otherVal = this.values( index ? 0 : 1 );
+				if ( allowed !== false ) {
+					this.values( index, newVal, true );
+				}
+			}
+		} else {
+			if ( newVal !== this.value() ) {
+				// A slide can be canceled by returning false from the slide callback
+				allowed = this._trigger( "slide", event, {
+					handle: this.handles[ index ],
+					value: newVal
+				} );
+				if ( allowed !== false ) {
+					this.value( newVal );
+				}
+			}
+		}
+	},
+
+	_stop: function( event, index ) {
+		var uiHash = {
+			handle: this.handles[ index ],
+			value: this.value()
+		};
+		if ( this.options.values && this.options.values.length ) {
+			uiHash.value = this.values( index );
+			uiHash.values = this.values();
+		}
+
+		this._trigger( "stop", event, uiHash );
+	},
+
+	_change: function( event, index ) {
+		if ( !this._keySliding && !this._mouseSliding ) {
+			var uiHash = {
+				handle: this.handles[ index ],
+				value: this.value()
+			};
+			if ( this.options.values && this.options.values.length ) {
+				uiHash.value = this.values( index );
+				uiHash.values = this.values();
+			}
+
+			//store the last changed value index for reference when handles overlap
+			this._lastChangedValue = index;
+
+			this._trigger( "change", event, uiHash );
+		}
+	},
+
+	value: function( newValue ) {
+		if ( arguments.length ) {
+			this.options.value = this._trimAlignValue( newValue );
+			this._refreshValue();
+			this._change( null, 0 );
+			return;
+		}
+
+		return this._value();
+	},
+
+	values: function( index, newValue ) {
+		var vals,
+			newValues,
+			i;
+
+		if ( arguments.length > 1 ) {
+			this.options.values[ index ] = this._trimAlignValue( newValue );
+			this._refreshValue();
+			this._change( null, index );
+			return;
+		}
+
+		if ( arguments.length ) {
+			if ( $.isArray( arguments[ 0 ] ) ) {
+				vals = this.options.values;
+				newValues = arguments[ 0 ];
+				for ( i = 0; i < vals.length; i += 1 ) {
+					vals[ i ] = this._trimAlignValue( newValues[ i ] );
+					this._change( null, i );
+				}
+				this._refreshValue();
+			} else {
+				if ( this.options.values && this.options.values.length ) {
+					return this._values( index );
+				} else {
+					return this.value();
+				}
+			}
+		} else {
+			return this._values();
+		}
+	},
+
+	_setOption: function( key, value ) {
+		var i,
+			valsLength = 0;
+
+		if ( key === "range" && this.options.range === true ) {
+			if ( value === "min" ) {
+				this.options.value = this._values( 0 );
+				this.options.values = null;
+			} else if ( value === "max" ) {
+				this.options.value = this._values( this.options.values.length-1 );
+				this.options.values = null;
+			}
+		}
+
+		if ( $.isArray( this.options.values ) ) {
+			valsLength = this.options.values.length;
+		}
+
+		$.Widget.prototype._setOption.apply( this, arguments );
+
+		switch ( key ) {
+			case "orientation":
+				this._detectOrientation();
+				this.element
+					.removeClass( "ui-slider-horizontal ui-slider-vertical" )
+					.addClass( "ui-slider-" + this.orientation );
+				this._refreshValue();
+				break;
+			case "value":
+				this._animateOff = true;
+				this._refreshValue();
+				this._change( null, 0 );
+				this._animateOff = false;
+				break;
+			case "values":
+				this._animateOff = true;
+				this._refreshValue();
+				for ( i = 0; i < valsLength; i += 1 ) {
+					this._change( null, i );
+				}
+				this._animateOff = false;
+				break;
+			case "min":
+			case "max":
+				this._animateOff = true;
+				this._refreshValue();
+				this._animateOff = false;
+				break;
+			case "range":
+				this._animateOff = true;
+				this._refresh();
+				this._animateOff = false;
+				break;
+		}
+	},
+
+	//internal value getter
+	// _value() returns value trimmed by min and max, aligned by step
+	_value: function() {
+		var val = this.options.value;
+		val = this._trimAlignValue( val );
+
+		return val;
+	},
+
+	//internal values getter
+	// _values() returns array of values trimmed by min and max, aligned by step
+	// _values( index ) returns single value trimmed by min and max, aligned by step
+	_values: function( index ) {
+		var val,
+			vals,
+			i;
+
+		if ( arguments.length ) {
+			val = this.options.values[ index ];
+			val = this._trimAlignValue( val );
+
+			return val;
+		} else if ( this.options.values && this.options.values.length ) {
+			// .slice() creates a copy of the array
+			// this copy gets trimmed by min and max and then returned
+			vals = this.options.values.slice();
+			for ( i = 0; i < vals.length; i+= 1) {
+				vals[ i ] = this._trimAlignValue( vals[ i ] );
+			}
+
+			return vals;
+		} else {
+			return [];
+		}
+	},
+
+	// returns the step-aligned value that val is closest to, between (inclusive) min and max
+	_trimAlignValue: function( val ) {
+		if ( val <= this._valueMin() ) {
+			return this._valueMin();
+		}
+		if ( val >= this._valueMax() ) {
+			return this._valueMax();
+		}
+		var step = ( this.options.step > 0 ) ? this.options.step : 1,
+			valModStep = (val - this._valueMin()) % step,
+			alignValue = val - valModStep;
+
+		if ( Math.abs(valModStep) * 2 >= step ) {
+			alignValue += ( valModStep > 0 ) ? step : ( -step );
+		}
+
+		// Since JavaScript has problems with large floats, round
+		// the final value to 5 digits after the decimal point (see #4124)
+		return parseFloat( alignValue.toFixed(5) );
+	},
+
+	_valueMin: function() {
+		return this.options.min;
+	},
+
+	_valueMax: function() {
+		return this.options.max;
+	},
+
+	_refreshValue: function() {
+		var lastValPercent, valPercent, value, valueMin, valueMax,
+			oRange = this.options.range,
+			o = this.options,
+			that = this,
+			animate = ( !this._animateOff ) ? o.animate : false,
+			_set = {};
+
+		if ( this.options.values && this.options.values.length ) {
+			this.handles.each(function( i ) {
+				valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
+				_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+				if ( that.options.range === true ) {
+					if ( that.orientation === "horizontal" ) {
+						if ( i === 0 ) {
+							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
+						}
+						if ( i === 1 ) {
+							that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+						}
+					} else {
+						if ( i === 0 ) {
+							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
+						}
+						if ( i === 1 ) {
+							that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+						}
+					}
+				}
+				lastValPercent = valPercent;
+			});
+		} else {
+			value = this.value();
+			valueMin = this._valueMin();
+			valueMax = this._valueMax();
+			valPercent = ( valueMax !== valueMin ) ?
+					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
+					0;
+			_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+
+			if ( oRange === "min" && this.orientation === "horizontal" ) {
+				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
+			}
+			if ( oRange === "max" && this.orientation === "horizontal" ) {
+				this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+			}
+			if ( oRange === "min" && this.orientation === "vertical" ) {
+				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
+			}
+			if ( oRange === "max" && this.orientation === "vertical" ) {
+				this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+			}
+		}
+	},
+
+	_handleEvents: {
+		keydown: function( event ) {
+			/*jshint maxcomplexity:25*/
+			var allowed, curVal, newVal, step,
+				index = $( event.target ).data( "ui-slider-handle-index" );
+
+			switch ( event.keyCode ) {
+				case $.ui.keyCode.HOME:
+				case $.ui.keyCode.END:
+				case $.ui.keyCode.PAGE_UP:
+				case $.ui.keyCode.PAGE_DOWN:
+				case $.ui.keyCode.UP:
+				case $.ui.keyCode.RIGHT:
+				case $.ui.keyCode.DOWN:
+				case $.ui.keyCode.LEFT:
+					event.preventDefault();
+					if ( !this._keySliding ) {
+						this._keySliding = true;
+						$( event.target ).addClass( "ui-state-active" );
+						allowed = this._start( event, index );
+						if ( allowed === false ) {
+							return;
+						}
+					}
+					break;
+			}
+
+			step = this.options.step;
+			if ( this.options.values && this.options.values.length ) {
+				curVal = newVal = this.values( index );
+			} else {
+				curVal = newVal = this.value();
+			}
+
+			switch ( event.keyCode ) {
+				case $.ui.keyCode.HOME:
+					newVal = this._valueMin();
+					break;
+				case $.ui.keyCode.END:
+					newVal = this._valueMax();
+					break;
+				case $.ui.keyCode.PAGE_UP:
+					newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
+					break;
+				case $.ui.keyCode.PAGE_DOWN:
+					newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
+					break;
+				case $.ui.keyCode.UP:
+				case $.ui.keyCode.RIGHT:
+					if ( curVal === this._valueMax() ) {
+						return;
+					}
+					newVal = this._trimAlignValue( curVal + step );
+					break;
+				case $.ui.keyCode.DOWN:
+				case $.ui.keyCode.LEFT:
+					if ( curVal === this._valueMin() ) {
+						return;
+					}
+					newVal = this._trimAlignValue( curVal - step );
+					break;
+			}
+
+			this._slide( event, index, newVal );
+		},
+		click: function( event ) {
+			event.preventDefault();
+		},
+		keyup: function( event ) {
+			var index = $( event.target ).data( "ui-slider-handle-index" );
+
+			if ( this._keySliding ) {
+				this._keySliding = false;
+				this._stop( event, index );
+				this._change( event, index );
+				$( event.target ).removeClass( "ui-state-active" );
+			}
+		}
+	}
+
+});}(jQuery));

+ 2 - 1
contrib/views/storm/src/main/resources/scripts/globalize/message/en.js

@@ -98,7 +98,8 @@
           cancel: 'Cancel',
           save: 'Save',
           yes: 'Yes',
-          no: 'No'
+          no: 'No',
+          apply: 'Apply'
         },
         h: {
           topologies: 'Topologies',

+ 8 - 1
contrib/views/storm/src/main/resources/scripts/main.js

@@ -80,6 +80,9 @@ require.config({
     'jquery.ui.widget': {
       deps: ['jquery']
     },
+    'jquery-ui-slider': {
+      deps: ['jquery', 'jquery-ui', 'jquery.ui.widget']
+    },
     globalize: {
       exports: 'Globalize'
     },
@@ -115,6 +118,7 @@ require.config({
     'backgrid': '../libs/bower/backgrid/js/backgrid',
     'jquery-ui': '../libs/bower/jquery-ui/js/jquery-ui-1.10.3.custom',
     'jquery.ui.widget': '../libs/bower/jquery-ui/js/jquery.ui.widget.min',
+    'jquery-ui-slider' : '../libs/bower/jquery-ui/js/jquery-ui-slider',
     'globalize': '../libs/bower/globalize/js/globalize',
     'gblMessages' : '../scripts/globalize',
     'bootbox': '../libs/bower/bootbox/js/bootbox',
@@ -137,8 +141,11 @@ require.config({
   enforceDefine: false
 });
 
-require(["App",
+define(["App",
   "router/Router",
+  'jquery-ui',
+  'jquery.ui.widget',
+  'jquery-ui-slider',
   "utils/Overrides",
   "arbor",
   "arbor-tween",

+ 7 - 6
contrib/views/storm/src/main/resources/scripts/router/Router.js

@@ -47,21 +47,22 @@ define([
 		},
 
 		bindRegions: function () {
+			var that = this;
 			require(['modules/Vent'], function(vent){
 				vent.on('Region:showTopologySection', function(){
 					App.rCluster.$el.removeClass('active').hide();
 					App.rTopology.$el.addClass('active').show();
 					if(App.rTopology.$el.children().hasClass('topologyDetailView')){
-						vent.trigger('Breadcrumb:Show');
+						that.topologyDetailAction(App.rTopology.currentView.model.get('id'));
+					} else {
+						that.topologySummaryAction();
 					}
 				});
 
 				vent.on('Region:showClusterSection', function(){
-					if(!App.rCluster.hasView()){
-						require(['views/Cluster/ClusterSummary'], function(ClusterSummaryView){
-							App.rCluster.show(new ClusterSummaryView());
-						});
-					}
+					require(['views/Cluster/ClusterSummary'], function(ClusterSummaryView){
+						App.rCluster.show(new ClusterSummaryView());
+					});
 					App.rTopology.$el.removeClass('active').hide();
 					App.rCluster.$el.addClass('active').show();
 					vent.trigger('Breadcrumb:Hide');

+ 137 - 56
contrib/views/storm/src/main/resources/scripts/utils/Overrides.js

@@ -1,27 +1,28 @@
 /**
-* 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.
-*/
+ * 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',
-        'utils/Utils',
-        'backgrid',
-        'bootstrap.filestyle',
-        'backbone.forms'], function (require, Globals, Utils) {
+  'utils/Globals',
+  'utils/Utils',
+  'backgrid',
+  'bootstrap.filestyle',
+  'backbone.forms'
+], function(require, Globals, Utils) {
   'use strict';
 
   /**********************************************************************
@@ -32,20 +33,20 @@ define(['require',
    * HtmlCell renders any html code
    * @class Backgrid.HtmlCell
    * @extends Backgrid.Cell
-  */
+   */
   var HtmlCell = Backgrid.HtmlCell = Backgrid.Cell.extend({
 
-     /** @property */
-     className: "html-cell",
-
-     render: function () {
-         this.$el.empty();
-         var rawValue = this.model.get(this.column.get("name"));
-         var formattedValue = this.formatter.fromRaw(rawValue, this.model);
-         this.$el.append(formattedValue);
-         this.delegateEvents();
-         return this;
-     }
+    /** @property */
+    className: "html-cell",
+
+    render: function() {
+      this.$el.empty();
+      var rawValue = this.model.get(this.column.get("name"));
+      var formattedValue = this.formatter.fromRaw(rawValue, this.model);
+      this.$el.append(formattedValue);
+      this.delegateEvents();
+      return this;
+    }
   });
 
   var UriCell = Backgrid.UriCell = Backgrid.Cell.extend({
@@ -53,13 +54,13 @@ define(['require',
     title: null,
     target: "_blank",
 
-    initialize: function (options) {
+    initialize: function(options) {
       UriCell.__super__.initialize.apply(this, arguments);
       this.title = options.title || this.title;
       this.target = options.target || this.target;
     },
 
-    render: function () {
+    render: function() {
       this.$el.empty();
       var rawValue = this.model.get(this.column.get("name"));
       var href = _.isFunction(this.column.get("href")) ? this.column.get('href')(this.model) : this.column.get('href');
@@ -69,13 +70,13 @@ define(['require',
         tabIndex: -1,
         href: href,
         title: this.title || formattedValue,
-        'class' : klass
+        'class': klass
       }).text(formattedValue));
 
-      if(this.column.has("iconKlass")){
+      if (this.column.has("iconKlass")) {
         var iconKlass = this.column.get("iconKlass");
         var iconTitle = this.column.get("iconTitle");
-        this.$el.find('a').append('<i class="'+iconKlass+'" title="'+iconTitle+'"></i>');
+        this.$el.find('a').append('<i class="' + iconKlass + '" title="' + iconTitle + '"></i>');
       }
       this.delegateEvents();
       return this;
@@ -111,34 +112,36 @@ define(['require',
        @param {Backgrid.Column} options.column
        @param {Backbone.Model} options.model
     */
-    initialize: function (options) {
+    initialize: function(options) {
 
       this.column = options.column;
       if (!(this.column instanceof Backgrid.Column)) {
         this.column = new Backgrid.Column(this.column);
       }
 
-      if(!this.column.has("checkedVal")){
+      if (!this.column.has("checkedVal")) {
         this.column.set("checkedVal", "true"); // it is not a boolean value for EPM
         this.column.set("uncheckedVal", "false");
       }
 
-      var column = this.column, model = this.model, $el = this.$el;
-      this.listenTo(column, "change:renderable", function (column, renderable) {
+      var column = this.column,
+        model = this.model,
+        $el = this.$el;
+      this.listenTo(column, "change:renderable", function(column, renderable) {
         $el.toggleClass("renderable", renderable);
       });
 
-      if (Backgrid.callByNeed(column.renderable(), column, model)){
+      if (Backgrid.callByNeed(column.renderable(), column, model)) {
         $el.addClass("renderable");
       }
 
-      this.listenTo(model, "change:" + column.get("name"), function () {
-        if (!$el.hasClass("editor")){
+      this.listenTo(model, "change:" + column.get("name"), function() {
+        if (!$el.hasClass("editor")) {
           this.render();
         }
       });
 
-      this.listenTo(model, "backgrid:select", function (model, selected) {
+      this.listenTo(model, "backgrid:select", function(model, selected) {
         this.$el.find("input[type=checkbox]").prop("checked", selected).change();
       });
 
@@ -148,14 +151,14 @@ define(['require',
     /**
        Focuses the checkbox.
     */
-    enterEditMode: function () {
+    enterEditMode: function() {
       this.$el.find("input[type=checkbox]").focus();
     },
 
     /**
        Unfocuses the checkbox.
     */
-    exitEditMode: function () {
+    exitEditMode: function() {
       this.$el.find("input[type=checkbox]").blur();
     },
 
@@ -164,7 +167,7 @@ define(['require',
        `backgrid:selected` event with a reference of the model and the
        checkbox's `checked` value.
     */
-    onChange: function () {
+    onChange: function() {
       var checked = this.$el.find("input[type=checkbox]").prop("checked");
       this.model.set(this.column.get("name"), checked);
       this.model.trigger("backgrid:selected", this.model, checked);
@@ -173,8 +176,9 @@ define(['require',
     /**
        Renders a checkbox in a table cell.
     */
-    render: function () {
-      var model = this.model, column = this.column;
+    render: function() {
+      var model = this.model,
+        column = this.column;
       var val = (model.get(column.get("name")) === column.get("checkedVal") || model.get(column.get("name")) === true) ? true : false;
       var editable = Backgrid.callByNeed(column.editable(), column, model);
 
@@ -193,16 +197,16 @@ define(['require',
   });
 
   Backbone.Form.editors.Fileupload = Backbone.Form.editors.Base.extend({
-    initialize: function(options){
+    initialize: function(options) {
       Backbone.Form.editors.Base.prototype.initialize.call(this, options);
       this.template = _.template('<input type="file" name="fileInput" class="filestyle">');
     },
-    render: function(){
-      this.$el.html( this.template );
+    render: function() {
+      this.$el.html(this.template);
       this.$(":file").filestyle();
       return this;
     },
-    getValue: function(){
+    getValue: function() {
       return $('input[name="fileInput"]')[0].files[0];
     }
   });
@@ -215,4 +219,81 @@ define(['require',
     return Backbone.$.ajax.apply(Backbone.$, arguments);
   };
 
-});
+  Backbone.Form.editors.RangeSlider = Backbone.Form.editors.Base.extend({
+    ui: {
+
+    },
+    events: {
+      'change #from': 'evChange',
+    },
+
+    initialize: function(options) {
+      this.options = options;
+      Backbone.Form.editors.Base.prototype.initialize.call(this, options);
+      this.template = _.template('<div id="scoreSlider" style="width: 50%; float: left; margin-top: 5px;" class="ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all" aria-disabled="false"><div class="ui-slider-range ui-widget-header ui-corner-all" style="left: 0%; width: 100%; "></div><a class="ui-slider-handle ui-state-default ui-corner-all" href="#" style="left: 0%; "></a></div><input type="number" id="from" class="input-mini" style="float:left; width: 60px; margin-left: 15px;" placeholder="0"/><div id="sliderLegend" style="width: 50%"><label id="startLabel" style="float: left;">0</label><label id="endLabel" style="float: right;">0</label></div>');
+    },
+
+    evChange: function(e) {
+      var val = e.currentTarget.value;
+      if (val == '') {
+        val = '0';
+      }
+
+      var sanitized = val.replace(/[^0-9]/g, '');
+
+      if (_.isNumber(parseInt(sanitized, 10))) {
+        if (sanitized < this.options.schema.options.max) {
+          $(e.currentTarget).val(sanitized);
+          this.$('#scoreSlider').slider('value', sanitized);
+        } else {
+          $(e.currentTarget).val(this.options.schema.options.max);
+          this.$('#scoreSlider').slider('value', this.options.schema.options.max);
+        }
+      }
+
+    },
+
+    /**
+     * Adds the editor to the DOM
+     */
+    render: function() {
+      this.$el.html(this.template);
+      this.setupSlider(this.options.schema.options);
+      if (this.value)
+        this.setValue(this.value);
+      return this;
+    },
+
+    getValue: function() {
+      return {
+        min: this.$("#from").val(),
+      };
+    },
+
+    setValue: function(value) {
+      this.$("#from").val(value);
+      return this.$('#scoreSlider').slider('value');
+    },
+    setupSlider: function(options) {
+      var that = this;
+      var minVal = options && options.min ? options.min : 0,
+        maxVal = options && options.max ? options.max : 0;
+      this.$('#scoreSlider').slider({
+        range: "max",
+        min: minVal,
+        max: maxVal,
+        value: this.value,
+        slide: function(event, ui) {
+          that.$("#from").val(ui.value);
+        }
+      });
+      this.$('#startLabel').text(minVal);
+      this.$('#endLabel').text(maxVal);
+      this.$('#from').attr("min", minVal);
+      this.$('#from').attr("max", maxVal);
+      if (this.options.schema.minEditorClass)
+        this.$('#from').addClass(this.options.schema.minEditorClass);
+    }
+  });
+
+});

+ 10 - 38
contrib/views/storm/src/main/resources/scripts/views/Cluster/ClusterSummary.js

@@ -73,9 +73,9 @@ define(['require',
       this.fetchData();
 
       this.$('.collapse').on('shown.bs.collapse', function() {
-        $(this).parent().find(".fa-caret-right").removeClass("fa-caret-right").addClass("fa-caret-down");
+        $(this).parent().find(".fa-plus-square").removeClass("fa-plus-square").addClass("fa-minus-square");
       }).on('hidden.bs.collapse', function() {
-        $(this).parent().find(".fa-caret-down").removeClass("fa-caret-down").addClass("fa-caret-right");
+        $(this).parent().find(".fa-minus-square").removeClass("fa-minus-square").addClass("fa-plus-square");
       });
 
     },
@@ -91,15 +91,15 @@ define(['require',
       this.clusterCollection.trigger('request', this.clusterCollection);
       model.fetch({
         success: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.clusterCollection.trigger('sync', that.clusterCollection);
           if (model) {
             that.clusterCollection.reset(model);
           }
-          that.startClusterSumPolling();
         },
         error: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.clusterCollection.trigger('error', that.clusterCollection);
-          that.startClusterSumPolling();
           Utils.notifyError(response.statusText);
           return null;
         }
@@ -111,6 +111,7 @@ define(['require',
       this.sprCollection.trigger('request', this.sprCollection);
       model.fetch({
         success: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.sprCollection.trigger('sync', that.sprCollection);
           if (model.has('supervisors') && model.get('supervisors').length) {
             var arr = [];
@@ -119,11 +120,10 @@ define(['require',
             });
             that.sprCollection.reset(arr);
           }
-          that.startSupervisorSumPolling();
         },
         error: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.sprCollection.trigger('error', that.sprCollection);
-          that.startSupervisorSumPolling();
           Utils.notifyError(response.statusText);
         }
       });
@@ -134,6 +134,7 @@ define(['require',
       this.nimbusConfigCollection.trigger('request', this.nimbusConfigCollection);
       model.fetch({
         success: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.nimbusConfigCollection.trigger('sync', that.nimbusConfigCollection);
           if (model) {
             var arr = [];
@@ -145,11 +146,10 @@ define(['require',
             }
             that.nimbusConfigCollection.reset(arr);
           }
-          that.startNimbusConfigPolling();
         },
         error: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.nimbusConfigCollection.trigger('error', that.nimbusConfigCollection);
-          that.startNimbusConfigPolling();
           Utils.notifyError(response.statusText);
         }
       });
@@ -160,6 +160,7 @@ define(['require',
       this.nbsCollection.trigger('request', this.nbsCollection);
       model.fetch({
         success: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.nbsCollection.trigger('sync', that.nbsCollection);
           if (model.has('nimbuses') && model.get('nimbuses').length) {
             var arr = [];
@@ -168,11 +169,10 @@ define(['require',
             });
             that.nbsCollection.reset(arr);
           }
-          that.startNimbusSummaryPolling();
         },
         error: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh');
           that.nbsCollection.trigger('error', that.nbsCollection);
-          that.startNimbusSummaryPolling();
           Utils.notifyError(response.statusText);
         }
       });
@@ -346,34 +346,6 @@ define(['require',
         label: localization.tt("lbl.value")
       }];
       return cols;
-    },
-
-    startClusterSumPolling: function(){
-      var that = this;
-      setTimeout(function(){
-        that.getClusterSummary(that.clusterModel);
-      }, Globals.settings.refreshInterval);
-    },
-
-    startSupervisorSumPolling: function(){
-      var that = this;
-      setTimeout(function(){
-        that.getSupervisorSummary(that.supervisorModel);
-      }, Globals.settings.refreshInterval);
-    },
-
-    startNimbusConfigPolling: function(){
-      var that = this;
-      setTimeout(function(){
-        that.getNimbusConfig(that.nimbusConfigModel);
-      }, Globals.settings.refreshInterval);
-    },
-
-    startNimbusSummaryPolling: function(){
-      var that = this;
-      setTimeout(function(){
-        that.getNimbusSummary(that.nimbusSummaryModel);
-      }, Globals.settings.refreshInterval);
     }
 
   });

+ 54 - 9
contrib/views/storm/src/main/resources/scripts/views/Topology/RebalanceForm.js

@@ -18,44 +18,72 @@
 
 define(['utils/LangSupport',
   'utils/Globals',
+  'hbs!tmpl/topology/rebalanceForm',
+  'models/Cluster',
   'backbone.forms'
-  ], function (localization, Globals) {
+  ], function (localization, Globals, tmpl, clusterModel) {
   'use strict';
 
   var RebalanceForm = Backbone.Form.extend({
 
+    template: tmpl,
+
     initialize: function (options) {
+      this.showSpout = false,
+      this.showBolt = false,
+      this.spoutData = [],
+      this.boltData = [];
       this.spoutCollection = options.spoutCollection;
       this.boltCollection = options.boltCollection;
+      this.model = options.model;
+      this.schemaObj = this.generateSchema();
+      this.templateData = {
+        "spoutFlag": this.showSpout,
+        "boltFlag": this.showBolt,
+        "spoutData": this.spoutData,
+        "boltData": this.boltData,
+        "showParallelism": (this.showSpout && this.showBolt) ? true : false
+      },
       Backbone.Form.prototype.initialize.call(this, options);
     },
 
-    schema: function () {
+    generateSchema: function(){
       var that = this;
+      var freeSlots = this.getClusterSummary(new clusterModel());
+      freeSlots += this.model.get('workers');
       var obj = {
         workers: {
-          type: 'Number',
-          title: localization.tt('lbl.workers')
+          type: 'RangeSlider',
+          title: localization.tt('lbl.workers'),
+          options: {"min":0,"max":freeSlots}
         }
       };
 
       if(that.spoutCollection.length){
         _.each(that.spoutCollection.models, function(model){
           if(model.has('spoutId')) {
-            obj[model.get('spoutId')] = {
+            that.showSpout = true;
+            var name = model.get('spoutId');
+            obj[name] = {
               type: 'Number',
-              title: model.get('spoutId')
+              title: name
             };
+            that.spoutData.push(name);
+            that.model.set(name,model.get('executors'));
           }
         });
       }
       if(that.boltCollection.length){
         _.each(that.boltCollection.models, function(model){
           if(model.has('boltId')) {
-            obj[model.get('boltId')] = {
+            var name = model.get('boltId');
+            that.showBolt = true;
+            obj[name] = {
               type: 'Number',
-              title: model.get('boltId')
+              title: name
             };
+            that.boltData.push(name);
+            that.model.set(name,model.get('executors'));
           }
         });
       }
@@ -64,7 +92,24 @@ define(['utils/LangSupport',
         title: localization.tt('lbl.waitTime')+'*',
         validators: ['required']
       }
-      return obj
+      return obj;
+    },
+
+    getClusterSummary: function(model){
+      var freeSlots = 0;
+      model.fetch({
+        async: false,
+        success: function(model, response, options) {
+          if (model) {
+            freeSlots = model.get('slotsFree');
+          }
+        }
+      });
+      return freeSlots;
+    },
+
+    schema: function () {
+      return this.schemaObj;
     },
 
     render: function(options){

+ 25 - 23
contrib/views/storm/src/main/resources/scripts/views/Topology/TopologyDetail.js

@@ -85,16 +85,16 @@ define(['require',
         sysBoltFlag: flag,
         windowTimeFrame: timeFrame,
         success: function(model, response, options) {
+          vent.trigger('LastUpdateRefresh', true);
           that.model = new vTopology(model);
           that.getDetails(that.model);
           that.render();
           that.disableBtnAction(model.status);
           that.$('#sysBolt').prop("checked",that.systemBoltFlag)
-          that.startPollingAction(that.model.get('id'), that.model.get('window'));
           $('.loading').hide();
         },
         error: function(model, response, options) {
-          that.startPollingAction(that.model.get('id'), that.model.get('window'));
+          vent.trigger('LastUpdateRefresh', true);
           $('.loading').hide();
           Utils.notifyError(model.statusText);
         }
@@ -133,9 +133,12 @@ define(['require',
       }
       this.$('.topology-title').html(this.model.has('name') ? this.model.get('name') : '');
       this.showDetailsTable(this.topologyDetailsColl);
-      if(this.summaryArr.length){
+      if(!_.isUndefined(this.summaryArr) && this.summaryArr.length){
         this.setTimeFrameOptions();
+      } else {
+        $('.loading').hide();
       }
+
       this.windowTimeFrame = this.model.get('window');
       this.$('#tFrame').val(this.windowTimeFrame);
       this.showSummaryTable(this.windowTimeFrame);
@@ -152,9 +155,9 @@ define(['require',
       }
 
       this.$('.collapse').on('shown.bs.collapse', function() {
-        $(this).parent().find(".fa-caret-right").removeClass("fa-caret-right").addClass("fa-caret-down");
+        $(this).parent().find(".fa-plus-square").removeClass("fa-plus-square").addClass("fa-minus-square");
       }).on('hidden.bs.collapse', function() {
-        $(this).parent().find(".fa-caret-down").removeClass("fa-caret-down").addClass("fa-caret-right");
+        $(this).parent().find(".fa-minus-square").removeClass("fa-minus-square").addClass("fa-plus-square");
       });
 
       this.$('[data-id="r_tableList"]').parent().removeClass('col-md-12');
@@ -165,7 +168,7 @@ define(['require',
 
     disableBtnAction: function(status){
       _.each(this.$el.find('.btn.btn-success.btn-sm'),function(elem){
-        $(elem).removeAttr('disabled')
+        $(elem).removeAttr('disabled');
       });
       switch(status){
         case "ACTIVE":
@@ -436,16 +439,20 @@ define(['require',
         this.onDialogClosed();
       }
       require(['views/Topology/RebalanceForm'], function(rebalanceForm){
+        var reBalanceModel = new Backbone.Model();
+        reBalanceModel.set('workers',that.topologyDetailsColl.models[0].get('workersTotal'));
+        reBalanceModel.set('waitTime',30);
         that.view = new rebalanceForm({
           spoutCollection : that.spoutsCollection,
-          boltCollection: that.boltsCollection
+          boltCollection: that.boltsCollection,
+          model: reBalanceModel
         });
         that.view.render();
 
         bootbox.dialog({
           message: that.view.$el,
           title: localization.tt('lbl.rebalanceTopology'),
-          className: "topology-modal",
+          className: "rebalance-modal",
           buttons: {
             cancel: {
               label: localization.tt('btn.cancel'),
@@ -455,7 +462,7 @@ define(['require',
               }
             },
             success: {
-              label: localization.tt('btn.save'),
+              label: localization.tt('btn.apply'),
               className: "btn-success",
               callback: function(){
                 var err = that.view.validate();
@@ -473,8 +480,8 @@ define(['require',
           attr = this.view.getValue(),
           obj = {"rebalanceOptions":{}};
 
-      if(!_.isUndefined(attr.workers) && !_.isNull(attr.workers)){
-        obj.rebalanceOptions.numWorkers = attr.workers;
+      if(!_.isUndefined(attr.workers.min) && !_.isNull(attr.workers.min)){
+        obj.rebalanceOptions.numWorkers = attr.workers.min;
       }
 
       var spoutBoltObj = {};
@@ -498,7 +505,13 @@ define(['require',
         type: 'POST',
         success: function(model, response, options){
           if(!_.isUndefined(model.error)){
-            Utils.notifyError(model.error);
+            if(model.errorMessage.search("msg:") != -1){
+              var startIndex = model.errorMessage.search("msg:") + 4;
+              var endIndex = model.errorMessage.split("\n")[0].search("\\)");
+              Utils.notifyError(model.error+":<br/>"+model.errorMessage.substring(startIndex, endIndex));
+            } else {
+              Utils.notifyError(model.error);
+            }
           } else {
             Utils.notifySuccess(localization.tt('dialogMsg.topologyRebalanceSuccessfully'));
             that.fetchData(that.model.get('id'), that.systemBoltFlag, that.windowTimeFrame);
@@ -703,9 +716,7 @@ define(['require',
     },
 
     getTopoConfigColumns: function() {
-
       var cols = [
-
         {
           name: "key",
           cell: "string",
@@ -718,15 +729,6 @@ define(['require',
         }
       ];
       return cols;
-    },
-
-    startPollingAction: function(id){
-      var that = this;
-      setTimeout(function(){
-        if(_.isEqual(typeof that.ui.BoltsSummaryDetails, "object")){
-          that.fetchData(id, that.systemBoltFlag, that.windowTimeFrame);
-        }
-      }, Globals.settings.refreshInterval);
     }
 
   });

+ 20 - 19
contrib/views/storm/src/main/resources/scripts/views/Topology/TopologySummary.js

@@ -37,7 +37,7 @@ define(['require',
     templateHelpers: function() {},
 
     events: {
-      'click [data-id="deployBtn"]': 'evDeployTopology'
+      // 'click [data-id="deployBtn"]': 'evDeployTopology'
     },
 
     ui: {
@@ -53,23 +53,31 @@ define(['require',
       vent.trigger('Breadcrumb:Hide');
     },
 
-    fetchSummary: function(flag) {
+    fetchSummary: function(topologyName) {
       var that = this;
       this.collection.fetch({
         success: function(collection, response, options) {
+          vent.trigger('LastUpdateRefresh');
           if (collection && collection.length) {
             var arr = [];
             _.each(collection.models[0].get('topologies'), function(object){
-              arr.push(new mTopology(object));
+              if(!_.isUndefined(topologyName) && object.name === topologyName){
+                Backbone.history.navigate('!/topology/'+object.id, {trigger:true});
+              } else {
+                arr.push(new mTopology(object));
+              }
             });
-            that.countActive = 0;
-            that.collection.reset(arr);
-            that.showSummaryDetail();
-            that.startPollingAction();
+            if(_.isUndefined(topologyName)){
+              that.countActive = 0;
+              that.collection.reset(arr);
+              that.showSummaryDetail();
+            } else {
+              $('.loading').hide();
+            }
           }
         },
         error: function(collection, response, options){
-          that.startPollingAction();
+          vent.trigger('LastUpdateRefresh');
           Utils.notifyError(response.statusText);
         }
       })
@@ -263,18 +271,21 @@ define(['require',
           response = JSON.parse(response);
         }
         if(_.isEqual(response.status, 'failed')){
+          $('.loading').hide();
           Utils.notifyError(response.error);
         } else {
           Utils.notifySuccess(localization.tt("dialogMsg.topologyDeployedSuccessfully"));
-          that.fetchSummary(true);
+          that.fetchSummary(attrs.name);
         }
       };
 
       var errorCallback = function(){
+        $('.loading').hide();
         Utils.notifyError(localization.tt("dialogMsg.topologyDeployFailed"));
       };
 
       Utils.uploadFile(url,formData,successCallback, errorCallback);
+      $('.loading').show();
     },
 
     onDialogClosed: function() {
@@ -283,16 +294,6 @@ define(['require',
         this.view.remove();
         this.view = null;
       }
-    },
-
-    startPollingAction: function(){
-      var that = this;
-      setTimeout(function() {
-        if(_.isEqual(typeof that.ui.summaryDetails, "object")){
-          that.fetchSummary();
-        }
-      }, Globals.settings.refreshInterval);
-
     }
 
   });

+ 27 - 7
contrib/views/storm/src/main/resources/scripts/views/site/Header.js

@@ -40,7 +40,8 @@ define(['require',
 
     events: {
       'click [data-id="topology"]': 'showTopologySection',
-      'click [data-id="cluster"]': 'showClusterSection'
+      'click [data-id="cluster"]': 'showClusterSection',
+      'click #refresh' : 'evRefresh'
     },
 
     initialize: function (options) {
@@ -57,20 +58,39 @@ define(['require',
       vent.on('Breadcrumb:Hide', function(){
         that.$('.breadcrumb').addClass('displayNone');
       });
+      vent.on('LastUpdateRefresh', function(flag){
+        if(flag)
+          that.$('.last-refreshed').css("margin-top","0px");
+        else
+          that.$('.last-refreshed').css("margin-top","35px");
+        that.$('#refreshTime').html(new Date().toLocaleString());
+      });
     },
 
     onRender: function () {},
 
     showTopologySection: function () {
-      this.ui.clusterLink.parent().removeClass('active');
-      this.ui.toplogyLink.parent().addClass('active');
-      vent.trigger('Region:showTopologySection');
+      if(!this.ui.toplogyLink.parent().hasClass('active')){
+        this.ui.clusterLink.parent().removeClass('active');
+        this.ui.toplogyLink.parent().addClass('active');
+        vent.trigger('Region:showTopologySection');
+      }
     },
 
     showClusterSection: function () {
-      this.ui.toplogyLink.parent().removeClass('active');
-      this.ui.clusterLink.parent().addClass('active');
-      vent.trigger('Region:showClusterSection');
+      if(!this.ui.clusterLink.parent().hasClass('active')){
+        this.ui.toplogyLink.parent().removeClass('active');
+        this.ui.clusterLink.parent().addClass('active');
+        vent.trigger('Region:showClusterSection');
+      }
+    },
+
+    evRefresh: function(){
+      if(this.ui.toplogyLink.parent().hasClass('active')){
+        vent.trigger('Region:showTopologySection');
+      } else {
+        vent.trigger('Region:showClusterSection');
+      }
     }
 
   });

+ 34 - 18
contrib/views/storm/src/main/resources/styles/default.css

@@ -1,20 +1,6 @@
 /**
-* 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.
-*/
+ * CSS Goes here
+ */
 
 /* Generic */
 /*body {
@@ -30,6 +16,16 @@ a:hover, a:focus {
   text-decoration: none;
 }
 
+fieldset {
+  border: 1px solid #CCC;
+  margin-bottom: 20px;
+}
+
+legend {
+  font-size: 13px;
+  padding: 0 7px;
+}
+
 [data-error]{
   color: red;
 }
@@ -72,6 +68,11 @@ a:hover, a:focus {
   border-left: 4px solid transparent;
 }
 
+.rebalance-modal .modal-body {
+  height: 470px;
+  overflow: auto;
+}
+
 /*.tab-content>.tab-pane {
   padding: 0 20px;
 }*/
@@ -117,7 +118,7 @@ a:hover, a:focus {
   background: green 50% 50% repeat-x;
 }
 
-.topology-modal .modal-header {
+.modal-header {
   color:#53c749;
 }
 
@@ -286,7 +287,7 @@ border-bottom: 0;
 }
 
 .breadcrumb {
-  line-height: 52px;
+  line-height: 35px;
   margin-left: 25px;
   font-size: 12px;
   color: #666;
@@ -309,4 +310,19 @@ th.sortable > div > a {
 table tr.recent > td {
   background-color: #3FC846;
   color: #fff;
+}
+.last-refreshed {
+  float: right;
+  font-size: 12px;
+  margin-right: 5px;
+  margin-top: 35px;
+  color: #8B8787;
+}
+.refresh-icon {
+  padding-left: 10px;
+  font-size: 1.5em;
+  margin-left: 7px;
+  color: #5cb85c;
+  border-left: 1px #BBB solid;
+  cursor: pointer;
 }

+ 4 - 4
contrib/views/storm/src/main/resources/templates/cluster/clusterSummary.html

@@ -31,7 +31,7 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
                 <div class="panel-heading" role="tab" id="headingOne">
                     <h6 class="panel-title">
                         <a data-toggle="collapse" href="#collapseNimbSum" aria-expanded="true" aria-controls="collapseNimbSum" class="">
-                        <i class="fa fa-caret-down"></i> {{tt "h.nimbusSummary"}}</a>
+                        <i class="fa fa-minus-square"></i> {{tt "h.nimbusSummary"}}</a>
                     </h6>
                 </div>
                 <div id="collapseNimbSum" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne" aria-expanded="true">
@@ -51,7 +51,7 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
                 <div class="panel-heading" role="tab" id="headingTwo">
                     <h6 class="panel-title">
                         <a data-toggle="collapse" href="#collapseSubSum" aria-expanded="true" aria-controls="collapseSubSum">
-                        <i class="fa fa-caret-down"></i> {{tt "h.supervisorSummary"}}</a>
+                        <i class="fa fa-minus-square"></i> {{tt "h.supervisorSummary"}}</a>
                     </h6>
                 </div>
                 <div id="collapseSubSum" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
@@ -71,10 +71,10 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
                 <div class="panel-heading" role="tab" id="headingThree">
                     <h6 class="panel-title">
                         <a data-toggle="collapse" href="#collapseNbs" aria-expanded="true" aria-controls="collapseNbs">
-                        <i class="fa fa-caret-down"></i> {{tt "h.nimbusConfiguration"}}</a>
+                        <i class="fa fa-plus-square"></i> {{tt "h.nimbusConfiguration"}}</a>
                     </h6>
                 </div>
-                <div id="collapseNbs" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
+                <div id="collapseNbs" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
                     <div class="panel-body"> <div class="row" id="nbsConfigTable"> </div> </div>
                 </div>
             </div>

+ 6 - 0
contrib/views/storm/src/main/resources/templates/site/header.html

@@ -19,5 +19,11 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
     <li role="presentation" class="active"><a role="tab" data-toggle="tab" data-id="topology">{{tt 'h.topologies'}}</a></li>
     <li role="presentation"><a role="tab" data-toggle="tab" data-id="cluster">{{tt 'h.cluster'}}</a></li>
     <span class="breadcrumb displayNone"><a href="#!/topology">{{tt 'h.topologies'}}</a> <i class="fa fa-angle-double-right seperator"></i><span id="breadcrumbName"></span></span>
+    <span class="last-refreshed">Last refreshed @
+      <span id="refreshTime"></span>
+      <span class="refresh-icon" id="refresh" data-toggle="tooltip" title="Refresh Now">
+        <i class="fa fa-refresh"></i>
+      </span>
+    </span>
   </ul>
 </div>

+ 26 - 0
contrib/views/storm/src/main/resources/templates/topology/rebalanceForm.html

@@ -0,0 +1,26 @@
+<form class="form-horizontal">
+  <fieldset>
+    <legend>Worker</legend>
+    <div class="" data-fields="workers"></div>
+  </fieldset>
+  {{#if showParallelism}}
+  <fieldset>
+      <legend>Parallelism</legend>
+    {{#if spoutFlag}}
+      {{#each spoutData}}
+        <div class="" data-fields="{{this}}"></div>
+      {{/each}}
+
+    {{/if}}
+    {{#if boltFlag}}
+      {{#each boltData}}
+        <div class="" data-fields="{{this}}"></div>
+      {{/each}}
+    {{/if}}
+  </fieldset>
+  {{/if}}
+  <fieldset>
+    <legend>Wait Time</legend>
+    <div class="" data-fields="waitTime"></div>
+  </fieldset>
+</form>

+ 6 - 6
contrib/views/storm/src/main/resources/templates/topology/topologyDetail.html

@@ -60,10 +60,10 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
         <div class="panel-heading" role="tab">
           <h6 class="panel-title">
             <a class="collapsed" data-toggle="collapse" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
-            <i class="fa fa-caret-down"></i> {{tt 'lbl.spouts'}}</a>
+            <i class="fa fa-plus-square"></i> {{tt 'lbl.spouts'}}</a>
           </h6>
         </div>
-        <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
+        <div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
           <div class="panel-body" id="SpoutsTable"></div>
         </div>
       </div>
@@ -73,10 +73,10 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
       <div class="panel panel-default">
         <div class="panel-heading" role="tab" id="headingTwo">
           <h6 class="panel-title">
-            <a class="collapsed" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"><span class="fa fa-caret-down"></span> {{tt 'lbl.bolts'}}</a>
+            <a class="collapsed" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"><span class="fa fa-plus-square"></span> {{tt 'lbl.bolts'}}</a>
           </h6>
         </div>
-        <div id="collapseTwo" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingTwo">
+        <div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
           <div class="panel-body">
             <div class="row statistics-row">
               <div id="BoltsSummaryTable"></div>
@@ -90,10 +90,10 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
       <div class="panel panel-default">
         <div class="panel-heading" role="tab" id="headingThree">
           <h6 class="panel-title">
-            <a class="collapsed" data-toggle="collapse" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree"><span class="fa fa-caret-down"></span> {{tt 'lbl.topologyConfig'}}</a>
+            <a class="collapsed" data-toggle="collapse" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree"><span class="fa fa-plus-square"></span> {{tt 'lbl.topologyConfig'}}</a>
           </h6>
         </div>
-        <div id="collapseThree" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingThree">
+        <div id="collapseThree" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
           <div class="panel-body">
             <div class="row statistics-row">
               <div id="TopologyConfigTable"></div>

+ 4 - 22
contrib/views/storm/src/main/resources/templates/topology/topologyForm.html

@@ -1,24 +1,6 @@
-<!--
-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. Kerberos, LDAP, Custom. Binary/Htt
--->
 <form class="form-horizontal">
-  <fieldset>
-    <div class="" data-fields="name"></div>
-    <div class="" data-fields="jar"></div>
-    <div class="" data-fields="topologyClass"></div>
-    <div class="" data-fields="arguments"></div>
-  </fieldset>
+  <div class="" data-fields="name"></div>
+  <div class="" data-fields="jar"></div>
+  <div class="" data-fields="topologyClass"></div>
+  <div class="" data-fields="arguments"></div>
 </form>

+ 2 - 2
contrib/views/storm/src/main/resources/templates/topology/topologySummary.html

@@ -16,8 +16,8 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
 -->
 <div class="row row-margin-bottom">
   <div class="col-md-6" data-id="summary"></div>
-  <div class="col-md-6">
+  <!-- <div class="col-md-6">
     <button class="btn btn-success btn-sm pull-right" data-id="deployBtn"> {{tt 'btn.deployNewTopology'}}  </button>
-  </div>
+  </div> -->
 </div>
 <div class="row" id="summaryTable"></div>

Some files were not shown because too many files changed in this diff