Skip to content

Instantly share code, notes, and snippets.

@royshil
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save royshil/3c225473e46efdb8f04b to your computer and use it in GitHub Desktop.
Save royshil/3c225473e46efdb8f04b to your computer and use it in GitHub Desktop.
<!--
This is an example template that implements the progress bar calls and view
-->
<html t:type="layout" title="Progress bar example"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
<p>
<t:alerts />
</p>
<div class="eg">
<t:actionlink t:id="StartOperation">Start</t:actionlink> |
<t:actionlink t:id="CancelOperation">Cancel</t:actionlink> <br/>
<t:progressbar id="myprogressbar" clientfunc="updateProgressbar"/>
<div class="progress">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
0%
</div>
</div>
<script type="text/javascript" language="JavaScript">
<!-- //
var updateProgressbar = function(v) {
if(v > 100 || v < 0) {
$('.progress').hide();
} else {
$(".progress").show();
$('.progress-bar').css('width', v+'%').attr('aria-valuenow', v).html(v+'%');
}
}
// -->
</script>
</div>
</html>
/*
This is the Tapestry component to communicate with the javascript
*/
package com.yourpackage.components;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.internal.util.CaptureResultCallback;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
@Import(library = "progressbar.js")
public class ProgressBar {
@Parameter(value = "100", defaultPrefix = BindingConstants.LITERAL)
private int period;
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String clientFunc;
@Inject
private ComponentResources resources;
@Environmental
private JavaScriptSupport javaScriptSupport;
@Inject
private Request request;
/*
Setup the client code to know what server-side function to call (the onTimer() function), how often to call it (period),
and what is the function on the client-side that should be called to update the view.
*/
@AfterRender
void afterRender(MarkupWriter writer) {
Link link = resources.createEventLink("timer");
JSONObject spec = new JSONObject();
spec.put("url", link.toAbsoluteURI());
spec.put("period", period);
spec.put("clientFunc", clientFunc);
javaScriptSupport.addScript("$.fn.myprogressbar(%s);", spec);
}
/*
When the client polls the server, in turn we poll the page for the onUpdateFromProgressBar() function, and
return the result to the client via JSON.
*/
Object onTimer() {
JSONObject spec = new JSONObject();
double value = 0.0;
CaptureResultCallback<Double> callback = new CaptureResultCallback<Double>();
resources.triggerEvent("update", new Object[] { value }, callback);
if (callback.getResult() == null) {
return spec;
}
value = callback.getResult();
spec.put("value", value);
return spec;
}
}
/*
This is a jQuery implementation of the Prototype class from Taha Hafez: http://permalink.gmane.org/gmane.comp.java.tapestry.user/85776
I also removed some unessecary stuff...
*/
(function($){
$.fn.myprogressbar = function(options)
{
var obj = this;
var settings = $.extend({
value: 0
}, options || {});
var bRunning = false;
var stopExecuter = function() {
bRunning = false;
}
var onSuccess = function(transport) {
if (typeof (transport.value) == "undefined") {
console.log("bad server response: " + json);
bRunning = false;
return;
}
this.value = transport.value;
if (settings.clientFunc) {
var func = settings.clientFunc + "(" + transport.value + ")";
eval(func);
}
if (transport.value >= 100) {
stopExecuter();
}
}
var execute = function(transport) {
if(!bRunning) bRunning = true;
console.log("starting worker loop: " + settings.url);
(function worker() {
$.ajax({
url: settings.url,
success: function(data) {
onSuccess(data);
},
complete: function() {
if(bRunning == true) {
setTimeout(worker, settings.period);
} else {
console.log("done progress bar");
}
}
});
})()
}
execute();
};
})(jQuery);
/*
This simple page shows what needs to be implemented on the server side
*/
package com.yourpackage.pages;
public class ProgressBarPage {
@Inject
ParallelExecutor executor;
@Property
@Persist
ProgressInvokable operationInvokable;
@Property
@Persist
private Future<String> futureOperation;
/*
The progress bar component will call this function whenever the client polls.
*/
double onUpdateFromProgressbar() {
return (operationInvokable!=null) ? operationInvokable.getProgress() : 101.0;
}
public Object onActionFromCancelOperation() {
if(futureOperation != null)
futureOperation.cancel(true);
operationInvokable = null;
futureOperation = null;
return this;
}
@Log
public Object onActionFromStartOperation() {
operationInvokable = new ProgressInvokable() {
double progress;
@Override
public double getProgress() {
return progress;
}
@Override
public String invoke() {
for(int i = 0; i < 100; i++) {
progress = (double)i;
Thread.sleep(100);
}
return null;
}
};
futureOperation = executor.invoke(operationInvokable);
return this;
}
}
/*
We need a simple extension of the Invokable to allow for keeping progress and retrieving it
*/
package com.yourpackage.util;
import org.apache.tapestry5.ioc.Invokable;
public interface ProgressInvokable extends Invokable<String> {
double getProgress();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment