From 949fb45841374204307b50f48d0d2d9ab921deb6 Mon Sep 17 00:00:00 2001
From: Sauli Tähkäpää
Date: Thu, 26 Jun 2014 09:35:05 +0300
Subject: Revert "Add scrollbars to ComboBox suggestion popup if low on screen
estate (#11929)"
This reverts commit ede8fbaad050c98682df9da935caf59a3a3787c6.
Change-Id: I6d2b6ad3901ac50782a9c8af6847c0b917d6027d
---
client/src/com/vaadin/client/ui/VFilterSelect.java | 45 ++++++++--------------
1 file changed, 15 insertions(+), 30 deletions(-)
(limited to 'client')
diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java
index 7157ef99b3..004072d04a 100644
--- a/client/src/com/vaadin/client/ui/VFilterSelect.java
+++ b/client/src/com/vaadin/client/ui/VFilterSelect.java
@@ -30,7 +30,6 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
@@ -213,8 +212,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
private final Element down = DOM.createDiv();
private final Element status = DOM.createDiv();
- private int desiredHeight = -1;
-
private boolean isPagingEnabled = true;
private long lastAutoClosed;
@@ -231,7 +228,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
debug("VFS.SP: constructor()");
setOwner(VFilterSelect.this);
menu = new SuggestionMenu();
- menu.getElement().getStyle().setOverflowY(Overflow.AUTO);
setWidget(menu);
getElement().getStyle().setZIndex(Z_INDEX);
@@ -554,12 +550,16 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
public void setPosition(int offsetWidth, int offsetHeight) {
debug("VFS.SP: setPosition()");
- int top = getPopupTop();
- int left = getPopupLeft();
+ int top = -1;
+ int left = -1;
- if (desiredHeight < 0) {
- desiredHeight = offsetHeight;
+ // reset menu size and retrieve its "natural" size
+ menu.setHeight("");
+ if (currentPage > 0) {
+ // fix height to avoid height change when getting to last page
+ menu.fixHeightTo(pageLength);
}
+ offsetHeight = getOffsetHeight();
final int desiredWidth = getMainWidth();
Element menuFirstChild = menu.getElement().getFirstChildElement();
@@ -585,20 +585,16 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
getContainerElement().getStyle().setWidth(rootWidth, Unit.PX);
}
- final int spaceAvailableBelow = Window.getClientHeight()
- - (top - Window.getScrollTop());
- final int spaceAvailableAbove = top - Window.getScrollTop()
- - VFilterSelect.this.getOffsetHeight();
- if (spaceAvailableBelow < desiredHeight
- && spaceAvailableBelow < spaceAvailableAbove) {
+ if (offsetHeight + getPopupTop() > Window.getClientHeight()
+ + Window.getScrollTop()) {
// popup on top of input instead
- top -= desiredHeight + VFilterSelect.this.getOffsetHeight();
- offsetHeight = desiredHeight;
+ top = getPopupTop() - offsetHeight
+ - VFilterSelect.this.getOffsetHeight();
if (top < 0) {
- offsetHeight += top;
top = 0;
}
} else {
+ top = getPopupTop();
/*
* Take popup top margin into account. getPopupTop() returns the
* top value including the margin but the value we give must not
@@ -606,19 +602,6 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
*/
int topMargin = (top - topPosition);
top -= topMargin;
- offsetHeight = Math.min(desiredHeight, spaceAvailableBelow);
- }
-
- /*
- * Resize popup and menu if calculated height doesn't match the
- * actual height
- */
- if (getOffsetHeight() != offsetHeight) {
- int menuHeight = offsetHeight - up.getOffsetHeight()
- - down.getOffsetHeight() - status.getOffsetHeight();
- menu.setHeight(menuHeight + "px");
- getContainerElement().getStyle().setHeight(offsetHeight,
- Unit.PX);
}
// fetch real width (mac FF bugs here due GWT popups overflow:auto )
@@ -631,6 +614,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
if (left < 0) {
left = 0;
}
+ } else {
+ left = getPopupLeft();
}
setPopupPosition(left, top);
}
--
cgit v1.2.3
From 1b73b00b2875c5eafa3c641b77f5d9efc2f7d929 Mon Sep 17 00:00:00 2001
From: Artur Signell
Date: Wed, 11 Jun 2014 20:35:04 +0300
Subject: Allow changing theme on the fly (#2874)
Change-Id: I70c153c4109428686002f985bfe1cb7cba2c9b31
---
.../vaadin/client/ApplicationConfiguration.java | 17 +-
.../com/vaadin/client/ApplicationConnection.java | 5 +-
client/src/com/vaadin/client/ResourceLoader.java | 23 +-
.../communication/TranslatedURLReference.java | 38 +++
.../communication/URLReference_Serializer.java | 12 +-
.../vaadin/client/debug/internal/InfoSection.java | 2 +-
.../com/vaadin/client/ui/AbstractConnector.java | 24 ++
client/src/com/vaadin/client/ui/VUI.java | 25 +-
.../src/com/vaadin/client/ui/ui/UIConnector.java | 288 +++++++++++++++++++--
server/src/com/vaadin/ui/UI.java | 27 +-
shared/src/com/vaadin/shared/ui/ui/UIState.java | 3 +-
.../src/com/vaadin/tests/tb3/AbstractTB3Test.java | 26 +-
.../vaadin/tests/themes/ThemeChangeOnTheFly.java | 103 ++++++++
.../tests/themes/ThemeChangeOnTheFlyTest.java | 110 ++++++++
14 files changed, 630 insertions(+), 73 deletions(-)
create mode 100644 client/src/com/vaadin/client/communication/TranslatedURLReference.java
create mode 100644 uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFly.java
create mode 100644 uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java
(limited to 'client')
diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java
index 543a48f61b..dc4711510e 100644
--- a/client/src/com/vaadin/client/ApplicationConfiguration.java
+++ b/client/src/com/vaadin/client/ApplicationConfiguration.java
@@ -49,6 +49,7 @@ import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.NoDataException;
import com.vaadin.client.metadata.TypeData;
import com.vaadin.client.ui.UnknownComponentConnector;
+import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.ui.ui.UIConstants;
@@ -82,7 +83,7 @@ public class ApplicationConfiguration implements EntryPoint {
return null;
} else {
return value +"";
- }
+ }
}-*/;
/**
@@ -103,7 +104,7 @@ public class ApplicationConfiguration implements EntryPoint {
} else {
// $entry not needed as function is not exported
return @java.lang.Boolean::valueOf(Z)(value);
- }
+ }
}-*/;
/**
@@ -124,7 +125,7 @@ public class ApplicationConfiguration implements EntryPoint {
} else {
// $entry not needed as function is not exported
return @java.lang.Integer::valueOf(I)(value);
- }
+ }
}-*/;
/**
@@ -284,14 +285,16 @@ public class ApplicationConfiguration implements EntryPoint {
return serviceUrl;
}
+ /**
+ * @return the theme name used when initializing the application
+ * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} to get the
+ * theme currently in use
+ */
+ @Deprecated
public String getThemeName() {
return getJsoConfiguration(id).getConfigString("theme");
}
- public String getThemeUri() {
- return getVaadinDirUrl() + "themes/" + getThemeName();
- }
-
/**
* Gets the URL of the VAADIN directory on the server.
*
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index 8bc43dda9a..50f22edf81 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -3118,7 +3118,7 @@ public class ApplicationConnection implements HasHandlers {
return null;
}
if (uidlUri.startsWith("theme://")) {
- final String themeUri = configuration.getThemeUri();
+ final String themeUri = getThemeUri();
if (themeUri == null) {
VConsole.error("Theme not set: ThemeResource will not be found. ("
+ uidlUri + ")");
@@ -3184,7 +3184,8 @@ public class ApplicationConnection implements HasHandlers {
* @return URI to the current theme
*/
public String getThemeUri() {
- return configuration.getThemeUri();
+ return configuration.getVaadinDirUrl() + "themes/"
+ + getUIConnector().getActiveTheme();
}
/**
diff --git a/client/src/com/vaadin/client/ResourceLoader.java b/client/src/com/vaadin/client/ResourceLoader.java
index 67b878001e..59ebd797d1 100644
--- a/client/src/com/vaadin/client/ResourceLoader.java
+++ b/client/src/com/vaadin/client/ResourceLoader.java
@@ -334,7 +334,20 @@ public class ResourceLoader {
}
}
- private native void addOnloadHandler(Element element,
+ /**
+ * Adds an onload listener to the given element, which should be a link or a
+ * script tag. The listener is called whenever loading is complete or an
+ * error occurred.
+ *
+ * @since
+ * @param element
+ * the element to attach a listener to
+ * @param listener
+ * the listener to call
+ * @param event
+ * the event passed to the listener
+ */
+ public static native void addOnloadHandler(Element element,
ResourceLoadListener listener, ResourceLoadEvent event)
/*-{
element.onload = $entry(function() {
@@ -349,11 +362,11 @@ public class ResourceLoader {
element.onreadystatechange = null;
listener.@com.vaadin.client.ResourceLoader.ResourceLoadListener::onError(Lcom/vaadin/client/ResourceLoader$ResourceLoadEvent;)(event);
});
- element.onreadystatechange = function() {
+ element.onreadystatechange = function() {
if ("loaded" === element.readyState || "complete" === element.readyState ) {
element.onload(arguments[0]);
}
- };
+ };
}-*/;
/**
@@ -479,12 +492,12 @@ public class ResourceLoader {
if (rules === undefined) {
rules = sheet.rules;
}
-
+
if (rules === null) {
// Style sheet loaded, but can't access length because of XSS -> assume there's something there
return 1;
}
-
+
// Return length so we can distinguish 0 (probably 404 error) from normal case.
return rules.length;
} catch (err) {
diff --git a/client/src/com/vaadin/client/communication/TranslatedURLReference.java b/client/src/com/vaadin/client/communication/TranslatedURLReference.java
new file mode 100644
index 0000000000..c863b7b796
--- /dev/null
+++ b/client/src/com/vaadin/client/communication/TranslatedURLReference.java
@@ -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.client.communication;
+
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.shared.communication.URLReference;
+
+public class TranslatedURLReference extends URLReference {
+
+ private ApplicationConnection connection;
+
+ /**
+ * @param connection
+ * the connection to set
+ */
+ public void setConnection(ApplicationConnection connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ public String getURL() {
+ return connection.translateVaadinUri(super.getURL());
+ }
+
+}
diff --git a/client/src/com/vaadin/client/communication/URLReference_Serializer.java b/client/src/com/vaadin/client/communication/URLReference_Serializer.java
index 586dd626f0..4ecdc606d2 100644
--- a/client/src/com/vaadin/client/communication/URLReference_Serializer.java
+++ b/client/src/com/vaadin/client/communication/URLReference_Serializer.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -30,14 +30,16 @@ public class URLReference_Serializer implements JSONSerializer {
@Override
public URLReference deserialize(Type type, JSONValue jsonValue,
ApplicationConnection connection) {
- URLReference reference = GWT.create(URLReference.class);
+ TranslatedURLReference reference = GWT
+ .create(TranslatedURLReference.class);
+ reference.setConnection(connection);
JSONObject json = (JSONObject) jsonValue;
if (json.containsKey(URL_FIELD)) {
JSONValue jsonURL = json.get(URL_FIELD);
String URL = (String) JsonDecoder.decodeValue(
new Type(String.class.getName(), null), jsonURL, null,
connection);
- reference.setURL(connection.translateVaadinUri(URL));
+ reference.setURL(URL);
}
return reference;
}
diff --git a/client/src/com/vaadin/client/debug/internal/InfoSection.java b/client/src/com/vaadin/client/debug/internal/InfoSection.java
index 23b77a94db..a7a84f5f8f 100644
--- a/client/src/com/vaadin/client/debug/internal/InfoSection.java
+++ b/client/src/com/vaadin/client/debug/internal/InfoSection.java
@@ -163,7 +163,7 @@ public class InfoSection implements Section {
addVersionInfo(configuration);
addRow("Widget set", GWT.getModuleName());
- addRow("Theme", connection.getConfiguration().getThemeName());
+ addRow("Theme", connection.getUIConnector().getActiveTheme());
String communicationMethodInfo = connection
.getCommunicationMethodName();
diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java
index a2e0d9cd54..e93ea0f507 100644
--- a/client/src/com/vaadin/client/ui/AbstractConnector.java
+++ b/client/src/com/vaadin/client/ui/AbstractConnector.java
@@ -28,6 +28,7 @@ import com.google.gwt.event.shared.HandlerManager;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.FastStringMap;
+import com.vaadin.client.FastStringSet;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
@@ -479,4 +480,27 @@ public abstract class AbstractConnector implements ServerConnector,
Set reg = getState().registeredEventListeners;
return (reg != null && reg.contains(eventIdentifier));
}
+
+ /**
+ * Force the connector to recheck its state variables as the variables or
+ * their meaning might have changed.
+ *
+ * @since 7.3
+ */
+ public void forceStateChange() {
+ StateChangeEvent event = new FullStateChangeEvent(this);
+ fireEvent(event);
+ }
+
+ private static class FullStateChangeEvent extends StateChangeEvent {
+ public FullStateChangeEvent(ServerConnector connector) {
+ super(connector, FastStringSet.create());
+ }
+
+ @Override
+ public boolean hasPropertyChanged(String property) {
+ return true;
+ }
+
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VUI.java b/client/src/com/vaadin/client/ui/VUI.java
index df24c3b1c7..eae4f6319d 100644
--- a/client/src/com/vaadin/client/ui/VUI.java
+++ b/client/src/com/vaadin/client/ui/VUI.java
@@ -48,11 +48,12 @@ import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
+import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.ui.ui.UIConstants;
/**
- *
+ *
*/
public class VUI extends SimplePanel implements ResizeHandler,
Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable,
@@ -61,9 +62,6 @@ public class VUI extends SimplePanel implements ResizeHandler,
private static int MONITOR_PARENT_TIMER_INTERVAL = 1000;
- /** For internal use only. May be removed or replaced in the future. */
- public String theme;
-
/** For internal use only. May be removed or replaced in the future. */
public String id;
@@ -319,19 +317,15 @@ public class VUI extends SimplePanel implements ResizeHandler,
}
}
- public String getTheme() {
- return theme;
- }
-
/**
- * Used to reload host page on theme changes.
- *
- * For internal use only. May be removed or replaced in the future.
+ * @return the name of the theme in use by this UI.
+ * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} instead.
*/
- public static native void reloadHostPage()
- /*-{
- $wnd.location.reload();
- }-*/;
+ @Deprecated
+ public String getTheme() {
+ return ((UIConnector) ConnectorMap.get(connection).getConnector(this))
+ .getActiveTheme();
+ }
/**
* Returns true if the body is NOT generated, i.e if someone else has made
@@ -530,4 +524,5 @@ public class VUI extends SimplePanel implements ResizeHandler,
});
}
}
+
}
diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java
index 1d2a49cbd1..8997509da6 100644
--- a/client/src/com/vaadin/client/ui/ui/UIConnector.java
+++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -18,6 +18,7 @@ package com.vaadin.client.ui.ui;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.logging.Logger;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -26,8 +27,10 @@ import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.HeadElement;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.StyleElement;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
@@ -51,12 +54,17 @@ import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.Focusable;
import com.vaadin.client.Paintable;
+import com.vaadin.client.ResourceLoader;
+import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
+import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.UIDL;
import com.vaadin.client.VConsole;
import com.vaadin.client.ValueMap;
+import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+import com.vaadin.client.ui.AbstractConnector;
import com.vaadin.client.ui.AbstractSingleComponentContainerConnector;
import com.vaadin.client.ui.ClickEventHandler;
import com.vaadin.client.ui.ShortcutActionHandler;
@@ -66,6 +74,7 @@ import com.vaadin.client.ui.VUI;
import com.vaadin.client.ui.layout.MayScrollChildren;
import com.vaadin.client.ui.window.WindowConnector;
import com.vaadin.server.Page.Styles;
+import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.communication.MethodInvocation;
import com.vaadin.shared.ui.ComponentStateUtil;
@@ -80,6 +89,7 @@ import com.vaadin.shared.ui.ui.UIClientRpc;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.ui.ui.UIServerRpc;
import com.vaadin.shared.ui.ui.UIState;
+import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.UI;
@Connect(value = UI.class, loadStyle = LoadStyle.EAGER)
@@ -88,6 +98,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
private HandlerRegistration childStateChangeHandlerRegistration;
+ private String activeTheme = null;
+
private final StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
@@ -197,14 +209,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
getWidget().immediate = getState().immediate;
getWidget().resizeLazy = uidl.hasAttribute(UIConstants.RESIZE_LAZY);
- String newTheme = uidl.getStringAttribute("theme");
- if (getWidget().theme != null && !newTheme.equals(getWidget().theme)) {
- // Complete page refresh is needed due css can affect layout
- // calculations etc
- getWidget().reloadHostPage();
- } else {
- getWidget().theme = newTheme;
- }
// this also implicitly removes old styles
String styles = "";
styles += getWidget().getStylePrimaryName() + " ";
@@ -399,15 +403,12 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
/**
* Reads CSS strings and resources injected by {@link Styles#inject} from
* the UIDL stream.
- *
+ *
* @param uidl
* The uidl which contains "css-resource" and "css-string" tags
*/
private void injectCSS(UIDL uidl) {
- final HeadElement head = HeadElement.as(Document.get()
- .getElementsByTagName(HeadElement.TAG).getItem(0));
-
/*
* Search the UIDL stream for CSS resources and strings to be injected.
*/
@@ -424,8 +425,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
link.setRel("stylesheet");
link.setHref(url);
link.setType("text/css");
- head.appendChild(link);
-
+ getHead().appendChild(link);
// Check if we have CSS string to inject
} else if (cssInjectionsUidl.getTag().equals("css-string")) {
for (Iterator> it2 = cssInjectionsUidl.getChildIterator(); it2
@@ -437,8 +437,53 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
}
}
+ /**
+ * Internal helper to get the
tag of the page
+ *
+ * @since
+ * @return the head element
+ */
+ private HeadElement getHead() {
+ return HeadElement.as(Document.get()
+ .getElementsByTagName(HeadElement.TAG).getItem(0));
+ }
+
+ /**
+ * Internal helper for removing any stylesheet with the given URL
+ *
+ * @since
+ * @param url
+ * the url to match with existing stylesheets
+ */
+ private void removeStylesheet(String url) {
+ NodeList linkTags = getHead().getElementsByTagName(
+ LinkElement.TAG);
+ for (int i = 0; i < linkTags.getLength(); i++) {
+ LinkElement link = LinkElement.as(linkTags.getItem(i));
+ if (!"stylesheet".equals(link.getRel())) {
+ continue;
+ }
+ if (!"text/css".equals(link.getType())) {
+ continue;
+ }
+ if (url.equals(link.getHref())) {
+ getHead().removeChild(link);
+ }
+ }
+ }
+
public void init(String rootPanelId,
ApplicationConnection applicationConnection) {
+ // Create a style tag for style injections so they don't end up in
+ // the theme tag in IE8-IE10 (we don't want to wipe them out if we
+ // change theme)
+ if (BrowserInfo.get().isIE()
+ && BrowserInfo.get().getBrowserMajorVersion() < 11) {
+ StyleElement style = Document.get().createStyleElement();
+ style.setType("text/css");
+ getHead().appendChild(style);
+ }
+
DOM.sinkEvents(getWidget().getElement(), Event.ONKEYDOWN
| Event.ONSCROLL);
@@ -448,9 +493,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
// the user
root.getElement().setInnerHTML("");
- String themeName = applicationConnection.getConfiguration()
- .getThemeName();
- root.addStyleName(themeName);
+ activeTheme = applicationConnection.getConfiguration().getThemeName();
root.add(getWidget());
@@ -538,7 +581,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
/**
* Checks if the given sub window is a child of this UI Connector
- *
+ *
* @deprecated Should be replaced by a more generic mechanism for getting
* non-ComponentConnector children
* @param wc
@@ -552,7 +595,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
/**
* Return an iterator for current subwindows. This method is meant for
* testing purposes only.
- *
+ *
* @return
*/
public List getSubWindows() {
@@ -579,7 +622,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
* public API instead of their state object directly. The page state might
* not be an independent state object but can be embedded in UI state.
*
- *
+ *
* @since 7.1
* @return state object of the page
*/
@@ -643,10 +686,10 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
/**
* Tries to scroll the viewport so that the given connector is in view.
- *
+ *
* @param componentConnector
* The connector which should be visible
- *
+ *
*/
public void scrollIntoView(final ComponentConnector componentConnector) {
if (componentConnector == null) {
@@ -740,7 +783,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
/**
* Invokes the layout analyzer on the server
- *
+ *
* @since 7.1
*/
public void analyzeLayouts() {
@@ -751,7 +794,7 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
* Sends a request to the server to print details to console that will help
* the developer to locate the corresponding server-side connector in the
* source code.
- *
+ *
* @since 7.1
* @param serverConnector
* the connector to locate
@@ -760,4 +803,195 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
getRpcProxy(DebugWindowServerRpc.class).showServerDebugInfo(
serverConnector);
}
+
+ @OnStateChange("theme")
+ void onThemeChange() {
+ final String oldTheme = activeTheme;
+ final String newTheme = getState().theme;
+ if (SharedUtil.equals(oldTheme, newTheme)) {
+ // This should only happen on the initial load when activeTheme has
+ // been updated in init
+ return;
+ }
+
+ final String oldThemeUrl = getThemeUrl(oldTheme);
+ final String newThemeUrl = getThemeUrl(newTheme);
+ getLogger().info("Changing theme from " + oldTheme + " to " + newTheme);
+ replaceTheme(oldTheme, newTheme, oldThemeUrl, newThemeUrl);
+ }
+
+ /**
+ * Loads the new theme and removes references to the old theme
+ *
+ * @param oldTheme
+ * The name of the old theme
+ * @param newTheme
+ * The name of the new theme
+ * @param oldThemeUrl
+ * The url of the old theme
+ * @param newThemeUrl
+ * The url of the new theme
+ */
+ private void replaceTheme(final String oldTheme, final String newTheme,
+ String oldThemeUrl, final String newThemeUrl) {
+
+ LinkElement tagToReplace = null;
+
+ if (oldTheme != null) {
+ NodeList linkTags = getHead().getElementsByTagName(
+ LinkElement.TAG);
+ for (int i = 0; i < linkTags.getLength(); i++) {
+ final LinkElement link = LinkElement.as(linkTags.getItem(i));
+ if ("stylesheet".equals(link.getRel())
+ && "text/css".equals(link.getType())
+ && oldThemeUrl.equals(link.getHref())) {
+ tagToReplace = link;
+ break;
+ }
+ }
+
+ if (tagToReplace == null) {
+ getLogger()
+ .warning(
+ "Did not find the link tag for the old theme ("
+ + oldThemeUrl
+ + "), adding a new stylesheet for the new theme ("
+ + newThemeUrl + ")");
+ }
+ }
+
+ if (newTheme != null) {
+ loadTheme(newTheme, newThemeUrl, tagToReplace);
+ } else {
+ if (tagToReplace != null) {
+ tagToReplace.getParentElement().removeChild(tagToReplace);
+ }
+
+ activateTheme(null);
+ }
+
+ }
+
+ /**
+ * Loads the given theme and replaces the given link element with the new
+ * theme link element.
+ *
+ * @param newTheme
+ * The name of the new theme
+ * @param newThemeUrl
+ * The url of the new theme
+ * @param tagToReplace
+ * The link element to replace. If null, then the new link
+ * element is added at the end.
+ */
+ private void loadTheme(final String newTheme, final String newThemeUrl,
+ final LinkElement tagToReplace) {
+ LinkElement newThemeLinkElement = Document.get().createLinkElement();
+ newThemeLinkElement.setRel("stylesheet");
+ newThemeLinkElement.setType("text/css");
+ newThemeLinkElement.setHref(newThemeUrl);
+ ResourceLoader.addOnloadHandler(newThemeLinkElement,
+ new ResourceLoadListener() {
+
+ @Override
+ public void onLoad(ResourceLoadEvent event) {
+ getLogger().info(
+ "Loading of " + newTheme + " from "
+ + newThemeUrl + " completed");
+
+ if (tagToReplace != null) {
+ tagToReplace.getParentElement().removeChild(
+ tagToReplace);
+ }
+ activateTheme(newTheme);
+ }
+
+ @Override
+ public void onError(ResourceLoadEvent event) {
+ getLogger().warning(
+ "Could not load theme from "
+ + getThemeUrl(newTheme));
+ }
+ }, null);
+
+ if (tagToReplace != null) {
+ getHead().insertBefore(newThemeLinkElement, tagToReplace);
+ } else {
+ getHead().appendChild(newThemeLinkElement);
+ }
+ }
+
+ /**
+ * Activates the new theme. Assumes the theme has been loaded and taken into
+ * use in the browser.
+ *
+ * @since 7.3
+ * @param newTheme
+ */
+ private void activateTheme(String newTheme) {
+ if (activeTheme != null) {
+ getWidget().getParent().removeStyleName(activeTheme);
+ }
+
+ activeTheme = newTheme;
+
+ if (newTheme != null) {
+ getWidget().getParent().addStyleName(newTheme);
+ }
+
+ forceStateChangeRecursively(UIConnector.this);
+ getLayoutManager().forceLayout();
+ }
+
+ /**
+ * Force a full recursive recheck of every connector's state variables.
+ *
+ * @see #forceStateChange()
+ *
+ * @since 7.3
+ */
+ protected static void forceStateChangeRecursively(
+ AbstractConnector connector) {
+ connector.forceStateChange();
+
+ for (ServerConnector child : connector.getChildren()) {
+ if (child instanceof AbstractConnector) {
+ forceStateChangeRecursively((AbstractConnector) child);
+ } else {
+ getLogger().warning(
+ "Could not force state change for unknown connector type: "
+ + child.getClass().getName());
+ }
+ }
+
+ }
+
+ /**
+ * Internal helper to get the theme URL for a given theme
+ *
+ * @since
+ * @param theme
+ * the name of the theme
+ * @return The URL the theme can be loaded from
+ */
+ private String getThemeUrl(String theme) {
+ return getConnection().translateVaadinUri(
+ ApplicationConstants.VAADIN_PROTOCOL_PREFIX + "themes/" + theme
+ + "/styles" + ".css");
+ }
+
+ /**
+ * Returns the name of the theme currently in used by the UI
+ *
+ * @since
+ * @return the theme name used by this UI
+ */
+ public String getActiveTheme() {
+ return activeTheme;
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(UIConnector.class.getName());
+ }
+
}
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index a72cbe5c30..4bde8a95b3 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -549,8 +549,6 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private boolean resizeLazy = false;
- private String theme;
-
private Navigator navigator;
private PushConnection pushConnection = null;
@@ -633,7 +631,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
this.embedId = embedId;
// Actual theme - used for finding CustomLayout templates
- theme = request.getParameter("theme");
+ getState(false).theme = request.getParameter("theme");
getPage().init(request);
@@ -1135,12 +1133,31 @@ public abstract class UI extends AbstractSingleComponentContainer implements
}
/**
- * Gets the theme that was used when the UI was initialized.
+ * Gets the theme currently in use by this UI
*
* @return the theme name
*/
public String getTheme() {
- return theme;
+ return getState(false).theme;
+ }
+
+ /**
+ * Sets the theme currently in use by this UI
+ *
+ * Calling this method will remove the old theme (CSS file) from the
+ * application and add the new theme.
+ *
+ * Note that this method is NOT SAFE to call in a portal environment or
+ * other environment where there are multiple UIs on the same page. The old
+ * CSS file will be removed even if there are other UIs on the page which
+ * are still using it.
+ *
+ * @since
+ * @param theme
+ * The new theme name
+ */
+ public void setTheme(String theme) {
+ getState().theme = theme;
}
/**
diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java
index 3c3785b7d5..abdc105ba7 100644
--- a/shared/src/com/vaadin/shared/ui/ui/UIState.java
+++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java
@@ -62,6 +62,7 @@ public class UIState extends TabIndexState {
* Configuration for the push channel
*/
public PushConfigurationState pushConfiguration = new PushConfigurationState();
+ public String theme;
{
primaryStyleName = "v-ui";
// Default is 1 for legacy reasons
@@ -95,7 +96,7 @@ public class UIState extends TabIndexState {
NotificationRole role) {
this.prefix = prefix;
this.postfix = postfix;
- this.notificationRole = role;
+ notificationRole = role;
}
}
diff --git a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java
index 406f1fe27c..14be226de6 100644
--- a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java
+++ b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java
@@ -16,6 +16,8 @@
package com.vaadin.tests.tb3;
+import static com.vaadin.tests.tb3.TB3Runner.localWebDriverIsUsed;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,7 +26,6 @@ import java.net.URL;
import java.util.Collections;
import java.util.List;
-import com.vaadin.testbench.TestBenchElement;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
@@ -49,13 +50,12 @@ import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
import com.vaadin.server.LegacyApplication;
import com.vaadin.server.UIProvider;
import com.vaadin.testbench.TestBench;
+import com.vaadin.testbench.TestBenchElement;
import com.vaadin.testbench.TestBenchTestCase;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.tb3.MultiBrowserTest.Browser;
import com.vaadin.ui.UI;
-import static com.vaadin.tests.tb3.TB3Runner.localWebDriverIsUsed;
-
/**
* Base class for TestBench 3+ tests. All TB3+ tests in the project should
* extend this class.
@@ -157,7 +157,8 @@ public abstract class AbstractTB3Test extends TestBenchTestCase {
}
protected WebElement getTooltipElement() {
- return getDriver().findElement(com.vaadin.testbench.By.className("v-tooltip-text"));
+ return getDriver().findElement(
+ com.vaadin.testbench.By.className("v-tooltip-text"));
}
protected Coordinates getCoordinates(TestBenchElement element) {
@@ -184,7 +185,22 @@ public abstract class AbstractTB3Test extends TestBenchTestCase {
* {@link #isPush()}.
*/
protected void openTestURL() {
- driver.get(getTestUrl());
+ openTestURL("");
+ }
+
+ /**
+ * Opens the given test (defined by {@link #getTestUrl()}, optionally with
+ * debug window and/or push (depending on {@link #isDebug()} and
+ * {@link #isPush()}.
+ */
+ protected void openTestURL(String extraParameters) {
+ String url = getTestUrl();
+ if (url.contains("?")) {
+ url = url + "&" + extraParameters;
+ } else {
+ url = url + "?" + extraParameters;
+ }
+ driver.get(url);
}
/**
diff --git a/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFly.java b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFly.java
new file mode 100644
index 0000000000..8989d88164
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFly.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2013 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.themes;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.util.PersonContainer;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.GridLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Image;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Table;
+
+@Theme("reindeer")
+public class ThemeChangeOnTheFly extends AbstractTestUIWithLog {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ Button inject = new Button("Inject blue background");
+ inject.addClickListener(new ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ getPage().getStyles().add(
+ ".v-app { background: blue !important;}");
+
+ }
+ });
+ addComponent(inject);
+
+ GridLayout gl = new GridLayout(2, 4);
+ gl.setCaption("Change theme by clicking a button");
+ for (final String theme : new String[] { "reindeer", "runo",
+ "chameleon", "base", null }) {
+ Button b = new Button(theme);
+ b.setId(theme + "");
+ b.addClickListener(new ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ getUI().setTheme(theme);
+ }
+ });
+ gl.addComponent(b);
+ }
+
+ Table t = new Table();
+ PersonContainer pc = PersonContainer.createWithTestData();
+ pc.addNestedContainerBean("address");
+ t.setContainerDataSource(pc);
+ gl.addComponent(t, 0, 3, 1, 3);
+ gl.setRowExpandRatio(3, 1);
+
+ gl.setWidth("500px");
+ gl.setHeight("800px");
+
+ HorizontalLayout images = new HorizontalLayout();
+ images.setSpacing(true);
+
+ Label l = new Label("Chameleon theme image in caption");
+ l.setIcon(new ThemeResource("img/magnifier.png"));
+ images.addComponent(l);
+ Image image = new Image("Runo theme image", new ThemeResource(
+ "icons/64/ok.png"));
+ images.addComponent(image);
+ image = new Image("Reindeer theme image", new ThemeResource(
+ "button/img/left-focus.png"));
+ images.addComponent(image);
+ addComponent(images);
+ addComponent(gl);
+
+ getLayout().setSpacing(true);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Test that you can change theme on the fly";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 2874;
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java
new file mode 100644
index 0000000000..681a7d762b
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2000-2013 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.themes;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class ThemeChangeOnTheFlyTest extends MultiBrowserTest {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.tb3.MultiBrowserTest#getBrowsersToTest()
+ */
+ @Override
+ public List getBrowsersToTest() {
+ // Seems like stylesheet onload is not fired on PhantomJS
+ List l = super.getBrowsersToTest();
+ l.remove(Browser.PHANTOMJS.getDesiredCapabilities());
+ return l;
+ }
+
+ @Test
+ public void injectedStyleAndThemeChange() throws IOException {
+ openTestURL();
+ $(ButtonElement.class).caption("Inject blue background").first()
+ .click();
+ changeTheme("runo");
+ compareScreen("runo-blue-background");
+ }
+
+ @Test
+ public void reindeerToOthers() throws IOException {
+ openTestURL();
+ compareScreen("reindeer");
+
+ changeThemeAndCompare("runo");
+ changeThemeAndCompare("chameleon");
+ changeThemeAndCompare("base");
+
+ }
+
+ @Test
+ public void runoToReindeer() throws IOException {
+ openTestURL("theme=runo");
+ compareScreen("runo");
+ changeThemeAndCompare("reindeer");
+ }
+
+ @Test
+ public void reindeerToNullToReindeer() throws IOException {
+ openTestURL();
+
+ changeThemeAndCompare("null");
+ changeThemeAndCompare("reindeer");
+ }
+
+ private void changeThemeAndCompare(String theme) throws IOException {
+ changeTheme(theme);
+ compareScreen(theme);
+ }
+
+ private void changeTheme(String theme) {
+ $(ButtonElement.class).id(theme).click();
+ if (theme.equals("null")) {
+ waitForThemeToChange("");
+ } else {
+ waitForThemeToChange(theme);
+ }
+ }
+
+ private void waitForThemeToChange(final String theme) {
+
+ final WebElement rootDiv = findElement(By
+ .xpath("//div[contains(@class,'v-app')]"));
+ waitUntil(new ExpectedCondition() {
+
+ @Override
+ public Boolean apply(WebDriver input) {
+ String rootClass = rootDiv.getAttribute("class").trim();
+ String expected = "v-app " + theme;
+ expected = expected.trim();
+ return rootClass.equals(expected);
+ }
+ }, 30);
+ }
+}
--
cgit v1.2.3
From a685cc8a595509c707aa692d32b1daa2fea4ca57 Mon Sep 17 00:00:00 2001
From: Mikael Grankvist
Date: Thu, 19 Jun 2014 09:31:19 +0300
Subject: IE11 now uses synchronous loading of script instead of async with
preloading. (#13956)
Change-Id: Ia61d672a55f69326b37420305f0108f2d7443b70
---
.../com/vaadin/client/ApplicationConnection.java | 16 +++--
client/src/com/vaadin/client/ResourceLoader.java | 39 ++++++++++++
.../vaadin/launcher/DevelopmentServerLauncher.java | 1 +
.../javascriptcomponent/JavaScriptPreloading.java | 65 +++++++++++++++++++
.../JavaScriptPreloadingTest.java | 74 ++++++++++++++++++++++
.../components/javascriptcomponent/js_label.js | 11 ++++
.../javascriptcomponent/wholly_different.js | 3 +
7 files changed, 204 insertions(+), 5 deletions(-)
create mode 100644 uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
create mode 100644 uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloadingTest.java
create mode 100755 uitest/src/com/vaadin/tests/components/javascriptcomponent/js_label.js
create mode 100755 uitest/src/com/vaadin/tests/components/javascriptcomponent/wholly_different.js
(limited to 'client')
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index 50f22edf81..c39beffd87 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -66,7 +66,6 @@ import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
-import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
@@ -2499,10 +2498,17 @@ public class ApplicationConnection implements HasHandlers {
ApplicationConfiguration.startDependencyLoading();
loader.loadScript(url, resourceLoadListener);
- // Preload all remaining
- for (int i = 0; i < dependencies.length(); i++) {
- String preloadUrl = translateVaadinUri(dependencies.get(i));
- loader.preloadResource(preloadUrl, null);
+ if (ResourceLoader.supportsInOrderScriptExecution()) {
+ for (int i = 0; i < dependencies.length(); i++) {
+ String preloadUrl = translateVaadinUri(dependencies.get(i));
+ loader.loadScript(preloadUrl, null);
+ }
+ } else {
+ // Preload all remaining
+ for (int i = 0; i < dependencies.length(); i++) {
+ String preloadUrl = translateVaadinUri(dependencies.get(i));
+ loader.preloadResource(preloadUrl, null);
+ }
}
}
diff --git a/client/src/com/vaadin/client/ResourceLoader.java b/client/src/com/vaadin/client/ResourceLoader.java
index 59ebd797d1..e88def1a72 100644
--- a/client/src/com/vaadin/client/ResourceLoader.java
+++ b/client/src/com/vaadin/client/ResourceLoader.java
@@ -204,6 +204,26 @@ public class ResourceLoader {
*/
public void loadScript(final String scriptUrl,
final ResourceLoadListener resourceLoadListener) {
+ loadScript(scriptUrl, resourceLoadListener,
+ !supportsInOrderScriptExecution());
+ }
+
+ /**
+ * Load a script and notify a listener when the script is loaded. Calling
+ * this method when the script is currently loading or already loaded
+ * doesn't cause the script to be loaded again, but the listener will still
+ * be notified when appropriate.
+ *
+ *
+ * @param scriptUrl
+ * url of script to load
+ * @param resourceLoadListener
+ * listener to notify when script is loaded
+ * @param async
+ * What mode the script.async attribute should be set to
+ */
+ public void loadScript(final String scriptUrl,
+ final ResourceLoadListener resourceLoadListener, boolean async) {
final String url = Util.getAbsoluteUrl(scriptUrl);
ResourceLoadEvent event = new ResourceLoadEvent(this, url, false);
if (loadedResources.contains(url)) {
@@ -236,6 +256,9 @@ public class ResourceLoader {
ScriptElement scriptTag = Document.get().createScriptElement();
scriptTag.setSrc(url);
scriptTag.setType("text/javascript");
+
+ scriptTag.setPropertyBoolean("async", async);
+
addOnloadHandler(scriptTag, new ResourceLoadListener() {
@Override
public void onLoad(ResourceLoadEvent event) {
@@ -251,6 +274,17 @@ public class ResourceLoader {
}
}
+ /**
+ * The current browser supports script.async='false' for maintaining
+ * execution order for dynamically-added scripts.
+ *
+ * @return Browser supports script.async='false'
+ */
+ public static boolean supportsInOrderScriptExecution() {
+ return BrowserInfo.get().isIE()
+ && BrowserInfo.get().getBrowserMajorVersion() >= 11;
+ }
+
/**
* Download a resource and notify a listener when the resource is loaded
* without attempting to interpret the resource. When a resource has been
@@ -316,6 +350,11 @@ public class ResourceLoader {
* XHR not tested - should work, probably causes other issues
-*/
if (BrowserInfo.get().isIE()) {
+ // If ie11+ for some reason gets a preload request
+ if (BrowserInfo.get().getBrowserMajorVersion() >= 11) {
+ throw new RuntimeException(
+ "Browser doesn't support preloading with text/cache");
+ }
ScriptElement element = Document.get().createScriptElement();
element.setSrc(url);
element.setType("text/cache");
diff --git a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
index d94518ca9c..e6e54a57a6 100644
--- a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
+++ b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
@@ -142,6 +142,7 @@ public class DevelopmentServerLauncher {
assignDefault(serverArgs, "webroot", "WebContent");
assignDefault(serverArgs, "httpPort", "" + serverPort);
assignDefault(serverArgs, "context", "");
+ assignDefault(serverArgs, "slowdown", "/run/APP/PUBLISHED/js_label.js");
int port = serverPort;
try {
diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
new file mode 100644
index 0000000000..e4fd3fec68
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
@@ -0,0 +1,65 @@
+/*
+ * 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.javascriptcomponent;
+
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.JavaScriptComponentState;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.AbstractJavaScriptComponent;
+
+public class IEJavaScriptPreloading extends AbstractTestUI {
+
+ public static class JsLabelState extends JavaScriptComponentState {
+ public String xhtml;
+ }
+
+ @JavaScript({ "js_label.js", "wholly_different.js" })
+ public class JsLabel extends AbstractJavaScriptComponent {
+
+ public JsLabel(final String xhtml) {
+ getState().xhtml = xhtml;
+ }
+
+ @Override
+ protected JsLabelState getState() {
+ return (JsLabelState) super.getState();
+ }
+ }
+
+ private final Log log = new Log(5);
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ addComponent(log);
+
+ final JsLabel c = new JsLabel("Hello World!");
+ c.setId("js-component");
+ addComponent(c);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Loading javascript component with multiple sourcefiles should not break IE11";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(13956);
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloadingTest.java b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloadingTest.java
new file mode 100644
index 0000000000..a9e7a1bca7
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloadingTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.javascriptcomponent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.openqa.selenium.Alert;
+import org.openqa.selenium.TimeoutException;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class JavaScriptPreloadingTest extends MultiBrowserTest {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.tb3.MultiBrowserTest#getBrowsersToTest()
+ */
+ @Override
+ public List getBrowsersToTest() {
+ List browsers = super.getBrowsersToTest();
+ browsers.remove(Browser.PHANTOMJS.getDesiredCapabilities());
+ return browsers;
+ }
+
+ @Test
+ public void scriptsShouldPreloadAndExecuteInCorrectOrder()
+ throws InterruptedException {
+ openTestURL();
+
+ try {
+ new WebDriverWait(driver, 10).until(ExpectedConditions
+ .alertIsPresent());
+ Alert alert = driver.switchTo().alert();
+ assertEquals("First", alert.getText());
+ alert.accept();
+
+ new WebDriverWait(driver, 10).until(ExpectedConditions
+ .alertIsPresent());
+ alert = driver.switchTo().alert();
+ assertEquals("Second", alert.getText());
+ alert.accept();
+
+ } catch (TimeoutException te) {
+ fail("@Javascript widget loading halted.");
+ }
+
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/js_label.js b/uitest/src/com/vaadin/tests/components/javascriptcomponent/js_label.js
new file mode 100755
index 0000000000..c4900daa81
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/js_label.js
@@ -0,0 +1,11 @@
+window.com_vaadin_tests_components_javascriptcomponent_JavaScriptPreloading_JsLabel = function() {
+ var e = this.getElement();
+
+ (function() {
+ e.innerHTML = "Widget executed javascript";
+ })();
+};
+
+(function() {
+ window.alert("First");
+})();
diff --git a/uitest/src/com/vaadin/tests/components/javascriptcomponent/wholly_different.js b/uitest/src/com/vaadin/tests/components/javascriptcomponent/wholly_different.js
new file mode 100755
index 0000000000..f22ef13a76
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/javascriptcomponent/wholly_different.js
@@ -0,0 +1,3 @@
+(function() {
+ window.alert("Second");
+})();
--
cgit v1.2.3