azkaban-uncached

Details

diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 5c4f1d4..7bf96fb 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -17,14 +17,11 @@
 <!DOCTYPE html> 
 <html>
 	<head>
-#parse( "azkaban/webapp/servlet/velocity/style.vm" )
-		<script type="text/javascript" src="${context}/js/jquery/jquery-1.9.1.js"></script>    
-		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-1.10.1.custom.js"></script>
-		<script type="text/javascript" src="${context}/js/underscore-1.4.4-min.js"></script>
-		<script type="text/javascript" src="${context}/js/namespace.js"></script>
-		<script type="text/javascript" src="${context}/js/backbone-0.9.10-min.js"></script>
-		<script type="text/javascript" src="${context}/js/jquery.simplemodal-1.4.4.js"></script>
 
+#parse("azkaban/webapp/servlet/velocity/style2.vm")
+#parse("azkaban/webapp/servlet/velocity/javascript.vm")
+
+		<script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-1.10.1.custom.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.common.utils.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.date.utils.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban.context.menu.js"></script>
@@ -47,128 +44,177 @@
 			var flowId = "${flowid}";
 			var execId = "${execid}";
 		</script>
-		<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
+		<link rel="stylesheet" type="text/css" href="${context}/css/azkaban-svg.css">
+		<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css">
 	</head>
 	<body>
-#set($current_page="all")
-#set($show_schedule="false")
-
-#parse( "azkaban/webapp/servlet/velocity/nav.vm" )
-		<div class="messaging"><p id="messageClose">X</p><p id="message"></p></div>  
-		<div class="content">
-#if($errorMsg)
-				<div class="box-error-message">$errorMsg</div>
+
+#set ($current_page="all")
+#set ($show_schedule="false")
+#parse ("azkaban/webapp/servlet/velocity/nav2.vm")
+
+		<div class="container">
+
+#if ($errorMsg)
+			<div class="panel panel-danger">
+				<div class="panel-heading">Error</div>
+				<div class="panel-body">
+					$errorMsg
+				</div>
+			</div>
 #else
-#if($error_message != "null")
-				<div class="box-error-message">$error_message</div>
-#elseif($success_message != "null")
-				<div class="box-success-message">$success_message</div>
-#end
+	#if ($error_message != "null")
+			<div class="alert alert-danger">$error_message</div>
+	#elseif ($success_message != "null")
+			<div class="alert alert-success">$success_message</div>
+	#end
 
-				<div id="all-jobs-content">
-					<div class="section-hd flow-header">
-						<h2><a href="${context}/executor?execid=${execid}">Flow Execution <span>$execid</span></a></h2>
-						<div class="section-sub-hd">
-							<h4><a href="${context}/manager?project=${projectName}">Project <span>$projectName</span></a></h4>
-							<h4 class="separator">&gt;</h4>
-							<h4><a href="${context}/manager?project=${projectName}&flow=${flowid}">Flow <span>$flowid</span></a></h4>
-							<h4 class="separator">&gt;</h4>
-							<h4><a href="${context}/executor?execid=${execid}">Execution <span>$execid</span></a></h4>
-						</div>
+	## Alert message
+
+			<div class="alert alert-dismissable alert-messaging" id="messaging">
+				<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
+				<p id="messaging-message"></p>
+			</div>
+
+	## Page header
+
+			<div class="page-header">
+				<h1>
+					<a href="${context}/executor?execid=${execid}">Flow Execution <small>$execid</small></a>
+				</h1>
+			</div>
+
+	## Breadcrumb
+				
+			<ol class="breadcrumb">
+				<li><a href="${context}/manager?project=${projectName}"><strong>Project</strong> $projectName</a></li>
+				<li><a href="${context}/manager?project=${projectName}&flow=${flowid}"><strong>Flow</strong> $flowid</a></li>
+				<li><a href="${context}/executor?execid=${execid}"><strong>Execution</strong> $execid</a></li>
+			</ol>
+		
+	## Flow status
+			
+			<div class="well">
+				<div class="row" id="flow-status">
+					<div class="col-lg-6">
+						<p><strong>Status</strong> <span id="flowStatus">-</span></p>
+						<p><strong>Submit User</strong> <span id="submitUser">-</span></p>
 					</div>
-					
-					<div id="headertabs" class="headertabs">
-						<ul>
-							<li><a id="graphViewLink" href="#graph">Graph</a></li>
-							<li class="lidivider">|</li>
-							<li><a id="jobslistViewLink" href="#jobslist">Job List</a></li>
-							<li class="lidivider">|</li>
-							<li><a id="flowLogViewLink" href="#log">Flow Log</a></li>
-						</ul>
-						<ul id="actionsBtns" class="buttons">
-							<li><div id="pausebtn" class="btn2">Pause</div></li>
-							<li><div id="resumebtn" class="btn2">Resume</div></li>
-							<li><div id="cancelbtn" class="btn6">Cancel</div></li>
-							<li><div id="retrybtn" class="btn1">Retry Failed</div></li>
-							<li><div id="executebtn" class="btn1">Prepare Execution</div></li>
-						</ul>
+					<div class="col-lg-6">
+						<p><strong>Start Time</strong> <span id="startTime">-</span></p>
+						<p><strong>End Time</strong> <span id="endTime">-</span></p>
+						<p><strong>Duration</strong> <span id="duration">-</span></p>
 					</div>
-					<div id="graphView">
-						<div class="relative">
-							<div id="jobList" class="jobList">
-								<div id="filterList" class="filterList">
-									<input id="filter" class="filter" placeholder="  Job Filter" />
-								</div>
-								<div id="list" class="list">
-								</div>
-								<div id="resetPanZoomBtn" class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
-							</div>
-							<div id="svgDiv" class="svgDiv">
-								<svg id="svgGraph" class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
-								</svg>
+				</div>
+			</div>
+
+	## Tabs and buttons.
+
+			<div class="row">
+				<div class="col-lg-8">
+					<ul class="nav nav-tabs" id="headertabs">
+						<li id="graphViewLink"><a href="#graph">Graph</a></li>
+						<li id="jobslistViewLink"><a href="#jobslist">Job List</a></li>
+						<li id="flowLogViewLink"><a href="#log">Flow Log</a></li>
+					</ul>
+				</div>
+				<div class="col-lg-4">
+					<button id="pausebtn" class="btn btn-primary">Pause</button>
+					<button id="resumebtn" class="btn btn-primary">Resume</button>
+					<button id="cancelbtn" class="btn btn-danger">Cancel</button>
+					<button id="retrybtn" class="btn btn-success">Retry Failed</button>
+					<button id="executebtn" class="btn btn-success">Prepare Execution</button>
+				</div>
+			</div>
+
+	## Graph View
+
+			<div class="row" id="graphView">
+				<div class="col-lg-4">
+					<div class="panel panel-default" id="jobList">
+						<div class="panel-heading">
+							<div class="pull-right">
+								<button type="button" class="btn btn-xs btn-default" id="resetPanZoomBtn">Reset Pan Zoom</button>
 							</div>
+							Jobs
 						</div>
-					</div>
-					<div id="jobListView" class="executionInfo">
-						<table>
-							<thead>
-								<tr>
-									<th>Name</th>
-									<th class="timeline">Timeline</th>
-									<th class="date">Start Time</th>
-									<th class="date">End Time</th>
-									<th class="elapse">Elapsed</th>
-									<th class="status">Status</th>
-									<th class="logs">Logs</th>
-								</tr>
-							</thead>
-							<tbody id="executableBody">
-							</tbody>
-						</table>
-					</div>
-					<div id="flowLogView" class="logView">
-						<div class="logHeader"><div class="logButtonRow"><div id="updateLogBtn" class="btn7">Refresh</div></div></div>
-						<div class="logViewer">
-							<pre id="logSection" class="log"></pre>
+						<div class="panel-body" id="filterList">
+							<input id="filter" type="text" placeholder="Job Filter">
+							<div id="list"></div>
 						</div>
 					</div>
 				</div>
-				
-				<div id="flow-status">
-					<table class="status">
-						<tr><td class="first">Status</td><td id="flowStatus">-</td></tr>
-						<tr><td class="first">Submit User</td><td id="submitUser">-</td></tr>
-					</table>
-					<table class="time">
-						<tr><td class="first">Start Time</td><td id="startTime">-</td></tr>
-						<tr><td class="first">End Time</td><td id="endTime">-</td></tr>
-						<tr><td class="first">Duration</td><td id="duration">-</td></tr>
+				<div class="col-lg-8">
+					<div id="svgDiv" class="well">
+						<svg id="svgGraph" class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
+						</svg>
+					</div>
+				</div>
+			</div>
+	
+	## Job List View
+
+			<div class="row" id="jobListView">
+				<div class="col-lg-12">
+					<table class="table table-striped table-bordered">
+						<thead>
+							<tr>
+								<th>Name</th>
+								<th class="timeline">Timeline</th>
+								<th class="date">Start Time</th>
+								<th class="date">End Time</th>
+								<th class="elapse">Elapsed</th>
+								<th class="status">Status</th>
+								<th class="logs">Logs</th>
+							</tr>
+						</thead>
+						<tbody id="executableBody">
+						</tbody>
 					</table>
 				</div>
+			</div>
 
-#parse( "azkaban/webapp/servlet/velocity/flowexecutionpanel.vm" )
-#end
-		</div>
+	## Flow Log View
 
-		
-		<div id="messageDialog" class="modal">
-			<h3 id="messageTitle">Error</h3>
-			<div class="messageDiv">
-				<p id="messageBox"></p>
+			<div class="row" id="flowLogView">
+				<div class="col-lg-12">
+					<div class="panel panel-default">
+						<div class="panel-heading">
+							<div class="pull-right">
+								<button type="button" id="updateLogBtn" class="btn btn-xs btn-default">Refresh</button>
+							</div>
+							Flow log
+						</div>
+						<div class="panel-body">
+							<pre id="logSection" class="log"></pre>
+						</div>
+					</div>
+				</div>
 			</div>
-		</div>
-		
-		<div id="invalid-session" class="modal">
-			<h3>Invalid Session</h3>
-			<p>Session has expired. Please re-login.</p>
-			<div class="actions">
-				<a class="yes btn2" id="login-btn" href="#">Re-login</a>
+	
+	## Error message message dialog.
+
+			<div class="modal fade" id="messageDialog">
+				<div class="modal-dialog">
+					<div class="modal-content">
+						<div class="modal-header" id="messageTitle">
+							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+							<h4 class="modal-title">Error</h4>
+						</div>
+						<div class="modal-body" id="messageDiv">
+							<p id="messageBox"></p>
+						</div>
+					</div>
+				</div>
 			</div>
+
+		<div id="contextMenu"></div>
+
+	#parse ("azkaban/webapp/servlet/velocity/invalidsessionmodal.vm")
+	#parse ("azkaban/webapp/servlet/velocity/flowexecutionpanel.vm")
+	#parse ("azkaban/webapp/servlet/velocity/messagedialog.vm")
+#end
+#parse ("azkaban/webapp/servlet/velocity/footer.vm")
 		</div>
-		
-		<div id="contextMenu">
-		</div>
-		#parse( "azkaban/webapp/servlet/velocity/messagedialog.vm" )
 	</body>
 </html>
-
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index 6afe415..8f18779 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -116,7 +116,7 @@
 							</div>
 							Jobs
 						</div>
-						<div class="panel-body">
+						<div class="panel-body" id="filterList">
 							<input id="filter" type="text" placeholder="Job Filter">
 							<div id="list"></div>
 						</div>
@@ -164,25 +164,7 @@
 			<div id="contextMenu">
 			</div>
 
-	## Modal dialog to be displayed when the user sesion is invalid.
-			
-			<div class="modal fade" id="invalid-session-modal">
-				<div class="modal-dialog">
-					<div class="modal-content">
-						<div class="modal-header">
-							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-							<h4 class="modal-title">Invalid Session</h4>
-						</div>
-						<div class="modal-body">
-							<p>Session has expired. Please re-login.</p>
-						</div>
-						<div class="modal-footer">
-							<button type="button" class="btn btn-primary" id="login-btn">Re-login</button>
-						</div>
-					</div>
-				</div>
-			</div>
-
+	#parse ("azkaban/webapp/servlet/velocity/invalidsessionmodal.vm")
 	#parse ("azkaban/webapp/servlet/velocity/flowexecutionpanel.vm")
 	#parse ("azkaban/webapp/servlet/velocity/messagedialog.vm")
 #end
diff --git a/src/java/azkaban/webapp/servlet/velocity/invalidsessionmodal.vm b/src/java/azkaban/webapp/servlet/velocity/invalidsessionmodal.vm
new file mode 100644
index 0000000..f0875e4
--- /dev/null
+++ b/src/java/azkaban/webapp/servlet/velocity/invalidsessionmodal.vm
@@ -0,0 +1,34 @@
+#*
+ * Copyright 2012 LinkedIn Corp.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+*#
+
+	## Modal dialog to be displayed when the user sesion is invalid.
+			
+			<div class="modal fade" id="invalid-session-modal">
+				<div class="modal-dialog">
+					<div class="modal-content">
+						<div class="modal-header">
+							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+							<h4 class="modal-title">Invalid Session</h4>
+						</div>
+						<div class="modal-body">
+							<p>Session has expired. Please re-login.</p>
+						</div>
+						<div class="modal-footer">
+							<button type="button" class="btn btn-primary" id="login-btn">Re-login</button>
+						</div>
+					</div>
+				</div>
+			</div>
diff --git a/src/java/azkaban/webapp/servlet/velocity/joblogpage.vm b/src/java/azkaban/webapp/servlet/velocity/joblogpage.vm
index 81a116a..5f982b2 100644
--- a/src/java/azkaban/webapp/servlet/velocity/joblogpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/joblogpage.vm
@@ -107,7 +107,7 @@
 					<div class="modal-content">
 						<div class="modal-header" id="messageTitle">
 							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-							<h4 class="modal-title">Upload Project Files</h4>
+							<h4 class="modal-title">Error</h4>
 						</div>
 						<div class="modal-body" id="messageDiv">
 							<p id="messageBox"></p>
diff --git a/src/web/js/azkaban.exflow.view.js b/src/web/js/azkaban.exflow.view.js
index 377703b..b571b56 100644
--- a/src/web/js/azkaban.exflow.view.js
+++ b/src/web/js/azkaban.exflow.view.js
@@ -28,12 +28,12 @@ var handleJobMenuClick = function(action, el, pos) {
 }
 
 var statusView;
-azkaban.StatusView= Backbone.View.extend({
-	initialize : function(settings) {
+azkaban.StatusView = Backbone.View.extend({
+	initialize: function(settings) {
 		this.model.bind('change:graph', this.render, this);
 		this.model.bind('change:update', this.statusUpdate, this);
 	},
-	render : function(evt) {
+	render: function(evt) {
 		var data = this.model.get("data");
 		
 		var user = data.submitUser;
@@ -41,7 +41,8 @@ azkaban.StatusView= Backbone.View.extend({
 		
 		this.statusUpdate(evt);
 	},
-	statusUpdate : function(evt) {
+	
+	statusUpdate: function(evt) {
 		var data = this.model.get("data");
 		
 		statusItem = $("#flowStatus");
@@ -83,202 +84,188 @@ azkaban.StatusView= Backbone.View.extend({
 });
 
 var flowTabView;
-azkaban.FlowTabView= Backbone.View.extend({
-  events : {
-  	"click #graphViewLink" : "handleGraphLinkClick",
-  	"click #jobslistViewLink" : "handleJobslistLinkClick",
-  	"click #flowLogViewLink" : "handleLogLinkClick",
-  	"click #cancelbtn" : "handleCancelClick",
-  	"click #executebtn" : "handleRestartClick",
-  	"click #pausebtn" : "handlePauseClick",
-  	"click #resumebtn" : "handleResumeClick",
-  	"click #retrybtn" : "handleRetryClick"
-  },
-  initialize : function(settings) {
-  	$("#cancelbtn").hide();
-  	$("#executebtn").hide();
-  	$("#pausebtn").hide();
-  	$("#resumebtn").hide();
-  	$("#retrybtn").hide();
-  
- 	this.model.bind('change:graph', this.handleFlowStatusChange, this);
-	this.model.bind('change:update', this.handleFlowStatusChange, this);
+azkaban.FlowTabView = Backbone.View.extend({
+	events: {
+		"click #graphViewLink": "handleGraphLinkClick",
+		"click #jobslistViewLink": "handleJobslistLinkClick",
+		"click #flowLogViewLink": "handleLogLinkClick",
+		"click #cancelbtn": "handleCancelClick",
+		"click #executebtn": "handleRestartClick",
+		"click #pausebtn": "handlePauseClick",
+		"click #resumebtn": "handleResumeClick",
+		"click #retrybtn": "handleRetryClick"
+	},
 	
-  	var selectedView = settings.selectedView;
-  	if (selectedView == "jobslist") {
-  		this.handleJobslistLinkClick();
-  	}
-  	else {
-  		this.handleGraphLinkClick();
-  	}
-  },
-  render: function() {
-  	console.log("render graph");
-  },
-  handleGraphLinkClick: function(){
-  	$("#jobslistViewLink").removeClass("selected");
-  	$("#graphViewLink").addClass("selected");
-  	$("#flowLogViewLink").removeClass("selected");
-  	
-  	$("#jobListView").hide();
-  	$("#graphView").show();
-  	$("#flowLogView").hide();
-  },
-  handleJobslistLinkClick: function() {
-  	$("#graphViewLink").removeClass("selected");
-  	$("#jobslistViewLink").addClass("selected");
-  	$("#flowLogViewLink").removeClass("selected");
-  	
-  	$("#graphView").hide();
-  	$("#jobListView").show();
-  	$("#flowLogView").hide();
-  },
-  handleLogLinkClick: function() {
-  	$("#graphViewLink").removeClass("selected");
-  	$("#jobslistViewLink").removeClass("selected");
-  	$("#flowLogViewLink").addClass("selected");
-  	
-  	$("#graphView").hide();
-  	$("#jobListView").hide();
-  	$("#flowLogView").show();
-  },
-  handleFlowStatusChange: function() {
-  	var data = this.model.get("data");
-  	$("#cancelbtn").hide();
-  	$("#executebtn").hide();
-  	$("#pausebtn").hide();
-  	$("#resumebtn").hide();
-  	$("#retrybtn").hide();
-
-  	if(data.status=="SUCCEEDED") {
-  	  	$("#executebtn").show();
-  	}
-  	else if (data.status=="FAILED") {
-  		$("#executebtn").show();
-  	}
-  	else if (data.status=="FAILED_FINISHING") {
-  		$("#cancelbtn").show();
-  		$("#executebtn").hide();
-  		$("#retrybtn").show();
-  	}
-  	else if (data.status=="RUNNING") {
-  		$("#cancelbtn").show();
-  		$("#pausebtn").show();
-  	}
-  	else if (data.status=="PAUSED") {
-  		$("#cancelbtn").show();
-  		$("#resumebtn").show();
-  	}
-  	else if (data.status=="WAITING") {
-  		$("#cancelbtn").show();
-  	}
-  	else if (data.status=="KILLED") {
-  		$("#executebtn").show();
-  	}
-  },
-  handleCancelClick : function(evt) {
-    var requestURL = contextURL + "/executor";
-	ajaxCall(
-		requestURL,
-		{"execid": execId, "ajax":"cancelFlow"},
-		function(data) {
-          console.log("cancel clicked");
-          if (data.error) {
-          	showDialog("Error", data.error);
-          }
-          else {
-            showDialog("Cancelled", "Flow has been cancelled.");
-
-            setTimeout(function() {updateStatus();}, 1100);
-          }
-      	}
-      );
-  },
-  handleRetryClick : function(evt) {
-      var graphData = graphModel.get("data");
-
-      var requestURL = contextURL + "/executor";
-	  ajaxCall(
-		requestURL,
-		{"execid": execId, "ajax":"retryFailedJobs"},
-		function(data) {
-          console.log("cancel clicked");
-          if (data.error) {
-          	showDialog("Error", data.error);
-          }
-          else {
-            showDialog("Retry", "Flow has been retried.");
-            setTimeout(function() {updateStatus();}, 1100);
-          }
-      	}
-      );
-  },
-  handleRestartClick : function(evt) {
-  	var data = graphModel.get("data");
-  	var nodes = data.nodes;
-  
-    var executingData = {
-  		project: projectName,
-  		ajax: "executeFlow",
-  		flow: flowId,
-  		execid: execId
-	};
-
-  	flowExecuteDialogView.show(executingData);
-  },
-  handlePauseClick : function(evt) {
-  	  var requestURL = contextURL + "/executor";
-		ajaxCall(
-	      requestURL,
-	      {"execid": execId, "ajax":"pauseFlow"},
-	      function(data) {
-	          console.log("pause clicked");
-	          if (data.error) {
-	          	showDialog("Error", data.error);
-	          }
-	          else {
-	            showDialog("Paused", "Flow has been paused.");
-	            
-            	setTimeout(function() {updateStatus();}, 1100);
-	          }
-	      }
-      );
-  },
-  handleResumeClick : function(evt) {
-     var requestURL = contextURL + "/executor";
-     ajaxCall(
-          requestURL,
-	      {"execid": execId, "ajax":"resumeFlow"},
-	      function(data) {
-	          console.log("pause clicked");
-	          if (data.error) {
-	          	showDialog("Error", data.error);
-	          }
-	          else {
-	          	showDialog("Resumed", "Flow has been resumed.");
-            	setTimeout(function() {updateStatus();}, 1100);
-	          }
-	      }
-	  );
-  }
+	initialize: function(settings) {
+		$("#cancelbtn").hide();
+		$("#executebtn").hide();
+		$("#pausebtn").hide();
+		$("#resumebtn").hide();
+		$("#retrybtn").hide();
+	
+		this.model.bind('change:graph', this.handleFlowStatusChange, this);
+		this.model.bind('change:update', this.handleFlowStatusChange, this);
+	
+		var selectedView = settings.selectedView;
+		if (selectedView == "jobslist") {
+			this.handleJobslistLinkClick();
+		}
+		else {
+			this.handleGraphLinkClick();
+		}
+	},
+	
+	render: function() {
+		console.log("render graph");
+	},
+	
+	handleGraphLinkClick: function(){
+		$("#jobslistViewLink").removeClass("active");
+		$("#graphViewLink").addClass("active");
+		$("#flowLogViewLink").removeClass("active");
+		
+		$("#jobListView").hide();
+		$("#graphView").show();
+		$("#flowLogView").hide();
+	},
+	
+	handleJobslistLinkClick: function() {
+		$("#graphViewLink").removeClass("active");
+		$("#jobslistViewLink").addClass("active");
+		$("#flowLogViewLink").removeClass("active");
+		
+		$("#graphView").hide();
+		$("#jobListView").show();
+		$("#flowLogView").hide();
+	},
+	
+	handleLogLinkClick: function() {
+		$("#graphViewLink").removeClass("active");
+		$("#jobslistViewLink").removeClass("active");
+		$("#flowLogViewLink").addClass("active");
+		
+		$("#graphView").hide();
+		$("#jobListView").hide();
+		$("#flowLogView").show();
+	},
+	
+	handleFlowStatusChange: function() {
+		var data = this.model.get("data");
+		$("#cancelbtn").hide();
+		$("#executebtn").hide();
+		$("#pausebtn").hide();
+		$("#resumebtn").hide();
+		$("#retrybtn").hide();
+
+		if (data.status == "SUCCEEDED") {
+				$("#executebtn").show();
+		}
+		else if (data.status == "FAILED") {
+			$("#executebtn").show();
+		}
+		else if (data.status == "FAILED_FINISHING") {
+			$("#cancelbtn").show();
+			$("#executebtn").hide();
+			$("#retrybtn").show();
+		}
+		else if (data.status == "RUNNING") {
+			$("#cancelbtn").show();
+			$("#pausebtn").show();
+		}
+		else if (data.status == "PAUSED") {
+			$("#cancelbtn").show();
+			$("#resumebtn").show();
+		}
+		else if (data.status == "WAITING") {
+			$("#cancelbtn").show();
+		}
+		else if (data.status == "KILLED") {
+			$("#executebtn").show();
+		}
+	},
+	
+	handleCancelClick: function(evt) {
+		var requestURL = contextURL + "/executor";
+		var requestData = {"execid": execId, "ajax": "cancelFlow"};
+		var successHandler = function(data) {
+			console.log("cancel clicked");
+			if (data.error) {
+				showDialog("Error", data.error);
+			}
+			else {
+				showDialog("Cancelled", "Flow has been cancelled.");
+				setTimeout(function() {updateStatus();}, 1100);
+			}
+		};
+		ajaxCall(requestURL, requestData, successHandler);
+	},
+	
+	handleRetryClick: function(evt) {
+		var graphData = graphModel.get("data");
+		var requestURL = contextURL + "/executor";
+		var requestData = {"execid": execId, "ajax":"retryFailedJobs"};
+		var successHandler = function(data) {
+			console.log("cancel clicked");
+			if (data.error) {
+				showDialog("Error", data.error);
+			}
+			else {
+				showDialog("Retry", "Flow has been retried.");
+				setTimeout(function() {updateStatus();}, 1100);
+			}
+		};
+		ajaxCall(requestURL, requestData, successHandler);
+	},
+	
+	handleRestartClick: function(evt) {
+		var data = graphModel.get("data");
+		var nodes = data.nodes;
+		var executingData = {
+			project: projectName,
+			ajax: "executeFlow",
+			flow: flowId,
+			execid: execId
+		};
+		flowExecuteDialogView.show(executingData);
+	},
+	
+	handlePauseClick: function(evt) {
+		var requestURL = contextURL + "/executor";
+		var requestData = {"execid": execId, "ajax":"pauseFlow"};
+		var successHandler = function(data) {
+			console.log("pause clicked");
+			if (data.error) {
+				showDialog("Error", data.error);
+			}
+			else {
+				showDialog("Paused", "Flow has been paused.");
+				setTimeout(function() {updateStatus();}, 1100);
+			}
+		};
+		ajaxCall(requestURL, requestData, successHandler);
+	},
+	
+	handleResumeClick: function(evt) {
+		var requestURL = contextURL + "/executor";
+		var requestData = {"execid": execId, "ajax":"resumeFlow"};
+		var successHandler = function(data) {
+			console.log("pause clicked");
+			if (data.error) {
+				showDialog("Error", data.error);
+			}
+			else {
+				showDialog("Resumed", "Flow has been resumed.");
+				setTimeout(function() {updateStatus();}, 1100);
+			}
+		};
+		ajaxCall(requestURL, requestData, successHandler);
+	}
 });
 
 var showDialog = function(title, message) {
-  $('#messageTitle').text(title);
-
-  $('#messageBox').text(message);
-
-  $('#messageDialog').modal({
-      closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
-      position: ["20%",],
-      containerId: 'confirm-container',
-      containerCss: {
-        'height': '220px',
-        'width': '565px'
-      },
-      onShow: function (dialog) {
-      }
-    });
+	$('#messageTitle').text(title);
+	$('#messageBox').text(message);
+	$('#messageDialog').modal();
 }
 
 var jobListView;
@@ -287,19 +274,22 @@ var mainSvgGraphView;
 var executionListView;
 azkaban.ExecutionListView = Backbone.View.extend({
 	events: {
-//		"click .progressBox" : "handleProgressBoxClick"
+		//"click .progressBox": "handleProgressBoxClick"
 	},
+	
 	initialize: function(settings) {
 		this.model.bind('change:graph', this.renderJobs, this);
 		this.model.bind('change:update', this.updateJobs, this);
 	},
+	
 	renderJobs: function(evt) {
 		var data = this.model.get("data");
 		var lastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
 		this.updateJobRow(data.nodes);
 		this.updateProgressBar(data);
 	},
-/*	handleProgressBoxClick: function(evt) {
+
+	/*handleProgressBoxClick: function(evt) {
 		var target = evt.currentTarget;
 		var job = target.job;
 		var attempt = target.attempt;
@@ -317,6 +307,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 	
 		contextMenuView.show(evt, menu);
 	},*/
+	
 	updateJobs: function(evt) {
 		var data = this.model.get("update");
 		var lastTime = data.endTime == -1 ? (new Date()).getTime() : data.endTime;
@@ -324,6 +315,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 		this.updateJobRow(data.nodes);
 		this.updateProgressBar(this.model.get("data"));
 	},
+	
 	updateJobRow: function(nodes) {
 		var executingBody = $("#executableBody");
 		nodes.sort(function(a,b) { return a.startTime - b.startTime; });
@@ -384,7 +376,7 @@ azkaban.ExecutionListView = Backbone.View.extend({
 				}
 				
 				if (node.endTime == -1) {
-//					$("#" + node.id + "-elapse").text("0 sec");
+					//$("#" + node.id + "-elapse").text("0 sec");
 					$("#" + nodeId + "-elapse").text(getDuration(node.startTime, (new Date()).getTime()));					
 				}
 				else {
@@ -393,8 +385,9 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			}
 		}
 	},
+	
 	updateProgressBar: function(data) {
-		if(data.startTime == -1) {
+		if (data.startTime == -1) {
 			return;
 		}
 		
@@ -424,24 +417,24 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			
 			// Add all the attempts
 			if (node.pastAttempts) {
-				var logURL = contextURL + "/executor?execid=" + execId + "&job=" + node.id + "&attempt=" +  node.pastAttempts.length;
+				var logURL = contextURL + "/executor?execid=" + execId + "&job=" + node.id + "&attempt=" +	node.pastAttempts.length;
 				var aId = node.id + "-log-link";
 				$("#" + aId).attr("href", logURL);
 				elem.attempt = node.pastAttempts.length;
 				
 				// Calculate the node attempt bars
-				for(var p = 0; p < node.pastAttempts.length; ++p) {
+				for (var p = 0; p < node.pastAttempts.length; ++p) {
 					var pastAttempt = node.pastAttempts[p];
 					var pastAttemptBox = $("#" + nodeId + "-progressbar-" + p);
 					
 					var left = (pastAttempt.startTime - flowStartTime)*factor;
-					var width =  Math.max((pastAttempt.endTime - pastAttempt.startTime)*factor, 3);
+					var width =	Math.max((pastAttempt.endTime - pastAttempt.startTime)*factor, 3);
 					
 					var margin = left - offsetLeft;
 					$(pastAttemptBox).css("margin-left", left - offsetLeft);
 					$(pastAttemptBox).css("width", width);
 					
-					$(pastAttemptBox).attr("title", "attempt:" + p + "  start:" + getHourMinSec(new Date(pastAttempt.startTime)) + "  end:" + getHourMinSec(new Date(pastAttempt.endTime)));
+					$(pastAttemptBox).attr("title", "attempt:" + p + "	start:" + getHourMinSec(new Date(pastAttempt.startTime)) + "	end:" + getHourMinSec(new Date(pastAttempt.endTime)));
 					offsetLeft += width + margin;
 				}
 			}
@@ -454,9 +447,10 @@ azkaban.ExecutionListView = Backbone.View.extend({
 			
 			elem.css("margin-left", left)
 			elem.css("width", width);
-			elem.attr("title", "attempt:" + elem.attempt + "  start:" + getHourMinSec(new Date(node.startTime)) + "  end:" + getHourMinSec(new Date(node.endTime)));
+			elem.attr("title", "attempt:" + elem.attempt + "	start:" + getHourMinSec(new Date(node.startTime)) + "	end:" + getHourMinSec(new Date(node.endTime)));
 		}
 	},
+	
 	addNodeRow: function(node) {
 		var executingBody = $("#executableBody");
 		var tr = document.createElement("tr");
@@ -536,31 +530,36 @@ azkaban.FlowLogView = Backbone.View.extend({
 		var model = this.model;
 		console.log("fetchLogs offset is " + offset)
 
-		$.ajax({async:false, 
-			url:requestURL,
-			data:{"execid": execId, "ajax":"fetchExecFlowLogs", "offset": offset, "length": 50000},
-			success:
-				function(data) {
-					console.log("fetchLogs");
-					if (data.error) {
-						console.log(data.error);
+		$.ajax({
+			async: false, 
+			url: requestURL,
+			data: {
+				"execid": execId, 
+				"ajax": "fetchExecFlowLogs", 
+				"offset": offset, 
+				"length": 50000
+			},
+			success: function(data) {
+				console.log("fetchLogs");
+				if (data.error) {
+					console.log(data.error);
+				}
+				else {
+					var log = $("#logSection").text();
+					if (!log) {
+						log = data.data;
 					}
 					else {
-						var log = $("#logSection").text();
-						if (!log) {
-							log = data.data;
-						}
-						else {
-							log += data.data;
-						}
-	
-						var newOffset = data.offset + data.length;
-	
-						$("#logSection").text(log);
-						model.set({"offset": newOffset, "log": log});
-						$(".logViewer").scrollTop(9999);
+						log += data.data;
 					}
+
+					var newOffset = data.offset + data.length;
+
+					$("#logSection").text(log);
+					model.set({"offset": newOffset, "log": log});
+					$(".logViewer").scrollTop(9999);
 				}
+			}
 		});
 	}
 });
@@ -576,48 +575,57 @@ var updateStatus = function() {
 	var oldData = graphModel.get("data");
 	var nodeMap = graphModel.get("nodeMap");
 	
-	ajaxCall(
-	      requestURL,
-	      {"execid": execId, "ajax":"fetchexecflowupdate", "lastUpdateTime": updateTime},
-	      function(data) {
-	          console.log("data updated");
-	          updateTime = data.updateTime;
-	          oldData.submitTime = data.submitTime;
-	          oldData.startTime = data.startTime;
-	          oldData.endTime = data.endTime;
-	          oldData.status = data.status;
-	          
-	          for (var i = 0; i < data.nodes.length; ++i) {
-	          	var node = data.nodes[i];
-	          	var oldNode = nodeMap[node.id];
-	          	oldNode.startTime = node.startTime;
-	          	oldNode.updateTime = node.updateTime;
-	          	oldNode.endTime = node.endTime;
-	          	oldNode.status = node.status;
-	          	oldNode.attempt = node.attempt;
-	          	if (oldNode.attempt > 0) {
-	          		oldNode.pastAttempts = node.pastAttempts;
-	          	}
-	          }
-
-	          graphModel.set({"update": data});
-	          graphModel.trigger("change:update");
-	      });
+	var requestData = {
+		"execid": execId, 
+		"ajax": "fetchexecflowupdate", 
+		"lastUpdateTime": updateTime
+	};
+
+	var successHandler = function(data) {
+		console.log("data updated");
+		updateTime = data.updateTime;
+		oldData.submitTime = data.submitTime;
+		oldData.startTime = data.startTime;
+		oldData.endTime = data.endTime;
+		oldData.status = data.status;
+		
+		for (var i = 0; i < data.nodes.length; ++i) {
+			var node = data.nodes[i];
+			var oldNode = nodeMap[node.id];
+			oldNode.startTime = node.startTime;
+			oldNode.updateTime = node.updateTime;
+			oldNode.endTime = node.endTime;
+			oldNode.status = node.status;
+			oldNode.attempt = node.attempt;
+			if (oldNode.attempt > 0) {
+				oldNode.pastAttempts = node.pastAttempts;
+			}
+		}
+
+		graphModel.set({"update": data});
+		graphModel.trigger("change:update");
+	};
+	ajaxCall(requestURL, requestData, successHandler);
 }
 
 var updateTime = -1;
 var updaterFunction = function() {
 	var oldData = graphModel.get("data");
-	var keepRunning = oldData.status != "SUCCEEDED" && oldData.status != "FAILED" && oldData.status != "KILLED";
+	var keepRunning = 
+			oldData.status != "SUCCEEDED" && 
+			oldData.status != "FAILED" && 
+			oldData.status != "KILLED";
 
 	if (keepRunning) {
 		updateStatus();
 
 		var data = graphModel.get("data");
-		if (data.status == "UNKNOWN" || data.status == "WAITING" || data.status == "PREPARING") {
+		if (data.status == "UNKNOWN" || 
+				data.status == "WAITING" || 
+				data.status == "PREPARING") {
 			setTimeout(function() {updaterFunction();}, 1000);
 		}
-		else if (data.status != "SUCCEEDED" && data.status != "FAILED" ) {
+		else if (data.status != "SUCCEEDED" && data.status != "FAILED") {
 			// 5 sec updates
 			setTimeout(function() {updaterFunction();}, 5000);
 		}
@@ -632,7 +640,10 @@ var updaterFunction = function() {
 
 var logUpdaterFunction = function() {
 	var oldData = graphModel.get("data");
-	var keepRunning = oldData.status != "SUCCEEDED" && oldData.status != "FAILED" && oldData.status != "KILLED";
+	var keepRunning = 
+			oldData.status != "SUCCEEDED" && 
+			oldData.status != "FAILED" && 
+			oldData.status != "KILLED";
 	if (keepRunning) {
 		// update every 30 seconds for the logs until finished
 		flowLogView.handleUpdate();
@@ -650,9 +661,9 @@ var exNodeClickCallback = function(event) {
 	var visualizerURL = contextURL + "/pigvisualizer?execid=" + execId + "&jobid=" + jobId;
 
 	var menu = [	
-			{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
-			{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
-			{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
+		{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
+		{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+		{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
 	];
 
 	contextMenuView.show(event, menu);
@@ -665,9 +676,9 @@ var exJobClickCallback = function(event) {
 	var visualizerURL = contextURL + "/pigvisualizer?execid=" + execId + "&jobid=" + jobId;
 
 	var menu = [	
-			{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
-			{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
-			{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
+		{title: "Open Job...", callback: function() {window.location.href = requestURL;}},
+		{title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+		{title: "Visualize Job...", callback: function() {window.location.href = visualizerURL;}}
 	];
 
 	contextMenuView.show(event, menu);
@@ -700,8 +711,8 @@ var attemptRightClick = function(event) {
 	var requestURL = contextURL + "/executor?project=" + projectName + "&execid=" + execId + "&job=" + job + "&attempt=" + attempt;
 
 	var menu = [	
-			{title: "Open Attempt Log...", callback: function() {window.location.href=requestURL;}},
-			{title: "Open Attempt Log in New Window...", callback: function() {window.open(requestURL);}}
+		{title: "Open Attempt Log...", callback: function() {window.location.href=requestURL;}},
+		{title: "Open Attempt Log in New Window...", callback: function() {window.open(requestURL);}}
 	];
 
 	contextMenuView.show(event, menu);
@@ -714,66 +725,88 @@ $(function() {
 	graphModel = new azkaban.GraphModel();
 	logModel = new azkaban.LogModel();
 	
-	flowTabView = new azkaban.FlowTabView({el:$( '#headertabs'), model: graphModel});
-	mainSvgGraphView = new azkaban.SvgGraphView({el:$('#svgDiv'), model: graphModel, rightClick:  { "node": exNodeClickCallback, "edge": exEdgeClickCallback, "graph": exGraphClickCallback }});
-	jobsListView = new azkaban.JobListView({el:$('#jobList'), model: graphModel, contextMenuCallback: exJobClickCallback});
-	flowLogView = new azkaban.FlowLogView({el:$('#flowLogView'), model: logModel});
-	statusView = new azkaban.StatusView({el:$('#flow-status'), model: graphModel});
+	flowTabView = new azkaban.FlowTabView({
+		el: $('#headertabs'), 
+		model: graphModel
+	});
+	mainSvgGraphView = new azkaban.SvgGraphView({
+		el: $('#svgDiv'), 
+		model: graphModel, 
+		rightClick:	{ 
+			"node": exNodeClickCallback, 
+			"edge": exEdgeClickCallback, 
+			"graph": exGraphClickCallback 
+		}
+	});
+	jobsListView = new azkaban.JobListView({
+		el: $('#jobList'), 
+		model: graphModel, 
+		contextMenuCallback: exJobClickCallback
+	});
+	flowLogView = new azkaban.FlowLogView({
+		el: $('#flowLogView'), 
+		model: logModel
+	});
+	statusView = new azkaban.StatusView({
+		el: $('#flow-status'), 
+		model: graphModel
+	});
 	
-	executionListView = new azkaban.ExecutionListView({el: $('#jobListView'), model:graphModel});
+	executionListView = new azkaban.ExecutionListView({
+		el: $('#jobListView'), 
+		model: graphModel
+	});
 	
 	var requestURL = contextURL + "/executor";
-
-	ajaxCall(
-	      requestURL,
-	      {"execid": execId, "ajax":"fetchexecflow"},
-	      function(data) {
-	          console.log("data fetched");
-	          graphModel.set({data: data});
-	          graphModel.set({disabled: {}});
-	          graphModel.trigger("change:graph");
-	          
-	          updateTime = Math.max(updateTime, data.submitTime);
-	          updateTime = Math.max(updateTime, data.startTime);
-	          updateTime = Math.max(updateTime, data.endTime);
-	          
-	          var nodeMap = {};
-	          for (var i = 0; i < data.nodes.length; ++i) {
-	             var node = data.nodes[i];
-	             nodeMap[node.id] = node;
-	             updateTime = Math.max(updateTime, node.startTime);
-	             updateTime = Math.max(updateTime, node.endTime);
-	          }
-	          for (var i = 0; i < data.edges.length; ++i) {
-	          	 var edge = data.edges[i];
-	          	 
-	          	 if (!nodeMap[edge.target].in) {
-	          	 	nodeMap[edge.target].in = {};
-	          	 }
-	          	 var targetInMap = nodeMap[edge.target].in;
-	          	 targetInMap[edge.from] = nodeMap[edge.from];
-	          	 
-	          	 if (!nodeMap[edge.from].out) {
-	          	 	nodeMap[edge.from].out = {};
-	          	 }
-	          	 var sourceOutMap = nodeMap[edge.from].out;
-	          	 sourceOutMap[edge.target] = nodeMap[edge.target];
-	          }
-	          
-	          graphModel.set({nodeMap: nodeMap});
-	          
-	          if (window.location.hash) {
-					var hash = window.location.hash;
-					if (hash == "#jobslist") {
-						flowTabView.handleJobslistLinkClick();
-					}
-					else if (hash == "#log") {
-						flowTabView.handleLogLinkClick();
-					}
-			 }
-	          
-	      	 updaterFunction();
-	      	 logUpdaterFunction();
-	      }
-	    );
+	var requestData = {"execid": execId, "ajax":"fetchexecflow"};
+	var successHandler = function(data) {
+		console.log("data fetched");
+		graphModel.set({data: data});
+		graphModel.set({disabled: {}});
+		graphModel.trigger("change:graph");
+		
+		updateTime = Math.max(updateTime, data.submitTime);
+		updateTime = Math.max(updateTime, data.startTime);
+		updateTime = Math.max(updateTime, data.endTime);
+		
+		var nodeMap = {};
+		for (var i = 0; i < data.nodes.length; ++i) {
+			var node = data.nodes[i];
+			nodeMap[node.id] = node;
+			updateTime = Math.max(updateTime, node.startTime);
+			updateTime = Math.max(updateTime, node.endTime);
+		}
+		for (var i = 0; i < data.edges.length; ++i) {
+			var edge = data.edges[i];
+			 
+			if (!nodeMap[edge.target].in) {
+				nodeMap[edge.target].in = {};
+			}
+			var targetInMap = nodeMap[edge.target].in;
+			targetInMap[edge.from] = nodeMap[edge.from];
+			 
+			if (!nodeMap[edge.from].out) {
+				nodeMap[edge.from].out = {};
+			}
+			var sourceOutMap = nodeMap[edge.from].out;
+			sourceOutMap[edge.target] = nodeMap[edge.target];
+		}
+		
+		graphModel.set({nodeMap: nodeMap});
+		if (window.location.hash) {
+			var hash = window.location.hash;
+			if (hash == "#jobslist") {
+				flowTabView.handleJobslistLinkClick();
+			}
+			else if (hash == "#log") {
+				flowTabView.handleLogLinkClick();
+			}
+		}
+		else {
+			flowTabView.handleGraphLinkClick();
+		}
+		updaterFunction();
+		logUpdaterFunction();
+	};
+	ajaxCall(requestURL, requestData, successHandler);
 });