Index: /issm/trunk-jpl/src/m/classes/clusters/generic.js
===================================================================
--- /issm/trunk-jpl/src/m/classes/clusters/generic.js	(revision 20822)
+++ /issm/trunk-jpl/src/m/classes/clusters/generic.js	(revision 20823)
@@ -46,16 +46,16 @@
 			fclose(fid);
 	} //}}}
-	this.UploadAndRun = function (md,callbackfunction,fid,toolkitsstring,solutionstring,name,runtimename) { //{{{
+	this.UploadAndRun = function (md,callbackfunction,callbackid,fid,toolkitsstring,solutionstring,name,runtimename) { //{{{
 
 		var request = new XMLHttpRequest();
-		$(".run-button").html(sprintf("%-16s", "CONNECTING...")).prop("disabled", true);
+		$(callbackid).html(sprintf("%-16s", "CONNECTING...")).prop("disabled", true);
 		request.position = 0; //Keep track of current parsing position in repsonseText
 		request.timeout = 30000;
 		request.ontimeout = function (event) { //{{{
-			$(".run-button").html(sprintf("%-16s", "RUN")).prop("disabled", false);
+			$(callbackid).html(sprintf("%-16s", "RUN")).prop("disabled", false);
 		} //}}}
 		request.upload.onprogress = function(event) { //{{{
 			var progress = (event.loaded / event.total * 100).toFixed(0);
-			$(".run-button").html(sprintf("%-20s", "UPLOADING: " + progress + "%"));
+			$(callbackid).html(sprintf("%-20s", "UPLOADING: " + progress + "%"));
         } //}}}
 		request.onprogress = function (event) { //{{{
@@ -68,9 +68,9 @@
 				endIndex = startIndex + chunkSize;
 				if (chunkSize >= 1024) { //Arbitrary maximium size of message (Must be below minimium size of model results)
-					$(".run-button").html(sprintf("%-20s", "DOWNLOADING: " + ((request.responseText.length - request.position) / chunkSize * 100).toFixed(0) + "%")).prop("disabled", true);
+					$(callbackid).html(sprintf("%-20s", "DOWNLOADING: " + ((request.responseText.length - request.position) / chunkSize * 100).toFixed(0) + "%")).prop("disabled", true);
 				}
 				else if (request.responseText.length >= endIndex) { //Ensure entire chunk is loaded
 					var responseChunk = request.responseText.slice(startIndex, endIndex);
-					$(".run-button").html(responseChunk);
+					$(callbackid).html(responseChunk);
 					request.position = endIndex;
 				}
@@ -96,5 +96,5 @@
 				console.log(e);
 			}
-			$(".run-button").html(sprintf("%-16s", "RUN")).prop("disabled", false);
+			$(callbackid).html(sprintf("%-16s", "RUN")).prop("disabled", false);
 			callbackfunction();
 		}; //}}}
Index: /issm/trunk-jpl/src/m/plot/plot_unit.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_unit.js	(revision 20822)
+++ /issm/trunk-jpl/src/m/plot/plot_unit.js	(revision 20823)
@@ -246,5 +246,5 @@
 				canvas["movieHandler"] = setInterval(function () {
 						node["movieFrame"] = canvas["movieFrame"];
-						if (canvas["moviePlay"]) {
+						if (canvas["moviePlay"] && canvas["movieIncrement"]) {
 							if (canvas["movieReverse"]) {
 								node["movieFrame"] = (((node["movieFrame"] - 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus
@@ -254,6 +254,11 @@
 							}
 						}
-						if (canvas["timeLabel"]) canvas["timeLabel"].html(node["movieTimestamps"][node["movieFrame"]].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr"));
-						if (canvas["progressBar"]) canvas["progressBar"].slider("value", node["movieFrame"]);
+						if (canvas["timeLabel"]) {
+							canvas["timeLabel"].html(node["movieTimestamps"][node["movieFrame"]].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr"));
+						}
+						if (canvas["progressBar"]) {
+							canvas["progressBar"].val(node["movieFrame"]);
+							canvas["progressBar"].slider('refresh');
+						}
 						node["buffers"] = initBuffers(gl,[node["arrays"][0],node["arrays"][1][node["movieFrame"]],node["arrays"][2]]);
 						canvas["movieFrame"] = node["movieFrame"];
@@ -261,6 +266,7 @@
 				if (canvas["progressBar"]) {
 					canvas["movieFrame"] = 0;
-					canvas["progressBar"].slider("value", 0);
-					canvas["progressBar"].slider("option", {max: node["movieLength"]-1});
+					canvas["progressBar"].val(0);
+					canvas["progressBar"].attr('max', node["movieLength"]-1);
+					canvas["progressBar"].slider('refresh');
 				}
 			}
Index: /issm/trunk-jpl/src/m/plot/slider.js
===================================================================
--- /issm/trunk-jpl/src/m/plot/slider.js	(revision 20822)
+++ /issm/trunk-jpl/src/m/plot/slider.js	(revision 20823)
@@ -1,143 +1,179 @@
-function slider() {
-	//SLIDER - Slider bar with initial value, callback on change, unique name, min/max value range, description message, fill color, widht/height, value precision, value step, and the id of the div to create the slider in.
-	//
-	//   Usage:
-	// 		slider('value',0,'callback',function(value){PlotGreenland(value,0);PlotSlr()},'name','greenland','min',0,'max',100,'message',['Remove ice: ','%'],'color','#BBBBBB','width','100%','height','24px','precision',2,'step',5,'slidersdiv','greenland-sliders');
+/*
+	Name:
+		sliderInit
+	
+	Description:
+		Initialize slider corresponding to passed selector with passed value,
+		minimum and maximum values, step (which is the increment for the 
+		slider), and callback function (which is called when slider is changed). 
+	
+		Options are passed as pairs to the function according to the usage 
+		example below.
+	
+	Usage:
+		sliderInit(
+			'selector', #some-element, 
+			'value', 0,
+			'min', -1,
+			'max', 1,
+			'step', 0.1,
+			'callback', function(value){someEngineFunction(value)}
+		);
+	
+	NOTE:	jQuery Mobile will reflect changes to slider in its input "label" 
+			based on the width of the slider. For example, step might be set to
+			0.1, but if the width of the slider is too narrow, changes to the 
+			slider will only be reflected in whole numeral increments in the
+			slider' label. That said, *all* sliders can be adjusted via the
+			"up" and "down" keys by whatever increment step is set to.
+*/
+function sliderInit(){
+	
+	// Convert arguments to options
+	var args 		= Array.prototype.slice.call(arguments);
+	var options 	= new pairoptions(args.slice());
 
-	//Convert arguments to options
-	var args = Array.prototype.slice.call(arguments);
-	var options = new pairoptions(args.slice());
+	// Recover option values
+	var selector	= options.getfieldvalue('selector', '');
+	var value 		= options.getfieldvalue('value', 0);
+	var callback 	= options.getfieldvalue('callback', function(event, ui){});
+	var min 		= options.getfieldvalue('min', 0.6 * value);
+	var max 		= options.getfieldvalue('max', 1.4 * value);
+	var step 		= options.getfieldvalue('step', 1);
 
-	//Recover option values:
-	var value = options.getfieldvalue('value',0);
-	var callback = options.getfieldvalue('callback',function(){});
-	var name = options.getfieldvalue('name','');
-	var min = options.getfieldvalue('min',0.6*value);
-	var max = options.getfieldvalue('max',1.4*value);
-	var width = options.getfieldvalue('width','auto');
-	var height = options.getfieldvalue('height',32);
-	var message = options.getfieldvalue('message','');
-	var startmessage = options.getfieldvalue('startmessage',message);
-	var middlemessage = options.getfieldvalue('middlemessage',message);
-	var endmessage = options.getfieldvalue('endmessage',message);
-	var color = options.getfieldvalue('color','#bbbbbb');
-	var precision = options.getfieldvalue('precision',3);
-	var step = options.getfieldvalue('step',1);
-	var slidersdiv = options.getfieldvalue('slidersdiv','slidersdiv');
+	/*
+		Update slider attributes.
+		
+		NOTE:	Although slider has already been created, need to call slider() 
+				in order to avoid:
+				
+					Error: cannot call methods on slider prior to 
+					initialization; attempted to call method 'refresh'
+					
+				Attempted all other methods for intialization of slider widget
+				from jQuery Mobile, and this is the only one that seemed to work
+				(see index.php for related markup).
+	*/
+	$(selector).slider();
+	$(selector).val(value);
+	$(selector).attr('min', min);
+	$(selector).attr('max', max);
+	$(selector).attr('step', step);
+	$(selector).on('slidestop', function(event, ui){
+		callback(parseFloat($(selector).val()));
+	});
 
-	$('<div class="'+name+'-slider"></div>').appendTo('#'+slidersdiv);
-	$('<div class="info'+name+'">'+startmessage[0]+value.toFixed(precision)+startmessage[1]+'</div>').appendTo('#'+slidersdiv);
-	var info=$('.info'+name);
-	$('.'+name+'-slider').slider({
-		range:'min',
-		value:value,
-		min:min,
-		max:max,
-		step:step,
-		slide:function(event,ui){
-			info.text(middlemessage[0]+ui.value.toFixed(precision)+middlemessage[1]);
-		},
-		stop:function(event,ui){
-			info.text(middlemessage[0]+ui.value.toFixed(precision)+middlemessage[1]);
-			callback(ui.value);
-			info.text(endmessage[0]+ui.value.toFixed(precision)+endmessage[1]);
+	/*
+		NOTE:	Slider must be "refreshed" after any JavaScript change to it, as
+				it is an AJAX object.
+	*/
+	$(selector).slider('refresh');
+}
+/* exported sliderInit, refreshErrorMessages */
+
+
+/*
+	Name:
+		sliderMoveInput
+	
+	Description:
+		Appends a jQuery Mobile slider input to an element whose selector 
+		adheres to the following protocol,
+		
+			destination = sliderSelector + '-value'
+			
+	Usage:
+		sliderMoveInput('#someSliderSelector');
+		
+	NOTE:	Destination element must, obviously, be hardcoded into markup for a
+			call to this function to work as expected.
+*/
+function sliderMoveInput(selector){
+	
+	$(selector).appendTo(selector + '-value');
+	$(selector).slider('refresh');
+}
+
+/*
+	value/label updated by node transient runs in plot_unit.	
+*/
+function progressInit(){
+	
+	// Convert arguments to options.
+	var args 			= Array.prototype.slice.call(arguments);
+	var options 		= new pairoptions(args.slice());
+	
+	// Recover option values
+	var sim 			= options.getfieldvalue('sim', '');
+	
+	var canvas 			= $(sim + '-canvas')[0];
+	var progressBar 	= $(sim + '-controls-slider-progress');
+	var playButton 		= $(sim + '-controls-button-play');
+	var reverseButton 	= $(sim + '-controls-button-reverse');
+	var timeText 		= $(sim + '-controls-text-time');
+	
+	/*
+		Update progress bar slider attributes.
+		
+		NOTE:	Although slider has already been created, need to call slider() 
+				in order to avoid:
+				
+					Error: cannot call methods on slider prior to 
+					initialization; attempted to call method 'refresh'
+					
+				Attempted all other methods for intialization of slider widget
+				from jQuery Mobile, and this is the only one that seemed to work
+				(see index.php for related markup).
+	*/
+	$(progressBar).slider();
+	$(progressBar).val(value);
+	$(progressBar).attr('min', 0);
+	$(progressBar).attr('max', 1);
+	$(progressBar).attr('step', 1);
+	$(progressBar).on('slidestop', function(event, ui){
+		canvas.movieIncrement = true;
+		canvas.movieFrame = parseInt($(progressBar).val());
+	});
+	$(progressBar).on('change', function(event, ui){
+		canvas.movieFrame = parseInt($(progressBar).val());
+	});
+	$(progressBar).on('slidestart', function(event, ui){
+		canvas.movieIncrement = false;	
+		canvas.movieFrame = parseInt($(progressBar).val());
+	});
+
+	/*
+		NOTE:	Slider must be "refreshed" after any JavaScript change to it, as
+				it is an AJAX object.
+	*/
+	$(progressBar).slider('refresh');
+	
+	// Attach progress bar slider to simulation.
+	canvas.progressBar = progressBar;
+
+	playButton.click(function(){
+		canvas.moviePlay = !canvas.moviePlay;
+		if (canvas.moviePlay){
+			playButton.find("span").removeClass("fa-play");
+			playButton.find("span").addClass("fa-pause");
+		}
+		else{
+			playButton.find("span").removeClass("fa-pause");
+			playButton.find("span").addClass("fa-play");
 		}
 	});
-	$('.'+name+'-slider.ui-slider').css({
-		width:'auto',
-		height:height,
-		background:color,
-		margin:'8px'
-	});
-	$('.'+name+'-slider .ui-slider-handle').css({
-		background:color,
-		height:parseInt(height)+8
-	});
-	$('.'+name+'-slider .ui-slider-range').css({
-		background:color
-	});
-}
-
-function progress() {
-	//PROGRESS - Progress bar with initial value, unique name, width/height, and the id of the div to create the slider in. One progress per canvas, value/label updated by node transient runs in plot_unit.
-	//
-	//   Usage:
-	//		progress('value',0,'name','hma','width','100%','height',sliderheight,'progressdiv','hma-progressdiv');
-	//
-	//   See also: PLOT_UNIT
 	
-	//Convert arguments to options
-	var args = Array.prototype.slice.call(arguments);
-	var options = new pairoptions(args.slice());
-	
-	var value = options.getfieldvalue('value',0);
-	var name = options.getfieldvalue('name','hma');
-	var min = options.getfieldvalue('min',0.6*value);
-	var max = options.getfieldvalue('max',1.4*value);
-	var width = options.getfieldvalue('width','auto');
-	var height = options.getfieldvalue('height',32);
-	var color = options.getfieldvalue('color','#bbbbbb');
-	var progressdiv = options.getfieldvalue('progressdiv','progressdiv');
-	
-	var canvas = $('#'+name)[0];
-	var progressbar = $('#'+name+'-progressbar');
-	var playbutton = $('#'+name+'-playbutton');
-	var reversebutton = $('#'+name+'-reversebutton');
-	var timelabel = $('#'+name+'-timelabel');
-
-	
-	playbutton.click(function() {
-		canvas.moviePlay = !canvas.moviePlay;
-		if (canvas.moviePlay) {
-			$("#playButton").html("&#10074&#10074");
+	reverseButton.click(function(){
+		canvas.movieReverse = !canvas.movieReverse;
+		if (canvas.movieReverse) {
+			reverseButton.find("span").removeClass("fa-backward");
+			reverseButton.find("span").addClass("fa-forward");
 		}
 		else {
-			$("#playButton").html("&#9654");
+			reverseButton.find("span").removeClass("fa-forward");
+			reverseButton.find("span").addClass("fa-backward");
 		}
 	});
-	reversebutton.click(function() {
-		canvas.movieReverse = !canvas.movieReverse;
-		if (canvas.movieReverse) {
-			reversebutton.html("&#9664&#9664");
-		}
-		else {
-			reversebutton.html("&#9654&#9654");
-		}
-	});
-	canvas.timeLabel = timelabel;
 	
-	$('<div class="'+name+'-progressbar bordered margin-8 padding-8"></div>').prependTo('#'+progressdiv);
-	$('.'+name+'-progressbar').slider({
-		range:'min',
-		value:0,
-		min:0,
-		max:1,
-		step:1,
-		start:function(event,ui){
-			canvas.movieFrame = ui.value;
-			canvas.movieIncrement = false;
-		},
-		slide:function(event,ui){
-			canvas.movieFrame = ui.value;
-		},
-		stop:function(event,ui){
-			canvas.movieFrame = ui.value;
-			canvas.movieIncrement = true;
-		}
-	});
-	$('.'+name+'-progressbar.ui-slider').css({
-		width:'auto',
-		height:height,
-		background:color,
-		margin:'8px'
-	});
-	$('.'+name+'-progressbar .ui-slider-handle').css({
-		background:color,
-		height:parseInt(height)+8
-	});
-	$('.'+name+'-progressbar .ui-slider-range').css({
-		background:'red'
-	});
-	
-	canvas.progressBar = $('.'+name+'-progressbar');
+	canvas.timeLabel = timeText;
 }
Index: /issm/trunk-jpl/src/m/solve/solve.js
===================================================================
--- /issm/trunk-jpl/src/m/solve/solve.js	(revision 20822)
+++ /issm/trunk-jpl/src/m/solve/solve.js	(revision 20823)
@@ -103,7 +103,13 @@
 
 	//callback function: 
-	function callbackfunction(){}; //default, do nothing if no callback requested.
-	if (options.getfieldvalue('callback',false)){
-		callbackfunction=options.getfieldvalue('callback');
+	function callbackfunction(){}; //default, do nothing if no callback function requested.
+	if (options.getfieldvalue('callbackfunction',false)){
+		callbackfunction=options.getfieldvalue('callbackfunction');
+	}
+	
+	//callback id: 
+	var callbackid = '.run-button'; //default, update .run-button elements with progress updates.
+	if (options.getfieldvalue('callbackid',false)){
+		callbackid=options.getfieldvalue('callbackid');
 	}
 
@@ -131,5 +137,5 @@
 
 		/*We are running somewhere else on a computational server. Send the buffer to that server and retrieve output: */
-		cluster.UploadAndRun(md,callbackfunction,fid,toolkitsstring,solutionstring,md.miscellaneous.name,md.priv.runtimename);
+		cluster.UploadAndRun(md,callbackfunction,callbackid,fid,toolkitsstring,solutionstring,md.miscellaneous.name,md.priv.runtimename);
 
 		return md;
