package com.wooki.components;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentEventCallback;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.corelib.base.AbstractLink;
import org.apache.tapestry5.internal.services.PageRenderQueue;
import org.apache.tapestry5.internal.util.Holder;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.PartialMarkupRenderer;
import org.apache.tapestry5.services.PartialMarkupRendererFilter;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import com.wooki.AppendPosition;
import com.wooki.MoreEventResult;
import com.wooki.WookiEventConstants;
/**
* This link will be used to append information to a zone. The position can be on top or at the
* bottom of the HTML element associated to the link.
*
* @author ccordenier
*/
public class MoreLink extends AbstractLink
{
/**
* Specify the first page index.
*/
@Parameter
private int first;
/**
* The name of the element to update.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String zone;
/**
* Where should the content be append : top or botttom
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL, value = "bottom")
private AppendPosition position;
/**
* The class of the element that shows the ajax loader image
*/
@Parameter(value = "load-more", defaultPrefix = BindingConstants.LITERAL)
private String loaderClass;
@Inject
private ComponentResources resources;
@Inject
private JavaScriptSupport renderSupport;
@Inject
private TypeCoercer typeCoercer;
@Inject
private PageRenderQueue pageRenderQueue;
private String loader;
@BeginRender
void initMoreLink(MarkupWriter writer)
{
if (isDisabled()) return;
Link link = resources.createEventLink(EventConstants.ACTION, first);
writer.element("div", "class", "more-link radied");
writeLink(writer, link);
loader = renderSupport.allocateClientId("loader");
JSONObject data = new JSONObject();
data.put("elt", this.getClientId());
data.put("position", this.position.toString());
data.put("zone", this.zone);
data.put("loader", loader);
data.put("url", link.toURI());
renderSupport.addInitializerCall("initMoreLink", data);
}
@AfterRender
void afterRender(MarkupWriter writer)
{
if (isDisabled()) return;
writer.end(); // <a>
writer.element("div", "id", loader, "class", this.loaderClass, "style", "display:none;");
writer.end(); // <div> loader
writer.end(); // <div>
}
/**
* When more is used via Javascript, href attribute is update via this method.
*
* @return the object to render.
*/
@SuppressWarnings("unchecked")
@OnEvent(value = EventConstants.ACTION)
public Object updateContext(final int page)
{
// Get the new context
final Holder<MoreEventResult> holder = Holder.create();
ComponentEventCallback callback = new ComponentEventCallback()
{
public boolean handleResult(Object result)
{
if (result == null) { return true; }
holder.put(typeCoercer.coerce(result, MoreEventResult.class));
return true;
}
};
resources.triggerEvent(WookiEventConstants.UPDATE_MORE_CONTEXT, new Object[]
{ page }, callback);
// Add results in JSON reply
pageRenderQueue.addPartialMarkupRendererFilter(new PartialMarkupRendererFilter()
{
public void renderMarkup(MarkupWriter writer, JSONObject reply,
PartialMarkupRenderer renderer)
{
if (holder.hasValue())
{
Link link = resources.createEventLink(EventConstants.ACTION, page + 1);
reply.put("href", link.toURI());
reply.put("hasMore", holder.get().hasMore());
}
else
{
reply.put("empty", true);
}
renderer.renderMarkup(writer, reply);
}
});
// Check if there is more data to render
if (holder.hasValue()) { return holder.get().getRenderable(); }
return null;
}
}