Project

General

Profile

Bug #872 » WebRenderer.java

Thim Anneessens, 06/25/2011 10:45 AM

 
/*
* Copyright (C) 2009 Emweb bvba, Leuven, Belgium.
*
* See the LICENSE file for terms of use.
*/
package eu.webtoolkit.jwt;

import java.util.*;
import java.util.regex.*;
import java.io.*;
import java.lang.ref.*;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.http.*;
import javax.servlet.*;
import eu.webtoolkit.jwt.*;
import eu.webtoolkit.jwt.chart.*;
import eu.webtoolkit.jwt.utils.*;
import eu.webtoolkit.jwt.servlet.*;

class WebRenderer implements SlotLearnerInterface {
public WebRenderer(WebSession session) {
super();
this.session_ = session;
this.visibleOnly_ = true;
this.rendered_ = false;
this.twoPhaseThreshold_ = 5000;
this.pageId_ = 0;
this.expectedAckId_ = 0;
this.cookiesToSet_ = new ArrayList<WebRenderer.Cookie>();
this.currentFormObjects_ = new HashMap<String, WObject>();
this.currentFormObjectsList_ = "";
this.collectedJS1_ = new StringWriter();
this.collectedJS2_ = new StringWriter();
this.invisibleJS_ = new StringWriter();
this.statelessJS_ = new StringWriter();
this.beforeLoadJS_ = new StringWriter();
this.updateMap_ = new HashSet<WWidget>();
this.learning_ = false;
}

public void setTwoPhaseThreshold(int bytes) {
this.twoPhaseThreshold_ = bytes;
}

public boolean isVisibleOnly() {
return this.visibleOnly_;
}

public void setVisibleOnly(boolean how) {
this.visibleOnly_ = how;
}

public void needUpdate(WWidget w, boolean laterOnly) {
this.updateMap_.add(w);
if (!laterOnly) {
this.moreUpdates_ = true;
}
}

public void doneUpdate(WWidget w) {
this.updateMap_.remove(w);
}

public void updateFormObjects(WWebWidget source, boolean checkDescendants) {
this.formObjectsChanged_ = true;
}

public void updateFormObjectsList(WApplication app) {
if (this.formObjectsChanged_) {
this.currentFormObjects_.clear();
app.domRoot_.getFormObjects(this.currentFormObjects_);
if (app.domRoot2_ != null) {
app.domRoot2_.getFormObjects(this.currentFormObjects_);
}
}
}

public Map<String, WObject> getFormObjects() {
return this.currentFormObjects_;
}

public void saveChanges() throws IOException {
this.collectJS(this.collectedJS1_);
}

public void discardChanges() throws IOException {
this.collectJS((Writer) null);
}

public void letReloadJS(WebResponse response, boolean newSession,
boolean embedded) throws IOException {
if (!embedded) {
this.setHeaders(response, "text/javascript; charset=UTF-8");
}
response.out().append("window.location.reload(true);");
}

public final void letReloadJS(WebResponse response, boolean newSession)
throws IOException {
letReloadJS(response, newSession, false);
}

public void letReloadHTML(WebResponse response, boolean newSession)
throws IOException {
this.setHeaders(response, "text/html; charset=UTF-8");
response.out().append("<html><script type=\"text/javascript\">");
this.letReloadJS(response, newSession, true);
response.out().append("</script><body></body></html>");
}

public boolean isDirty() {
return !this.updateMap_.isEmpty() || this.formObjectsChanged_
|| this.session_.getApp().afterLoadJavaScript_.length() != 0
|| this.collectedJS1_.getBuffer().length() > 0
|| this.collectedJS2_.getBuffer().length() > 0
|| this.invisibleJS_.getBuffer().length() > 0;
}

public int getPageId() {
return this.pageId_;
}

public void serveResponse(WebResponse response) throws IOException {
switch (response.getResponseType()) {
case Update:
this.serveJavaScriptUpdate(response);
break;
case Page:
if (this.session_.getApp() != null) {
this.serveMainpage(response);
} else {
this.serveBootstrap(response);
}
break;
case Script:
this.serveMainscript(response);
break;
}
}

// public void serveError(WebResponse request, RuntimeException error) ;
public void serveError(WebResponse response, String message)
throws IOException {
boolean js = response.getResponseType() != WebRequest.ResponseType.Page;
WApplication app = this.session_.getApp();
if (!js || !(app != null)) {
response.setContentType("text/html");
response.out().append("<title>Error occurred.</title>").append(
"<h2>Error occurred.</h2>").append(
WWebWidget.escapeText(new WString(message), true)
.toString()).append('\n');
} else {
response
.out()
.append(app.getJavaScriptClass())
.append(
"._p_.quit();document.title = 'Error occurred.';document.body.innerHtml='<h2>Error occurred.</h2>' +");
DomElement.jsStringLiteral(response.out(), message, '\'');
response.out().append(";");
}
}

public void serveLinkedCss(WebResponse response) throws IOException {
WApplication app = this.session_.getApp();
response.setContentType("text/css");
if (app.getCssTheme().length() != 0) {
response.out().append("@import url(\"").append(
WApplication.getResourcesUrl()).append("/themes/").append(
app.getCssTheme()).append("/wt.css\");\n");
if (app.getEnvironment().agentIsIE()) {
response.out().append("@import url(\"").append(
WApplication.getResourcesUrl()).append("/themes/")
.append(app.getCssTheme()).append("/wt_ie.css\");\n");
}
if (app.getEnvironment().getAgent() == WEnvironment.UserAgent.IE6) {
response.out().append("@import url(\"").append(
WApplication.getResourcesUrl()).append("/themes/")
.append(app.getCssTheme()).append("/wt_ie6.css\");\n");
}
}
for (int i = 0; i < app.styleSheets_.size(); ++i) {
String url = app.styleSheets_.get(i).uri;
response.out().append("@import url(\"").append(
app.fixRelativeUrl(url)).append("\")");
if (app.styleSheets_.get(i).media.length() != 0
&& !app.styleSheets_.get(i).media.equals("all")) {
response.out().append(' ')
.append(app.styleSheets_.get(i).media);
}
response.out().append(";\n");
}
app.styleSheetsAdded_ = 0;
}

public void setCookie(final String name, final String value, int maxAge,
final String domain, final String path) {
this.cookiesToSet_.add(new WebRenderer.Cookie(name, value, path,
domain, maxAge));
}

public boolean isPreLearning() {
return this.learning_;
}

public void learningIncomplete() {
this.learningIncomplete_ = true;
}

public void ackUpdate(int updateId) {
if (updateId == this.expectedAckId_) {
this.setJSSynced(false);
++this.expectedAckId_;
}
}

public void streamRedirectJS(Writer out, String redirect)
throws IOException {
if (this.session_.getApp() != null) {
out.append("if (window.").append(
this.session_.getApp().getJavaScriptClass()).append(") ")
.append(this.session_.getApp().getJavaScriptClass())
.append("._p_.setHash('").append(
this.session_.getApp().getInternalPath()).append(
".');\n");
}
out.append("if (window.location.replace) window.location.replace('")
.append(redirect).append("');else window.location.href='")
.append(redirect).append("';\n");
}

static class Cookie {
public String name;
public String value;
public String path;
public String domain;
public int maxAge;

public Cookie(String n, String v, String p, String d, int m) {
this.name = n;
this.value = v;
this.path = p;
this.domain = d;
this.maxAge = m;
}
}

private WebSession session_;
private boolean visibleOnly_;
private boolean rendered_;
private int twoPhaseThreshold_;
private int pageId_;
private int expectedAckId_;
private List<WebRenderer.Cookie> cookiesToSet_;
private Map<String, WObject> currentFormObjects_;
private String currentFormObjectsList_;
private boolean formObjectsChanged_;

private void setHeaders(WebResponse response, final String mimeType) {
for (int i = 0; i < this.cookiesToSet_.size(); ++i) {
String cookies = "";
String value = this.cookiesToSet_.get(i).value;
cookies += DomElement.urlEncodeS(this.cookiesToSet_.get(i).name)
+ "=" + DomElement.urlEncodeS(value) + "; Version=1;";
if (this.cookiesToSet_.get(i).maxAge != -1) {
cookies += " Max-Age="
+ String.valueOf(this.cookiesToSet_.get(i).maxAge)
+ ";";
}
if (this.cookiesToSet_.get(i).domain.length() != 0) {
cookies += " Domain=" + this.cookiesToSet_.get(i).domain + ";";
}
if (this.cookiesToSet_.get(i).path.length() != 0) {
cookies += " Path=" + this.cookiesToSet_.get(i).path + ";";
}
if (cookies.length() != 0) {
response.addHeader("Set-Cookie", cookies);
}
}
this.cookiesToSet_.clear();
response.addHeader("Cache-Control", "no-cache");
response.setContentType(mimeType);
}

private void serveJavaScriptUpdate(WebResponse response) throws IOException {
this.rendered_ = true;
this.setHeaders(response, "text/javascript; charset=UTF-8");
this.collectJavaScript();
response.out().append(this.collectedJS1_.toString()).append(
this.collectedJS2_.toString()).append(
this.session_.getApp().getJavaScriptClass()).append(
"._p_.response(").append(String.valueOf(this.expectedAckId_))
.append(");");
if (response.isWebSocketRequest() || response.isWebSocketMessage()) {
this.setJSSynced(false);
}
}

private void serveMainscript(WebResponse response) throws IOException {
Configuration conf = this.session_.getController().getConfiguration();
boolean widgetset = this.session_.getType() == EntryPointType.WidgetSet;
this.setHeaders(response, "text/javascript; charset=UTF-8");
if (!widgetset) {
String redirect = this.session_.getRedirect();
if (redirect.length() != 0) {
this.streamRedirectJS(response.out(), redirect);
return;
}
}
WApplication app = this.session_.getApp();
final boolean xhtml = app.getEnvironment().getContentType() == WEnvironment.ContentType.XHTML1;
final boolean innerHtml = !xhtml || app.getEnvironment().agentIsGecko();
this.formObjectsChanged_ = true;
this.currentFormObjectsList_ = this.createFormObjectsList(app);
FileServe jquery = new FileServe(WtServlet.JQuery_js);
jquery.stream(response.out());
FileServe script = new FileServe(WtServlet.Wt_js);
script
.setCondition(
"CATCH_ERROR",
conf.getErrorReporting() != Configuration.ErrorReporting.NoErrors);
script
.setCondition(
"SHOW_STACK",
conf.getErrorReporting() == Configuration.ErrorReporting.ErrorMessageWithStack);
script.setCondition("DYNAMIC_JS", false);
script.setVar("WT_CLASS", "Wt3_1_8");
script.setVar("APP_CLASS", app.getJavaScriptClass());
script.setVar("AUTO_JAVASCRIPT", "(function(){" + app.autoJavaScript_
+ "})");
script.setCondition("STRICTLY_SERIALIZED_EVENTS", conf
.isSerializedEvents());
script.setCondition("WEB_SOCKETS", conf.isWebSockets());
script.setVar("INNER_HTML", innerHtml);
script.setVar("FORM_OBJECTS", '[' + this.currentFormObjectsList_ + ']');
script.setVar("RELATIVE_URL", WWebWidget.jsStringLiteral(this.session_
.getBootstrapUrl(response,
WebSession.BootstrapOption.ClearInternalPath)));
script.setVar("KEEP_ALIVE", String
.valueOf(conf.getSessionTimeout() / 2));
script.setVar("INITIAL_HASH", WWebWidget.jsStringLiteral(app
.getInternalPath()));
script.setVar("INDICATOR_TIMEOUT", conf.getIndicatorTimeout());
script
.setVar("SERVER_PUSH_TIMEOUT",
conf.getServerPushTimeout() * 1000);
script.setVar("PAGE_ID", this.pageId_);
script
.setVar(
"CLOSE_CONNECTION",
conf.getServerType() == Configuration.ServerType.WtHttpdServer
&& this.session_.getEnv().agentIsGecko()
&& this.session_.getEnv().getAgent().getValue() < WEnvironment.UserAgent.Firefox3_0
.getValue());
script.stream(response.out());
app.autoJavaScriptChanged_ = false;
if (!this.rendered_) {
this.serveMainAjax(response);
} else {
if (app.enableAjax_) {
this.collectedJS1_.append(this.beforeLoadJS_.toString());
this.beforeLoadJS_ = new StringWriter();
this.collectedJS1_
.append("var domRoot = ")
.append(app.domRoot_.getJsRef())
.append(
";var form = Wt3_1_8.getElement('Wt-form');domRoot.style.display = form.style.display;document.body.replaceChild(domRoot, form);");
int librariesLoaded = this.loadScriptLibraries(
this.collectedJS1_, app);
this.collectedJS1_.append(app.getNewBeforeLoadJavaScript());
this.collectedJS2_.append(
"domRoot.style.visibility = 'visible';").append(
app.getJavaScriptClass()).append(
"._p_.autoJavaScript();");
this.loadScriptLibraries(this.collectedJS2_, app,
librariesLoaded);
app.enableAjax_ = false;
}
response.out().append("window.").append(app.getJavaScriptClass())
.append("LoadWidgetTree = function(){\n");
this.visibleOnly_ = false;
this.collectJavaScript();
this.updateLoadIndicator(this.collectedJS1_, app, true);
response.out().append(this.collectedJS1_.toString()).append(
app.getJavaScriptClass()).append("._p_.response(").append(
String.valueOf(this.expectedAckId_)).append(");");
response.out().append(app.getJavaScriptClass()).append(
"._p_.update(null, 'load', null, false);").append(
this.collectedJS2_.toString()).append("};").append(
app.getJavaScriptClass()).append("._p_.setServerPush(")
.append(app.isUpdatesEnabled() ? "true" : "false").append(
");").append("$(document).ready(function() { ")
.append(app.getJavaScriptClass()).append(
"._p_.load(true);});\n");
}
}

private void serveBootstrap(WebResponse response) throws IOException {
boolean xhtml = this.session_.getEnv().getContentType() == WEnvironment.ContentType.XHTML1;
Configuration conf = this.session_.getController().getConfiguration();
FileServe boot = new FileServe(WtServlet.Boot_html);
this.setPageVars(boot);
StringWriter noJsRedirectUrl = new StringWriter();
DomElement.htmlAttributeValue(noJsRedirectUrl, this.session_
.getBootstrapUrl(response,
WebSession.BootstrapOption.KeepInternalPath)
+ "&js=no");
boot.setVar("REDIRECT_URL", noJsRedirectUrl.toString());
if (xhtml) {
boot.setVar("AUTO_REDIRECT", "");
boot.setVar("NOSCRIPT_TEXT", conf.getRedirectMessage());
} else {
boot.setVar("AUTO_REDIRECT",
"<noscript><meta http-equiv=\"refresh\" content=\"0;url="
+ noJsRedirectUrl.toString() + "\"></noscript>");
boot.setVar("NOSCRIPT_TEXT", conf.getRedirectMessage());
}
StringWriter bootStyleUrl = new StringWriter();
DomElement.htmlAttributeValue(bootStyleUrl, this.session_
.getMostRelativeUrl()
+ "&request=style");
boot.setVar("BOOT_STYLE_URL", bootStyleUrl.toString());
response.addHeader("Cache-Control", "no-cache, no-store");
response.addHeader("Expires", "-1");
String contentType = xhtml ? "application/xhtml+xml" : "text/html";
contentType += "; charset=UTF-8";
this.setHeaders(response, contentType);
this.streamBootContent(response, boot, false);
boot.stream(response.out());
this.rendered_ = false;
}

private void serveMainpage(WebResponse response) throws IOException {
++this.expectedAckId_;
++this.pageId_;
Configuration conf = this.session_.getController().getConfiguration();
WApplication app = this.session_.getApp();
if (!app.getEnvironment().hasAjax()
&& (app.internalPathIsChanged_ && !app.oldInternalPath_
.equals(app.newInternalPath_))) {
app.oldInternalPath_ = app.newInternalPath_;
this.session_.redirect(this.session_.appendSessionQuery(app
.getBookmarkUrl(app.newInternalPath_)));
}
String redirect = this.session_.getRedirect();
if (redirect.length() != 0) {
response.setStatus(302);
response.sendRedirect(redirect);
return;
}
WWebWidget mainWebWidget = app.domRoot_;
this.visibleOnly_ = true;
DomElement mainElement = mainWebWidget.createSDomElement(app);
this.rendered_ = true;
this.setJSSynced(true);
final boolean xhtml = app.getEnvironment().getContentType() == WEnvironment.ContentType.XHTML1;
String styleSheets = "";
if (app.getCssTheme().length() != 0) {
styleSheets += "<link href=\"" + WApplication.getResourcesUrl()
+ "/themes/" + app.getCssTheme()
+ "/wt.css\" rel=\"stylesheet\" type=\"text/css\""
+ (xhtml ? "/>" : ">") + "\n";
if (app.getEnvironment().agentIsIE()) {
styleSheets += "<link href=\"" + WApplication.getResourcesUrl()
+ "/themes/" + app.getCssTheme()
+ "/wt_ie.css\" rel=\"stylesheet\" type=\"text/css\""
+ (xhtml ? "/>" : ">") + "\n";
}
if (app.getEnvironment().getAgent() == WEnvironment.UserAgent.IE6) {
styleSheets += "<link href=\"" + WApplication.getResourcesUrl()
+ "/themes/" + app.getCssTheme()
+ "/wt_ie6.css\" rel=\"stylesheet\" type=\"text/css\""
+ (xhtml ? "/>" : ">") + "\n";
}
}
for (int i = 0; i < app.styleSheets_.size(); ++i) {
String url = app.styleSheets_.get(i).uri;
url = StringUtils.replace(url, '&', "&amp;");
styleSheets += "<link href=\"" + app.fixRelativeUrl(url)
+ "\" rel=\"stylesheet\" type=\"text/css\"";
if (app.styleSheets_.get(i).media.length() != 0
&& !app.styleSheets_.get(i).media.equals("all")) {
styleSheets += " media=\"" + app.styleSheets_.get(i).media
+ '"';
}
styleSheets += xhtml ? "/>" : ">";
styleSheets += "\n";
}
app.styleSheetsAdded_ = 0;
this.beforeLoadJS_ = new StringWriter();
// MODIF
StringBuilder jsBuilder=new StringBuilder(255);
for (int i = 0; i < app.scriptLibraries_.size(); ++i) {
String url = app.scriptLibraries_.get(i).uri;
url = StringUtils.replace(url, '&', "&amp;");
/*
* MODIF
*/
jsBuilder.append("<script src='" + app.fixRelativeUrl(url)+ "'></script>\n");
// styleSheets += "<script src='" + app.fixRelativeUrl(url)
// + "'></script>\n";
/*
* END MODIF
*/
this.beforeLoadJS_.append(app.scriptLibraries_.get(i).beforeLoadJS);
}
// MODIF
String scripts=jsBuilder.toString();
app.scriptLibrariesAdded_ = 0;
app.newBeforeLoadJavaScript_ = app.beforeLoadJavaScript_;
boolean hybridPage = this.session_.isProgressiveBoot()
|| this.session_.getEnv().hasAjax();
FileServe page = new FileServe(hybridPage ? WtServlet.Hybrid_html
: WtServlet.Plain_html);
this.setPageVars(page);
// MODIF
page.setVar("SCRIPTS",scripts);
page.setVar("SESSION_ID", this.session_.getSessionId());
String url = app.getEnvironment().agentIsSpiderBot()
|| conf.getSessionTracking() == Configuration.SessionTracking.CookiesURL
&& this.session_.getEnv().supportsCookies() ? this.session_
.getBookmarkUrl(app.newInternalPath_) : this.session_
.getMostRelativeUrl(app.newInternalPath_);
url = app.fixRelativeUrl(url);
url = StringUtils.replace(url, '&', "&amp;");
page.setVar("RELATIVE_URL", url);
if (conf.isInlineCss()) {
page.setVar("STYLESHEET", app.getStyleSheet().getCssText(true));
} else {
page.setVar("STYLESHEET", "");
}
page.setVar("STYLESHEETS", styleSheets);
page.setVar("TITLE", WWebWidget.escapeText(app.getTitle()).toString());
app.titleChanged_ = false;
if (hybridPage) {
response.addHeader("Cache-Control", "no-cache, no-store");
response.addHeader("Expires", "-1");
}
String contentType = xhtml ? "application/xhtml+xml" : "text/html";
contentType += "; charset=UTF-8";
this.setHeaders(response, contentType);
this.formObjectsChanged_ = true;
this.currentFormObjectsList_ = this.createFormObjectsList(app);
if (hybridPage) {
this.streamBootContent(response, page, true);
}
page.streamUntil(response.out(), "HTML");
List<DomElement.TimeoutEvent> timeouts = new ArrayList<DomElement.TimeoutEvent>();
{
EscapeOStream js = new EscapeOStream();
EscapeOStream out = new EscapeOStream(response.out());
mainElement.asHTML(out, js, timeouts);
app.afterLoadJavaScript_ = js.toString() + app.afterLoadJavaScript_;
;
app.domRoot_.doneRerender();
}
int refresh;
if (app.getEnvironment().hasAjax()) {
StringWriter str = new StringWriter();
DomElement.createTimeoutJs(str, timeouts, app);
app.doJavaScript(str.toString());
refresh = 1000000;
} else {
if (app.isQuited()) {
refresh = 1000000;
} else {
refresh = conf.getSessionTimeout() / 3;
for (int i = 0; i < timeouts.size(); ++i) {
refresh = Math
.min(refresh, 1 + timeouts.get(i).msec / 1000);
}
}
}
page.setVar("REFRESH", String.valueOf(refresh));
page.stream(response.out());
app.internalPathIsChanged_ = false;
}

private void serveMainAjax(WebResponse response) throws IOException {
Configuration conf = this.session_.getController().getConfiguration();
boolean widgetset = this.session_.getType() == EntryPointType.WidgetSet;
WApplication app = this.session_.getApp();
WWebWidget mainWebWidget = app.domRoot_;
this.visibleOnly_ = true;
app.loadingIndicatorWidget_.show();
DomElement mainElement = mainWebWidget.createSDomElement(app);
app.loadingIndicatorWidget_.hide();
app.scriptLibrariesAdded_ = app.scriptLibraries_.size();
int librariesLoaded = this.loadScriptLibraries(response.out(), app);
response.out().append('\n').append(app.getNewBeforeLoadJavaScript());
if (!widgetset) {
response.out().append("window.").append(app.getJavaScriptClass())
.append("LoadWidgetTree = function(){\n");
}
if (widgetset || !this.session_.isBootStyleResponse()) {
if (app.getCssTheme().length() != 0) {
response.out().append("Wt3_1_8").append(".addStyleSheet('")
.append(WApplication.getResourcesUrl()).append(
"/themes/").append(app.getCssTheme()).append(
"/wt.css', 'all');");
if (app.getEnvironment().agentIsIE()) {
response.out().append("Wt3_1_8").append(".addStyleSheet('")
.append(WApplication.getResourcesUrl()).append(
"/themes/").append(app.getCssTheme())
.append("/wt_ie.css', 'all');");
}
if (app.getEnvironment().getAgent() == WEnvironment.UserAgent.IE6) {
response.out().append("Wt3_1_8").append(".addStyleSheet('")
.append(WApplication.getResourcesUrl()).append(
"/themes/").append(app.getCssTheme())
.append("/wt_ie6.css', 'all');");
}
}
app.styleSheetsAdded_ = app.styleSheets_.size();
this.loadStyleSheets(response.out(), app);
}
if (conf.isInlineCss()) {
app.getStyleSheet().javaScriptUpdate(app, response.out(), true);
}
response
.out()
.append("document.body.parentNode.className='")
.append(app.htmlClass_)
.append("';")
.append("document.body.className='")
.append(this.getBodyClassRtl())
.append("';")
.append("document.body.setAttribute('dir', '")
.append(
app.getLayoutDirection() == LayoutDirection.LeftToRight ? "LTR"
: "RTL").append("');");
app.bodyHtmlClassChanged_ = false;
Writer s = response.out();
mainElement.addToParent(s, "document.body", widgetset ? 0 : -1, app);
;
if (app.isQuited()) {
s.append(app.getJavaScriptClass()).append("._p_.quit();");
}
if (widgetset) {
app.domRoot2_.rootAsJavaScript(app, s, true);
}
this.setJSSynced(true);
this.preLearnStateless(app, this.collectedJS1_);
response.out().append(this.collectedJS1_.toString());
this.collectedJS1_ = new StringWriter();
this.updateLoadIndicator(response.out(), app, true);
if (widgetset) {
String historyE = app.getEnvironment().getParameter("Wt-history");
if (historyE != null) {
response.out().append("Wt3_1_8")
.append(".history.initialize('").append(
historyE.charAt(0)).append("-field', '")
.append(historyE.charAt(0)).append("-iframe');\n");
}
}
response.out().append(app.getAfterLoadJavaScript()).append(
"{var o=null,e=null;").append(
app.hideLoadingIndicator_.getJavaScript()).append("}");
if (widgetset) {
response.out().append("$(document).ready(function() { ").append(
app.getJavaScriptClass()).append("._p_.load(false);});\n");
}
if (!app.isQuited()) {
response.out().append(this.session_.getApp().getJavaScriptClass())
.append("._p_.update(null, 'load', null, false);\n");
}
if (!widgetset) {
response.out().append("};\n");
response.out().append(app.getJavaScriptClass()).append(
"._p_.setServerPush(").append(
app.isUpdatesEnabled() ? "true" : "false").append(");\n")
.append("$(document).ready(function() { ").append(
app.getJavaScriptClass()).append(
"._p_.load(true);});\n");
}
this.loadScriptLibraries(response.out(), app, librariesLoaded);
}

// private void serveWidgetSet(WebResponse request) ;
private void collectJavaScript() throws IOException {
WApplication app = this.session_.getApp();
Configuration conf = this.session_.getController().getConfiguration();
this.collectedJS1_.append(this.invisibleJS_.toString());
this.invisibleJS_ = new StringWriter();
if (conf.isInlineCss()) {
app.getStyleSheet()
.javaScriptUpdate(app, this.collectedJS1_, false);
}
this.loadStyleSheets(this.collectedJS1_, app);
if (app.bodyHtmlClassChanged_) {
this.collectedJS1_
.append("document.body.parentNode.className='")
.append(app.htmlClass_)
.append("';")
.append("document.body.className='")
.append(this.getBodyClassRtl())
.append("';")
.append("document.body.setAttribute('dir', '")
.append(
app.getLayoutDirection() == LayoutDirection.LeftToRight ? "LTR"
: "RTL").append("');");
app.bodyHtmlClassChanged_ = false;
}
int librariesLoaded = this.loadScriptLibraries(this.collectedJS1_, app);
this.loadScriptLibraries(this.collectedJS2_, app, librariesLoaded);
this.collectedJS1_.append(app.getNewBeforeLoadJavaScript());
if (app.domRoot2_ != null) {
app.domRoot2_.rootAsJavaScript(app, this.collectedJS1_, false);
}
this.collectJavaScriptUpdate(this.collectedJS1_);
if (this.visibleOnly_) {
boolean needFetchInvisible = false;
if (!this.updateMap_.isEmpty()) {
needFetchInvisible = true;
if (this.twoPhaseThreshold_ > 0) {
this.visibleOnly_ = false;
this.collectJavaScriptUpdate(this.invisibleJS_);
if (this.invisibleJS_.getBuffer().length() < (int) this.twoPhaseThreshold_) {
this.collectedJS1_.append(this.invisibleJS_.toString());
this.invisibleJS_ = new StringWriter();
needFetchInvisible = false;
}
this.visibleOnly_ = true;
}
}
if (needFetchInvisible) {
this.collectedJS1_.append(app.getJavaScriptClass()).append(
"._p_.update(null, 'none', null, false);");
}
}
if (app.autoJavaScriptChanged_) {
this.collectedJS1_.append(app.getJavaScriptClass()).append(
"._p_.autoJavaScript=function(){").append(
app.autoJavaScript_).append("};");
app.autoJavaScriptChanged_ = false;
}
this.visibleOnly_ = true;
app.domRoot_.doneRerender();
if (app.domRoot2_ != null) {
app.domRoot2_.doneRerender();
}
String redirect = this.session_.getRedirect();
if (redirect.length() != 0) {
this.streamRedirectJS(this.collectedJS1_, redirect);
}
}

private void collectChanges(List<DomElement> changes) {
WApplication app = this.session_.getApp();
do {
this.moreUpdates_ = false;
OrderedMultiMap<Integer, WWidget> depthOrder = new OrderedMultiMap<Integer, WWidget>();
for (Iterator<WWidget> i_it = this.updateMap_.iterator(); i_it
.hasNext();) {
WWidget i = i_it.next();
int depth = 1;
WWidget ww = i;
WWidget w = ww;
for (; w.getParent() != null; ++depth) {
w = w.getParent();
}
if (w != app.domRoot_ && w != app.domRoot2_) {
depth = 0;
}
depthOrder.put(depth, ww);
}
for (Iterator<Map.Entry<Integer, WWidget>> i_it = depthOrder
.entrySet().iterator(); i_it.hasNext();) {
Map.Entry<Integer, WWidget> i = i_it.next();
boolean j = this.updateMap_.contains(i.getValue());
if (j != false) {
WWidget w = i.getValue();
if (i.getKey() == 0) {
w.getWebWidget().propagateRenderOk();
continue;
}
if (!this.learning_ && this.visibleOnly_) {
if (w.isRendered()) {
w.getSDomChanges(changes, app);
}
} else {
w.getSDomChanges(changes, app);
}
}
}
} while (!this.learning_ && this.moreUpdates_);
}

private void collectJavaScriptUpdate(Writer out) throws IOException {
WApplication app = this.session_.getApp();
out.append('{');
this.collectJS(out);
this.preLearnStateless(app, out);
if (this.formObjectsChanged_) {
String formObjectsList = this.createFormObjectsList(app);
if (!formObjectsList.equals(this.currentFormObjectsList_)) {
this.currentFormObjectsList_ = formObjectsList;
out.append(app.getJavaScriptClass()).append(
"._p_.setFormObjects([").append(
this.currentFormObjectsList_).append("]);");
}
}
out.append(app.getAfterLoadJavaScript());
if (app.isQuited()) {
out.append(app.getJavaScriptClass()).append("._p_.quit();");
}
this.updateLoadIndicator(out, app, false);
out.append('}');
}

private void loadStyleSheets(Writer out, WApplication app)
throws IOException {
int first = app.styleSheets_.size() - app.styleSheetsAdded_;
for (int i = first; i < app.styleSheets_.size(); ++i) {
out.append("Wt3_1_8").append(".addStyleSheet('").append(
app.fixRelativeUrl(app.styleSheets_.get(i).uri)).append(
"', '").append(app.styleSheets_.get(i).media).append(
"');\n");
}
app.styleSheetsAdded_ = 0;
}

private int loadScriptLibraries(Writer out, WApplication app, int count)
throws IOException {
if (count == -1) {
int first = app.scriptLibraries_.size() - app.scriptLibrariesAdded_;
for (int i = first; i < app.scriptLibraries_.size(); ++i) {
String uri = app
.fixRelativeUrl(app.scriptLibraries_.get(i).uri);
out.append(app.scriptLibraries_.get(i).beforeLoadJS).append(
app.getJavaScriptClass()).append("._p_.loadScript('")
.append(uri).append("',");
DomElement.jsStringLiteral(out,
app.scriptLibraries_.get(i).symbol, '\'');
out.append(");\n");
out.append(app.getJavaScriptClass()).append("._p_.onJsLoad(\"")
.append(uri).append("\",function() {\n");
}
count = app.scriptLibrariesAdded_;
app.scriptLibrariesAdded_ = 0;
return count;
} else {
if (count != 0) {
out.append(app.getJavaScriptClass()).append(
"._p_.autoJavaScript();");
for (int i = 0; i < count; ++i) {
out.append("});");
}
}
return 0;
}
}

private final int loadScriptLibraries(Writer out, WApplication app)
throws IOException {
return loadScriptLibraries(out, app, -1);
}

private void updateLoadIndicator(Writer out, WApplication app, boolean all)
throws IOException {
if (app.showLoadingIndicator_.needsUpdate(all)) {
out.append(
"showLoadingIndicator = function() {var o=null,e=null;\n")
.append(app.showLoadingIndicator_.getJavaScript()).append(
"};\n");
app.showLoadingIndicator_.updateOk();
}
if (app.hideLoadingIndicator_.needsUpdate(all)) {
out.append(
"hideLoadingIndicator = function() {var o=null,e=null;\n")
.append(app.hideLoadingIndicator_.getJavaScript()).append(
"};\n");
app.hideLoadingIndicator_.updateOk();
}
}

private void setJSSynced(boolean invisibleToo) {
this.collectedJS1_ = new StringWriter();
this.collectedJS2_ = new StringWriter();
if (!invisibleToo) {
this.collectedJS1_.append(this.invisibleJS_.toString());
}
this.invisibleJS_ = new StringWriter();
}

private String createFormObjectsList(WApplication app) {
this.updateFormObjectsList(app);
String result = "";
for (Iterator<Map.Entry<String, WObject>> i_it = this.currentFormObjects_
.entrySet().iterator(); i_it.hasNext();) {
Map.Entry<String, WObject> i = i_it.next();
if (result.length() != 0) {
result += ',';
}
result += "'" + i.getKey() + "'";
}
this.formObjectsChanged_ = false;
return result;
}

private void preLearnStateless(WApplication app, Writer out)
throws IOException {
boolean isIEMobile = app.getEnvironment().agentIsIEMobile();
if (isIEMobile || !this.session_.getEnv().hasAjax()) {
return;
}
this.collectJS(out);
Map<String, WeakReference<AbstractEventSignal>> ss = this.session_
.getApp().exposedSignals();
for (Iterator<Map.Entry<String, WeakReference<AbstractEventSignal>>> i_it = ss
.entrySet().iterator(); i_it.hasNext();) {
Map.Entry<String, WeakReference<AbstractEventSignal>> i = i_it
.next();
AbstractEventSignal s = i.getValue().get();
if (!(s != null)) {
i_it.remove();
continue;
}
if (s.getSender() == app) {
s.processPreLearnStateless(this);
}
WWidget ww = ((s.getSender()) instanceof WWidget ? (WWidget) (s
.getSender()) : null);
if (ww != null && ww.isRendered()) {
s.processPreLearnStateless(this);
}
}
out.append(this.statelessJS_.toString());
this.statelessJS_ = new StringWriter();
}

private StringWriter collectedJS1_;
private StringWriter collectedJS2_;
private StringWriter invisibleJS_;
private StringWriter statelessJS_;
StringWriter beforeLoadJS_;

private void collectJS(Writer js) throws IOException {
List<DomElement> changes = new ArrayList<DomElement>();
this.collectChanges(changes);
WApplication app = this.session_.getApp();
if (js != null) {
js.append(app.getNewBeforeLoadJavaScript());
EscapeOStream sout = new EscapeOStream(js);
for (int i = 0; i < changes.size(); ++i) {
changes.get(i).asJavaScript(sout, DomElement.Priority.Delete);
}
for (int i = 0; i < changes.size(); ++i) {
changes.get(i).asJavaScript(sout, DomElement.Priority.Update);
;
}
} else {
for (int i = 0; i < changes.size(); ++i) {
;
}
}
if (js != null) {
if (app.titleChanged_) {
js.append(app.getJavaScriptClass()).append("._p_.setTitle(")
.append(
WString.toWString(app.getTitle())
.getJsStringLiteral()).append(");\n");
}
if (app.closeMessageChanged_) {
js.append(app.getJavaScriptClass()).append(
"._p_.setCloseMessage(").append(
WString.toWString(app.getCloseMessage())
.getJsStringLiteral()).append(");\n");
}
}
app.titleChanged_ = false;
app.closeMessageChanged_ = false;
if (js != null) {
int librariesLoaded = this.loadScriptLibraries(js, app);
if (app.internalPathIsChanged_) {
js.append(app.getJavaScriptClass()).append("._p_.setHash('")
.append(app.newInternalPath_).append("');\n");
}
js.append(app.getAfterLoadJavaScript());
this.loadScriptLibraries(js, app, librariesLoaded);
} else {
app.getAfterLoadJavaScript();
}
app.internalPathIsChanged_ = false;
}

private void setPageVars(FileServe page) {
boolean xhtml = this.session_.getEnv().getContentType() == WEnvironment.ContentType.XHTML1;
WApplication app = this.session_.getApp();
page.setVar("DOCTYPE", this.session_.getDocType());
String htmlAttr = "";
if (app != null && app.htmlClass_.length() != 0) {
htmlAttr = " class=\"" + app.htmlClass_ + "\"";
}
if (xhtml) {
page.setVar("HTMLATTRIBUTES",
"xmlns=\"http://www.w3.org/1999/xhtml\"" + htmlAttr);
page.setVar("METACLOSE", "/>");
} else {
if (this.session_.getEnv().agentIsIE()) {
page.setVar("HTMLATTRIBUTES",
"xmlns:v=\"urn:schemas-microsoft-com:vml\" lang=\"en\" dir=\"ltr\""
+ htmlAttr);
} else {
page.setVar("HTMLATTRIBUTES", "lang=\"en\" dir=\"ltr\""
+ htmlAttr);
}
page.setVar("METACLOSE", ">");
}
String attr = this.getBodyClassRtl();
if (attr.length() != 0) {
attr = " class=\"" + attr + "\"";
}
if (app != null
&& app.getLayoutDirection() == LayoutDirection.RightToLeft) {
attr += " dir=\"RTL\"";
}
page.setVar("BODYATTRIBUTES", attr);
page.setVar("HEADDECLARATIONS", this.getHeadDeclarations());
page.setCondition("FORM", !this.session_.getEnv().agentIsSpiderBot()
&& !this.session_.getEnv().hasAjax());
}

private void streamBootContent(WebResponse response, FileServe boot,
boolean hybrid) throws IOException {
Configuration conf = this.session_.getController().getConfiguration();
FileServe bootJs = new FileServe(WtServlet.Boot_js);
boot.setVar("BLANK_HTML", this.session_.getBootstrapUrl(response,
WebSession.BootstrapOption.ClearInternalPath)
+ "&amp;request=resource&amp;resource=blank");
boot.setVar("SESSION_ID", this.session_.getSessionId());
boot.setVar("APP_CLASS", "Wt");
bootJs.setVar("SELF_URL", this.safeJsStringLiteral(this.session_
.getBootstrapUrl(response,
WebSession.BootstrapOption.KeepInternalPath)));
bootJs.setVar("SESSION_ID", this.session_.getSessionId());
bootJs.setVar("RANDOMSEED", String.valueOf(MathUtils.randomInt()));
bootJs.setVar("RELOAD_IS_NEWSESSION", conf.isReloadIsNewSession());
bootJs
.setVar(
"USE_COOKIES",
conf.getSessionTracking() == Configuration.SessionTracking.CookiesURL);
bootJs.setVar("AJAX_CANONICAL_URL", this
.safeJsStringLiteral(this.session_.ajaxCanonicalUrl(response)));
bootJs.setVar("APP_CLASS", "Wt");
bootJs.setCondition("HYBRID", hybrid);
boolean xhtml = this.session_.getEnv().getContentType() == WEnvironment.ContentType.XHTML1;
bootJs.setCondition("DEFER_SCRIPT", !xhtml);
String internalPath = hybrid ? this.safeJsStringLiteral(this.session_
.getApp().getInternalPath()) : "";
bootJs.setVar("INTERNAL_PATH", internalPath);
boot.streamUntil(response.out(), "BOOT_JS");
bootJs.stream(response.out());
}

private String getHeadDeclarations() {
final boolean xhtml = this.session_.getEnv().getContentType() == WEnvironment.ContentType.XHTML1;
EscapeOStream result = new EscapeOStream();
if (this.session_.getApp() != null) {
for (int i = 0; i < this.session_.getApp().metaHeaders_.size(); ++i) {
WApplication.MetaHeader m = this.session_.getApp().metaHeaders_
.get(i);
result.append("<meta");
if (m.name.length() != 0) {
if (m.type == MetaHeaderType.MetaName) {
result.append(" name=\"");
} else {
result.append(" http-equiv=\"");
}
result.pushEscape(EscapeOStream.RuleSet.HtmlAttribute);
result.append(m.name);
result.popEscape();
result.append('"');
}
if (m.lang.length() != 0) {
result.append(" lang=\"");
result.pushEscape(EscapeOStream.RuleSet.HtmlAttribute);
result.append(m.lang);
result.popEscape();
result.append('"');
}
result.append(" content=\"");
result.pushEscape(EscapeOStream.RuleSet.HtmlAttribute);
result.append(m.content.toString());
result.popEscape();
result.append(xhtml ? "\"/>" : "\">");
}
} else {
if (this.session_.getEnv().agentIsIE()
&& this.session_.getEnv().getAgent().getValue() <= WEnvironment.UserAgent.IE8
.getValue()) {
result.append(
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=7")
.append(xhtml ? "\"/>" : "\">").append('\n');
}
}
if (this.session_.getFavicon().length() != 0) {
result
.append(
"<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\"")
.append(this.session_.getFavicon()).append(
xhtml ? "\"/>" : "\">");
}
return result.toString();
}

private String getBodyClassRtl() {
if (this.session_.getApp() != null) {
String s = this.session_.getApp().bodyClass_;
if (s.length() != 0) {
s += ' ';
}
s += this.session_.getApp().getLayoutDirection() == LayoutDirection.LeftToRight ? "Wt-ltr"
: "Wt-rtl";
return s;
} else {
return "";
}
}

private Set<WWidget> updateMap_;
private boolean learning_;
private boolean learningIncomplete_;
private boolean moreUpdates_;

private String safeJsStringLiteral(String value) {
String s = WWebWidget.jsStringLiteral(value);
return StringUtils.replace(s, "<", "<'+'");
}

public String learn(AbstractEventSignal.LearningListener slot)
throws IOException {
if (slot.getType() == SlotType.PreLearnStateless) {
this.learning_ = true;
}
this.learningIncomplete_ = false;
slot.trigger();
StringWriter js = new StringWriter();
this.collectJS(js);
String result = js.toString();
if (slot.getType() == SlotType.PreLearnStateless) {
slot.undoTrigger();
this.collectJS((Writer) null);
this.learning_ = false;
} else {
this.statelessJS_.append(result);
}
if (!this.learningIncomplete_) {
slot.setJavaScript(result);
}
this.collectJS(this.statelessJS_);
return result;
}
}
(1-1/2)