/* JQueryRenderPatch.java
Purpose:
Description:
History:
Sun Jan 17 11:48:04 TST 2010, Created by tomyeh
Copyright (C) 2010 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.zkplus.liferay;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.lang.Library;
import org.zkoss.lang.Strings;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.sys.PageRenderPatch;
import org.zkoss.zk.ui.sys.RequestInfo;
/**
* Used to patch the rendering result of a ZK portlet for Liferay.
* When using ZK portlets with Liferay under Internet Explorer, we have
* to delay the processing at the client a bit.
*
* <p>To use it, you have to specify a library property called
* ""org.zkoss.zk.portlet.PageRenderPatch.class" with this class's name
* ("org.zkoss.zkplus.liferay.JQueryRenderPatch").
*
* <p>You can further control the behavior of this patch by use of
* a library property called "org.zkoss.zkplus.liferary.jQueryPatch"
* (refer to {@link #JQUERY_PATCH} for details).
*
* @author tomyeh, sam
* @since 5.0.0
*/
public class JQueryRenderPatch implements PageRenderPatch {
private static final Logger log = LoggerFactory.getLogger(JQueryRenderPatch.class);
/** A library property to indicate how to apply the so-called jQuery patch.
* <p>Default: "500" (it means 500 milliseconds)
*
* <p>You can specify a number to indicate how many milliseconds to wait
* before replacing with the correct content.
* If negative, the patch is ignored.
*/
public static final String JQUERY_PATCH = "org.zkoss.zkplus.liferary.jQueryPatch";
private int _delay = -1;
public JQueryRenderPatch() {
final String val = Library.getProperty(JQUERY_PATCH);
try {
_delay = Integer.parseInt(val);
} catch (Throwable ex) {
log.warn("Ignored delay time specified in " + JQUERY_PATCH + ": " + val);
}
}
/** Returns the number of milliseconds to wait before replacing with
* the correct content.
* <p>Default: depends on the value defined in the {@link #JQUERY_PATCH}
* library property.
*/
public int getDelay() {
return _delay;
}
/** Sets the number of milliseconds to wait before replacing with
* the correct content.
* @see #JQUERY_PATCH
*/
public void setDelay(int delay) {
_delay = delay;
}
/** It returns an instance of StringWriter if {@link #getDelay} is non-negative,
* or null if negative (means no patch).
*/
public Writer beforeRender(RequestInfo reqInfo) {
return _delay >= 0 ? new StringWriter() : null;
//we cannot retrieve HTTP request's header so no need to
//apply the patch for particular browsers, such as ie
}
public void patchRender(RequestInfo reqInfo, Page page, Writer result, Writer out) throws IOException {
final String extid = page.getUuid() + "-ext";
String[] html = processHtml(((StringWriter) result).toString());
//we have to process CSS and append it to HEAD
out.write(html[0]);
out.write("<div id=\"");
out.write(extid);
out.write("\"></div><script>setTimeout(function(){\njQuery('#");
out.write(extid);
out.write("').append('");
//we have to use append() since it is evaluated synchronously
//while replaceWith() is not
out.write(Strings.escape(html[1], Strings.ESCAPE_JAVASCRIPT));
out.write("');},");
out.write("" + getBrowserDelay());
out.write(");</script>");
}
protected String getBrowserDelay() {
return "" + _delay;
}
protected String[] processHtml(String html) {
boolean isAppendCSS = false;
StringBuffer script = new StringBuffer(
"<script>function _zkCSS(uri){var e=document.createElement(\"LINK\");e.rel=\"stylesheet\";e.type=\"text/css\";e.href=uri;document.getElementsByTagName(\"HEAD\")[0].appendChild(e);};");
Pattern p = Pattern.compile("<link[^>]+href=[\"']?([^'\"> ]+)[\"']?[^>]*>");
StringBuffer buffer = new StringBuffer();
int parseStart = 0, scriptStart = 0, scriptEnd = 0;
for (scriptStart = html.indexOf("<script"); scriptStart != -1;) {
if (parseStart < scriptStart) {
Matcher m = p.matcher(html.substring(parseStart, scriptStart));
while (m.find()) {
isAppendCSS = true;
String uri = m.group(1);
script.append("_zkCSS('" + uri + "');");
m.appendReplacement(buffer, "");
}
m.appendTail(buffer);
}
scriptEnd = html.indexOf("</script>", scriptStart);
if (scriptEnd == -1)
break;
scriptEnd += "</script>".length();
buffer.append(html.subSequence(scriptStart, scriptEnd));
if ((scriptStart = html.indexOf("<script", scriptEnd)) != -1)
parseStart = scriptEnd;
else {
buffer.append(html.substring(scriptEnd, html.length()));
break;
}
}
String[] ret = { "", html };
if (isAppendCSS) {
script.append("</script>");
ret[0] = script.toString();
ret[1] = buffer.toString();
}
return ret;
}
}