Index: ../trunk-jpl/src/m/classes/clusters/generic.js =================================================================== --- ../trunk-jpl/src/m/classes/clusters/generic.js (revision 20822) +++ ../trunk-jpl/src/m/classes/clusters/generic.js (revision 20823) @@ -45,18 +45,18 @@ fprintf(fid,'mpiexec -np %i %s/%s %s %s %s 2> %s.errlog >%s.outlog ',cluster.np,cluster.codepath,executable,EnumToString(solution),cluster.executionpath+'/'+dirname,modelname,modelname,modelname); 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) { //{{{ //Receive updates by parsing message length as a 32-bit hex string of form 0x*09ABCDEF)) @@ -67,11 +67,11 @@ startIndex = endIndex; 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; } } @@ -95,7 +95,7 @@ catch (e) { console.log(e); } - $(".run-button").html(sprintf("%-16s", "RUN")).prop("disabled", false); + $(callbackid).html(sprintf("%-16s", "RUN")).prop("disabled", false); callbackfunction(); }; //}}} Index: ../trunk-jpl/src/m/plot/slider.js =================================================================== --- ../trunk-jpl/src/m/plot/slider.js (revision 20822) +++ ../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())); + }); - $('
').appendTo('#'+slidersdiv); - $('
'+startmessage[0]+value.toFixed(precision)+startmessage[1]+'
').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]); - } - }); - $('.'+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 - }); + /* + NOTE: Slider must be "refreshed" after any JavaScript change to it, as + it is an AJAX object. + */ + $(selector).slider('refresh'); } +/* exported sliderInit, refreshErrorMessages */ -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 + +/* + Name: + sliderMoveInput - //Convert arguments to options - var args = Array.prototype.slice.call(arguments); - var options = new pairoptions(args.slice()); + 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){ - 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'); + $(selector).appendTo(selector + '-value'); + $(selector).slider('refresh'); +} + +/* + value/label updated by node transient runs in plot_unit. +*/ +function progressInit(){ - var canvas = $('#'+name)[0]; - var progressbar = $('#'+name+'-progressbar'); - var playbutton = $('#'+name+'-playbutton'); - var reversebutton = $('#'+name+'-reversebutton'); - var timelabel = $('#'+name+'-timelabel'); + // 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'); - playbutton.click(function() { + // Attach progress bar slider to simulation. + canvas.progressBar = progressBar; + + playButton.click(function(){ canvas.moviePlay = !canvas.moviePlay; - if (canvas.moviePlay) { - $("#playButton").html("❚❚"); + if (canvas.moviePlay){ + playButton.find("span").removeClass("fa-play"); + playButton.find("span").addClass("fa-pause"); } - else { - $("#playButton").html("▶"); + else{ + playButton.find("span").removeClass("fa-pause"); + playButton.find("span").addClass("fa-play"); } }); - reversebutton.click(function() { + + reverseButton.click(function(){ canvas.movieReverse = !canvas.movieReverse; if (canvas.movieReverse) { - reversebutton.html("◀◀"); + reverseButton.find("span").removeClass("fa-backward"); + reverseButton.find("span").addClass("fa-forward"); } else { - reversebutton.html("▶▶"); + reverseButton.find("span").removeClass("fa-forward"); + reverseButton.find("span").addClass("fa-backward"); } }); - canvas.timeLabel = timelabel; - $('
').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; +} \ No newline at end of file Index: ../trunk-jpl/src/m/plot/plot_unit.js =================================================================== --- ../trunk-jpl/src/m/plot/plot_unit.js (revision 20822) +++ ../trunk-jpl/src/m/plot/plot_unit.js (revision 20823) @@ -245,7 +245,7 @@ if (canvas["movieHandler"]) clearInterval(canvas["movieHandler"]); 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 } @@ -253,15 +253,21 @@ node["movieFrame"] = (((node["movieFrame"] + 1) % node["movieLength"]) + node["movieLength"]) % node["movieLength"]; //Handle negative modulus } } - 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"]; }, node["movieInterval"]); 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: ../trunk-jpl/src/m/solve/solve.js =================================================================== --- ../trunk-jpl/src/m/solve/solve.js (revision 20822) +++ ../trunk-jpl/src/m/solve/solve.js (revision 20823) @@ -102,10 +102,16 @@ toolkitsstring= md.toolkits.ToolkitsFile(md.miscellaneous.name + '.toolkits'); // toolkits file //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'); + } if (cluster.classname() == 'local'){ //{{{ @@ -130,7 +136,7 @@ else { //{{{ /*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;