azkaban-developers

Merge pull request #136 from davidzchen/ui-tweaks Connect

2/5/2014 3:58:11 AM
2.5.0-rc1

Details

build.xml 8(+4 -4)

diff --git a/build.xml b/build.xml
index c195aaa..ae0373f 100644
--- a/build.xml
+++ b/build.xml
@@ -91,7 +91,7 @@
 		<mkdir dir="${dist.dust.dir}" />
 		<exec dir="${dust.src.dir}" executable="make" failonerror="true"/>
 		<copy todir="${dist.dust.dir}">
-      		<fileset dir="${dust.src.dir}/obj" includes="*.js" />
+			<fileset dir="${dust.src.dir}/obj" includes="*.js" />
 		</copy>
 	</target>
 	
@@ -99,10 +99,10 @@
 		<!-- Compile LESS to CSS -->
 		<delete dir="${dist.less.dir}" />
 		<mkdir dir="${dist.less.dir}" />
-    	<exec dir="${less.src.dir}" executable="make" failonerror="true"/>
+		<exec dir="${less.src.dir}" executable="make" failonerror="true"/>
 		<copy todir="${dist.less.dir}" >
-      		<fileset dir="${less.src.dir}/obj" includes="*.css" />
-    	</copy>
+			<fileset dir="${less.src.dir}/obj" includes="*.css" />
+		</copy>
 	</target>
 	
 	<target name="repo.file" depends="git.info" description="Create a file to reference the git commit">
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index cc6940b..104d167 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -32,6 +32,8 @@
 		<script type="text/javascript" src="${context}/js/flowstats.js"></script>
 		
 		<script type="text/javascript" src="${context}/js/azkaban/view/time-graph.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/util/schedule.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/view/schedule-sla.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow-stats.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow-execute-dialog.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/flow.js"></script>
@@ -166,6 +168,7 @@
 	#parse ("azkaban/webapp/servlet/velocity/invalidsessionmodal.vm")
 	#parse ("azkaban/webapp/servlet/velocity/flowexecutionpanel.vm")
 	#parse ("azkaban/webapp/servlet/velocity/messagedialog.vm")
+	#parse ("azkaban/webapp/servlet/velocity/slapanel.vm")
 		</div><!-- /.container -->
 #end
 	</body>
diff --git a/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
index 1aa2610..4eac2cc 100644
--- a/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/scheduledflowpage.vm
@@ -26,7 +26,9 @@
 		<script type="text/javascript" src="${context}/js/moment.min.js"></script>
 		<script type="text/javascript" src="${context}/js/bootstrap-datetimepicker.min.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/table-sort.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/view/schedule-sla.js"></script>
 		<script type="text/javascript" src="${context}/js/azkaban/view/scheduled.js"></script>
+		<script type="text/javascript" src="${context}/js/azkaban/util/schedule.js"></script>
 		<script type="text/javascript">
 			var contextURL = "${context}";
 			var currentTime = ${currentTime};
@@ -106,51 +108,9 @@
 			</div><!-- /row -->
 
   ## Set SLA modal.
+  
+	#parse ("azkaban/webapp/servlet/velocity/slapanel.vm")
 
-			<div class="modal modal-wide" id="sla-options">
-				<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">SLA Options</h4>
-						</div>
-						<div class="modal-body">
-							<h4>SLA Alert Emails</h4>
-							<fieldset>
-								<div class="form-group">
-									<label>SLA Alert Emails</label>
-									<textarea id="slaEmails" class="form-control"></textarea>
-								</div>
-							</fieldset>
-							<h4>Flow SLA Rules</h4>
-							<table class="table table-striped" id="flowRulesTbl">
-								<thead>
-									<tr>
-										<th>Flow/Job</th>
-										<th>Sla Rule</th>
-										<th>Duration</th>
-										<th>Email Action</th>
-										<th>Kill Action</th>
-									</tr>
-								</thead>
-								<tbody>
-									<tr id="addRow">
-										<td id="addRow-col" colspan="5">
-											<span class="addIcon"></span>
-											<button type="button" class="btn btn-xs btn-success">Add Row</button>
-										</td>
-									</tr>
-								</tbody>
-							</table>
-						</div>
-						<div class="modal-footer">
-							<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
-							<!--<button type="button" class="btn btn-danger" id="remove-sla-btn">Remove SLA</button>-->
-							<button type="button" class="btn btn-primary" id="set-sla-btn">Set/Change SLA</button>
-						</div>
-					</div>
-				</div>
-			</div>
 		</div>
 #end
 	</body>
diff --git a/src/java/azkaban/webapp/servlet/velocity/slapanel.vm b/src/java/azkaban/webapp/servlet/velocity/slapanel.vm
new file mode 100644
index 0000000..a4ed58b
--- /dev/null
+++ b/src/java/azkaban/webapp/servlet/velocity/slapanel.vm
@@ -0,0 +1,60 @@
+#*
+ * 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.
+*#
+
+			<div class="modal modal-wide" id="sla-options">
+				<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">SLA Options</h4>
+						</div>
+						<div class="modal-body">
+							<h4>SLA Alert Emails</h4>
+							<fieldset>
+								<div class="form-group">
+									<label>SLA Alert Emails</label>
+									<textarea id="slaEmails" class="form-control"></textarea>
+								</div>
+							</fieldset>
+							<h4>Flow SLA Rules</h4>
+							<table class="table table-striped" id="flowRulesTbl">
+								<thead>
+									<tr>
+										<th>Flow/Job</th>
+										<th>Sla Rule</th>
+										<th>Duration</th>
+										<th>Email Action</th>
+										<th>Kill Action</th>
+									</tr>
+								</thead>
+								<tbody>
+									<tr id="addRow">
+										<td id="addRow-col" colspan="5">
+											<span class="addIcon"></span>
+											<button type="button" class="btn btn-xs btn-success">Add Row</button>
+										</td>
+									</tr>
+								</tbody>
+							</table>
+						</div>
+						<div class="modal-footer">
+							<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+							<!--<button type="button" class="btn btn-danger" id="remove-sla-btn">Remove SLA</button>-->
+							<button type="button" class="btn btn-primary" id="set-sla-btn">Set/Change SLA</button>
+						</div>
+					</div>
+				</div>
+			</div>
diff --git a/src/web/js/azkaban/util/schedule.js b/src/web/js/azkaban/util/schedule.js
new file mode 100644
index 0000000..77f6954
--- /dev/null
+++ b/src/web/js/azkaban/util/schedule.js
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+function removeSched(scheduleId) {
+	var scheduleURL = contextURL + "/schedule"
+	var redirectURL = contextURL + "/schedule"
+	var requestData = {
+		"action": "removeSched", 
+		"scheduleId":scheduleId
+	};
+	var successHandler = function(data) {
+		if (data.error) {
+			$('#errorMsg').text(data.error);
+		}
+		else {
+			window.location = redirectURL;
+		}
+	};
+	$.post(scheduleURL, requestData, successHandler, "json");
+}
+
+function removeSla(scheduleId) {
+	var scheduleURL = contextURL + "/schedule"
+	var redirectURL = contextURL + "/schedule"
+	var requestData = {
+		"action": "removeSla", 
+		"scheduleId": scheduleId
+	};
+	var successHandler = function(data) {
+		if (data.error) {
+			$('#errorMsg').text(data.error)
+		}
+		else {
+			window.location = redirectURL
+		}
+	};
+	$.post(scheduleURL, requestData, successHandler, "json");
+}
diff --git a/src/web/js/azkaban/view/flow.js b/src/web/js/azkaban/view/flow.js
index 734ecd0..b5466c4 100644
--- a/src/web/js/azkaban/view/flow.js
+++ b/src/web/js/azkaban/view/flow.js
@@ -383,6 +383,7 @@ var flowStatsView;
 var flowStatsModel;
 
 var executionsTimeGraphView;
+var slaView;
 
 $(function() {
 	var selected;
@@ -433,8 +434,9 @@ $(function() {
     modelField: 'executions'
 	});
 	
+	slaView = new azkaban.ChangeSlaView({el:$('#sla-options')});
+	
 	var requestURL = contextURL + "/manager";
-
 	// Set up the Flow options view. Create a new one every time :p
 	$('#executebtn').click(function() {
 		var data = graphModel.get("data");
diff --git a/src/web/js/azkaban/view/scheduled.js b/src/web/js/azkaban/view/scheduled.js
index b182c27..101324e 100644
--- a/src/web/js/azkaban/view/scheduled.js
+++ b/src/web/js/azkaban/view/scheduled.js
@@ -16,318 +16,7 @@
 
 $.namespace('azkaban');
 
-function removeSched(scheduleId) {
-	var scheduleURL = contextURL + "/schedule"
-	var redirectURL = contextURL + "/schedule"
-	var requestData = {
-		"action": "removeSched", 
-		"scheduleId":scheduleId
-	};
-	var successHandler = function(data) {
-		if (data.error) {
-			$('#errorMsg').text(data.error);
-		}
-		else {
-			window.location = redirectURL;
-		}
-	};
-	$.post(scheduleURL, requestData, successHandler, "json");
-}
-
-function removeSla(scheduleId) {
-	var scheduleURL = contextURL + "/schedule"
-	var redirectURL = contextURL + "/schedule"
-	var requestData = {
-		"action": "removeSla", 
-		"scheduleId": scheduleId
-	};
-	var successHandler = function(data) {
-		if (data.error) {
-			$('#errorMsg').text(data.error)
-		}
-		else {
-			window.location = redirectURL
-		}
-	};
-	$.post(scheduleURL, requestData, successHandler, "json");
-}
-
 var slaView;
-azkaban.ChangeSlaView = Backbone.View.extend({
-	events: {
-		"click": "closeEditingTarget",
-		"click #set-sla-btn": "handleSetSla",	
-		"click #remove-sla-btn": "handleRemoveSla",
-		"click #addRow": "handleAddRow"
-	},
-	
-	initialize: function(setting) {
-		$('#sla-options').on('hidden.bs.modal', function() {
-			slaView.handleSlaCancel();
-		});
-	},
-	
-	handleSlaCancel: function() {
-		console.log("Clicked cancel button");
-		var scheduleURL = contextURL + "/schedule";
-		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
-		var rows = tFlowRules.rows;
-		var rowLength = rows.length
-		for (var i = 0; i < rowLength-1; i++) {
-			tFlowRules.deleteRow(0);
-		}
-	},
-	
-	initFromSched: function(scheduleId, flowName) {
-		this.scheduleId = scheduleId;
-		var scheduleURL = contextURL + "/schedule"
-		this.scheduleURL = scheduleURL;
-		
-		var indexToName = {};
-		var nameToIndex = {};
-		var indexToText = {};
-		this.indexToName = indexToName;
-		this.nameToIndex = nameToIndex;
-		this.indexToText = indexToText;
-		
-		var ruleBoxOptions = ["SUCCESS", "FINISH"];
-		this.ruleBoxOptions = ruleBoxOptions;
-		
-		var fetchScheduleData = {
-			"scheduleId": this.scheduleId, 
-			"ajax": "slaInfo"
-		};
-	
-		var successHandler = function(data) {
-			if (data.error) {
-				alert(data.error);
-				return;
-			}
-			if (data.slaEmails) {
-				$('#slaEmails').val(data.slaEmails.join());
-			}
-			
-			var allJobNames = data.allJobNames;
-			
-			indexToName[0] = "";
-			nameToIndex[flowName] = 0;
-			indexToText[0] = "flow " + flowName;
-			for (var i = 1; i <= allJobNames.length; i++) {
-				indexToName[i] = allJobNames[i-1];
-				nameToIndex[allJobNames[i-1]] = i;
-				indexToText[i] = "job " + allJobNames[i-1];
-			}
-			
-			// populate with existing settings
-			if (data.settings) {
-				var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
-				for (var setting in data.settings) {
-					var rFlowRule = tFlowRules.insertRow(0);
-					
-					var cId = rFlowRule.insertCell(-1);
-					var idSelect = document.createElement("select");
-					idSelect.setAttribute("class", "form-control");
-					for (var i in indexToName) {
-						idSelect.options[i] = new Option(indexToText[i], indexToName[i]);
-						if (data.settings[setting].id == indexToName[i]) {
-							idSelect.options[i].selected = true;
-						}
-					}								
-					cId.appendChild(idSelect);
-					
-					var cRule = rFlowRule.insertCell(-1);
-					var ruleSelect = document.createElement("select");
-					ruleSelect.setAttribute("class", "form-control");
-					for (var i in ruleBoxOptions) {
-						ruleSelect.options[i] = new Option(ruleBoxOptions[i], ruleBoxOptions[i]);
-						if (data.settings[setting].rule == ruleBoxOptions[i]) {
-							ruleSelect.options[i].selected = true;
-						}
-					}
-					cRule.appendChild(ruleSelect);
-					
-					var cDuration = rFlowRule.insertCell(-1);
-					var duration = document.createElement("input");
-					duration.type = "text";
-					duration.setAttribute("class", "form-control durationpick");
-					var rawMinutes = data.settings[setting].duration;
-					var intMinutes = rawMinutes.substring(0, rawMinutes.length-1);
-					var minutes = parseInt(intMinutes);
-					var hours = Math.floor(minutes / 60);
-					minutes = minutes % 60;
-					duration.value = hours + ":" + minutes;
-					cDuration.appendChild(duration);
-
-					var cEmail = rFlowRule.insertCell(-1);
-					var emailCheck = document.createElement("input");
-					emailCheck.type = "checkbox";
-					for (var act in data.settings[setting].actions) {
-						if (data.settings[setting].actions[act] == "EMAIL") {
-							emailCheck.checked = true;
-						}
-					}
-					cEmail.appendChild(emailCheck);
-					
-					var cKill = rFlowRule.insertCell(-1);
-					var killCheck = document.createElement("input");
-					killCheck.type = "checkbox";
-					for (var act in data.settings[setting].actions) {
-						if (data.settings[setting].actions[act] == "KILL") {
-							killCheck.checked = true;
-						}
-					}
-					cKill.appendChild(killCheck);
-					$('.durationpick').datetimepicker({
-            pickDate: false,
-            use24hours: true
-          });
-				}
-			}
-      $('.durationpick').datetimepicker({
-        pickDate: false,
-        use24hours: true
-      });
-		};
-
-		$.get(this.scheduleURL, fetchScheduleData, successHandler, "json");
-		
-		$('#sla-options').modal();
-		
-		//this.schedFlowOptions = sched.flowOptions
-		console.log("Loaded schedule info. Ready to set SLA.");
-	},
-	
-	handleRemoveSla: function(evt) {
-		console.log("Clicked remove sla button");
-		var scheduleURL = this.scheduleURL;
-		var redirectURL = this.scheduleURL;
-		var requestData = {
-			"action": "removeSla", 
-			"scheduleId": this.scheduleId
-		};
-		var successHandler = function(data) {
-			if (data.error) {
-				$('#errorMsg').text(data.error)
-			}
-			else {
-				window.location = redirectURL
-			}
-		};
-		$.post(scheduleURL, requestData, successHanlder, "json");
-	},
-	
-	handleSetSla: function(evt) {
-		var slaEmails = $('#slaEmails').val();
-		var settings = {};
-		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
-		for (var row = 0; row < tFlowRules.rows.length-1; row++) {
-			var rFlowRule = tFlowRules.rows[row];
-			var id = rFlowRule.cells[0].firstChild.value;
-			var rule = rFlowRule.cells[1].firstChild.value;
-			var duration = rFlowRule.cells[2].firstChild.value;
-			var email = rFlowRule.cells[3].firstChild.checked;
-			var kill = rFlowRule.cells[4].firstChild.checked;
-			settings[row] = id + "," + rule + "," + duration + "," + email + "," + kill; 
-		}
-
-		var slaData = {
-			scheduleId: this.scheduleId,
-			ajax: "setSla",			
-			slaEmails: slaEmails,
-			settings: settings
-		};
-
-		var scheduleURL = this.scheduleURL;
-		var successHandler = function(data) {
-			if (data.error) {
-				alert(data.error);
-			}
-			else {
-				tFlowRules.length = 0;
-				window.location = scheduleURL;
-			}
-		};
-		$.post(scheduleURL, slaData, successHandler, "json");
-	},
-	
-	handleAddRow: function(evt) {
-		var indexToName = this.indexToName;
-		var nameToIndex = this.nameToIndex;
-		var indexToText = this.indexToText;
-		var ruleBoxOptions = this.ruleBoxOptions;
-
-		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
-		var rFlowRule = tFlowRules.insertRow(tFlowRules.rows.length-1);
-		
-		var cId = rFlowRule.insertCell(-1);
-		var idSelect = document.createElement("select");
-		idSelect.setAttribute("class", "form-control");
-		for (var i in indexToName) {
-			idSelect.options[i] = new Option(indexToText[i], indexToName[i]);
-		}
-		cId.appendChild(idSelect);
-		
-		var cRule = rFlowRule.insertCell(-1);
-		var ruleSelect = document.createElement("select");
-		ruleSelect.setAttribute("class", "form-control");
-		for (var i in ruleBoxOptions) {
-			ruleSelect.options[i] = new Option(ruleBoxOptions[i], ruleBoxOptions[i]);
-		}
-		cRule.appendChild(ruleSelect);
-		
-		var cDuration = rFlowRule.insertCell(-1);
-		var duration = document.createElement("input");
-		duration.type = "text";
-		duration.setAttribute("class", "durationpick form-control");
-		cDuration.appendChild(duration);
-
-		var cEmail = rFlowRule.insertCell(-1);
-		var emailCheck = document.createElement("input");
-		emailCheck.type = "checkbox";
-		cEmail.appendChild(emailCheck);
-		
-		var cKill = rFlowRule.insertCell(-1);
-		var killCheck = document.createElement("input");
-		killCheck.type = "checkbox";
-		cKill.appendChild(killCheck);
-		
-    $('.durationpick').datetimepicker({ 
-      pickDate: false,
-      use24hours: true
-    });
-		return rFlowRule;
-	},
-	
-	handleEditColumn: function(evt) {
-		var curTarget = evt.currentTarget;
-		if (this.editingTarget != curTarget) {
-			this.closeEditingTarget();
-			
-			var text = $(curTarget).children(".spanValue").text();
-			$(curTarget).empty();
-						
-			var input = document.createElement("input");
-			$(input).attr("type", "text");
-			$(input).css("width", "100%");
-			$(input).val(text);
-			$(curTarget).addClass("editing");
-			$(curTarget).append(input);
-			$(input).focus();
-			this.editingTarget = curTarget;
-		}
-	},
-	
-	handleRemoveColumn: function(evt) {
-		var curTarget = evt.currentTarget;
-		// Should be the table
-		var row = curTarget.parentElement.parentElement;
-		$(row).remove();
-	},
-	
-	closeEditingTarget: function(evt) {
-	}
-});
-
 var tableSorterView;
 $(function() {
 	slaView = new azkaban.ChangeSlaView({el:$('#sla-options')});
diff --git a/src/web/js/azkaban/view/schedule-sla.js b/src/web/js/azkaban/view/schedule-sla.js
new file mode 100644
index 0000000..f2f316a
--- /dev/null
+++ b/src/web/js/azkaban/view/schedule-sla.js
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+$.namespace('azkaban');
+
+azkaban.ChangeSlaView = Backbone.View.extend({
+	events: {
+		"click": "closeEditingTarget",
+		"click #set-sla-btn": "handleSetSla",	
+		"click #remove-sla-btn": "handleRemoveSla",
+		"click #addRow": "handleAddRow"
+	},
+	
+	initialize: function(setting) {
+		$('#sla-options').on('hidden.bs.modal', function() {
+			slaView.handleSlaCancel();
+		});
+	},
+	
+	handleSlaCancel: function() {
+		console.log("Clicked cancel button");
+		var scheduleURL = contextURL + "/schedule";
+		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
+		var rows = tFlowRules.rows;
+		var rowLength = rows.length
+		for (var i = 0; i < rowLength-1; i++) {
+			tFlowRules.deleteRow(0);
+		}
+	},
+	
+	initFromSched: function(scheduleId, flowName) {
+		this.scheduleId = scheduleId;
+		var scheduleURL = contextURL + "/schedule"
+		this.scheduleURL = scheduleURL;
+		
+		var indexToName = {};
+		var nameToIndex = {};
+		var indexToText = {};
+		this.indexToName = indexToName;
+		this.nameToIndex = nameToIndex;
+		this.indexToText = indexToText;
+		
+		var ruleBoxOptions = ["SUCCESS", "FINISH"];
+		this.ruleBoxOptions = ruleBoxOptions;
+		
+		var fetchScheduleData = {
+			"scheduleId": this.scheduleId, 
+			"ajax": "slaInfo"
+		};
+	
+		var successHandler = function(data) {
+			if (data.error) {
+				alert(data.error);
+				return;
+			}
+			if (data.slaEmails) {
+				$('#slaEmails').val(data.slaEmails.join());
+			}
+			
+			var allJobNames = data.allJobNames;
+			
+			indexToName[0] = "";
+			nameToIndex[flowName] = 0;
+			indexToText[0] = "flow " + flowName;
+			for (var i = 1; i <= allJobNames.length; i++) {
+				indexToName[i] = allJobNames[i-1];
+				nameToIndex[allJobNames[i-1]] = i;
+				indexToText[i] = "job " + allJobNames[i-1];
+			}
+			
+			// populate with existing settings
+			if (data.settings) {
+				var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
+				for (var setting in data.settings) {
+					var rFlowRule = tFlowRules.insertRow(0);
+					
+					var cId = rFlowRule.insertCell(-1);
+					var idSelect = document.createElement("select");
+					idSelect.setAttribute("class", "form-control");
+					for (var i in indexToName) {
+						idSelect.options[i] = new Option(indexToText[i], indexToName[i]);
+						if (data.settings[setting].id == indexToName[i]) {
+							idSelect.options[i].selected = true;
+						}
+					}								
+					cId.appendChild(idSelect);
+					
+					var cRule = rFlowRule.insertCell(-1);
+					var ruleSelect = document.createElement("select");
+					ruleSelect.setAttribute("class", "form-control");
+					for (var i in ruleBoxOptions) {
+						ruleSelect.options[i] = new Option(ruleBoxOptions[i], ruleBoxOptions[i]);
+						if (data.settings[setting].rule == ruleBoxOptions[i]) {
+							ruleSelect.options[i].selected = true;
+						}
+					}
+					cRule.appendChild(ruleSelect);
+					
+					var cDuration = rFlowRule.insertCell(-1);
+					var duration = document.createElement("input");
+					duration.type = "text";
+					duration.setAttribute("class", "form-control durationpick");
+					var rawMinutes = data.settings[setting].duration;
+					var intMinutes = rawMinutes.substring(0, rawMinutes.length-1);
+					var minutes = parseInt(intMinutes);
+					var hours = Math.floor(minutes / 60);
+					minutes = minutes % 60;
+					duration.value = hours + ":" + minutes;
+					cDuration.appendChild(duration);
+
+					var cEmail = rFlowRule.insertCell(-1);
+					var emailCheck = document.createElement("input");
+					emailCheck.type = "checkbox";
+					for (var act in data.settings[setting].actions) {
+						if (data.settings[setting].actions[act] == "EMAIL") {
+							emailCheck.checked = true;
+						}
+					}
+					cEmail.appendChild(emailCheck);
+					
+					var cKill = rFlowRule.insertCell(-1);
+					var killCheck = document.createElement("input");
+					killCheck.type = "checkbox";
+					for (var act in data.settings[setting].actions) {
+						if (data.settings[setting].actions[act] == "KILL") {
+							killCheck.checked = true;
+						}
+					}
+					cKill.appendChild(killCheck);
+					$('.durationpick').datetimepicker({
+						pickDate: false,
+						use24hours: true
+					});
+				}
+			}
+			$('.durationpick').datetimepicker({
+				pickDate: false,
+				use24hours: true
+			});
+		};
+
+		$.get(this.scheduleURL, fetchScheduleData, successHandler, "json");
+		
+		$('#sla-options').modal();
+		
+		//this.schedFlowOptions = sched.flowOptions
+		console.log("Loaded schedule info. Ready to set SLA.");
+	},
+	
+	handleRemoveSla: function(evt) {
+		console.log("Clicked remove sla button");
+		var scheduleURL = this.scheduleURL;
+		var redirectURL = this.scheduleURL;
+		var requestData = {
+			"action": "removeSla", 
+			"scheduleId": this.scheduleId
+		};
+		var successHandler = function(data) {
+			if (data.error) {
+				$('#errorMsg').text(data.error)
+			}
+			else {
+				window.location = redirectURL
+			}
+		};
+		$.post(scheduleURL, requestData, successHanlder, "json");
+	},
+	
+	handleSetSla: function(evt) {
+		var slaEmails = $('#slaEmails').val();
+		var settings = {};
+		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
+		for (var row = 0; row < tFlowRules.rows.length-1; row++) {
+			var rFlowRule = tFlowRules.rows[row];
+			var id = rFlowRule.cells[0].firstChild.value;
+			var rule = rFlowRule.cells[1].firstChild.value;
+			var duration = rFlowRule.cells[2].firstChild.value;
+			var email = rFlowRule.cells[3].firstChild.checked;
+			var kill = rFlowRule.cells[4].firstChild.checked;
+			settings[row] = id + "," + rule + "," + duration + "," + email + "," + kill; 
+		}
+
+		var slaData = {
+			scheduleId: this.scheduleId,
+			ajax: "setSla",			
+			slaEmails: slaEmails,
+			settings: settings
+		};
+
+		var scheduleURL = this.scheduleURL;
+		var successHandler = function(data) {
+			if (data.error) {
+				alert(data.error);
+			}
+			else {
+				tFlowRules.length = 0;
+				window.location = scheduleURL;
+			}
+		};
+		$.post(scheduleURL, slaData, successHandler, "json");
+	},
+	
+	handleAddRow: function(evt) {
+		var indexToName = this.indexToName;
+		var nameToIndex = this.nameToIndex;
+		var indexToText = this.indexToText;
+		var ruleBoxOptions = this.ruleBoxOptions;
+
+		var tFlowRules = document.getElementById("flowRulesTbl").tBodies[0];
+		var rFlowRule = tFlowRules.insertRow(tFlowRules.rows.length-1);
+		
+		var cId = rFlowRule.insertCell(-1);
+		var idSelect = document.createElement("select");
+		idSelect.setAttribute("class", "form-control");
+		for (var i in indexToName) {
+			idSelect.options[i] = new Option(indexToText[i], indexToName[i]);
+		}
+		cId.appendChild(idSelect);
+		
+		var cRule = rFlowRule.insertCell(-1);
+		var ruleSelect = document.createElement("select");
+		ruleSelect.setAttribute("class", "form-control");
+		for (var i in ruleBoxOptions) {
+			ruleSelect.options[i] = new Option(ruleBoxOptions[i], ruleBoxOptions[i]);
+		}
+		cRule.appendChild(ruleSelect);
+		
+		var cDuration = rFlowRule.insertCell(-1);
+		var duration = document.createElement("input");
+		duration.type = "text";
+		duration.setAttribute("class", "durationpick form-control");
+		cDuration.appendChild(duration);
+
+		var cEmail = rFlowRule.insertCell(-1);
+		var emailCheck = document.createElement("input");
+		emailCheck.type = "checkbox";
+		cEmail.appendChild(emailCheck);
+		
+		var cKill = rFlowRule.insertCell(-1);
+		var killCheck = document.createElement("input");
+		killCheck.type = "checkbox";
+		cKill.appendChild(killCheck);
+		
+		$('.durationpick').datetimepicker({ 
+			pickDate: false,
+			use24hours: true
+		});
+		return rFlowRule;
+	},
+	
+	handleEditColumn: function(evt) {
+		var curTarget = evt.currentTarget;
+		if (this.editingTarget != curTarget) {
+			this.closeEditingTarget();
+			
+			var text = $(curTarget).children(".spanValue").text();
+			$(curTarget).empty();
+						
+			var input = document.createElement("input");
+			$(input).attr("type", "text");
+			$(input).css("width", "100%");
+			$(input).val(text);
+			$(curTarget).addClass("editing");
+			$(curTarget).append(input);
+			$(input).focus();
+			this.editingTarget = curTarget;
+		}
+	},
+	
+	handleRemoveColumn: function(evt) {
+		var curTarget = evt.currentTarget;
+		// Should be the table
+		var row = curTarget.parentElement.parentElement;
+		$(row).remove();
+	},
+	
+	closeEditingTarget: function(evt) {
+	}
+});