Wednesday, October 24, 2007

modifying wicket ajax javascript output

Today I had a bit of challenge with Wicket. Wicket does a lot of ajax work for you, and most of the time you don't really have to think about things like javascript or ajax at all. However, here is the use case where Wicket didn't have me covered.


The application shows a list - and each row has an href, that if mouseover, a modalwindow pops up(this is similar to movie details coming up in netflix). Unfortuneately, wicket does not provide a built in way to "wait" on the mouseover- and I needed to prevent an inadvertent mouseover from opening the window.


I sent an email to wicket users mail list. Matej suggested I write an "IAjaxDecorator" (within about 20 minutes of my sending the email BTW) This would allow my code to add javascript code to the javascript that wicket is already emitting. Wicket code for the mouseover looks like so:

var wcall=wicketAjaxGet('?wicket:interface=:17:panelForList:datatable:rows:1:cells:1:cell:showModal2::IBehaviorListener:0:', function() { }.bind(this), function() { }.bind(this));return !wcall;


And the typical javascript to timeout looks like so:


var t=setTimeout("javascript statement",milliseconds)

and the javascript to cancel the timeout looks like this:

clearTimeout(setTimeout_variable)


So, I needed a javascript function that would deal with running the wicket function, but include all the setTimeout command for javascript. So I created this javascript:


function ExecutorWithTimeout()
{
ExecutorWithTimeout.t;
}

ExecutorWithTimeout.prototype.execute = function(func, delay){
ExecutorWithTimeout.t = setTimeout(func, delay);
};
ExecutorWithTimeout.prototype.cancel = function(){
if (ExecutorWithTimeout.t!= null)
{
clearTimeout(ExecutorWithTimeout.t);
}
};



If you can improve my javascript, feel free to let me know. It is certainly not my main skillset! Anyway, the javascript class has a static member that tracks the status of the timeout. The onmouseover for the href calls the execute function, and my href onmouseout calls the cancel() function.

So, time to wire this into wicket.
Create a new class that extends AjaxEventBehavior, such that we can return a decorator for the wicket javascript.


public abstract class AjaxEventBehaviorForTimeout extends AjaxEventBehavior {

public AjaxEventBehaviorForTimeout(String event) {
super(event);
}

@Override
protected IAjaxCallDecorator getAjaxCallDecorator() {
return new IAjaxCallDecorator(){

public CharSequence decorateOnFailureScript(CharSequence script) {
return null;
}
public CharSequence decorateOnSuccessScript(CharSequence script) {
return null;
}
public CharSequence decorateScript(CharSequence script) {
return new AppendingStringBuffer("new ExecutorWithTimeout().execute(").append(" function() { ").append(
script).append("},2000);");

}


};


Finally, wire up the onmouseout to call the javascript cancel above, first creating a behavior to attach to the link component:



public class OnMouseoutSetTimeoutBehavior extends AbstractBehavior{

@Override
public void onComponentTag(Component component, ComponentTag tag) {

tag.getAttributes().put("onmouseout", "new ExecutorWithTimeout().cancel();");
}



Then adding this behavior to the link back on the actual Page class:

link.add(new OnMouseoutSetTimeoutBehavior());

Remember you'll need an include for the above javascript if you put it in a seperate file.

1 comment:

Unknown said...

Thanks! This is exactly what I needed!