]> source.dussan.org Git - vaadin-framework.git/commitdiff
reformatted with proper code formatter on
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Tue, 13 Oct 2009 07:50:12 +0000 (07:50 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Tue, 13 Oct 2009 07:50:12 +0000 (07:50 +0000)
svn changeset:9178/svn branch:6.2

src/com/vaadin/terminal/gwt/client/ApplicationConnection.java

index 969baa160f0d7854288e83a61ec3b8f610802ca2..e9f2fba3d11bfc7aa8890c2fe1bc4ba3554b75f1 100755 (executable)
@@ -44,1738 +44,1738 @@ import com.vaadin.terminal.gwt.client.ui.VNotification.HideEvent;
  * Entry point classes define <code>onModuleLoad()</code>.
  */
 public class ApplicationConnection {
-       private static final String MODIFIED_CLASSNAME = "v-modified";
+    private static final String MODIFIED_CLASSNAME = "v-modified";
 
-       private static final String REQUIRED_CLASSNAME_EXT = "-required";
+    private static final String REQUIRED_CLASSNAME_EXT = "-required";
 
-       private static final String ERROR_CLASSNAME_EXT = "-error";
+    private static final String ERROR_CLASSNAME_EXT = "-error";
 
-       public static final String VAR_RECORD_SEPARATOR = "\u001e";
+    public static final String VAR_RECORD_SEPARATOR = "\u001e";
 
-       public static final String VAR_FIELD_SEPARATOR = "\u001f";
+    public static final String VAR_FIELD_SEPARATOR = "\u001f";
 
-       public static final String VAR_BURST_SEPARATOR = "\u001d";
+    public static final String VAR_BURST_SEPARATOR = "\u001d";
 
-       public static final String VAR_ARRAYITEM_SEPARATOR = "\u001c";
+    public static final String VAR_ARRAYITEM_SEPARATOR = "\u001c";
 
-       public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key";
-       /**
-        * @deprecated use UIDL_SECURITY_TOKEN_ID instead
-        */
-       @Deprecated
-       public static final String UIDL_SECURITY_HEADER = UIDL_SECURITY_TOKEN_ID;
+    public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key";
+    /**
+     * @deprecated use UIDL_SECURITY_TOKEN_ID instead
+     */
+    @Deprecated
+    public static final String UIDL_SECURITY_HEADER = UIDL_SECURITY_TOKEN_ID;
 
-       public static final String PARAM_UNLOADBURST = "onunloadburst";
+    public static final String PARAM_UNLOADBURST = "onunloadburst";
 
-       public static final String ATTRIBUTE_DESCRIPTION = "description";
-       public static final String ATTRIBUTE_ERROR = "error";
+    public static final String ATTRIBUTE_DESCRIPTION = "description";
+    public static final String ATTRIBUTE_ERROR = "error";
 
-       private String uidl_security_key = "init";
+    private String uidl_security_key = "init";
 
-       private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
+    private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
 
-       private static Console console;
+    private static Console console;
 
-       private final ArrayList<String> pendingVariables = new ArrayList<String>();
+    private final ArrayList<String> pendingVariables = new ArrayList<String>();
 
-       private final ComponentDetailMap idToPaintableDetail = ComponentDetailMap
-                       .create();
+    private final ComponentDetailMap idToPaintableDetail = ComponentDetailMap
+            .create();
 
-       private final WidgetSet widgetSet;
+    private final WidgetSet widgetSet;
 
-       private VContextMenu contextMenu = null;
+    private VContextMenu contextMenu = null;
 
-       private Timer loadTimer;
-       private Timer loadTimer2;
-       private Timer loadTimer3;
-       private Element loadElement;
+    private Timer loadTimer;
+    private Timer loadTimer2;
+    private Timer loadTimer3;
+    private Element loadElement;
 
-       private final VView view;
+    private final VView view;
 
-       private boolean applicationRunning = false;
+    private boolean applicationRunning = false;
 
-       private int activeRequests = 0;
+    private int activeRequests = 0;
 
-       /** Parameters for this application connection loaded from the web-page */
-       private final ApplicationConfiguration configuration;
+    /** Parameters for this application connection loaded from the web-page */
+    private final ApplicationConfiguration configuration;
 
-       /** List of pending variable change bursts that must be submitted in order */
-       private final ArrayList<ArrayList<String>> pendingVariableBursts = new ArrayList<ArrayList<String>>();
+    /** List of pending variable change bursts that must be submitted in order */
+    private final ArrayList<ArrayList<String>> pendingVariableBursts = new ArrayList<ArrayList<String>>();
 
-       /** Timer for automatic refirect to SessionExpiredURL */
-       private Timer redirectTimer;
+    /** Timer for automatic refirect to SessionExpiredURL */
+    private Timer redirectTimer;
 
-       /** redirectTimer scheduling interval in seconds */
-       private int sessionExpirationInterval;
+    /** redirectTimer scheduling interval in seconds */
+    private int sessionExpirationInterval;
 
-       private ArrayList<Paintable> relativeSizeChanges = new ArrayList<Paintable>();;
-       private ArrayList<Paintable> componentCaptionSizeChanges = new ArrayList<Paintable>();;
+    private ArrayList<Paintable> relativeSizeChanges = new ArrayList<Paintable>();;
+    private ArrayList<Paintable> componentCaptionSizeChanges = new ArrayList<Paintable>();;
 
-       private Date requestStartTime;
+    private Date requestStartTime;
 
-       private boolean validatingLayouts = false;
+    private boolean validatingLayouts = false;
 
-       private Set<Paintable> zeroWidthComponents = null;
+    private Set<Paintable> zeroWidthComponents = null;
 
-       private Set<Paintable> zeroHeightComponents = null;
+    private Set<Paintable> zeroHeightComponents = null;
 
-       public ApplicationConnection(WidgetSet widgetSet,
-                       ApplicationConfiguration cnf) {
-               this.widgetSet = widgetSet;
-               configuration = cnf;
-               windowName = configuration.getInitialWindowName();
-               if (isDebugMode()) {
-                       console = new VDebugConsole(this, cnf, !isQuietDebugMode());
-               } else {
-                       console = new NullConsole();
-               }
-
-               ComponentLocator componentLocator = new ComponentLocator(this);
-
-               String appRootPanelName = cnf.getRootPanelId();
-               // remove the end (window name) of autogenerated rootpanel id
-               appRootPanelName = appRootPanelName.replaceFirst("-\\d+$", "");
-
-               initializeTestbenchHooks(componentLocator, appRootPanelName);
-
-               initializeClientHooks();
-
-               view = new VView(cnf.getRootPanelId());
-               showLoadingIndicator();
-
-       }
-
-       /**
-        * Starts this application. Don't call this method directly - it's called by
-        * {@link ApplicationConfiguration#startNextApplication()}, which should be
-        * called once this application has started (first response received) or
-        * failed to start. This ensures that the applications are started in order,
-        * to avoid session-id problems.
-        */
-       void start() {
-               makeUidlRequest("", true, false, false);
-       }
-
-       private native void initializeTestbenchHooks(
-                       ComponentLocator componentLocator, String TTAppId)
-       /*-{
-           var ap = this;
-           var client = {};
-           client.isActive = function() {
-               return ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::hasActiveRequest()() || ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::isLoadingIndicatorVisible()();
-           }
-           var vi = ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::getVersionInfo()();
-           if (vi) {
-               client.getVersionInfo = function() {
-                   return vi;
-               }
-           }
-
-           client.getElementByPath = function(id) {
-              return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
-           }
-           client.getPathForElement = function(element) {
-              return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
-           }
-
-           if(!$wnd.vaadin.clients) {
-              $wnd.vaadin.clients = {};
-           }
-
-           $wnd.vaadin.clients[TTAppId] = client;
-       }-*/;
-
-       /**
-        * Helper for tt initialization
-        */
-       @SuppressWarnings("unused")
-       private JavaScriptObject getVersionInfo() {
-               return configuration.getVersionInfoJSObject();
-       }
-
-       /**
-        * Publishes a JavaScript API for mash-up applications.
-        * <ul>
-        * <li><code>vaadin.forceSync()</code> sends pending variable changes, in
-        * effect synchronizing the server and client state. This is done for all
-        * applications on host page.</li>
-        * <li><code>vaadin.postRequestHooks</code> is a map of functions which gets
-        * called after each XHR made by vaadin application. Note, that it is
-        * attaching js functions responsibility to create the variable like this:
-        
-        * <code><pre>
-        * if(!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();}
-        * postRequestHooks.myHook = function(appId) {
-        *          if(appId == "MyAppOfInterest") {
-        *                  // do the staff you need on xhr activity
-        *          }
-        * }
-        * </pre></code> First parameter passed to these functions is the identifier
-        * of Vaadin application that made the request.
-        * </ul>
-        
-        * TODO make this multi-app aware
-        */
-       private native void initializeClientHooks()
-       /*-{
-           var app = this;
-           var oldSync;
-           if($wnd.vaadin.forceSync) {
-               oldSync = $wnd.vaadin.forceSync;
-           }
-           $wnd.vaadin.forceSync = function() {
-               if(oldSync) {
-                   oldSync();
-               }
-               app.@com.vaadin.terminal.gwt.client.ApplicationConnection::sendPendingVariableChanges()();
-           }
-           var oldForceLayout;
-           if($wnd.vaadin.forceLayout) {
-               oldForceLayout = $wnd.vaadin.forceLayout;
-           }
-           $wnd.vaadin.forceLayout = function() {
-               if(oldForceLayout) {
-                   oldForceLayout();
-               }
-               app.@com.vaadin.terminal.gwt.client.ApplicationConnection::forceLayout()();
-           }
-       }-*/;
-
-       /**
-        * Runs possibly registered client side post request hooks. This is expected
-        * to be run after each uidl request made by Vaadin application.
-        
-        * @param appId
-        */
-       private static native void runPostRequestHooks(String appId)
-       /*-{
-           if($wnd.vaadin.postRequestHooks) {
-               for(var hook in $wnd.vaadin.postRequestHooks) {
-                   if(typeof($wnd.vaadin.postRequestHooks[hook]) == "function") {
-                       try {
-                           $wnd.vaadin.postRequestHooks[hook](appId);
-                       } catch(e) {}
-                   }
-               }
-           }
-       }-*/;
-
-       public static Console getConsole() {
-               return console;
-       }
-
-       /**
-        * Checks if client side is in debug mode. Practically this is invoked by
-        * adding ?debug parameter to URI.
-        
-        * @return true if client side is currently been debugged
-        */
-       public native static boolean isDebugMode()
-       /*-{
-           if($wnd.vaadin.debug) {
-               var parameters = $wnd.location.search;
-               var re = /debug[^\/]*$/;
-               return re.test(parameters);
-           } else {
-               return false;
-           }
-       }-*/;
-
-       private native static boolean isQuietDebugMode()
-       /*-{
-           var uri = $wnd.location;
-           var re = /debug=q[^\/]*$/;
-           return re.test(uri);
-       }-*/;
-
-       public String getAppUri() {
-               return configuration.getApplicationUri();
-       };
-
-       public boolean hasActiveRequest() {
-               return (activeRequests > 0);
-       }
-
-       private void makeUidlRequest(final String requestData,
-                       final boolean repaintAll, final boolean forceSync,
-                       final boolean analyzeLayouts) {
-               startRequest();
-
-               // Security: double cookie submission pattern
-               final String rd = uidl_security_key + VAR_BURST_SEPARATOR + requestData;
-
-               console.log("Making UIDL Request with params: " + rd);
-               String uri = getAppUri() + "UIDL" + configuration.getPathInfo();
-               if (repaintAll) {
-                       // collect some client side data that will be sent to server on
-                       // initial uidl request
-                       int clientHeight = Window.getClientHeight();
-                       int clientWidth = Window.getClientWidth();
-                       com.google.gwt.dom.client.Element pe = view.getElement()
-                                       .getParentElement();
-                       int offsetHeight = pe.getOffsetHeight();
-                       int offsetWidth = pe.getOffsetWidth();
-                       int screenWidth = BrowserInfo.get().getScreenWidth();
-                       int screenHeight = BrowserInfo.get().getScreenHeight();
-
-                       String token = History.getToken();
-
-                       // TODO figure out how client and view size could be used better on
-                       // server. screen size can be accessed via Browser object, but other
-                       // values currently only via transaction listener.
-                       uri += "?repaintAll=1&" + "sh=" + screenHeight + "&sw="
-                                       + screenWidth + "&cw=" + clientWidth + "&ch="
-                                       + clientHeight + "&vw=" + offsetWidth + "&vh="
-                                       + offsetHeight + "&fr=" + token;
-                       if (analyzeLayouts) {
-                               uri += "&analyzeLayouts=1";
-                       }
-               }
-               if (windowName != null && windowName.length() > 0) {
-                       uri += (repaintAll ? "&" : "?") + "windowName=" + windowName;
-               }
-
-               if (!forceSync) {
-                       boolean success = false;
-                       final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
-                                       uri);
-                       // TODO enable timeout
-                       // rb.setTimeoutMillis(timeoutMillis);
-                       rb.setHeader("Content-Type", "text/plain;charset=utf-8");
-                       try {
-                               rb.sendRequest(rd, new RequestCallback() {
-                                       public void onError(Request request, Throwable exception) {
-                                               showCommunicationError(exception.getMessage());
-                                               endRequest();
-                                               if (!applicationRunning) {
-                                                       // start failed, let's try to start the next app
-                                                       ApplicationConfiguration.startNextApplication();
-                                               }
-                                       }
-
-                                       public void onResponseReceived(Request request,
-                                                       Response response) {
-                                               console.log("Server visit took "
-                                                               + String.valueOf((new Date()).getTime()
-                                                                               - requestStartTime.getTime()) + "ms");
-
-                                               switch (response.getStatusCode()) {
-                                               case 0:
-                                                       showCommunicationError("Invalid status code 0 (server down?)");
-                                                       return;
-                                                       // TODO could add more cases
-                                               case 503:
-                                                       // We'll assume msec instead of the usual seconds
-                                                       int delay = Integer.parseInt(response
-                                                                       .getHeader("Retry-After"));
-                                                       console.log("503, retrying in " + delay + "msec");
-                                                       (new Timer() {
-                                                               @Override
-                                                               public void run() {
-                                                                       activeRequests--;
-                                                                       makeUidlRequest(requestData, repaintAll,
-                                                                                       forceSync, analyzeLayouts);
-                                                               }
-                                                       }).schedule(delay);
-                                                       return;
-
-                                               }
-
-                                               if (applicationRunning) {
-                                                       handleReceivedJSONMessage(response);
-                                               } else {
-                                                       applicationRunning = true;
-                                                       handleWhenCSSLoaded(response);
-                                                       ApplicationConfiguration.startNextApplication();
-                                               }
-                                       }
-
-                                       int cssWaits = 0;
-                                       static final int MAX_CSS_WAITS = 20;
-
-                                       private void handleWhenCSSLoaded(final Response response) {
-                                               int heightOfLoadElement = DOM.getElementPropertyInt(
-                                                               loadElement, "offsetHeight");
-                                               if (heightOfLoadElement == 0
-                                                               && cssWaits < MAX_CSS_WAITS) {
-                                                       (new Timer() {
-                                                               @Override
-                                                               public void run() {
-                                                                       handleWhenCSSLoaded(response);
-                                                               }
-                                                       }).schedule(50);
-                                                       console
-                                                                       .log("Assuming CSS loading is not complete, "
-                                                                                       + "postponing render phase. "
-                                                                                       + "(.v-loading-indicator height == 0)");
-                                                       cssWaits++;
-                                               } else {
-                                                       handleReceivedJSONMessage(response);
-                                                       if (cssWaits >= MAX_CSS_WAITS) {
-                                                               console
-                                                                               .error("CSS files may have not loaded properly.");
-                                                       }
-                                               }
-                                       }
-
-                               });
-
-                       } catch (final RequestException e) {
-                               ClientExceptionHandler.displayError(e);
-                               endRequest();
-                       }
-               } else {
-                       // Synchronized call, discarded response
-
-                       syncSendForce(((HTTPRequestImpl) GWT.create(HTTPRequestImpl.class))
-                                       .createXmlHTTPRequest(), uri + "&" + PARAM_UNLOADBURST
-                                       + "=1", rd);
-               }
-       }
-
-       /**
-        * Shows the communication error notification. The 'details' only go to the
-        * console for now.
-        
-        * @param details
-        *            Optional details for debugging.
-        */
-       private void showCommunicationError(String details) {
-               console.error("Communication error: " + details);
-               String html = "";
-               if (configuration.getCommunicationErrorCaption() != null) {
-                       html += "<h1>" + configuration.getCommunicationErrorCaption()
-                                       + "</h1>";
-               }
-               if (configuration.getCommunicationErrorMessage() != null) {
-                       html += "<p>" + configuration.getCommunicationErrorMessage()
-                                       + "</p>";
-               }
-               if (html.length() > 0) {
-                       VNotification n = new VNotification(1000 * 60 * 45);
-                       n.addEventListener(new NotificationRedirect(configuration
-                                       .getCommunicationErrorUrl()));
-                       n
-                                       .show(html, VNotification.CENTERED_TOP,
-                                                       VNotification.STYLE_SYSTEM);
-               } else {
-                       redirect(configuration.getCommunicationErrorUrl());
-               }
-       }
-
-       private native void syncSendForce(JavaScriptObject xmlHttpRequest,
-                       String uri, String requestData)
-       /*-{
-           try {
-               xmlHttpRequest.open("POST", uri, false);
-               xmlHttpRequest.setRequestHeader("Content-Type", "text/plain;charset=utf-8");
-               xmlHttpRequest.send(requestData);
-           } catch (e) {
-              // No errors are managed as this is synchronous forceful send that can just fail
-           }
-           this.@com.vaadin.terminal.gwt.client.ApplicationConnection::endRequest()();
-       }-*/;
-
-       private void startRequest() {
-               activeRequests++;
-               requestStartTime = new Date();
-               // show initial throbber
-               if (loadTimer == null) {
-                       loadTimer = new Timer() {
-                               @Override
-                               public void run() {
-                                       /*
-                                        * IE7 does not properly cancel the event with
-                                        * loadTimer.cancel() so we have to check that we really
-                                        * should make it visible
-                                        */
-                                       if (loadTimer != null) {
-                                               showLoadingIndicator();
-                                       }
-
-                               }
-                       };
-                       // First one kicks in at 300ms
-               }
-               loadTimer.schedule(300);
-       }
-
-       private void endRequest() {
-               if (applicationRunning) {
-                       checkForPendingVariableBursts();
-                       runPostRequestHooks(configuration.getRootPanelId());
-               }
-               activeRequests--;
-               // deferring to avoid flickering
-               DeferredCommand.addCommand(new Command() {
-                       public void execute() {
-                               if (activeRequests == 0) {
-                                       hideLoadingIndicator();
-                               }
-                       }
-               });
-       }
-
-       /**
-        * This method is called after applying uidl change set to application.
-        
-        * It will clean current and queued variable change sets. And send next
-        * change set if it exists.
-        */
-       private void checkForPendingVariableBursts() {
-               cleanVariableBurst(pendingVariables);
-               if (pendingVariableBursts.size() > 0) {
-                       for (Iterator<ArrayList<String>> iterator = pendingVariableBursts
-                                       .iterator(); iterator.hasNext();) {
-                               cleanVariableBurst(iterator.next());
-                       }
-                       ArrayList<String> nextBurst = pendingVariableBursts.get(0);
-                       pendingVariableBursts.remove(0);
-                       buildAndSendVariableBurst(nextBurst, false);
-               }
-       }
-
-       /**
-        * Cleans given queue of variable changes of such changes that came from
-        * components that do not exist anymore.
-        
-        * @param variableBurst
-        */
-       private void cleanVariableBurst(ArrayList<String> variableBurst) {
-               for (int i = 1; i < variableBurst.size(); i += 2) {
-                       String id = variableBurst.get(i);
-                       id = id.substring(0, id.indexOf(VAR_FIELD_SEPARATOR));
-                       if (!idToPaintableDetail.containsKey(id)) {
-                               // variable owner does not exist anymore
-                               variableBurst.remove(i - 1);
-                               variableBurst.remove(i - 1);
-                               i -= 2;
-                               ApplicationConnection.getConsole().log(
-                                               "Removed variable from removed component: " + id);
-                       }
-               }
-       }
-
-       private void showLoadingIndicator() {
-               // show initial throbber
-               if (loadElement == null) {
-                       loadElement = DOM.createDiv();
-                       DOM.setStyleAttribute(loadElement, "position", "absolute");
-                       DOM.appendChild(view.getElement(), loadElement);
-                       ApplicationConnection.getConsole().log("inserting load indicator");
-               }
-               DOM.setElementProperty(loadElement, "className", "v-loading-indicator");
-               DOM.setStyleAttribute(loadElement, "display", "block");
-               // Initialize other timers
-               loadTimer2 = new Timer() {
-                       @Override
-                       public void run() {
-                               DOM.setElementProperty(loadElement, "className",
-                                               "v-loading-indicator-delay");
-                       }
-               };
-               // Second one kicks in at 1500ms from request start
-               loadTimer2.schedule(1200);
-
-               loadTimer3 = new Timer() {
-                       @Override
-                       public void run() {
-                               DOM.setElementProperty(loadElement, "className",
-                                               "v-loading-indicator-wait");
-                       }
-               };
-               // Third one kicks in at 5000ms from request start
-               loadTimer3.schedule(4700);
-       }
-
-       private void hideLoadingIndicator() {
-               if (loadTimer != null) {
-                       loadTimer.cancel();
-                       if (loadTimer2 != null) {
-                               loadTimer2.cancel();
-                               loadTimer3.cancel();
-                       }
-                       loadTimer = null;
-               }
-               if (loadElement != null) {
-                       DOM.setStyleAttribute(loadElement, "display", "none");
-               }
-       }
-
-       public boolean isLoadingIndicatorVisible() {
-               if (loadElement == null) {
-                       return false;
-               }
-               if (loadElement.getStyle().getProperty("display").equals("none")) {
-                       return false;
-               }
-
-               return true;
-       }
-
-       private static native ValueMap parseJSONResponse(String jsonText)
-       /*-{
-           try {
-               return JSON.parse(jsonText);
-           } catch(ignored) {
-               return eval('(' + jsonText + ')');
-           }
-       }-*/;
-
-       private void handleReceivedJSONMessage(Response response) {
-               final Date start = new Date();
-               String jsonText = response.getText();
-               // for(;;);[realjson]
-               jsonText = jsonText.substring(9, jsonText.length() - 1);
-               ValueMap json;
-               try {
-                       json = parseJSONResponse(jsonText);
-               } catch (final Exception e) {
-                       endRequest();
-                       showCommunicationError(e.getMessage() + " - Original JSON-text:");
-                       console.log(jsonText);
-                       return;
-               }
-
-               ApplicationConnection.getConsole()
-                               .log(
-                                               "JSON parsing took "
-                                                               + (new Date().getTime() - start.getTime()));
-               // Handle redirect
-               if (json.containsKey("redirect")) {
-                       String url = json.getValueMap("redirect").getString("url");
-                       console.log("redirecting to " + url);
-                       redirect(url);
-                       return;
-               }
-
-               // Get security key
-               if (json.containsKey(UIDL_SECURITY_TOKEN_ID)) {
-                       uidl_security_key = json.getString(UIDL_SECURITY_TOKEN_ID);
-               }
-
-               if (json.containsKey("resources")) {
-                       ValueMap resources = json.getValueMap("resources");
-                       JsArrayString keyArray = resources.getKeyArray();
-                       int l = keyArray.length();
-                       for (int i = 0; i < l; i++) {
-                               String key = keyArray.get(i);
-                               resourcesMap.put(key, resources.getAsString(key));
-                       }
-               }
-
-               if (json.containsKey("typeMappings")) {
-                       configuration.addComponentMappings(
-                                       json.getValueMap("typeMappings"), widgetSet);
-               }
-
-               if (json.containsKey("locales")) {
-                       // Store locale data
-                       JsArray<ValueMap> valueMapArray = json
-                                       .getJSValueMapArray("locales");
-                       LocaleService.addLocales(valueMapArray);
-               }
-
-               ValueMap meta = null;
-               if (json.containsKey("meta")) {
-                       meta = json.getValueMap("meta");
-                       if (meta.containsKey("repaintAll")) {
-                               view.clear();
-                               idToPaintableDetail.clear();
-                               if (meta.containsKey("invalidLayouts")) {
-                                       validatingLayouts = true;
-                                       zeroWidthComponents = new HashSet<Paintable>();
-                                       zeroHeightComponents = new HashSet<Paintable>();
-                               }
-                       }
-                       if (meta.containsKey("timedRedirect")) {
-                               final ValueMap timedRedirect = meta
-                                               .getValueMap("timedRedirect");
-                               redirectTimer = new Timer() {
-                                       @Override
-                                       public void run() {
-                                               redirect(timedRedirect.getString("url"));
-                                       }
-                               };
-                               sessionExpirationInterval = timedRedirect.getInt("interval");
-                       }
-               }
-
-               if (redirectTimer != null) {
-                       redirectTimer.schedule(1000 * sessionExpirationInterval);
-               }
-
-               // Process changes
-               JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
-
-               ArrayList<Paintable> updatedWidgets = new ArrayList<Paintable>();
-               relativeSizeChanges.clear();
-               componentCaptionSizeChanges.clear();
-
-               int length = changes.length();
-               for (int i = 0; i < length; i++) {
-                       try {
-                               final UIDL change = changes.get(i).cast();
-                               try {
-                                       console.dirUIDL(change);
-                               } catch (final Exception e) {
-                                       ClientExceptionHandler.displayError(e);
-                                       // TODO: dir doesn't work in any browser although it should
-                                       // work (works in hosted mode)
-                                       // it partially did at some part but now broken.
-                               }
-                               final UIDL uidl = change.getChildUIDL(0);
-                               // TODO optimize
-                               final Paintable paintable = getPaintable(uidl.getId());
-                               if (paintable != null) {
-                                       paintable.updateFromUIDL(uidl, this);
-                                       // paintable may have changed during render to another
-                                       // implementation, use the new one for updated widgets map
-                                       updatedWidgets.add(idToPaintableDetail.get(uidl.getId())
-                                                       .getComponent());
-                               } else {
-                                       if (!uidl.getTag().equals("0")) {
-                                               ClientExceptionHandler
-                                                               .displayError("Received update for "
-                                                                               + uidl.getTag()
-                                                                               + ", but there is no such paintable ("
-                                                                               + uidl.getId() + ") rendered.");
-                                       } else {
-                                               view.updateFromUIDL(uidl, this);
-                                       }
-                               }
-                       } catch (final Throwable e) {
-                               ClientExceptionHandler.displayError(e);
-                       }
-               }
-
-               // Check which widgets' size has been updated
-               Set<Paintable> sizeUpdatedWidgets = new HashSet<Paintable>();
-
-               updatedWidgets.addAll(relativeSizeChanges);
-               sizeUpdatedWidgets.addAll(componentCaptionSizeChanges);
-
-               for (Paintable paintable : updatedWidgets) {
-                       ComponentDetail detail = idToPaintableDetail.get(getPid(paintable));
-                       Widget widget = (Widget) paintable;
-                       Size oldSize = detail.getOffsetSize();
-                       Size newSize = new Size(widget.getOffsetWidth(), widget
-                                       .getOffsetHeight());
-
-                       if (oldSize == null || !oldSize.equals(newSize)) {
-                               sizeUpdatedWidgets.add(paintable);
-                               detail.setOffsetSize(newSize);
-                       }
-
-               }
-
-               Util.componentSizeUpdated(sizeUpdatedWidgets);
-
-               if (meta != null) {
-                       if (meta.containsKey("appError")) {
-                               ValueMap error = meta.getValueMap("appError");
-                               String html = "";
-                               if (error.containsKey("caption")
-                                               && error.getString("caption") != null) {
-                                       html += "<h1>" + error.getAsString("caption") + "</h1>";
-                               }
-                               if (error.containsKey("message")
-                                               && error.getString("message") != null) {
-                                       html += "<p>" + error.getAsString("message") + "</p>";
-                               }
-                               String url = null;
-                               if (error.containsKey("url")) {
-                                       url = error.getString("url");
-                               }
-
-                               if (html.length() != 0) {
-                                       /* 45 min */
-                                       VNotification n = new VNotification(1000 * 60 * 45);
-                                       n.addEventListener(new NotificationRedirect(url));
-                                       n.show(html, VNotification.CENTERED_TOP,
-                                                       VNotification.STYLE_SYSTEM);
-                               } else {
-                                       redirect(url);
-                               }
-                               applicationRunning = false;
-                       }
-                       if (validatingLayouts) {
-                               getConsole().printLayoutProblems(meta, this,
-                                               zeroHeightComponents, zeroWidthComponents);
-                               zeroHeightComponents = null;
-                               zeroWidthComponents = null;
-                               validatingLayouts = false;
-
-                       }
-               }
-
-               final long prosessingTime = (new Date().getTime()) - start.getTime();
-               console.log(" Processing time was " + String.valueOf(prosessingTime)
-                               + "ms for " + jsonText.length() + " characters of JSON");
-               console.log("Referenced paintables: " + idToPaintableDetail.size());
-
-               endRequest();
-       }
-
-       /**
-        * This method assures that all pending variable changes are sent to server.
-        * Method uses synchronized xmlhttprequest and does not return before the
-        * changes are sent. No UIDL updates are processed and thus UI is left in
-        * inconsistent state. This method should be called only when closing
-        * windows - normally sendPendingVariableChanges() should be used.
-        */
-       public void sendPendingVariableChangesSync() {
-               if (applicationRunning) {
-                       pendingVariableBursts.add(pendingVariables);
-                       ArrayList<String> nextBurst = pendingVariableBursts.get(0);
-                       pendingVariableBursts.remove(0);
-                       buildAndSendVariableBurst(nextBurst, true);
-               }
-       }
-
-       // Redirect browser, null reloads current page
-       private static native void redirect(String url)
-       /*-{
-           if (url) {
-              $wnd.location = url;
-           } else {
-               $wnd.location.reload(false);
-           }
-       }-*/;
-
-       public void registerPaintable(String id, Paintable paintable) {
-               ComponentDetail componentDetail = new ComponentDetail();
-               componentDetail.setComponent(paintable);
-               idToPaintableDetail.put(id, componentDetail);
-               setPid(((Widget) paintable).getElement(), id);
-       }
-
-       private native void setPid(Element el, String pid)
-       /*-{
-           el.tkPid = pid;
-       }-*/;
-
-       public String getPid(Paintable paintable) {
-               return getPid(((Widget) paintable).getElement());
-       }
-
-       public native String getPid(Element el)
-       /*-{
-           return el.tkPid;
-       }-*/;
-
-       public Element getElementByPid(String pid) {
-               return ((Widget) getPaintable(pid)).getElement();
-       }
-
-       public void unregisterPaintable(Paintable p) {
-               if (p == null) {
-                       ApplicationConnection.getConsole().error(
-                                       "WARN: Trying to unregister null paintable");
-                       return;
-               }
-               String id = getPid(p);
-               idToPaintableDetail.remove(id);
-               if (p instanceof HasWidgets) {
-                       unregisterChildPaintables((HasWidgets) p);
-               }
-       }
-
-       public void unregisterChildPaintables(HasWidgets container) {
-               final Iterator<Widget> it = container.iterator();
-               while (it.hasNext()) {
-                       final Widget w = it.next();
-                       if (w instanceof Paintable) {
-                               unregisterPaintable((Paintable) w);
-                       } else if (w instanceof HasWidgets) {
-                               unregisterChildPaintables((HasWidgets) w);
-                       }
-               }
-       }
-
-       /**
-        * Returns Paintable element by its id
-        
-        * @param id
-        *            Paintable ID
-        */
-       public Paintable getPaintable(String id) {
-               ComponentDetail componentDetail = idToPaintableDetail.get(id);
-               if (componentDetail == null) {
-                       return null;
-               } else {
-                       return componentDetail.getComponent();
-               }
-       }
-
-       private void addVariableToQueue(String paintableId, String variableName,
-                       String encodedValue, boolean immediate, char type) {
-               final String id = paintableId + VAR_FIELD_SEPARATOR + variableName
-                               + VAR_FIELD_SEPARATOR + type;
-               for (int i = 1; i < pendingVariables.size(); i += 2) {
-                       if ((pendingVariables.get(i)).equals(id)) {
-                               pendingVariables.remove(i - 1);
-                               pendingVariables.remove(i - 1);
-                               break;
-                       }
-               }
-               pendingVariables.add(encodedValue);
-               pendingVariables.add(id);
-               if (immediate) {
-                       sendPendingVariableChanges();
-               }
-       }
-
-       /**
-        * This method sends currently queued variable changes to server. It is
-        * called when immediate variable update must happen.
-        
-        * To ensure correct order for variable changes (due servers multithreading
-        * or network), we always wait for active request to be handler before
-        * sending a new one. If there is an active request, we will put varible
-        * "burst" to queue that will be purged after current request is handled.
-        
-        */
-       @SuppressWarnings("unchecked")
-       public void sendPendingVariableChanges() {
-               if (applicationRunning) {
-                       if (hasActiveRequest()) {
-                               // skip empty queues if there are pending bursts to be sent
-                               if (pendingVariables.size() > 0
-                                               || pendingVariableBursts.size() == 0) {
-                                       ArrayList<String> burst = (ArrayList<String>) pendingVariables
-                                                       .clone();
-                                       pendingVariableBursts.add(burst);
-                                       pendingVariables.clear();
-                               }
-                       } else {
-                               buildAndSendVariableBurst(pendingVariables, false);
-                       }
-               }
-       }
-
-       /**
-        * Build the variable burst and send it to server.
-        
-        * When sync is forced, we also force sending of all pending variable-bursts
-        * at the same time. This is ok as we can assume that DOM will never be
-        * updated after this.
-        
-        * @param pendingVariables
-        *            Vector of variable changes to send
-        * @param forceSync
-        *            Should we use synchronous request?
-        */
-       private void buildAndSendVariableBurst(ArrayList<String> pendingVariables,
-                       boolean forceSync) {
-               final StringBuffer req = new StringBuffer();
-
-               while (!pendingVariables.isEmpty()) {
-                       for (int i = 0; i < pendingVariables.size(); i++) {
-                               if (i > 0) {
-                                       if (i % 2 == 0) {
-                                               req.append(VAR_RECORD_SEPARATOR);
-                                       } else {
-                                               req.append(VAR_FIELD_SEPARATOR);
-                                       }
-                               }
-                               req.append(pendingVariables.get(i));
-                       }
-
-                       pendingVariables.clear();
-                       // Append all the busts to this synchronous request
-                       if (forceSync && !pendingVariableBursts.isEmpty()) {
-                               pendingVariables = pendingVariableBursts.get(0);
-                               pendingVariableBursts.remove(0);
-                               req.append(VAR_BURST_SEPARATOR);
-                       }
-               }
-               makeUidlRequest(req.toString(), false, forceSync, false);
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       Paintable newValue, boolean immediate) {
-               String pid = (newValue != null) ? getPid(newValue) : null;
-               addVariableToQueue(paintableId, variableName, pid, immediate, 'p');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       String newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, newValue, immediate, 's');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       int newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
-                               'i');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       long newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
-                               'l');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       float newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
-                               'f');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       double newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
-                               'd');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       boolean newValue, boolean immediate) {
-               addVariableToQueue(paintableId, variableName, newValue ? "true"
-                               : "false", immediate, 'b');
-       }
-
-       public void updateVariable(String paintableId, String variableName,
-                       Object[] values, boolean immediate) {
-               final StringBuffer buf = new StringBuffer();
-               for (int i = 0; i < values.length; i++) {
-                       if (i > 0) {
-                               buf.append(VAR_ARRAYITEM_SEPARATOR);
-                       }
-                       buf.append(values[i].toString());
-               }
-               addVariableToQueue(paintableId, variableName, buf.toString(),
-                               immediate, 'a');
-       }
-
-       /**
-        * Update generic component features.
-        
-        * <h2>Selecting correct implementation</h2>
-        
-        * <p>
-        * The implementation of a component depends on many properties, including
-        * styles, component features, etc. Sometimes the user changes those
-        * properties after the component has been created. Calling this method in
-        * the beginning of your updateFromUIDL -method automatically replaces your
-        * component with more appropriate if the requested implementation changes.
-        * </p>
-        
-        * <h2>Caption, icon, error messages and description</h2>
-        
-        * <p>
-        * Component can delegate management of caption, icon, error messages and
-        * description to parent layout. This is optional an should be decided by
-        * component author
-        * </p>
-        
-        * <h2>Component visibility and disabling</h2>
-        
-        * This method will manage component visibility automatically and if
-        * component is an instanceof FocusWidget, also handle component disabling
-        * when needed.
-        
-        * @param component
-        *            Widget to be updated, expected to implement an instance of
-        *            Paintable
-        * @param uidl
-        *            UIDL to be painted
-        * @param manageCaption
-        *            True if you want to delegate caption, icon, description and
-        *            error message management to parent.
-        
-        * @return Returns true iff no further painting is needed by caller
-        */
-       public boolean updateComponent(Widget component, UIDL uidl,
-                       boolean manageCaption) {
-               String pid = getPid(component.getElement());
-               if (pid == null) {
-                       getConsole().error(
-                                       "Trying to update an unregistered component: "
-                                                       + Util.getSimpleName(component));
-                       return true;
-               }
-
-               ComponentDetail componentDetail = idToPaintableDetail.get(pid);
-
-               if (componentDetail == null) {
-                       getConsole().error(
-                                       "ComponentDetail not found for "
-                                                       + Util.getSimpleName(component) + " with PID "
-                                                       + pid + ". This should not happen.");
-                       return true;
-               }
-
-               // If the server request that a cached instance should be used, do
-               // nothing
-               if (uidl.getBooleanAttribute("cached")) {
-                       return true;
-               }
-
-               // Visibility
-               boolean visible = !uidl.getBooleanAttribute("invisible");
-               boolean wasVisible = component.isVisible();
-               component.setVisible(visible);
-               if (wasVisible != visible) {
-                       // Changed invisibile <-> visible
-                       if (wasVisible && manageCaption) {
-                               // Must hide caption when component is hidden
-                               final Container parent = Util.getLayout(component);
-                               if (parent != null) {
-                                       parent.updateCaption((Paintable) component, uidl);
-                               }
-
-                       }
-               }
-
-               if (!visible) {
-                       // component is invisible, delete old size to notify parent, if
-                       // later make visible
-                       componentDetail.setOffsetSize(null);
-                       return true;
-               }
-
-               // Switch to correct implementation if needed
-               if (!widgetSet.isCorrectImplementation(component, uidl, configuration)) {
-                       final Container parent = Util.getLayout(component);
-                       if (parent != null) {
-                               final Widget w = (Widget) widgetSet.createWidget(uidl,
-                                               configuration);
-                               parent.replaceChildComponent(component, w);
-                               unregisterPaintable((Paintable) component);
-                               registerPaintable(uidl.getId(), (Paintable) w);
-                               ((Paintable) w).updateFromUIDL(uidl, this);
-                               return true;
-                       }
-               }
-
-               boolean enabled = !uidl.getBooleanAttribute("disabled");
-               if (component instanceof FocusWidget) {
-                       FocusWidget fw = (FocusWidget) component;
-                       if (uidl.hasAttribute("tabindex")) {
-                               fw.setTabIndex(uidl.getIntAttribute("tabindex"));
-                       }
-                       // Disabled state may affect tabindex
-                       fw.setEnabled(enabled);
-               }
-
-               StringBuffer styleBuf = new StringBuffer();
-               final String primaryName = component.getStylePrimaryName();
-               styleBuf.append(primaryName);
-
-               // first disabling and read-only status
-               if (!enabled) {
-                       styleBuf.append(" ");
-                       styleBuf.append("v-disabled");
-               }
-               if (uidl.getBooleanAttribute("readonly")) {
-                       styleBuf.append(" ");
-                       styleBuf.append("v-readonly");
-               }
-
-               // add additional styles as css classes, prefixed with component default
-               // stylename
-               if (uidl.hasAttribute("style")) {
-                       final String[] styles = uidl.getStringAttribute("style").split(" ");
-                       for (int i = 0; i < styles.length; i++) {
-                               styleBuf.append(" ");
-                               styleBuf.append(primaryName);
-                               styleBuf.append("-");
-                               styleBuf.append(styles[i]);
-                               styleBuf.append(" ");
-                               styleBuf.append(styles[i]);
-                       }
-               }
-
-               // add modified classname to Fields
-               if (uidl.hasAttribute("modified") && component instanceof Field) {
-                       styleBuf.append(" ");
-                       styleBuf.append(MODIFIED_CLASSNAME);
-               }
-
-               TooltipInfo tooltipInfo = componentDetail.getTooltipInfo(null);
-               // Update tooltip
-               if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) {
-                       tooltipInfo
-                                       .setTitle(uidl.getStringAttribute(ATTRIBUTE_DESCRIPTION));
-               } else {
-                       tooltipInfo.setTitle(null);
-               }
-
-               // add error classname to components w/ error
-               if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
-                       tooltipInfo.setErrorUidl(uidl.getErrors());
-                       styleBuf.append(" ");
-                       styleBuf.append(primaryName);
-                       styleBuf.append(ERROR_CLASSNAME_EXT);
-               } else {
-                       tooltipInfo.setErrorUidl(null);
-               }
-
-               // add required style to required components
-               if (uidl.hasAttribute("required")) {
-                       styleBuf.append(" ");
-                       styleBuf.append(primaryName);
-                       styleBuf.append(REQUIRED_CLASSNAME_EXT);
-               }
-
-               // Styles + disabled & readonly
-               component.setStyleName(styleBuf.toString());
-
-               // Set captions
-               if (manageCaption) {
-                       final Container parent = Util.getLayout(component);
-                       if (parent != null) {
-                               parent.updateCaption((Paintable) component, uidl);
-                       }
-               }
-
-               if (configuration.useDebugIdInDOM() && uidl.getId().startsWith("PID_S")) {
-                       DOM.setElementProperty(component.getElement(), "id", uidl.getId()
-                                       .substring(5));
-               }
-
-               /*
-                * updateComponentSize need to be after caption update so caption can be
-                * taken into account
-                */
-
-               updateComponentSize(componentDetail, uidl);
-
-               return false;
-       }
-
-       private void updateComponentSize(ComponentDetail cd, UIDL uidl) {
-               String w = uidl.hasAttribute("width") ? uidl
-                               .getStringAttribute("width") : "";
-
-               String h = uidl.hasAttribute("height") ? uidl
-                               .getStringAttribute("height") : "";
-
-               float relativeWidth = Util.parseRelativeSize(w);
-               float relativeHeight = Util.parseRelativeSize(h);
-
-               // First update maps so they are correct in the setHeight/setWidth calls
-               if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
-                       // One or both is relative
-                       FloatSize relativeSize = new FloatSize(relativeWidth,
-                                       relativeHeight);
-                       if (cd.getRelativeSize() == null && cd.getOffsetSize() != null) {
-                               // The component has changed from absolute size to relative size
-                               relativeSizeChanges.add(cd.getComponent());
-                       }
-                       cd.setRelativeSize(relativeSize);
-               } else if (relativeHeight < 0.0 && relativeWidth < 0.0) {
-                       if (cd.getRelativeSize() != null) {
-                               // The component has changed from relative size to absolute size
-                               relativeSizeChanges.add(cd.getComponent());
-                       }
-                       cd.setRelativeSize(null);
-               }
-
-               Widget component = (Widget) cd.getComponent();
-               // Set absolute sizes
-               if (relativeHeight < 0.0) {
-                       component.setHeight(h);
-               }
-               if (relativeWidth < 0.0) {
-                       component.setWidth(w);
-               }
-
-               // Set relative sizes
-               if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
-                       // One or both is relative
-                       handleComponentRelativeSize(cd);
-               }
-
-       }
-
-       /**
-        * Traverses recursively child widgets until ContainerResizedListener child
-        * widget is found. They will delegate it further if needed.
-        
-        * @param container
-        */
-       private boolean runningLayout = false;
-
-       public void runDescendentsLayout(HasWidgets container) {
-               if (runningLayout) {
-                       // getConsole().log(
-                       // "Already running descendents layout. Not running again for "
-                       // + Util.getSimpleName(container));
-                       return;
-               }
-               runningLayout = true;
-               internalRunDescendentsLayout(container);
-               runningLayout = false;
-       }
-
-       /**
-        * This will cause re-layouting of all components. Mainly used for
-        * development. Published to JavaScript.
-        */
-       public void forceLayout() {
-               Set<Paintable> set = new HashSet<Paintable>();
-               for (ComponentDetail cd : idToPaintableDetail.values()) {
-                       set.add(cd.getComponent());
-               }
-               Util.componentSizeUpdated(set);
-       }
-
-       private void internalRunDescendentsLayout(HasWidgets container) {
-               // getConsole().log(
-               // "runDescendentsLayout(" + Util.getSimpleName(container) + ")");
-               final Iterator<Widget> childWidgets = container.iterator();
-               while (childWidgets.hasNext()) {
-                       final Widget child = childWidgets.next();
-
-                       if (child instanceof Paintable) {
-
-                               if (handleComponentRelativeSize(child)) {
-                                       /*
-                                        * Only need to propagate event if "child" has a relative
-                                        * size
-                                        */
-
-                                       if (child instanceof ContainerResizedListener) {
-                                               ((ContainerResizedListener) child).iLayout();
-                                       }
-
-                                       if (child instanceof HasWidgets) {
-                                               final HasWidgets childContainer = (HasWidgets) child;
-                                               internalRunDescendentsLayout(childContainer);
-                                       }
-                               }
-                       } else if (child instanceof HasWidgets) {
-                               // propagate over non Paintable HasWidgets
-                               internalRunDescendentsLayout((HasWidgets) child);
-                       }
-
-               }
-       }
-
-       /**
-        * Converts relative sizes into pixel sizes.
-        
-        * @param child
-        * @return true if the child has a relative size
-        */
-       private boolean handleComponentRelativeSize(ComponentDetail cd) {
-               if (cd == null) {
-                       return false;
-               }
-               boolean debugSizes = false;
-
-               FloatSize relativeSize = cd.getRelativeSize();
-               if (relativeSize == null) {
-                       return false;
-               }
-               Widget widget = (Widget) cd.getComponent();
-
-               boolean horizontalScrollBar = false;
-               boolean verticalScrollBar = false;
-
-               Container parent = Util.getLayout(widget);
-               RenderSpace renderSpace;
-
-               // Parent-less components (like sub-windows) are relative to browser
-               // window.
-               if (parent == null) {
-                       renderSpace = new RenderSpace(Window.getClientWidth(), Window
-                                       .getClientHeight());
-               } else {
-                       renderSpace = parent.getAllocatedSpace(widget);
-               }
-
-               if (relativeSize.getHeight() >= 0) {
-                       if (renderSpace != null) {
-
-                               if (renderSpace.getScrollbarSize() > 0) {
-                                       if (relativeSize.getWidth() > 100) {
-                                               horizontalScrollBar = true;
-                                       } else if (relativeSize.getWidth() < 0
-                                                       && renderSpace.getWidth() > 0) {
-                                               int offsetWidth = widget.getOffsetWidth();
-                                               int width = renderSpace.getWidth();
-                                               if (offsetWidth > width) {
-                                                       horizontalScrollBar = true;
-                                               }
-                                       }
-                               }
-
-                               int height = renderSpace.getHeight();
-                               if (horizontalScrollBar) {
-                                       height -= renderSpace.getScrollbarSize();
-                               }
-                               if (validatingLayouts && height <= 0) {
-                                       zeroHeightComponents.add(cd.getComponent());
-                               }
-
-                               height = (int) (height * relativeSize.getHeight() / 100.0);
-
-                               if (height < 0) {
-                                       height = 0;
-                               }
-
-                               if (debugSizes) {
-                                       getConsole()
-                                                       .log(
-                                                                       "Widget "
-                                                                                       + Util.getSimpleName(widget)
-                                                                                       + "/"
-                                                                                       + getPid(widget.getElement())
-                                                                                       + " relative height "
-                                                                                       + relativeSize.getHeight()
-                                                                                       + "% of "
-                                                                                       + renderSpace.getHeight()
-                                                                                       + "px (reported by "
-
-                                                                                       + Util.getSimpleName(parent)
-                                                                                       + "/"
-                                                                                       + (parent == null ? "?" : parent
-                                                                                                       .hashCode()) + ") : "
-                                                                                       + height + "px");
-                               }
-                               widget.setHeight(height + "px");
-                       } else {
-                               widget.setHeight(relativeSize.getHeight() + "%");
-                               ApplicationConnection.getConsole().error(
-                                               Util.getLayout(widget).getClass().getName()
-                                                               + " did not produce allocatedSpace for "
-                                                               + widget.getClass().getName());
-                       }
-               }
-
-               if (relativeSize.getWidth() >= 0) {
-
-                       if (renderSpace != null) {
-
-                               int width = renderSpace.getWidth();
-
-                               if (renderSpace.getScrollbarSize() > 0) {
-                                       if (relativeSize.getHeight() > 100) {
-                                               verticalScrollBar = true;
-                                       } else if (relativeSize.getHeight() < 0
-                                                       && renderSpace.getHeight() > 0
-                                                       && widget.getOffsetHeight() > renderSpace
-                                                                       .getHeight()) {
-                                               verticalScrollBar = true;
-                                       }
-                               }
-
-                               if (verticalScrollBar) {
-                                       width -= renderSpace.getScrollbarSize();
-                               }
-                               if (validatingLayouts && width <= 0) {
-                                       zeroWidthComponents.add(cd.getComponent());
-                               }
-
-                               width = (int) (width * relativeSize.getWidth() / 100.0);
-
-                               if (width < 0) {
-                                       width = 0;
-                               }
-
-                               if (debugSizes) {
-                                       getConsole().log(
-                                                       "Widget " + Util.getSimpleName(widget) + "/"
-                                                                       + getPid(widget.getElement())
-                                                                       + " relative width "
-                                                                       + relativeSize.getWidth() + "% of "
-                                                                       + renderSpace.getWidth()
-                                                                       + "px (reported by "
-                                                                       + Util.getSimpleName(parent) + "/"
-                                                                       + (parent == null ? "?" : getPid(parent))
-                                                                       + ") : " + width + "px");
-                               }
-                               widget.setWidth(width + "px");
-                       } else {
-                               widget.setWidth(relativeSize.getWidth() + "%");
-                               ApplicationConnection.getConsole().error(
-                                               Util.getLayout(widget).getClass().getName()
-                                                               + " did not produce allocatedSpace for "
-                                                               + widget.getClass().getName());
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * Converts relative sizes into pixel sizes.
-        
-        * @param child
-        * @return true if the child has a relative size
-        */
-       public boolean handleComponentRelativeSize(Widget child) {
-               return handleComponentRelativeSize(idToPaintableDetail.get(getPid(child
-                               .getElement())));
-
-       }
-
-       public FloatSize getRelativeSize(Widget widget) {
-               return idToPaintableDetail.get(getPid(widget.getElement()))
-                               .getRelativeSize();
-       }
-
-       /**
-        * Get either existing or new Paintable for given UIDL.
-        
-        * If corresponding Paintable has been previously painted, return it.
-        * Otherwise create and register a new Paintable from UIDL. Caller must
-        * update the returned Paintable from UIDL after it has been connected to
-        * parent.
-        
-        * @param uidl
-        *            UIDL to create Paintable from.
-        * @return Either existing or new Paintable corresponding to UIDL.
-        */
-       public Paintable getPaintable(UIDL uidl) {
-               final String id = uidl.getId();
-               Paintable w = getPaintable(id);
-               if (w != null) {
-                       return w;
-               } else {
-                       w = widgetSet.createWidget(uidl, configuration);
-                       registerPaintable(id, w);
-                       return w;
-
-               }
-       }
-
-       /**
-        * Returns a Paintable element by its root element
-        
-        * @param element
-        *            Root element of the paintable
-        */
-       public Paintable getPaintable(Element element) {
-               return getPaintable(getPid(element));
-       }
-
-       public String getResource(String name) {
-               return resourcesMap.get(name);
-       }
-
-       /**
-        * Singleton method to get instance of app's context menu.
-        
-        * @return VContextMenu object
-        */
-       public VContextMenu getContextMenu() {
-               if (contextMenu == null) {
-                       contextMenu = new VContextMenu();
-                       DOM.setElementProperty(contextMenu.getElement(), "id",
-                                       "PID_VAADIN_CM");
-               }
-               return contextMenu;
-       }
-
-       /**
-        * Translates custom protocols in UIDL URI's to be recognizable by browser.
-        * All uri's from UIDL should be routed via this method before giving them
-        * to browser due URI's in UIDL may contain custom protocols like theme://.
-        
-        * @param uidlUri
-        *            Vaadin URI from uidl
-        * @return translated URI ready for browser
-        */
-       public String translateVaadinUri(String uidlUri) {
-               if (uidlUri == null) {
-                       return null;
-               }
-               if (uidlUri.startsWith("theme://")) {
-                       final String themeUri = configuration.getThemeUri();
-                       if (themeUri == null) {
-                               console
-                                               .error("Theme not set: ThemeResource will not be found. ("
-                                                               + uidlUri + ")");
-                       }
-                       uidlUri = themeUri + uidlUri.substring(7);
-               }
-               return uidlUri;
-       }
-
-       public String getThemeUri() {
-               return configuration.getThemeUri();
-       }
-
-       /**
-        * Listens for Notification hide event, and redirects. Used for system
-        * messages, such as session expired.
-        
-        */
-       private class NotificationRedirect implements VNotification.EventListener {
-               String url;
-
-               NotificationRedirect(String url) {
-                       this.url = url;
-               }
-
-               public void notificationHidden(HideEvent event) {
-                       redirect(url);
-               }
-
-       }
-
-       /* Extended title handling */
-
-       /**
-        * Data showed in tooltips are stored centrilized as it may be needed in
-        * varios place: caption, layouts, and in owner components themselves.
-        
-        * Updating TooltipInfo is done in updateComponent method.
-        
-        */
-       public TooltipInfo getTooltipTitleInfo(Paintable titleOwner, Object key) {
-               if (null == titleOwner) {
-                       return null;
-               }
-               ComponentDetail cd = idToPaintableDetail.get(getPid(titleOwner));
-               if (null != cd) {
-                       return cd.getTooltipInfo(key);
-               } else {
-                       return null;
-               }
-       }
-
-       private final VTooltip tooltip = new VTooltip(this);
-
-       /**
-        * Component may want to delegate Tooltip handling to client. Layouts add
-        * Tooltip (description, errors) to caption, but some components may want
-        * them to appear one other elements too.
-        
-        * Events wanted by this handler are same as in Tooltip.TOOLTIP_EVENTS
-        
-        * @param event
-        * @param owner
-        */
-       public void handleTooltipEvent(Event event, Paintable owner) {
-               tooltip.handleTooltipEvent(event, owner, null);
-
-       }
-
-       /**
-        * Component may want to delegate Tooltip handling to client. Layouts add
-        * Tooltip (description, errors) to caption, but some components may want
-        * them to appear one other elements too.
-        
-        * Events wanted by this handler are same as in Tooltip.TOOLTIP_EVENTS
-        
-        * @param event
-        * @param owner
-        * @param key
-        *            the key for tooltip if this is "additional" tooltip, null for
-        *            components "main tooltip"
-        */
-       public void handleTooltipEvent(Event event, Paintable owner, Object key) {
-               tooltip.handleTooltipEvent(event, owner, key);
-
-       }
-
-       /**
-        * Adds PNG-fix conditionally (only for IE6) to the specified IMG -element.
-        
-        * @param el
-        *            the IMG element to fix
-        */
-       public void addPngFix(Element el) {
-               BrowserInfo b = BrowserInfo.get();
-               if (b.isIE6()) {
-                       Util.addPngFix(el, getThemeUri() + "/../runo/common/img/blank.gif");
-               }
-       }
-
-       /*
-        * Helper to run layout functions triggered by child components with a
-        * decent interval.
-        */
-       private final Timer layoutTimer = new Timer() {
-
-               private boolean isPending = false;
-
-               @Override
-               public void schedule(int delayMillis) {
-                       if (!isPending) {
-                               super.schedule(delayMillis);
-                               isPending = true;
-                       }
-               }
-
-               @Override
-               public void run() {
-                       getConsole().log(
-                                       "Running re-layout of " + view.getClass().getName());
-                       runDescendentsLayout(view);
-                       isPending = false;
-               }
-       };
-
-       /**
-        * Components can call this function to run all layout functions. This is
-        * usually done, when component knows that its size has changed.
-        */
-       public void requestLayoutPhase() {
-               layoutTimer.schedule(500);
-       }
-
-       private String windowName = null;
-
-       /**
-        * Reset the name of the current browser-window. This should reflect the
-        * window-name used in the server, but might be different from the
-        * window-object target-name on client.
-        
-        * @param stringAttribute
-        *            New name for the window.
-        */
-       public void setWindowName(String newName) {
-               windowName = newName;
-       }
-
-       public void captionSizeUpdated(Paintable component) {
-               componentCaptionSizeChanges.add(component);
-       }
-
-       public void analyzeLayouts() {
-               makeUidlRequest("", true, false, true);
-       }
-
-       public VView getView() {
-               return view;
-       }
-
-       /**
-        * If component has several tooltips in addition to the one provided by
-        * {@link com.vaadin.ui.AbstractComponent}, component can register them with
-        * this method.
-        * <p>
-        * Component must also pipe events to
-        * {@link #handleTooltipEvent(Event, Paintable, Object)} method.
-        * <p>
-        * This method can also be used to deregister tooltips by using null as
-        * tooltip
-        
-        * @param paintable
-        *            Paintable "owning" this tooltip
-        * @param key
-        *            key assosiated with given tooltip. Can be any object. For
-        *            example a related dom element. Same key must be given for
-        *            {@link #handleTooltipEvent(Event, Paintable, Object)} method.
-        
-        * @param tooltip
-        *            the TooltipInfo object containing details shown in tooltip,
-        *            null if deregistering tooltip
-        */
-       public void registerTooltip(Paintable paintable, Object key,
-                       TooltipInfo tooltip) {
-               ComponentDetail componentDetail = idToPaintableDetail
-                               .get(getPid(paintable));
-               componentDetail.putAdditionalTooltip(key, tooltip);
-       }
-
-       public ApplicationConfiguration getConfiguration() {
-               return configuration;
-       }
+    public ApplicationConnection(WidgetSet widgetSet,
+            ApplicationConfiguration cnf) {
+        this.widgetSet = widgetSet;
+        configuration = cnf;
+        windowName = configuration.getInitialWindowName();
+        if (isDebugMode()) {
+            console = new VDebugConsole(this, cnf, !isQuietDebugMode());
+        } else {
+            console = new NullConsole();
+        }
+
+        ComponentLocator componentLocator = new ComponentLocator(this);
+
+        String appRootPanelName = cnf.getRootPanelId();
+        // remove the end (window name) of autogenerated rootpanel id
+        appRootPanelName = appRootPanelName.replaceFirst("-\\d+$", "");
+
+        initializeTestbenchHooks(componentLocator, appRootPanelName);
+
+        initializeClientHooks();
+
+        view = new VView(cnf.getRootPanelId());
+        showLoadingIndicator();
+
+    }
+
+    /**
+     * Starts this application. Don't call this method directly - it's called by
+     * {@link ApplicationConfiguration#startNextApplication()}, which should be
+     * called once this application has started (first response received) or
+     * failed to start. This ensures that the applications are started in order,
+     * to avoid session-id problems.
+     */
+    void start() {
+        makeUidlRequest("", true, false, false);
+    }
+
+    private native void initializeTestbenchHooks(
+            ComponentLocator componentLocator, String TTAppId)
+    /*-{
+        var ap = this;
+        var client = {};
+        client.isActive = function() {
+            return ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::hasActiveRequest()() || ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::isLoadingIndicatorVisible()();
+        }
+        var vi = ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::getVersionInfo()();
+        if (vi) {
+            client.getVersionInfo = function() {
+                return vi;
+            }
+        }
+
+        client.getElementByPath = function(id) {
+           return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
+        }
+        client.getPathForElement = function(element) {
+           return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
+        }
+
+        if(!$wnd.vaadin.clients) {
+           $wnd.vaadin.clients = {};
+        }
+
+        $wnd.vaadin.clients[TTAppId] = client;
+    }-*/;
+
+    /**
+     * Helper for tt initialization
+     */
+    @SuppressWarnings("unused")
+    private JavaScriptObject getVersionInfo() {
+        return configuration.getVersionInfoJSObject();
+    }
+
+    /**
+     * Publishes a JavaScript API for mash-up applications.
+     * <ul>
+     * <li><code>vaadin.forceSync()</code> sends pending variable changes, in
+     * effect synchronizing the server and client state. This is done for all
+     * applications on host page.</li>
+     * <li><code>vaadin.postRequestHooks</code> is a map of functions which gets
+     * called after each XHR made by vaadin application. Note, that it is
+     * attaching js functions responsibility to create the variable like this:
+     * 
+     * <code><pre>
+     * if(!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();}
+     * postRequestHooks.myHook = function(appId) {
+     *          if(appId == "MyAppOfInterest") {
+     *                  // do the staff you need on xhr activity
+     *          }
+     * }
+     * </pre></code> First parameter passed to these functions is the identifier
+     * of Vaadin application that made the request.
+     * </ul>
+     * 
+     * TODO make this multi-app aware
+     */
+    private native void initializeClientHooks()
+    /*-{
+        var app = this;
+        var oldSync;
+        if($wnd.vaadin.forceSync) {
+            oldSync = $wnd.vaadin.forceSync;
+        }
+        $wnd.vaadin.forceSync = function() {
+            if(oldSync) {
+                oldSync();
+            }
+            app.@com.vaadin.terminal.gwt.client.ApplicationConnection::sendPendingVariableChanges()();
+        }
+        var oldForceLayout;
+        if($wnd.vaadin.forceLayout) {
+            oldForceLayout = $wnd.vaadin.forceLayout;
+        }
+        $wnd.vaadin.forceLayout = function() {
+            if(oldForceLayout) {
+                oldForceLayout();
+            }
+            app.@com.vaadin.terminal.gwt.client.ApplicationConnection::forceLayout()();
+        }
+    }-*/;
+
+    /**
+     * Runs possibly registered client side post request hooks. This is expected
+     * to be run after each uidl request made by Vaadin application.
+     * 
+     * @param appId
+     */
+    private static native void runPostRequestHooks(String appId)
+    /*-{
+        if($wnd.vaadin.postRequestHooks) {
+            for(var hook in $wnd.vaadin.postRequestHooks) {
+                if(typeof($wnd.vaadin.postRequestHooks[hook]) == "function") {
+                    try {
+                        $wnd.vaadin.postRequestHooks[hook](appId);
+                    } catch(e) {}
+                }
+            }
+        }
+    }-*/;
+
+    public static Console getConsole() {
+        return console;
+    }
+
+    /**
+     * Checks if client side is in debug mode. Practically this is invoked by
+     * adding ?debug parameter to URI.
+     * 
+     * @return true if client side is currently been debugged
+     */
+    public native static boolean isDebugMode()
+    /*-{
+        if($wnd.vaadin.debug) {
+            var parameters = $wnd.location.search;
+            var re = /debug[^\/]*$/;
+            return re.test(parameters);
+        } else {
+            return false;
+        }
+    }-*/;
+
+    private native static boolean isQuietDebugMode()
+    /*-{
+        var uri = $wnd.location;
+        var re = /debug=q[^\/]*$/;
+        return re.test(uri);
+    }-*/;
+
+    public String getAppUri() {
+        return configuration.getApplicationUri();
+    };
+
+    public boolean hasActiveRequest() {
+        return (activeRequests > 0);
+    }
+
+    private void makeUidlRequest(final String requestData,
+            final boolean repaintAll, final boolean forceSync,
+            final boolean analyzeLayouts) {
+        startRequest();
+
+        // Security: double cookie submission pattern
+        final String rd = uidl_security_key + VAR_BURST_SEPARATOR + requestData;
+
+        console.log("Making UIDL Request with params: " + rd);
+        String uri = getAppUri() + "UIDL" + configuration.getPathInfo();
+        if (repaintAll) {
+            // collect some client side data that will be sent to server on
+            // initial uidl request
+            int clientHeight = Window.getClientHeight();
+            int clientWidth = Window.getClientWidth();
+            com.google.gwt.dom.client.Element pe = view.getElement()
+                    .getParentElement();
+            int offsetHeight = pe.getOffsetHeight();
+            int offsetWidth = pe.getOffsetWidth();
+            int screenWidth = BrowserInfo.get().getScreenWidth();
+            int screenHeight = BrowserInfo.get().getScreenHeight();
+
+            String token = History.getToken();
+
+            // TODO figure out how client and view size could be used better on
+            // server. screen size can be accessed via Browser object, but other
+            // values currently only via transaction listener.
+            uri += "?repaintAll=1&" + "sh=" + screenHeight + "&sw="
+                    + screenWidth + "&cw=" + clientWidth + "&ch="
+                    + clientHeight + "&vw=" + offsetWidth + "&vh="
+                    + offsetHeight + "&fr=" + token;
+            if (analyzeLayouts) {
+                uri += "&analyzeLayouts=1";
+            }
+        }
+        if (windowName != null && windowName.length() > 0) {
+            uri += (repaintAll ? "&" : "?") + "windowName=" + windowName;
+        }
+
+        if (!forceSync) {
+            boolean success = false;
+            final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
+                    uri);
+            // TODO enable timeout
+            // rb.setTimeoutMillis(timeoutMillis);
+            rb.setHeader("Content-Type", "text/plain;charset=utf-8");
+            try {
+                rb.sendRequest(rd, new RequestCallback() {
+                    public void onError(Request request, Throwable exception) {
+                        showCommunicationError(exception.getMessage());
+                        endRequest();
+                        if (!applicationRunning) {
+                            // start failed, let's try to start the next app
+                            ApplicationConfiguration.startNextApplication();
+                        }
+                    }
+
+                    public void onResponseReceived(Request request,
+                            Response response) {
+                        console.log("Server visit took "
+                                + String.valueOf((new Date()).getTime()
+                                        - requestStartTime.getTime()) + "ms");
+
+                        switch (response.getStatusCode()) {
+                        case 0:
+                            showCommunicationError("Invalid status code 0 (server down?)");
+                            return;
+                            // TODO could add more cases
+                        case 503:
+                            // We'll assume msec instead of the usual seconds
+                            int delay = Integer.parseInt(response
+                                    .getHeader("Retry-After"));
+                            console.log("503, retrying in " + delay + "msec");
+                            (new Timer() {
+                                @Override
+                                public void run() {
+                                    activeRequests--;
+                                    makeUidlRequest(requestData, repaintAll,
+                                            forceSync, analyzeLayouts);
+                                }
+                            }).schedule(delay);
+                            return;
+
+                        }
+
+                        if (applicationRunning) {
+                            handleReceivedJSONMessage(response);
+                        } else {
+                            applicationRunning = true;
+                            handleWhenCSSLoaded(response);
+                            ApplicationConfiguration.startNextApplication();
+                        }
+                    }
+
+                    int cssWaits = 0;
+                    static final int MAX_CSS_WAITS = 20;
+
+                    private void handleWhenCSSLoaded(final Response response) {
+                        int heightOfLoadElement = DOM.getElementPropertyInt(
+                                loadElement, "offsetHeight");
+                        if (heightOfLoadElement == 0
+                                && cssWaits < MAX_CSS_WAITS) {
+                            (new Timer() {
+                                @Override
+                                public void run() {
+                                    handleWhenCSSLoaded(response);
+                                }
+                            }).schedule(50);
+                            console
+                                    .log("Assuming CSS loading is not complete, "
+                                            + "postponing render phase. "
+                                            + "(.v-loading-indicator height == 0)");
+                            cssWaits++;
+                        } else {
+                            handleReceivedJSONMessage(response);
+                            if (cssWaits >= MAX_CSS_WAITS) {
+                                console
+                                        .error("CSS files may have not loaded properly.");
+                            }
+                        }
+                    }
+
+                });
+
+            } catch (final RequestException e) {
+                ClientExceptionHandler.displayError(e);
+                endRequest();
+            }
+        } else {
+            // Synchronized call, discarded response
+
+            syncSendForce(((HTTPRequestImpl) GWT.create(HTTPRequestImpl.class))
+                    .createXmlHTTPRequest(), uri + "&" + PARAM_UNLOADBURST
+                    + "=1", rd);
+        }
+    }
+
+    /**
+     * Shows the communication error notification. The 'details' only go to the
+     * console for now.
+     * 
+     * @param details
+     *            Optional details for debugging.
+     */
+    private void showCommunicationError(String details) {
+        console.error("Communication error: " + details);
+        String html = "";
+        if (configuration.getCommunicationErrorCaption() != null) {
+            html += "<h1>" + configuration.getCommunicationErrorCaption()
+                    + "</h1>";
+        }
+        if (configuration.getCommunicationErrorMessage() != null) {
+            html += "<p>" + configuration.getCommunicationErrorMessage()
+                    + "</p>";
+        }
+        if (html.length() > 0) {
+            VNotification n = new VNotification(1000 * 60 * 45);
+            n.addEventListener(new NotificationRedirect(configuration
+                    .getCommunicationErrorUrl()));
+            n
+                    .show(html, VNotification.CENTERED_TOP,
+                            VNotification.STYLE_SYSTEM);
+        } else {
+            redirect(configuration.getCommunicationErrorUrl());
+        }
+    }
+
+    private native void syncSendForce(JavaScriptObject xmlHttpRequest,
+            String uri, String requestData)
+    /*-{
+        try {
+            xmlHttpRequest.open("POST", uri, false);
+            xmlHttpRequest.setRequestHeader("Content-Type", "text/plain;charset=utf-8");
+            xmlHttpRequest.send(requestData);
+        } catch (e) {
+           // No errors are managed as this is synchronous forceful send that can just fail
+        }
+        this.@com.vaadin.terminal.gwt.client.ApplicationConnection::endRequest()();
+    }-*/;
+
+    private void startRequest() {
+        activeRequests++;
+        requestStartTime = new Date();
+        // show initial throbber
+        if (loadTimer == null) {
+            loadTimer = new Timer() {
+                @Override
+                public void run() {
+                    /*
+                     * IE7 does not properly cancel the event with
+                     * loadTimer.cancel() so we have to check that we really
+                     * should make it visible
+                     */
+                    if (loadTimer != null) {
+                        showLoadingIndicator();
+                    }
+
+                }
+            };
+            // First one kicks in at 300ms
+        }
+        loadTimer.schedule(300);
+    }
+
+    private void endRequest() {
+        if (applicationRunning) {
+            checkForPendingVariableBursts();
+            runPostRequestHooks(configuration.getRootPanelId());
+        }
+        activeRequests--;
+        // deferring to avoid flickering
+        DeferredCommand.addCommand(new Command() {
+            public void execute() {
+                if (activeRequests == 0) {
+                    hideLoadingIndicator();
+                }
+            }
+        });
+    }
+
+    /**
+     * This method is called after applying uidl change set to application.
+     * 
+     * It will clean current and queued variable change sets. And send next
+     * change set if it exists.
+     */
+    private void checkForPendingVariableBursts() {
+        cleanVariableBurst(pendingVariables);
+        if (pendingVariableBursts.size() > 0) {
+            for (Iterator<ArrayList<String>> iterator = pendingVariableBursts
+                    .iterator(); iterator.hasNext();) {
+                cleanVariableBurst(iterator.next());
+            }
+            ArrayList<String> nextBurst = pendingVariableBursts.get(0);
+            pendingVariableBursts.remove(0);
+            buildAndSendVariableBurst(nextBurst, false);
+        }
+    }
+
+    /**
+     * Cleans given queue of variable changes of such changes that came from
+     * components that do not exist anymore.
+     * 
+     * @param variableBurst
+     */
+    private void cleanVariableBurst(ArrayList<String> variableBurst) {
+        for (int i = 1; i < variableBurst.size(); i += 2) {
+            String id = variableBurst.get(i);
+            id = id.substring(0, id.indexOf(VAR_FIELD_SEPARATOR));
+            if (!idToPaintableDetail.containsKey(id)) {
+                // variable owner does not exist anymore
+                variableBurst.remove(i - 1);
+                variableBurst.remove(i - 1);
+                i -= 2;
+                ApplicationConnection.getConsole().log(
+                        "Removed variable from removed component: " + id);
+            }
+        }
+    }
+
+    private void showLoadingIndicator() {
+        // show initial throbber
+        if (loadElement == null) {
+            loadElement = DOM.createDiv();
+            DOM.setStyleAttribute(loadElement, "position", "absolute");
+            DOM.appendChild(view.getElement(), loadElement);
+            ApplicationConnection.getConsole().log("inserting load indicator");
+        }
+        DOM.setElementProperty(loadElement, "className", "v-loading-indicator");
+        DOM.setStyleAttribute(loadElement, "display", "block");
+        // Initialize other timers
+        loadTimer2 = new Timer() {
+            @Override
+            public void run() {
+                DOM.setElementProperty(loadElement, "className",
+                        "v-loading-indicator-delay");
+            }
+        };
+        // Second one kicks in at 1500ms from request start
+        loadTimer2.schedule(1200);
+
+        loadTimer3 = new Timer() {
+            @Override
+            public void run() {
+                DOM.setElementProperty(loadElement, "className",
+                        "v-loading-indicator-wait");
+            }
+        };
+        // Third one kicks in at 5000ms from request start
+        loadTimer3.schedule(4700);
+    }
+
+    private void hideLoadingIndicator() {
+        if (loadTimer != null) {
+            loadTimer.cancel();
+            if (loadTimer2 != null) {
+                loadTimer2.cancel();
+                loadTimer3.cancel();
+            }
+            loadTimer = null;
+        }
+        if (loadElement != null) {
+            DOM.setStyleAttribute(loadElement, "display", "none");
+        }
+    }
+
+    public boolean isLoadingIndicatorVisible() {
+        if (loadElement == null) {
+            return false;
+        }
+        if (loadElement.getStyle().getProperty("display").equals("none")) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private static native ValueMap parseJSONResponse(String jsonText)
+    /*-{
+        try {
+            return JSON.parse(jsonText);
+        } catch(ignored) {
+            return eval('(' + jsonText + ')');
+        }
+    }-*/;
+
+    private void handleReceivedJSONMessage(Response response) {
+        final Date start = new Date();
+        String jsonText = response.getText();
+        // for(;;);[realjson]
+        jsonText = jsonText.substring(9, jsonText.length() - 1);
+        ValueMap json;
+        try {
+            json = parseJSONResponse(jsonText);
+        } catch (final Exception e) {
+            endRequest();
+            showCommunicationError(e.getMessage() + " - Original JSON-text:");
+            console.log(jsonText);
+            return;
+        }
+
+        ApplicationConnection.getConsole()
+                .log(
+                        "JSON parsing took "
+                                + (new Date().getTime() - start.getTime()));
+        // Handle redirect
+        if (json.containsKey("redirect")) {
+            String url = json.getValueMap("redirect").getString("url");
+            console.log("redirecting to " + url);
+            redirect(url);
+            return;
+        }
+
+        // Get security key
+        if (json.containsKey(UIDL_SECURITY_TOKEN_ID)) {
+            uidl_security_key = json.getString(UIDL_SECURITY_TOKEN_ID);
+        }
+
+        if (json.containsKey("resources")) {
+            ValueMap resources = json.getValueMap("resources");
+            JsArrayString keyArray = resources.getKeyArray();
+            int l = keyArray.length();
+            for (int i = 0; i < l; i++) {
+                String key = keyArray.get(i);
+                resourcesMap.put(key, resources.getAsString(key));
+            }
+        }
+
+        if (json.containsKey("typeMappings")) {
+            configuration.addComponentMappings(
+                    json.getValueMap("typeMappings"), widgetSet);
+        }
+
+        if (json.containsKey("locales")) {
+            // Store locale data
+            JsArray<ValueMap> valueMapArray = json
+                    .getJSValueMapArray("locales");
+            LocaleService.addLocales(valueMapArray);
+        }
+
+        ValueMap meta = null;
+        if (json.containsKey("meta")) {
+            meta = json.getValueMap("meta");
+            if (meta.containsKey("repaintAll")) {
+                view.clear();
+                idToPaintableDetail.clear();
+                if (meta.containsKey("invalidLayouts")) {
+                    validatingLayouts = true;
+                    zeroWidthComponents = new HashSet<Paintable>();
+                    zeroHeightComponents = new HashSet<Paintable>();
+                }
+            }
+            if (meta.containsKey("timedRedirect")) {
+                final ValueMap timedRedirect = meta
+                        .getValueMap("timedRedirect");
+                redirectTimer = new Timer() {
+                    @Override
+                    public void run() {
+                        redirect(timedRedirect.getString("url"));
+                    }
+                };
+                sessionExpirationInterval = timedRedirect.getInt("interval");
+            }
+        }
+
+        if (redirectTimer != null) {
+            redirectTimer.schedule(1000 * sessionExpirationInterval);
+        }
+
+        // Process changes
+        JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
+
+        ArrayList<Paintable> updatedWidgets = new ArrayList<Paintable>();
+        relativeSizeChanges.clear();
+        componentCaptionSizeChanges.clear();
+
+        int length = changes.length();
+        for (int i = 0; i < length; i++) {
+            try {
+                final UIDL change = changes.get(i).cast();
+                try {
+                    console.dirUIDL(change);
+                } catch (final Exception e) {
+                    ClientExceptionHandler.displayError(e);
+                    // TODO: dir doesn't work in any browser although it should
+                    // work (works in hosted mode)
+                    // it partially did at some part but now broken.
+                }
+                final UIDL uidl = change.getChildUIDL(0);
+                // TODO optimize
+                final Paintable paintable = getPaintable(uidl.getId());
+                if (paintable != null) {
+                    paintable.updateFromUIDL(uidl, this);
+                    // paintable may have changed during render to another
+                    // implementation, use the new one for updated widgets map
+                    updatedWidgets.add(idToPaintableDetail.get(uidl.getId())
+                            .getComponent());
+                } else {
+                    if (!uidl.getTag().equals("0")) {
+                        ClientExceptionHandler
+                                .displayError("Received update for "
+                                        + uidl.getTag()
+                                        + ", but there is no such paintable ("
+                                        + uidl.getId() + ") rendered.");
+                    } else {
+                        view.updateFromUIDL(uidl, this);
+                    }
+                }
+            } catch (final Throwable e) {
+                ClientExceptionHandler.displayError(e);
+            }
+        }
+
+        // Check which widgets' size has been updated
+        Set<Paintable> sizeUpdatedWidgets = new HashSet<Paintable>();
+
+        updatedWidgets.addAll(relativeSizeChanges);
+        sizeUpdatedWidgets.addAll(componentCaptionSizeChanges);
+
+        for (Paintable paintable : updatedWidgets) {
+            ComponentDetail detail = idToPaintableDetail.get(getPid(paintable));
+            Widget widget = (Widget) paintable;
+            Size oldSize = detail.getOffsetSize();
+            Size newSize = new Size(widget.getOffsetWidth(), widget
+                    .getOffsetHeight());
+
+            if (oldSize == null || !oldSize.equals(newSize)) {
+                sizeUpdatedWidgets.add(paintable);
+                detail.setOffsetSize(newSize);
+            }
+
+        }
+
+        Util.componentSizeUpdated(sizeUpdatedWidgets);
+
+        if (meta != null) {
+            if (meta.containsKey("appError")) {
+                ValueMap error = meta.getValueMap("appError");
+                String html = "";
+                if (error.containsKey("caption")
+                        && error.getString("caption") != null) {
+                    html += "<h1>" + error.getAsString("caption") + "</h1>";
+                }
+                if (error.containsKey("message")
+                        && error.getString("message") != null) {
+                    html += "<p>" + error.getAsString("message") + "</p>";
+                }
+                String url = null;
+                if (error.containsKey("url")) {
+                    url = error.getString("url");
+                }
+
+                if (html.length() != 0) {
+                    /* 45 min */
+                    VNotification n = new VNotification(1000 * 60 * 45);
+                    n.addEventListener(new NotificationRedirect(url));
+                    n.show(html, VNotification.CENTERED_TOP,
+                            VNotification.STYLE_SYSTEM);
+                } else {
+                    redirect(url);
+                }
+                applicationRunning = false;
+            }
+            if (validatingLayouts) {
+                getConsole().printLayoutProblems(meta, this,
+                        zeroHeightComponents, zeroWidthComponents);
+                zeroHeightComponents = null;
+                zeroWidthComponents = null;
+                validatingLayouts = false;
+
+            }
+        }
+
+        final long prosessingTime = (new Date().getTime()) - start.getTime();
+        console.log(" Processing time was " + String.valueOf(prosessingTime)
+                + "ms for " + jsonText.length() + " characters of JSON");
+        console.log("Referenced paintables: " + idToPaintableDetail.size());
+
+        endRequest();
+    }
+
+    /**
+     * This method assures that all pending variable changes are sent to server.
+     * Method uses synchronized xmlhttprequest and does not return before the
+     * changes are sent. No UIDL updates are processed and thus UI is left in
+     * inconsistent state. This method should be called only when closing
+     * windows - normally sendPendingVariableChanges() should be used.
+     */
+    public void sendPendingVariableChangesSync() {
+        if (applicationRunning) {
+            pendingVariableBursts.add(pendingVariables);
+            ArrayList<String> nextBurst = pendingVariableBursts.get(0);
+            pendingVariableBursts.remove(0);
+            buildAndSendVariableBurst(nextBurst, true);
+        }
+    }
+
+    // Redirect browser, null reloads current page
+    private static native void redirect(String url)
+    /*-{
+        if (url) {
+           $wnd.location = url;
+        } else {
+            $wnd.location.reload(false);
+        }
+    }-*/;
+
+    public void registerPaintable(String id, Paintable paintable) {
+        ComponentDetail componentDetail = new ComponentDetail();
+        componentDetail.setComponent(paintable);
+        idToPaintableDetail.put(id, componentDetail);
+        setPid(((Widget) paintable).getElement(), id);
+    }
+
+    private native void setPid(Element el, String pid)
+    /*-{
+        el.tkPid = pid;
+    }-*/;
+
+    public String getPid(Paintable paintable) {
+        return getPid(((Widget) paintable).getElement());
+    }
+
+    public native String getPid(Element el)
+    /*-{
+        return el.tkPid;
+    }-*/;
+
+    public Element getElementByPid(String pid) {
+        return ((Widget) getPaintable(pid)).getElement();
+    }
+
+    public void unregisterPaintable(Paintable p) {
+        if (p == null) {
+            ApplicationConnection.getConsole().error(
+                    "WARN: Trying to unregister null paintable");
+            return;
+        }
+        String id = getPid(p);
+        idToPaintableDetail.remove(id);
+        if (p instanceof HasWidgets) {
+            unregisterChildPaintables((HasWidgets) p);
+        }
+    }
+
+    public void unregisterChildPaintables(HasWidgets container) {
+        final Iterator<Widget> it = container.iterator();
+        while (it.hasNext()) {
+            final Widget w = it.next();
+            if (w instanceof Paintable) {
+                unregisterPaintable((Paintable) w);
+            } else if (w instanceof HasWidgets) {
+                unregisterChildPaintables((HasWidgets) w);
+            }
+        }
+    }
+
+    /**
+     * Returns Paintable element by its id
+     * 
+     * @param id
+     *            Paintable ID
+     */
+    public Paintable getPaintable(String id) {
+        ComponentDetail componentDetail = idToPaintableDetail.get(id);
+        if (componentDetail == null) {
+            return null;
+        } else {
+            return componentDetail.getComponent();
+        }
+    }
+
+    private void addVariableToQueue(String paintableId, String variableName,
+            String encodedValue, boolean immediate, char type) {
+        final String id = paintableId + VAR_FIELD_SEPARATOR + variableName
+                + VAR_FIELD_SEPARATOR + type;
+        for (int i = 1; i < pendingVariables.size(); i += 2) {
+            if ((pendingVariables.get(i)).equals(id)) {
+                pendingVariables.remove(i - 1);
+                pendingVariables.remove(i - 1);
+                break;
+            }
+        }
+        pendingVariables.add(encodedValue);
+        pendingVariables.add(id);
+        if (immediate) {
+            sendPendingVariableChanges();
+        }
+    }
+
+    /**
+     * This method sends currently queued variable changes to server. It is
+     * called when immediate variable update must happen.
+     * 
+     * To ensure correct order for variable changes (due servers multithreading
+     * or network), we always wait for active request to be handler before
+     * sending a new one. If there is an active request, we will put varible
+     * "burst" to queue that will be purged after current request is handled.
+     * 
+     */
+    @SuppressWarnings("unchecked")
+    public void sendPendingVariableChanges() {
+        if (applicationRunning) {
+            if (hasActiveRequest()) {
+                // skip empty queues if there are pending bursts to be sent
+                if (pendingVariables.size() > 0
+                        || pendingVariableBursts.size() == 0) {
+                    ArrayList<String> burst = (ArrayList<String>) pendingVariables
+                            .clone();
+                    pendingVariableBursts.add(burst);
+                    pendingVariables.clear();
+                }
+            } else {
+                buildAndSendVariableBurst(pendingVariables, false);
+            }
+        }
+    }
+
+    /**
+     * Build the variable burst and send it to server.
+     * 
+     * When sync is forced, we also force sending of all pending variable-bursts
+     * at the same time. This is ok as we can assume that DOM will never be
+     * updated after this.
+     * 
+     * @param pendingVariables
+     *            Vector of variable changes to send
+     * @param forceSync
+     *            Should we use synchronous request?
+     */
+    private void buildAndSendVariableBurst(ArrayList<String> pendingVariables,
+            boolean forceSync) {
+        final StringBuffer req = new StringBuffer();
+
+        while (!pendingVariables.isEmpty()) {
+            for (int i = 0; i < pendingVariables.size(); i++) {
+                if (i > 0) {
+                    if (i % 2 == 0) {
+                        req.append(VAR_RECORD_SEPARATOR);
+                    } else {
+                        req.append(VAR_FIELD_SEPARATOR);
+                    }
+                }
+                req.append(pendingVariables.get(i));
+            }
+
+            pendingVariables.clear();
+            // Append all the busts to this synchronous request
+            if (forceSync && !pendingVariableBursts.isEmpty()) {
+                pendingVariables = pendingVariableBursts.get(0);
+                pendingVariableBursts.remove(0);
+                req.append(VAR_BURST_SEPARATOR);
+            }
+        }
+        makeUidlRequest(req.toString(), false, forceSync, false);
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            Paintable newValue, boolean immediate) {
+        String pid = (newValue != null) ? getPid(newValue) : null;
+        addVariableToQueue(paintableId, variableName, pid, immediate, 'p');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            String newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, newValue, immediate, 's');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            int newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
+                'i');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            long newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
+                'l');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            float newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
+                'f');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            double newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
+                'd');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            boolean newValue, boolean immediate) {
+        addVariableToQueue(paintableId, variableName, newValue ? "true"
+                : "false", immediate, 'b');
+    }
+
+    public void updateVariable(String paintableId, String variableName,
+            Object[] values, boolean immediate) {
+        final StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < values.length; i++) {
+            if (i > 0) {
+                buf.append(VAR_ARRAYITEM_SEPARATOR);
+            }
+            buf.append(values[i].toString());
+        }
+        addVariableToQueue(paintableId, variableName, buf.toString(),
+                immediate, 'a');
+    }
+
+    /**
+     * Update generic component features.
+     * 
+     * <h2>Selecting correct implementation</h2>
+     * 
+     * <p>
+     * The implementation of a component depends on many properties, including
+     * styles, component features, etc. Sometimes the user changes those
+     * properties after the component has been created. Calling this method in
+     * the beginning of your updateFromUIDL -method automatically replaces your
+     * component with more appropriate if the requested implementation changes.
+     * </p>
+     * 
+     * <h2>Caption, icon, error messages and description</h2>
+     * 
+     * <p>
+     * Component can delegate management of caption, icon, error messages and
+     * description to parent layout. This is optional an should be decided by
+     * component author
+     * </p>
+     * 
+     * <h2>Component visibility and disabling</h2>
+     * 
+     * This method will manage component visibility automatically and if
+     * component is an instanceof FocusWidget, also handle component disabling
+     * when needed.
+     * 
+     * @param component
+     *            Widget to be updated, expected to implement an instance of
+     *            Paintable
+     * @param uidl
+     *            UIDL to be painted
+     * @param manageCaption
+     *            True if you want to delegate caption, icon, description and
+     *            error message management to parent.
+     * 
+     * @return Returns true iff no further painting is needed by caller
+     */
+    public boolean updateComponent(Widget component, UIDL uidl,
+            boolean manageCaption) {
+        String pid = getPid(component.getElement());
+        if (pid == null) {
+            getConsole().error(
+                    "Trying to update an unregistered component: "
+                            + Util.getSimpleName(component));
+            return true;
+        }
+
+        ComponentDetail componentDetail = idToPaintableDetail.get(pid);
+
+        if (componentDetail == null) {
+            getConsole().error(
+                    "ComponentDetail not found for "
+                            + Util.getSimpleName(component) + " with PID "
+                            + pid + ". This should not happen.");
+            return true;
+        }
+
+        // If the server request that a cached instance should be used, do
+        // nothing
+        if (uidl.getBooleanAttribute("cached")) {
+            return true;
+        }
+
+        // Visibility
+        boolean visible = !uidl.getBooleanAttribute("invisible");
+        boolean wasVisible = component.isVisible();
+        component.setVisible(visible);
+        if (wasVisible != visible) {
+            // Changed invisibile <-> visible
+            if (wasVisible && manageCaption) {
+                // Must hide caption when component is hidden
+                final Container parent = Util.getLayout(component);
+                if (parent != null) {
+                    parent.updateCaption((Paintable) component, uidl);
+                }
+
+            }
+        }
+
+        if (!visible) {
+            // component is invisible, delete old size to notify parent, if
+            // later make visible
+            componentDetail.setOffsetSize(null);
+            return true;
+        }
+
+        // Switch to correct implementation if needed
+        if (!widgetSet.isCorrectImplementation(component, uidl, configuration)) {
+            final Container parent = Util.getLayout(component);
+            if (parent != null) {
+                final Widget w = (Widget) widgetSet.createWidget(uidl,
+                        configuration);
+                parent.replaceChildComponent(component, w);
+                unregisterPaintable((Paintable) component);
+                registerPaintable(uidl.getId(), (Paintable) w);
+                ((Paintable) w).updateFromUIDL(uidl, this);
+                return true;
+            }
+        }
+
+        boolean enabled = !uidl.getBooleanAttribute("disabled");
+        if (component instanceof FocusWidget) {
+            FocusWidget fw = (FocusWidget) component;
+            if (uidl.hasAttribute("tabindex")) {
+                fw.setTabIndex(uidl.getIntAttribute("tabindex"));
+            }
+            // Disabled state may affect tabindex
+            fw.setEnabled(enabled);
+        }
+
+        StringBuffer styleBuf = new StringBuffer();
+        final String primaryName = component.getStylePrimaryName();
+        styleBuf.append(primaryName);
+
+        // first disabling and read-only status
+        if (!enabled) {
+            styleBuf.append(" ");
+            styleBuf.append("v-disabled");
+        }
+        if (uidl.getBooleanAttribute("readonly")) {
+            styleBuf.append(" ");
+            styleBuf.append("v-readonly");
+        }
+
+        // add additional styles as css classes, prefixed with component default
+        // stylename
+        if (uidl.hasAttribute("style")) {
+            final String[] styles = uidl.getStringAttribute("style").split(" ");
+            for (int i = 0; i < styles.length; i++) {
+                styleBuf.append(" ");
+                styleBuf.append(primaryName);
+                styleBuf.append("-");
+                styleBuf.append(styles[i]);
+                styleBuf.append(" ");
+                styleBuf.append(styles[i]);
+            }
+        }
+
+        // add modified classname to Fields
+        if (uidl.hasAttribute("modified") && component instanceof Field) {
+            styleBuf.append(" ");
+            styleBuf.append(MODIFIED_CLASSNAME);
+        }
+
+        TooltipInfo tooltipInfo = componentDetail.getTooltipInfo(null);
+        // Update tooltip
+        if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) {
+            tooltipInfo
+                    .setTitle(uidl.getStringAttribute(ATTRIBUTE_DESCRIPTION));
+        } else {
+            tooltipInfo.setTitle(null);
+        }
+
+        // add error classname to components w/ error
+        if (uidl.hasAttribute(ATTRIBUTE_ERROR)) {
+            tooltipInfo.setErrorUidl(uidl.getErrors());
+            styleBuf.append(" ");
+            styleBuf.append(primaryName);
+            styleBuf.append(ERROR_CLASSNAME_EXT);
+        } else {
+            tooltipInfo.setErrorUidl(null);
+        }
+
+        // add required style to required components
+        if (uidl.hasAttribute("required")) {
+            styleBuf.append(" ");
+            styleBuf.append(primaryName);
+            styleBuf.append(REQUIRED_CLASSNAME_EXT);
+        }
+
+        // Styles + disabled & readonly
+        component.setStyleName(styleBuf.toString());
+
+        // Set captions
+        if (manageCaption) {
+            final Container parent = Util.getLayout(component);
+            if (parent != null) {
+                parent.updateCaption((Paintable) component, uidl);
+            }
+        }
+
+        if (configuration.useDebugIdInDOM() && uidl.getId().startsWith("PID_S")) {
+            DOM.setElementProperty(component.getElement(), "id", uidl.getId()
+                    .substring(5));
+        }
+
+        /*
+         * updateComponentSize need to be after caption update so caption can be
+         * taken into account
+         */
+
+        updateComponentSize(componentDetail, uidl);
+
+        return false;
+    }
+
+    private void updateComponentSize(ComponentDetail cd, UIDL uidl) {
+        String w = uidl.hasAttribute("width") ? uidl
+                .getStringAttribute("width") : "";
+
+        String h = uidl.hasAttribute("height") ? uidl
+                .getStringAttribute("height") : "";
+
+        float relativeWidth = Util.parseRelativeSize(w);
+        float relativeHeight = Util.parseRelativeSize(h);
+
+        // First update maps so they are correct in the setHeight/setWidth calls
+        if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
+            // One or both is relative
+            FloatSize relativeSize = new FloatSize(relativeWidth,
+                    relativeHeight);
+            if (cd.getRelativeSize() == null && cd.getOffsetSize() != null) {
+                // The component has changed from absolute size to relative size
+                relativeSizeChanges.add(cd.getComponent());
+            }
+            cd.setRelativeSize(relativeSize);
+        } else if (relativeHeight < 0.0 && relativeWidth < 0.0) {
+            if (cd.getRelativeSize() != null) {
+                // The component has changed from relative size to absolute size
+                relativeSizeChanges.add(cd.getComponent());
+            }
+            cd.setRelativeSize(null);
+        }
+
+        Widget component = (Widget) cd.getComponent();
+        // Set absolute sizes
+        if (relativeHeight < 0.0) {
+            component.setHeight(h);
+        }
+        if (relativeWidth < 0.0) {
+            component.setWidth(w);
+        }
+
+        // Set relative sizes
+        if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
+            // One or both is relative
+            handleComponentRelativeSize(cd);
+        }
+
+    }
+
+    /**
+     * Traverses recursively child widgets until ContainerResizedListener child
+     * widget is found. They will delegate it further if needed.
+     * 
+     * @param container
+     */
+    private boolean runningLayout = false;
+
+    public void runDescendentsLayout(HasWidgets container) {
+        if (runningLayout) {
+            // getConsole().log(
+            // "Already running descendents layout. Not running again for "
+            // + Util.getSimpleName(container));
+            return;
+        }
+        runningLayout = true;
+        internalRunDescendentsLayout(container);
+        runningLayout = false;
+    }
+
+    /**
+     * This will cause re-layouting of all components. Mainly used for
+     * development. Published to JavaScript.
+     */
+    public void forceLayout() {
+        Set<Paintable> set = new HashSet<Paintable>();
+        for (ComponentDetail cd : idToPaintableDetail.values()) {
+            set.add(cd.getComponent());
+        }
+        Util.componentSizeUpdated(set);
+    }
+
+    private void internalRunDescendentsLayout(HasWidgets container) {
+        // getConsole().log(
+        // "runDescendentsLayout(" + Util.getSimpleName(container) + ")");
+        final Iterator<Widget> childWidgets = container.iterator();
+        while (childWidgets.hasNext()) {
+            final Widget child = childWidgets.next();
+
+            if (child instanceof Paintable) {
+
+                if (handleComponentRelativeSize(child)) {
+                    /*
+                     * Only need to propagate event if "child" has a relative
+                     * size
+                     */
+
+                    if (child instanceof ContainerResizedListener) {
+                        ((ContainerResizedListener) child).iLayout();
+                    }
+
+                    if (child instanceof HasWidgets) {
+                        final HasWidgets childContainer = (HasWidgets) child;
+                        internalRunDescendentsLayout(childContainer);
+                    }
+                }
+            } else if (child instanceof HasWidgets) {
+                // propagate over non Paintable HasWidgets
+                internalRunDescendentsLayout((HasWidgets) child);
+            }
+
+        }
+    }
+
+    /**
+     * Converts relative sizes into pixel sizes.
+     * 
+     * @param child
+     * @return true if the child has a relative size
+     */
+    private boolean handleComponentRelativeSize(ComponentDetail cd) {
+        if (cd == null) {
+            return false;
+        }
+        boolean debugSizes = false;
+
+        FloatSize relativeSize = cd.getRelativeSize();
+        if (relativeSize == null) {
+            return false;
+        }
+        Widget widget = (Widget) cd.getComponent();
+
+        boolean horizontalScrollBar = false;
+        boolean verticalScrollBar = false;
+
+        Container parent = Util.getLayout(widget);
+        RenderSpace renderSpace;
+
+        // Parent-less components (like sub-windows) are relative to browser
+        // window.
+        if (parent == null) {
+            renderSpace = new RenderSpace(Window.getClientWidth(), Window
+                    .getClientHeight());
+        } else {
+            renderSpace = parent.getAllocatedSpace(widget);
+        }
+
+        if (relativeSize.getHeight() >= 0) {
+            if (renderSpace != null) {
+
+                if (renderSpace.getScrollbarSize() > 0) {
+                    if (relativeSize.getWidth() > 100) {
+                        horizontalScrollBar = true;
+                    } else if (relativeSize.getWidth() < 0
+                            && renderSpace.getWidth() > 0) {
+                        int offsetWidth = widget.getOffsetWidth();
+                        int width = renderSpace.getWidth();
+                        if (offsetWidth > width) {
+                            horizontalScrollBar = true;
+                        }
+                    }
+                }
+
+                int height = renderSpace.getHeight();
+                if (horizontalScrollBar) {
+                    height -= renderSpace.getScrollbarSize();
+                }
+                if (validatingLayouts && height <= 0) {
+                    zeroHeightComponents.add(cd.getComponent());
+                }
+
+                height = (int) (height * relativeSize.getHeight() / 100.0);
+
+                if (height < 0) {
+                    height = 0;
+                }
+
+                if (debugSizes) {
+                    getConsole()
+                            .log(
+                                    "Widget "
+                                            + Util.getSimpleName(widget)
+                                            + "/"
+                                            + getPid(widget.getElement())
+                                            + " relative height "
+                                            + relativeSize.getHeight()
+                                            + "% of "
+                                            + renderSpace.getHeight()
+                                            + "px (reported by "
+
+                                            + Util.getSimpleName(parent)
+                                            + "/"
+                                            + (parent == null ? "?" : parent
+                                                    .hashCode()) + ") : "
+                                            + height + "px");
+                }
+                widget.setHeight(height + "px");
+            } else {
+                widget.setHeight(relativeSize.getHeight() + "%");
+                ApplicationConnection.getConsole().error(
+                        Util.getLayout(widget).getClass().getName()
+                                + " did not produce allocatedSpace for "
+                                + widget.getClass().getName());
+            }
+        }
+
+        if (relativeSize.getWidth() >= 0) {
+
+            if (renderSpace != null) {
+
+                int width = renderSpace.getWidth();
+
+                if (renderSpace.getScrollbarSize() > 0) {
+                    if (relativeSize.getHeight() > 100) {
+                        verticalScrollBar = true;
+                    } else if (relativeSize.getHeight() < 0
+                            && renderSpace.getHeight() > 0
+                            && widget.getOffsetHeight() > renderSpace
+                                    .getHeight()) {
+                        verticalScrollBar = true;
+                    }
+                }
+
+                if (verticalScrollBar) {
+                    width -= renderSpace.getScrollbarSize();
+                }
+                if (validatingLayouts && width <= 0) {
+                    zeroWidthComponents.add(cd.getComponent());
+                }
+
+                width = (int) (width * relativeSize.getWidth() / 100.0);
+
+                if (width < 0) {
+                    width = 0;
+                }
+
+                if (debugSizes) {
+                    getConsole().log(
+                            "Widget " + Util.getSimpleName(widget) + "/"
+                                    + getPid(widget.getElement())
+                                    + " relative width "
+                                    + relativeSize.getWidth() + "% of "
+                                    + renderSpace.getWidth()
+                                    + "px (reported by "
+                                    + Util.getSimpleName(parent) + "/"
+                                    + (parent == null ? "?" : getPid(parent))
+                                    + ") : " + width + "px");
+                }
+                widget.setWidth(width + "px");
+            } else {
+                widget.setWidth(relativeSize.getWidth() + "%");
+                ApplicationConnection.getConsole().error(
+                        Util.getLayout(widget).getClass().getName()
+                                + " did not produce allocatedSpace for "
+                                + widget.getClass().getName());
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Converts relative sizes into pixel sizes.
+     * 
+     * @param child
+     * @return true if the child has a relative size
+     */
+    public boolean handleComponentRelativeSize(Widget child) {
+        return handleComponentRelativeSize(idToPaintableDetail.get(getPid(child
+                .getElement())));
+
+    }
+
+    public FloatSize getRelativeSize(Widget widget) {
+        return idToPaintableDetail.get(getPid(widget.getElement()))
+                .getRelativeSize();
+    }
+
+    /**
+     * Get either existing or new Paintable for given UIDL.
+     * 
+     * If corresponding Paintable has been previously painted, return it.
+     * Otherwise create and register a new Paintable from UIDL. Caller must
+     * update the returned Paintable from UIDL after it has been connected to
+     * parent.
+     * 
+     * @param uidl
+     *            UIDL to create Paintable from.
+     * @return Either existing or new Paintable corresponding to UIDL.
+     */
+    public Paintable getPaintable(UIDL uidl) {
+        final String id = uidl.getId();
+        Paintable w = getPaintable(id);
+        if (w != null) {
+            return w;
+        } else {
+            w = widgetSet.createWidget(uidl, configuration);
+            registerPaintable(id, w);
+            return w;
+
+        }
+    }
+
+    /**
+     * Returns a Paintable element by its root element
+     * 
+     * @param element
+     *            Root element of the paintable
+     */
+    public Paintable getPaintable(Element element) {
+        return getPaintable(getPid(element));
+    }
+
+    public String getResource(String name) {
+        return resourcesMap.get(name);
+    }
+
+    /**
+     * Singleton method to get instance of app's context menu.
+     * 
+     * @return VContextMenu object
+     */
+    public VContextMenu getContextMenu() {
+        if (contextMenu == null) {
+            contextMenu = new VContextMenu();
+            DOM.setElementProperty(contextMenu.getElement(), "id",
+                    "PID_VAADIN_CM");
+        }
+        return contextMenu;
+    }
+
+    /**
+     * Translates custom protocols in UIDL URI's to be recognizable by browser.
+     * All uri's from UIDL should be routed via this method before giving them
+     * to browser due URI's in UIDL may contain custom protocols like theme://.
+     * 
+     * @param uidlUri
+     *            Vaadin URI from uidl
+     * @return translated URI ready for browser
+     */
+    public String translateVaadinUri(String uidlUri) {
+        if (uidlUri == null) {
+            return null;
+        }
+        if (uidlUri.startsWith("theme://")) {
+            final String themeUri = configuration.getThemeUri();
+            if (themeUri == null) {
+                console
+                        .error("Theme not set: ThemeResource will not be found. ("
+                                + uidlUri + ")");
+            }
+            uidlUri = themeUri + uidlUri.substring(7);
+        }
+        return uidlUri;
+    }
+
+    public String getThemeUri() {
+        return configuration.getThemeUri();
+    }
+
+    /**
+     * Listens for Notification hide event, and redirects. Used for system
+     * messages, such as session expired.
+     * 
+     */
+    private class NotificationRedirect implements VNotification.EventListener {
+        String url;
+
+        NotificationRedirect(String url) {
+            this.url = url;
+        }
+
+        public void notificationHidden(HideEvent event) {
+            redirect(url);
+        }
+
+    }
+
+    /* Extended title handling */
+
+    /**
+     * Data showed in tooltips are stored centrilized as it may be needed in
+     * varios place: caption, layouts, and in owner components themselves.
+     * 
+     * Updating TooltipInfo is done in updateComponent method.
+     * 
+     */
+    public TooltipInfo getTooltipTitleInfo(Paintable titleOwner, Object key) {
+        if (null == titleOwner) {
+            return null;
+        }
+        ComponentDetail cd = idToPaintableDetail.get(getPid(titleOwner));
+        if (null != cd) {
+            return cd.getTooltipInfo(key);
+        } else {
+            return null;
+        }
+    }
+
+    private final VTooltip tooltip = new VTooltip(this);
+
+    /**
+     * Component may want to delegate Tooltip handling to client. Layouts add
+     * Tooltip (description, errors) to caption, but some components may want
+     * them to appear one other elements too.
+     * 
+     * Events wanted by this handler are same as in Tooltip.TOOLTIP_EVENTS
+     * 
+     * @param event
+     * @param owner
+     */
+    public void handleTooltipEvent(Event event, Paintable owner) {
+        tooltip.handleTooltipEvent(event, owner, null);
+
+    }
+
+    /**
+     * Component may want to delegate Tooltip handling to client. Layouts add
+     * Tooltip (description, errors) to caption, but some components may want
+     * them to appear one other elements too.
+     * 
+     * Events wanted by this handler are same as in Tooltip.TOOLTIP_EVENTS
+     * 
+     * @param event
+     * @param owner
+     * @param key
+     *            the key for tooltip if this is "additional" tooltip, null for
+     *            components "main tooltip"
+     */
+    public void handleTooltipEvent(Event event, Paintable owner, Object key) {
+        tooltip.handleTooltipEvent(event, owner, key);
+
+    }
+
+    /**
+     * Adds PNG-fix conditionally (only for IE6) to the specified IMG -element.
+     * 
+     * @param el
+     *            the IMG element to fix
+     */
+    public void addPngFix(Element el) {
+        BrowserInfo b = BrowserInfo.get();
+        if (b.isIE6()) {
+            Util.addPngFix(el, getThemeUri() + "/../runo/common/img/blank.gif");
+        }
+    }
+
+    /*
+     * Helper to run layout functions triggered by child components with a
+     * decent interval.
+     */
+    private final Timer layoutTimer = new Timer() {
+
+        private boolean isPending = false;
+
+        @Override
+        public void schedule(int delayMillis) {
+            if (!isPending) {
+                super.schedule(delayMillis);
+                isPending = true;
+            }
+        }
+
+        @Override
+        public void run() {
+            getConsole().log(
+                    "Running re-layout of " + view.getClass().getName());
+            runDescendentsLayout(view);
+            isPending = false;
+        }
+    };
+
+    /**
+     * Components can call this function to run all layout functions. This is
+     * usually done, when component knows that its size has changed.
+     */
+    public void requestLayoutPhase() {
+        layoutTimer.schedule(500);
+    }
+
+    private String windowName = null;
+
+    /**
+     * Reset the name of the current browser-window. This should reflect the
+     * window-name used in the server, but might be different from the
+     * window-object target-name on client.
+     * 
+     * @param stringAttribute
+     *            New name for the window.
+     */
+    public void setWindowName(String newName) {
+        windowName = newName;
+    }
+
+    public void captionSizeUpdated(Paintable component) {
+        componentCaptionSizeChanges.add(component);
+    }
+
+    public void analyzeLayouts() {
+        makeUidlRequest("", true, false, true);
+    }
+
+    public VView getView() {
+        return view;
+    }
+
+    /**
+     * If component has several tooltips in addition to the one provided by
+     * {@link com.vaadin.ui.AbstractComponent}, component can register them with
+     * this method.
+     * <p>
+     * Component must also pipe events to
+     * {@link #handleTooltipEvent(Event, Paintable, Object)} method.
+     * <p>
+     * This method can also be used to deregister tooltips by using null as
+     * tooltip
+     * 
+     * @param paintable
+     *            Paintable "owning" this tooltip
+     * @param key
+     *            key assosiated with given tooltip. Can be any object. For
+     *            example a related dom element. Same key must be given for
+     *            {@link #handleTooltipEvent(Event, Paintable, Object)} method.
+     * 
+     * @param tooltip
+     *            the TooltipInfo object containing details shown in tooltip,
+     *            null if deregistering tooltip
+     */
+    public void registerTooltip(Paintable paintable, Object key,
+            TooltipInfo tooltip) {
+        ComponentDetail componentDetail = idToPaintableDetail
+                .get(getPid(paintable));
+        componentDetail.putAdditionalTooltip(key, tooltip);
+    }
+
+    public ApplicationConfiguration getConfiguration() {
+        return configuration;
+    }
 
 }