]> source.dussan.org Git - vaadin-framework.git/commitdiff
Include UI class @JavaScript and @StyleSheet in bootstrap html (#9045)
authorLeif Åstrand <leif@vaadin.com>
Sat, 10 Jan 2015 20:19:36 +0000 (22:19 +0200)
committerArtur Signell <artur@vaadin.com>
Mon, 12 Jan 2015 14:25:23 +0000 (14:25 +0000)
Change-Id: I9d4243fa6f91ba5bc3449d0a3ec24f209e6360e6

15 files changed:
WebContent/VAADIN/themes/tests-valo/uiDependency.css [new file with mode: 0644]
client/src/com/vaadin/client/ApplicationConfiguration.java
client/src/com/vaadin/client/ApplicationConnection.java
client/src/com/vaadin/client/communication/AtmospherePushConnection.java
client/src/com/vaadin/client/communication/Heartbeat.java
client/src/com/vaadin/client/extensions/BrowserWindowOpenerConnector.java
client/tests/src/com/vaadin/client/ApplicationConnectionURLGenerationTest.java
server/src/com/vaadin/server/BootstrapHandler.java
server/src/com/vaadin/server/communication/PortletBootstrapHandler.java
shared/src/com/vaadin/shared/ApplicationConstants.java
shared/src/com/vaadin/shared/VaadinUriResolver.java [new file with mode: 0644]
shared/src/com/vaadin/shared/util/SharedUtil.java
uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtml.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtmlTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/ui/uiDependency.js [new file with mode: 0644]

diff --git a/WebContent/VAADIN/themes/tests-valo/uiDependency.css b/WebContent/VAADIN/themes/tests-valo/uiDependency.css
new file mode 100644 (file)
index 0000000..e69de29
index 0ef37df13072f5837ae84fc2b9f6b34a7a760d2d..5eeeabe7436af7a28550c5820a10f50ff505d989 100644 (file)
@@ -276,8 +276,7 @@ public class ApplicationConfiguration implements EntryPoint {
      *         <code>false</code> if the path info goes after the service URL
      */
     public boolean useServiceUrlPathParam() {
-        return getJsoConfiguration(id).getConfigBoolean(
-                ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER) == Boolean.TRUE;
+        return getServiceUrlParameterName() != null;
     }
 
     /**
@@ -289,12 +288,8 @@ public class ApplicationConfiguration implements EntryPoint {
      * @return The parameter name, by default <code>v-resourcePath</code>
      */
     public String getServiceUrlParameterName() {
-        String prefix = getJsoConfiguration(id).getConfigString(
-                ApplicationConstants.SERVICE_URL_PARAMETER_NAMESPACE);
-        if (prefix == null) {
-            prefix = "";
-        }
-        return prefix + ApplicationConstants.V_RESOURCE_PATH;
+        return getJsoConfiguration(id).getConfigString(
+                ApplicationConstants.SERVICE_URL_PARAMETER_NAME);
     }
 
     public String getRootPanelId() {
index cd69f6c186714a06271249aaa2613dd81e2d0910..3431f064319b8219c2393903d850075333477acd 100644 (file)
@@ -99,12 +99,14 @@ import com.vaadin.client.ui.window.WindowConnector;
 import com.vaadin.shared.AbstractComponentState;
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.shared.JsonConstants;
+import com.vaadin.shared.VaadinUriResolver;
 import com.vaadin.shared.Version;
 import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
 import com.vaadin.shared.communication.MethodInvocation;
 import com.vaadin.shared.communication.SharedState;
 import com.vaadin.shared.ui.ui.UIConstants;
 import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
+import com.vaadin.shared.util.SharedUtil;
 
 /**
  * This is the client side communication "engine", managing client-server
@@ -472,6 +474,33 @@ public class ApplicationConnection implements HasHandlers {
 
     private boolean tooltipInitialized = false;
 
+    private final VaadinUriResolver uriResolver = new VaadinUriResolver() {
+        @Override
+        protected String getVaadinDirUrl() {
+            return getConfiguration().getVaadinDirUrl();
+        }
+
+        @Override
+        protected String getServiceUrlParameterName() {
+            return getConfiguration().getServiceUrlParameterName();
+        }
+
+        @Override
+        protected String getServiceUrl() {
+            return getConfiguration().getServiceUrl();
+        }
+
+        @Override
+        protected String getThemeUri() {
+            return ApplicationConnection.this.getThemeUri();
+        }
+
+        @Override
+        protected String encodeQueryStringParameterValue(String queryString) {
+            return URL.encodeQueryString(queryString);
+        }
+    };
+
     public static class MultiStepDuration extends Duration {
         private int previousStep = elapsedMillis();
 
@@ -823,10 +852,10 @@ public class ApplicationConnection implements HasHandlers {
                 + ApplicationConstants.UIDL_PATH + '/');
 
         if (extraParams != null && extraParams.length() > 0) {
-            uri = addGetParameters(uri, extraParams);
+            uri = SharedUtil.addGetParameters(uri, extraParams);
         }
-        uri = addGetParameters(uri, UIConstants.UI_ID_PARAMETER + "="
-                + configuration.getUIId());
+        uri = SharedUtil.addGetParameters(uri, UIConstants.UI_ID_PARAMETER
+                + "=" + configuration.getUIId());
 
         doUidlRequest(uri, payload);
 
@@ -3185,67 +3214,7 @@ public class ApplicationConnection implements HasHandlers {
      * @return translated URI ready for browser
      */
     public String translateVaadinUri(String uidlUri) {
-        if (uidlUri == null) {
-            return null;
-        }
-        if (uidlUri.startsWith("theme://")) {
-            final String themeUri = getThemeUri();
-            if (themeUri == null) {
-                VConsole.error("Theme not set: ThemeResource will not be found. ("
-                        + uidlUri + ")");
-            }
-            uidlUri = themeUri + uidlUri.substring(7);
-        }
-
-        if (uidlUri.startsWith(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX)) {
-            // getAppUri *should* always end with /
-            // substring *should* always start with / (published:///foo.bar
-            // without published://)
-            uidlUri = ApplicationConstants.APP_PROTOCOL_PREFIX
-                    + ApplicationConstants.PUBLISHED_FILE_PATH
-                    + uidlUri
-                            .substring(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX
-                                    .length());
-            // Let translation of app:// urls take care of the rest
-        }
-        if (uidlUri.startsWith(ApplicationConstants.APP_PROTOCOL_PREFIX)) {
-            String relativeUrl = uidlUri
-                    .substring(ApplicationConstants.APP_PROTOCOL_PREFIX
-                            .length());
-            ApplicationConfiguration conf = getConfiguration();
-            String serviceUrl = conf.getServiceUrl();
-            if (conf.useServiceUrlPathParam()) {
-                // Should put path in v-resourcePath parameter and append query
-                // params to base portlet url
-                String[] parts = relativeUrl.split("\\?", 2);
-                String path = parts[0];
-
-                // If there's a "?" followed by something, append it as a query
-                // string to the base URL
-                if (parts.length > 1) {
-                    String appUrlParams = parts[1];
-                    serviceUrl = addGetParameters(serviceUrl, appUrlParams);
-                }
-                if (!path.startsWith("/")) {
-                    path = '/' + path;
-                }
-                String pathParam = conf.getServiceUrlParameterName() + "="
-                        + URL.encodeQueryString(path);
-                serviceUrl = addGetParameters(serviceUrl, pathParam);
-                uidlUri = serviceUrl;
-            } else {
-                uidlUri = serviceUrl + relativeUrl;
-            }
-        }
-        if (uidlUri.startsWith(ApplicationConstants.VAADIN_PROTOCOL_PREFIX)) {
-            final String vaadinUri = configuration.getVaadinDirUrl();
-            String relativeUrl = uidlUri
-                    .substring(ApplicationConstants.VAADIN_PROTOCOL_PREFIX
-                            .length());
-            uidlUri = vaadinUri + relativeUrl;
-        }
-
-        return uidlUri;
+        return uriResolver.resolveVaadinUri(uidlUri);
     }
 
     /**
@@ -3362,35 +3331,12 @@ public class ApplicationConnection implements HasHandlers {
      *            One or more parameters in the format "a=b" or "c=d&e=f". An
      *            empty string is allowed but will not modify the url.
      * @return The modified URI with the get parameters in extraParams added.
+     * @deprecated Use {@link SharedUtil#addGetParameters(String,String)}
+     *             instead
      */
+    @Deprecated
     public static String addGetParameters(String uri, String extraParams) {
-        if (extraParams == null || extraParams.length() == 0) {
-            return uri;
-        }
-        // RFC 3986: The query component is indicated by the first question
-        // mark ("?") character and terminated by a number sign ("#") character
-        // or by the end of the URI.
-        String fragment = null;
-        int hashPosition = uri.indexOf('#');
-        if (hashPosition != -1) {
-            // Fragment including "#"
-            fragment = uri.substring(hashPosition);
-            // The full uri before the fragment
-            uri = uri.substring(0, hashPosition);
-        }
-
-        if (uri.contains("?")) {
-            uri += "&";
-        } else {
-            uri += "?";
-        }
-        uri += extraParams;
-
-        if (fragment != null) {
-            uri += fragment;
-        }
-
-        return uri;
+        return SharedUtil.addGetParameters(uri, extraParams);
     }
 
     ConnectorMap getConnectorMap() {
index d10449ccaa71a47f570ae97bd3345d9697f39488..a2346db1866b15254087fa2b648a69cc5dbc1696 100644 (file)
@@ -35,6 +35,7 @@ import com.vaadin.shared.Version;
 import com.vaadin.shared.communication.PushConstants;
 import com.vaadin.shared.ui.ui.UIConstants;
 import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
+import com.vaadin.shared.util.SharedUtil;
 
 /**
  * The default {@link PushConnection} implementation that uses Atmosphere for
@@ -185,7 +186,7 @@ public class AtmospherePushConnection implements PushConnection {
         }
 
         // uri is needed to identify the right connection when closing
-        uri = ApplicationConnection.addGetParameters(baseUrl, extraParams);
+        uri = SharedUtil.addGetParameters(baseUrl, extraParams);
 
         VConsole.log("Establishing push connection");
         socket = doConnect(uri, getConfig());
index b9493d45202e91e9570002b3da5009dcda60f352..5d15e5585fec673d3b02e61a79248f20183009e5 100644 (file)
@@ -28,6 +28,7 @@ import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
 import com.vaadin.client.ApplicationConnection.ConnectionStatusEvent;
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.shared.ui.ui.UIConstants;
+import com.vaadin.shared.util.SharedUtil;
 
 /**
  * Handles sending of heartbeats to the server and reacting to the response
@@ -63,7 +64,7 @@ public class Heartbeat {
 
         setInterval(connection.getConfiguration().getHeartbeatInterval());
 
-        uri = ApplicationConnection.addGetParameters(connection
+        uri = SharedUtil.addGetParameters(connection
                 .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
                         + ApplicationConstants.HEARTBEAT_PATH + '/'),
                 UIConstants.UI_ID_PARAMETER + "="
index 58457c1b7bf0c7c2b519b46b7add955b7faa31f7..11e3e80a14668e38c20483551ee8ccab484df0d4 100644 (file)
@@ -23,12 +23,12 @@ import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.http.client.URL;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.ComponentConnector;
 import com.vaadin.client.ServerConnector;
 import com.vaadin.server.BrowserWindowOpener;
 import com.vaadin.shared.ui.BrowserWindowOpenerState;
 import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.util.SharedUtil;
 
 /**
  * Client-side code for {@link BrowserWindowOpener}
@@ -81,8 +81,7 @@ public class BrowserWindowOpenerConnector extends AbstractExtensionConnector
                 }
             }
 
-            url = ApplicationConnection
-                    .addGetParameters(url, params.toString());
+            url = SharedUtil.addGetParameters(url, params.toString());
         }
 
         if (getState().uriFragment != null) {
index 36baa163cbcb59ce7cc38652f2cb8be7c9220ef3..f529ba08867fef44e5f720e107ee2e9ded9e8fb6 100644 (file)
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
 
+import com.vaadin.shared.util.SharedUtil;
+
 public class ApplicationConnectionURLGenerationTest {
 
     private static final String[] URIS = new String[] {
@@ -51,23 +53,23 @@ public class ApplicationConnectionURLGenerationTest {
         for (int i = 0; i < URIS.length; i++) {
             // Adding nothing
             assertEquals(URIS[i],
-                    ApplicationConnection.addGetParameters(URIS[i], ""));
+                    SharedUtil.addGetParameters(URIS[i], ""));
 
             // Adding a=b&c=d
             assertEquals(URIS_WITH_ABCD_PARAM[i],
-                    ApplicationConnection.addGetParameters(URIS[i], "a=b&c=d"));
+                    SharedUtil.addGetParameters(URIS[i], "a=b&c=d"));
 
             // Fragments
             if (URIS_WITH_ABCD_PARAM_AND_FRAGMENT[i].length() > 0) {
                 assertEquals(
                         URIS_WITH_ABCD_PARAM_AND_FRAGMENT[i],
-                        ApplicationConnection.addGetParameters(URIS[i]
+                        SharedUtil.addGetParameters(URIS[i]
                                 + "#fragment", "a=b&c=d"));
 
                 // Empty fragment
                 assertEquals(URIS_WITH_ABCD_PARAM_AND_FRAGMENT[i].replace(
                         "#fragment", "#"),
-                        ApplicationConnection.addGetParameters(URIS[i] + "#",
+                        SharedUtil.addGetParameters(URIS[i] + "#",
                                 "a=b&c=d"));
             }
         }
index c45e2b70e019e364ddd53b3372f702fcb67d038e..e74f6d7c45f03a9f0bd34c475667e6c883cf6693 100644 (file)
@@ -37,10 +37,14 @@ import org.jsoup.nodes.Element;
 import org.jsoup.nodes.Node;
 import org.jsoup.parser.Tag;
 
+import com.google.gwt.thirdparty.guava.common.net.UrlEscapers;
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.StyleSheet;
 import com.vaadin.annotations.Viewport;
 import com.vaadin.annotations.ViewportGeneratorClass;
 import com.vaadin.server.communication.AtmospherePushConnection;
 import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.VaadinUriResolver;
 import com.vaadin.shared.Version;
 import com.vaadin.shared.communication.PushMode;
 import com.vaadin.ui.UI;
@@ -76,6 +80,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
         private String themeName;
         private String appId;
         private PushMode pushMode;
+        private JsonObject applicationParameters;
+        private VaadinUriResolver uriResolver;
 
         public BootstrapContext(VaadinResponse response,
                 BootstrapFragmentResponse bootstrapResponse) {
@@ -149,6 +155,71 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
             return bootstrapResponse;
         }
 
+        public JsonObject getApplicationParameters() {
+            if (applicationParameters == null) {
+                applicationParameters = BootstrapHandler.this
+                        .getApplicationParameters(this);
+            }
+
+            return applicationParameters;
+        }
+
+        public VaadinUriResolver getUriResolver() {
+            if (uriResolver == null) {
+                uriResolver = new BootstrapUriResolver(this);
+            }
+
+            return uriResolver;
+        }
+    }
+
+    private class BootstrapUriResolver extends VaadinUriResolver {
+        private final BootstrapContext context;
+
+        public BootstrapUriResolver(BootstrapContext bootstrapContext) {
+            context = bootstrapContext;
+        }
+
+        @Override
+        protected String getVaadinDirUrl() {
+            return context.getApplicationParameters().getString(
+                    ApplicationConstants.VAADIN_DIR_URL);
+        }
+
+        @Override
+        protected String getThemeUri() {
+            return getVaadinDirUrl() + "themes/" + context.getThemeName();
+        }
+
+        @Override
+        protected String getServiceUrlParameterName() {
+            return getConfigOrNull(ApplicationConstants.SERVICE_URL_PARAMETER_NAME);
+        }
+
+        @Override
+        protected String getServiceUrl() {
+            String serviceUrl = getConfigOrNull(ApplicationConstants.SERVICE_URL);
+            if (serviceUrl == null) {
+                return "./";
+            } else if (!serviceUrl.endsWith("/")) {
+                serviceUrl += "/";
+            }
+            return serviceUrl;
+        }
+
+        private String getConfigOrNull(String name) {
+            JsonObject parameters = context.getApplicationParameters();
+            if (parameters.hasKey(name)) {
+                return parameters.getString(name);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        protected String encodeQueryStringParameterValue(String queryString) {
+            return UrlEscapers.urlFormParameterEscaper().escape(queryString);
+        }
     }
 
     @Override
@@ -345,11 +416,41 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
                     .attr("href", themeUri + "/favicon.ico");
         }
 
+        JavaScript javaScript = uiClass.getAnnotation(JavaScript.class);
+        if (javaScript != null) {
+            String[] resources = javaScript.value();
+            for (String resource : resources) {
+                String url = registerDependency(context, uiClass, resource);
+                head.appendElement("script").attr("type", "text/javascript")
+                        .attr("src", url);
+            }
+        }
+
+        StyleSheet styleSheet = uiClass.getAnnotation(StyleSheet.class);
+        if (styleSheet != null) {
+            String[] resources = styleSheet.value();
+            for (String resource : resources) {
+                String url = registerDependency(context, uiClass, resource);
+                head.appendElement("link").attr("rel", "stylesheet")
+                        .attr("type", "text/css").attr("href", url);
+            }
+        }
+
         Element body = document.body();
         body.attr("scroll", "auto");
         body.addClass(ApplicationConstants.GENERATED_BODY_CLASSNAME);
     }
 
+    private String registerDependency(BootstrapContext context,
+            Class<? extends UI> uiClass, String resource) {
+        String url = context.getSession().getCommunicationManager()
+                .registerDependency(resource, uiClass);
+
+        url = context.getUriResolver().resolveVaadinUri(url);
+
+        return url;
+    }
+
     protected String getMainDivStyle(BootstrapContext context) {
         return null;
     }
@@ -457,7 +558,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
 
     protected void appendMainScriptTagContents(BootstrapContext context,
             StringBuilder builder) throws IOException {
-        JsonObject appConfig = getApplicationParameters(context);
+        JsonObject appConfig = context.getApplicationParameters();
 
         boolean isDebug = !context.getSession().getConfiguration()
                 .isProductionMode();
@@ -490,8 +591,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
         }
     }
 
-    protected JsonObject getApplicationParameters(BootstrapContext context)
-            throws PaintException {
+    protected JsonObject getApplicationParameters(BootstrapContext context) {
         VaadinRequest request = context.getRequest();
         VaadinSession session = context.getSession();
         VaadinService vaadinService = request.getService();
index 1d1a300cd18c77283705a94bd744553d789ae9f4..289309b631b5ff98704922ba5628313784938ec0 100644 (file)
@@ -26,7 +26,6 @@ import javax.portlet.RenderResponse;
 import javax.portlet.ResourceURL;
 
 import com.vaadin.server.BootstrapHandler;
-import com.vaadin.server.PaintException;
 import com.vaadin.server.VaadinPortlet;
 import com.vaadin.server.VaadinPortlet.VaadinLiferayRequest;
 import com.vaadin.server.VaadinPortletRequest;
@@ -36,6 +35,7 @@ import com.vaadin.server.VaadinResponse;
 import com.vaadin.server.VaadinService;
 import com.vaadin.server.VaadinSession;
 import com.vaadin.shared.ApplicationConstants;
+
 import elemental.json.JsonObject;
 
 public class PortletBootstrapHandler extends BootstrapHandler {
@@ -92,8 +92,7 @@ public class PortletBootstrapHandler extends BootstrapHandler {
     }
 
     @Override
-    protected JsonObject getApplicationParameters(BootstrapContext context)
-            throws PaintException {
+    protected JsonObject getApplicationParameters(BootstrapContext context) {
         JsonObject parameters = super.getApplicationParameters(context);
         VaadinPortletResponse response = (VaadinPortletResponse) context
                 .getResponse();
@@ -105,17 +104,16 @@ public class PortletBootstrapHandler extends BootstrapHandler {
         resourceURL.setResourceID("v-browserDetails");
         parameters.put("browserDetailsUrl", resourceURL.toString());
 
-        // Always send path info as a query parameter
-        parameters
-                .put(ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER, true);
+        String serviceUrlParameterName = ApplicationConstants.V_RESOURCE_PATH;
 
         // If we are running in Liferay then we need to prefix all parameters
         // with the portlet namespace
         if (request instanceof VaadinLiferayRequest) {
-            parameters.put(
-                    ApplicationConstants.SERVICE_URL_PARAMETER_NAMESPACE,
-                    response.getPortletResponse().getNamespace());
+            serviceUrlParameterName = response.getPortletResponse()
+                    .getNamespace() + serviceUrlParameterName;
         }
+        parameters.put(ApplicationConstants.SERVICE_URL_PARAMETER_NAME,
+                serviceUrlParameterName);
 
         return parameters;
     }
index 44c972462aad3e4b4290c2dc647056d4980bf083..d7aaee6267a6e89b22cf387986ceff5d1cabc016 100644 (file)
@@ -48,11 +48,7 @@ public class ApplicationConstants implements Serializable {
 
     public static final String SERVICE_URL = "serviceUrl";
 
-    public static final String SERVICE_URL_PATH_AS_PARAMETER = "usePathParameter";
-
-    // Denotes the namespace which parameters should be prefixed with when
-    // passed as GET parameters. Currently only used by Liferay.
-    public static final String SERVICE_URL_PARAMETER_NAMESPACE = "pathParameterNS";
+    public static final String SERVICE_URL_PARAMETER_NAME = "pathParameterName";
 
     // Javadocs in ApplicationConfiguration should be updated if this is changed
     public static final String V_RESOURCE_PATH = "v-resourcePath";
diff --git a/shared/src/com/vaadin/shared/VaadinUriResolver.java b/shared/src/com/vaadin/shared/VaadinUriResolver.java
new file mode 100644 (file)
index 0000000..10edfe8
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared;
+
+import java.io.Serializable;
+
+import com.vaadin.shared.util.SharedUtil;
+
+public abstract class VaadinUriResolver implements Serializable {
+
+    public String resolveVaadinUri(String vaadinUri) {
+        if (vaadinUri == null) {
+            return null;
+        }
+        if (vaadinUri.startsWith("theme://")) {
+            final String themeUri = getThemeUri();
+            vaadinUri = themeUri + vaadinUri.substring(7);
+        }
+
+        if (vaadinUri
+                .startsWith(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX)) {
+            // getAppUri *should* always end with /
+            // substring *should* always start with / (published:///foo.bar
+            // without published://)
+            vaadinUri = ApplicationConstants.APP_PROTOCOL_PREFIX
+                    + ApplicationConstants.PUBLISHED_FILE_PATH
+                    + vaadinUri
+                            .substring(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX
+                                    .length());
+            // Let translation of app:// urls take care of the rest
+        }
+        if (vaadinUri.startsWith(ApplicationConstants.APP_PROTOCOL_PREFIX)) {
+            String relativeUrl = vaadinUri
+                    .substring(ApplicationConstants.APP_PROTOCOL_PREFIX
+                            .length());
+            String serviceUrl = getServiceUrl();
+            String serviceUrlParameterName = getServiceUrlParameterName();
+            if (serviceUrlParameterName != null) {
+                // Should put path in v-resourcePath parameter and append query
+                // params to base portlet url
+                String[] parts = relativeUrl.split("\\?", 2);
+                String path = parts[0];
+
+                // If there's a "?" followed by something, append it as a query
+                // string to the base URL
+                if (parts.length > 1) {
+                    String appUrlParams = parts[1];
+                    serviceUrl = SharedUtil.addGetParameters(serviceUrl,
+                            appUrlParams);
+                }
+                if (!path.startsWith("/")) {
+                    path = '/' + path;
+                }
+                String pathParam = serviceUrlParameterName + "="
+                        + encodeQueryStringParameterValue(path);
+                serviceUrl = SharedUtil.addGetParameters(serviceUrl, pathParam);
+                vaadinUri = serviceUrl;
+            } else {
+                vaadinUri = serviceUrl + relativeUrl;
+            }
+        }
+        if (vaadinUri.startsWith(ApplicationConstants.VAADIN_PROTOCOL_PREFIX)) {
+            final String vaadinDirUri = getVaadinDirUrl();
+            String relativeUrl = vaadinUri
+                    .substring(ApplicationConstants.VAADIN_PROTOCOL_PREFIX
+                            .length());
+            vaadinUri = vaadinDirUri + relativeUrl;
+        }
+
+        return vaadinUri;
+    }
+
+    protected abstract String getVaadinDirUrl();
+
+    protected abstract String getServiceUrlParameterName();
+
+    protected abstract String getServiceUrl();
+
+    protected abstract String getThemeUri();
+
+    protected abstract String encodeQueryStringParameterValue(String queryString);
+}
index 7276f418fa501976649047b1444968820442d433..cc98d11abd6cdb3549a03d4a5ab39def7e77bc8e 100644 (file)
@@ -60,4 +60,45 @@ public class SharedUtil implements Serializable {
      */
     public static final String SIZE_PATTERN = "^(-?\\d*(?:\\.\\d+)?)(%|px|em|rem|ex|in|cm|mm|pt|pc)?$";
 
+    /**
+     * Adds the get parameters to the uri and returns the new uri that contains
+     * the parameters.
+     *
+     * @param uri
+     *            The uri to which the parameters should be added.
+     * @param extraParams
+     *            One or more parameters in the format "a=b" or "c=d&e=f". An
+     *            empty string is allowed but will not modify the url.
+     * @return The modified URI with the get parameters in extraParams added.
+     */
+    public static String addGetParameters(String uri, String extraParams) {
+        if (extraParams == null || extraParams.length() == 0) {
+            return uri;
+        }
+        // RFC 3986: The query component is indicated by the first question
+        // mark ("?") character and terminated by a number sign ("#") character
+        // or by the end of the URI.
+        String fragment = null;
+        int hashPosition = uri.indexOf('#');
+        if (hashPosition != -1) {
+            // Fragment including "#"
+            fragment = uri.substring(hashPosition);
+            // The full uri before the fragment
+            uri = uri.substring(0, hashPosition);
+        }
+    
+        if (uri.contains("?")) {
+            uri += "&";
+        } else {
+            uri += "?";
+        }
+        uri += extraParams;
+    
+        if (fragment != null) {
+            uri += fragment;
+        }
+    
+        return uri;
+    }
+
 }
diff --git a/uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtml.java b/uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtml.java
new file mode 100644 (file)
index 0000000..96210b2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.ui;
+
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.StyleSheet;
+import com.vaadin.annotations.Theme;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Label;
+
+@JavaScript("uiDependency.js")
+@StyleSheet("theme://uiDependency.css")
+@Theme("tests-valo")
+public class UiDependenciesInHtml extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        Label statusBox = new Label("Status box");
+        statusBox.setId("statusBox");
+        addComponent(statusBox);
+
+        getPage().getJavaScript().execute("window.reportUiDependencyStatus();");
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtmlTest.java b/uitest/src/com/vaadin/tests/components/ui/UiDependenciesInHtmlTest.java
new file mode 100644 (file)
index 0000000..188a0ae
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.ui;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class UiDependenciesInHtmlTest extends SingleBrowserTest {
+
+    @Test
+    public void testUiDependencisInHtml() {
+        openTestURL();
+
+        String statusText = findElement(By.id("statusBox")).getText();
+
+        Assert.assertEquals(
+                "Script loaded before vaadinBootstrap.js: true\nStyle tag before vaadin theme: true",
+                statusText);
+    }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/ui/uiDependency.js b/uitest/src/com/vaadin/tests/components/ui/uiDependency.js
new file mode 100644 (file)
index 0000000..4a5775c
--- /dev/null
@@ -0,0 +1,24 @@
+(function() {
+       var loadedBeforeVaadin = (window.vaadin === undefined);
+       
+       window.reportUiDependencyStatus = function() {
+               var styleIndex = 1000;
+               var themeIndex = -1;
+               
+               var stylesheets = document.querySelectorAll("link[rel=stylesheet]");
+               for(var i = 0; i < stylesheets.length; i++) {
+                       var stylesheet = stylesheets[i];
+                       var href = stylesheet.getAttribute("href"); 
+                       if (href.indexOf("uiDependency.css") > -1) {
+                               styleIndex = i;
+                       } else if (href.indexOf("styles.css" > -1)) {
+                               themeIndex = i;
+                       }
+               }
+               
+               var status = "Script loaded before vaadinBootstrap.js: " + loadedBeforeVaadin;
+               status += "<br />Style tag before vaadin theme: " + (styleIndex < themeIndex);
+               
+               document.getElementById("statusBox").innerHTML = status;
+       }
+})();
\ No newline at end of file