summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--all/src/main/templates/release-notes.html1
-rw-r--r--client/src/main/java/com/vaadin/client/DependencyLoader.java113
-rw-r--r--client/src/main/java/com/vaadin/client/ResourceLoader.java52
-rw-r--r--documentation/gwt/gwt-advanced.asciidoc12
-rw-r--r--server/src/main/java/com/vaadin/annotations/HtmlImport.java73
-rw-r--r--server/src/main/java/com/vaadin/annotations/InternalContainerAnnotationForHtml.java45
-rw-r--r--server/src/main/java/com/vaadin/annotations/JavaScript.java1
-rw-r--r--server/src/main/java/com/vaadin/server/BootstrapHandler.java59
-rw-r--r--server/src/main/java/com/vaadin/server/communication/UidlWriter.java54
-rw-r--r--server/src/main/java/com/vaadin/ui/Dependency.java174
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java6
-rw-r--r--uitest/src/main/java/com/vaadin/tests/htmlimport/HtmlImportUI.java49
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/components/ui/uiDependency.js2
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/label.html10
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/label.js3
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.html10
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.js3
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.html10
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.js3
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.html10
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.js14
-rw-r--r--uitest/src/main/resources/com/vaadin/tests/htmlimport/webcomponents-lite.min.js12
-rw-r--r--uitest/src/test/java/com/vaadin/tests/htmlimport/HtmlImportUITest.java114
23 files changed, 677 insertions, 153 deletions
diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html
index 9b58510f2c..71abd7bf9b 100644
--- a/all/src/main/templates/release-notes.html
+++ b/all/src/main/templates/release-notes.html
@@ -91,6 +91,7 @@
<li>Vaadin Framework is now based on Java 8</li>
<li>Vaadin Framework is using GWT 2.8, allowing client side code to use Java 8</li>
<li>Other Framework dependencies including Atmosphere have been upgraded</li>
+ <li>Easier to integrate web components using HTML imports (<tt>@HtmlImport</tt>) and custom tag name support for <tt>AbstractJavaScriptComponent</tt></li>
</ul>
<p>
diff --git a/client/src/main/java/com/vaadin/client/DependencyLoader.java b/client/src/main/java/com/vaadin/client/DependencyLoader.java
index d217c824b2..4a8e054a54 100644
--- a/client/src/main/java/com/vaadin/client/DependencyLoader.java
+++ b/client/src/main/java/com/vaadin/client/DependencyLoader.java
@@ -17,7 +17,7 @@ package com.vaadin.client;
import java.util.logging.Logger;
-import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.Command;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
@@ -34,10 +34,31 @@ import com.vaadin.client.ResourceLoader.ResourceLoadListener;
*/
public class DependencyLoader {
- private static final String STYLE_DEPENDENCIES = "styleDependencies";
- private static final String SCRIPT_DEPENDENCIES = "scriptDependencies";
+ private static final String DEPENDENCIES = "dependencies";
private ApplicationConnection connection = null;
+ private ResourceLoader loader = ResourceLoader.get();
+
+ private ResourceLoadListener dependencyLoadingTracker = new ResourceLoadListener() {
+
+ @Override
+ public void onLoad(ResourceLoadEvent event) {
+ ApplicationConfiguration.endDependencyLoading();
+ }
+
+ @Override
+ public void onError(ResourceLoadEvent event) {
+ String error = event.getResourceUrl() + " could not be loaded.";
+ if (event.getResourceUrl().endsWith("css")) {
+ error += " or the load detection failed because the stylesheet is empty.";
+ }
+ getLogger().severe(error);
+ // The show must go on
+ onLoad(event);
+ }
+
+ };
+
/**
* Sets the ApplicationConnection this instance is connected to.
*
@@ -61,83 +82,43 @@ public class DependencyLoader {
/**
* Loads the any dependencies present in the given json snippet.
*
- * Scans the key "{@literal scriptDependencies}" for JavaScripts and the key
- * "{@literal styleDependencies}" for style sheets.
+ * Handles all dependencies found with the key "{@literal dependencies}".
*
- * Ensures that the given JavaScript dependencies are loaded in the given
- * order. Does not ensure anything about stylesheet order.
+ * Ensures that
+ * <ul>
+ * <li>JavaScript dependencies are loaded in the given order.
+ * <li>HTML imports are loaded after all JavaScripts are loaded and
+ * executed.
+ * <li>Style sheets are loaded and evaluated in some undefined order
+ * </ul>
*
* @param json
* the JSON containing the dependencies to load
*/
public void loadDependencies(ValueMap json) {
- if (json.containsKey(SCRIPT_DEPENDENCIES)) {
- loadScriptDependencies(json.getJSStringArray(SCRIPT_DEPENDENCIES));
- }
- if (json.containsKey(STYLE_DEPENDENCIES)) {
- loadStyleDependencies(json.getJSStringArray(STYLE_DEPENDENCIES));
- }
-
- }
-
- private void loadStyleDependencies(JsArrayString dependencies) {
- // Assuming no reason to interpret in a defined order
- ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
- @Override
- public void onLoad(ResourceLoadEvent event) {
- ApplicationConfiguration.endDependencyLoading();
- }
-
- @Override
- public void onError(ResourceLoadEvent event) {
- getLogger().severe(event.getResourceUrl()
- + " could not be loaded, or the load detection failed because the stylesheet is empty.");
- // The show must go on
- onLoad(event);
- }
- };
- ResourceLoader loader = ResourceLoader.get();
- for (int i = 0; i < dependencies.length(); i++) {
- String url = translateVaadinUri(dependencies.get(i));
- ApplicationConfiguration.startDependencyLoading();
- loader.loadStylesheet(url, resourceLoadListener);
- }
- }
-
- private void loadScriptDependencies(final JsArrayString dependencies) {
- if (dependencies.length() == 0) {
+ if (!json.containsKey(DEPENDENCIES)) {
return;
}
+ JsArray<ValueMap> deps = json.getJSValueMapArray(DEPENDENCIES);
- // Listener that loads the next when one is completed
- ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
- @Override
- public void onLoad(ResourceLoadEvent event) {
- // Call start for next before calling end for current
+ for (int i = 0; i < deps.length(); i++) {
+ ValueMap dep = deps.get(i);
+ String type = dep.getAsString("type");
+ String url = connection.translateVaadinUri(dep.getAsString("url"));
+ ApplicationConfiguration.startDependencyLoading();
+ if (type.equals("STYLESHEET")) {
+ loader.loadStylesheet(url, dependencyLoadingTracker);
+ } else if (type.equals("JAVASCRIPT")) {
+ loader.loadScript(url, dependencyLoadingTracker);
+ } else if (type.equals("HTMLIMPORT")) {
+ loader.loadHtmlImport(url, dependencyLoadingTracker);
+ } else {
ApplicationConfiguration.endDependencyLoading();
+ throw new IllegalArgumentException("Unknown type: " + type);
}
-
- @Override
- public void onError(ResourceLoadEvent event) {
- getLogger().severe(
- event.getResourceUrl() + " could not be loaded.");
- // The show must go on
- onLoad(event);
- }
- };
-
- ResourceLoader loader = ResourceLoader.get();
- for (int i = 0; i < dependencies.length(); i++) {
- ApplicationConfiguration.startDependencyLoading();
- String preloadUrl = translateVaadinUri(dependencies.get(i));
- loader.loadScript(preloadUrl, resourceLoadListener);
}
}
- private String translateVaadinUri(String url) {
- return connection.translateVaadinUri(url);
- }
-
private static Logger getLogger() {
return Logger.getLogger(DependencyLoader.class.getName());
}
diff --git a/client/src/main/java/com/vaadin/client/ResourceLoader.java b/client/src/main/java/com/vaadin/client/ResourceLoader.java
index a20daced44..f58453ced5 100644
--- a/client/src/main/java/com/vaadin/client/ResourceLoader.java
+++ b/client/src/main/java/com/vaadin/client/ResourceLoader.java
@@ -135,7 +135,7 @@ public class ResourceLoader {
Document document = Document.get();
head = document.getElementsByTagName("head").getItem(0);
- // detect already loaded scripts and stylesheets
+ // detect already loaded scripts, html imports and stylesheets
NodeList<Element> scripts = document.getElementsByTagName("script");
for (int i = 0; i < scripts.getLength(); i++) {
ScriptElement element = ScriptElement.as(scripts.getItem(i));
@@ -154,6 +154,10 @@ public class ResourceLoader {
&& href.length() != 0) {
loadedResources.add(href);
}
+ if ("import".equalsIgnoreCase(rel) && href != null
+ && href.length() != 0) {
+ loadedResources.add(href);
+ }
}
}
@@ -215,6 +219,48 @@ public class ResourceLoader {
}
/**
+ * Loads an HTML import and notify a listener when the HTML import is
+ * loaded. Calling this method when the HTML import is currently loading or
+ * already loaded doesn't cause the HTML import to be loaded again, but the
+ * listener will still be notified when appropriate.
+ *
+ * @param htmlUrl
+ * url of HTML import to load
+ * @param resourceLoadListener
+ * listener to notify when the HTML import is loaded
+ */
+ public void loadHtmlImport(final String htmlUrl,
+ final ResourceLoadListener resourceLoadListener) {
+ final String url = WidgetUtil.getAbsoluteUrl(htmlUrl);
+ ResourceLoadEvent event = new ResourceLoadEvent(this, url);
+ if (loadedResources.contains(url)) {
+ if (resourceLoadListener != null) {
+ resourceLoadListener.onLoad(event);
+ }
+ return;
+ }
+
+ if (addListener(url, resourceLoadListener, loadListeners)) {
+ LinkElement linkTag = Document.get().createLinkElement();
+ linkTag.setAttribute("rel", "import");
+ linkTag.setAttribute("href", url);
+
+ addOnloadHandler(linkTag, new ResourceLoadListener() {
+ @Override
+ public void onLoad(ResourceLoadEvent event) {
+ fireLoad(event);
+ }
+
+ @Override
+ public void onError(ResourceLoadEvent event) {
+ fireError(event);
+ }
+ }, event);
+ head.appendChild(linkTag);
+ }
+ }
+
+ /**
* 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.
@@ -355,12 +401,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/documentation/gwt/gwt-advanced.asciidoc b/documentation/gwt/gwt-advanced.asciidoc
index 51fc2ef3db..f6991ef0e1 100644
--- a/documentation/gwt/gwt-advanced.asciidoc
+++ b/documentation/gwt/gwt-advanced.asciidoc
@@ -19,9 +19,10 @@ occurs in the [methodname]#handleUIDLMessage()# method in
[classname]#ApplicationConnection#, but the logic can be quite overwhelming, so
we describe the phases in the following summary.
-. Any dependencies defined by using [classname]#@JavaScript# or
-[classname]#@StyleSheet# on the server-side class are loaded. Processing does
-not continue until the browser confirms that they have been loaded.
+. Any dependencies defined by using [classname]#@JavaScript#,
+[classname]#@StyleSheet# or [classname]#@HtmlImport# on the server-side class
+are loaded. Processing does not continue until the browser confirms that they
+have been loaded.
. New connectors are instantiated and [methodname]#init()# is run for each
[interfacename]#Connector#.
@@ -57,8 +58,3 @@ and then notifying any [interfacename]#ElementResizeListener#s, as well as
calling the appropriate layout method for the connectors that implement either
[classname]#SimpleManagedLayout# or [classname]#DirectionalManagedLayout#
interface.
-
-
-
-
-
diff --git a/server/src/main/java/com/vaadin/annotations/HtmlImport.java b/server/src/main/java/com/vaadin/annotations/HtmlImport.java
new file mode 100644
index 0000000000..f0c52ff171
--- /dev/null
+++ b/server/src/main/java/com/vaadin/annotations/HtmlImport.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2000-2016 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.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.server.ClientConnector;
+
+/**
+ * If this annotation is present on a {@link ClientConnector} class, the
+ * framework ensures the referenced HTML imports are loaded before the init
+ * method for the corresponding client-side connector is invoked.
+ * <p>
+ * Note that not all browsers yet support HTML imports. If a polyfill is needed
+ * to load HTML imports, it must be loaded before HTML Imports can be loaded.
+ * There is no automatic loading of any polyfill.
+ * <p>
+ * <ul>
+ * <li>Relative URLs are interpreted as relative to the {@code VAADIN} folder,
+ * i.e. {@literal bower_components/paper-slider/paper-slider.html} is equal to
+ * {@literal vaadin://bower_components/paper-slider/paper-slider.html}.
+ * <li>Absolute URLs including protocol and host are used as is on the
+ * client-side.
+ * </ul>
+ * Note that it is a good idea to use relative URLs and place all HTML imports
+ * in the same folder. Polymer elements rely on importing dependencies using
+ * relative paths {@literal ../../other-element/other-element.html}, which will
+ * not work if they are installed in different locations.
+ * <p>
+ * HTML imports are added to the page after any {@code @JavaScript} dependencies
+ * added at the same time.
+ * <p>
+ * Example:
+ * <code>@HtmlImport("bower_components/paper-slider/paper-slider.html")</code>
+ * on the class com.example.MyConnector would load the file
+ * http://host.com/VAADIN/bower_components/paper-slider/paper-slider.html before
+ * the {@code init()} method of the client side connector is invoked.
+ *
+ * @author Vaadin Ltd
+ * @since 8.0.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Repeatable(InternalContainerAnnotationForHtml.class)
+public @interface HtmlImport {
+
+ /**
+ * HTML file URL(s) to load before using the annotated
+ * {@link ClientConnector} in the browser.
+ *
+ * @return html file URL(s) to load
+ */
+ String[] value();
+}
diff --git a/server/src/main/java/com/vaadin/annotations/InternalContainerAnnotationForHtml.java b/server/src/main/java/com/vaadin/annotations/InternalContainerAnnotationForHtml.java
new file mode 100644
index 0000000000..be009bc4ce
--- /dev/null
+++ b/server/src/main/java/com/vaadin/annotations/InternalContainerAnnotationForHtml.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2016 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.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation enabling using multiple {@link HtmlImport @HtmlImport}
+ * annotations.
+ * <p>
+ * <b>NOT meant to be used</b>, for multiple HTML dependencies,
+ * {@link HtmlImport @HtmlImport} should be used instead.
+ *
+ * @author Vaadin Ltd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface InternalContainerAnnotationForHtml {
+
+ /**
+ * Not to be used, instead multiple {@link HtmlImport @HtmlImport}
+ * annotations should be used.
+ *
+ * @return an array of the HtmlImport annotations
+ */
+ HtmlImport[] value();
+}
diff --git a/server/src/main/java/com/vaadin/annotations/JavaScript.java b/server/src/main/java/com/vaadin/annotations/JavaScript.java
index 6ea8904c79..d6c35bdd4c 100644
--- a/server/src/main/java/com/vaadin/annotations/JavaScript.java
+++ b/server/src/main/java/com/vaadin/annotations/JavaScript.java
@@ -69,4 +69,5 @@ public @interface JavaScript {
* @return an array of JavaScript file URLs
*/
public String[] value();
+
}
diff --git a/server/src/main/java/com/vaadin/server/BootstrapHandler.java b/server/src/main/java/com/vaadin/server/BootstrapHandler.java
index be0e9e4593..d94101152e 100644
--- a/server/src/main/java/com/vaadin/server/BootstrapHandler.java
+++ b/server/src/main/java/com/vaadin/server/BootstrapHandler.java
@@ -23,12 +23,15 @@ import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.logging.Logger;
import javax.servlet.http.HttpServletResponse;
@@ -39,8 +42,6 @@ import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.parser.Tag;
-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;
@@ -48,6 +49,8 @@ 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.Dependency;
+import com.vaadin.ui.Dependency.Type;
import com.vaadin.ui.UI;
import elemental.json.Json;
@@ -437,29 +440,25 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
.attr("href", themeUri + "/favicon.ico");
}
- JavaScript[] javaScripts = uiClass
- .getAnnotationsByType(JavaScript.class);
- if (javaScripts != null) {
- for (JavaScript javaScript : javaScripts) {
- 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[] styleSheets = uiClass
- .getAnnotationsByType(StyleSheet.class);
- if (styleSheets != null) {
- for (StyleSheet styleSheet : styleSheets) {
- 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);
- }
+ Collection<? extends Dependency> deps = Dependency.findDependencies(
+ Collections.singletonList(uiClass),
+ context.getSession().getCommunicationManager());
+ for (Dependency dependency : deps) {
+ Type type = dependency.getType();
+ String url = context.getUriResolver()
+ .resolveVaadinUri(dependency.getUrl());
+ if (type == Type.HTMLIMPORT) {
+ head.appendElement("link").attr("rel", "import").attr("href",
+ url);
+ } else if (type == Type.JAVASCRIPT) {
+ head.appendElement("script").attr("type", "text/javascript")
+ .attr("src", url);
+ } else if (type == Type.STYLESHEET) {
+ head.appendElement("link").attr("rel", "stylesheet")
+ .attr("type", "text/css").attr("href", url);
+ } else {
+ getLogger().severe("Ignoring unknown dependency type "
+ + dependency.getType());
}
}
@@ -468,14 +467,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
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;
+ private static Logger getLogger() {
+ return Logger.getLogger(BootstrapHandler.class.getName());
}
protected String getMainDivStyle(BootstrapContext context) {
diff --git a/server/src/main/java/com/vaadin/server/communication/UidlWriter.java b/server/src/main/java/com/vaadin/server/communication/UidlWriter.java
index f8f6c2fc5b..4f94f4780b 100644
--- a/server/src/main/java/com/vaadin/server/communication/UidlWriter.java
+++ b/server/src/main/java/com/vaadin/server/communication/UidlWriter.java
@@ -29,8 +29,6 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import com.vaadin.annotations.JavaScript;
-import com.vaadin.annotations.StyleSheet;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.JsonPaintTarget;
import com.vaadin.server.LegacyCommunicationManager;
@@ -40,10 +38,12 @@ import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.ui.ConnectorTracker;
+import com.vaadin.ui.Dependency;
import com.vaadin.ui.UI;
import elemental.json.Json;
import elemental.json.JsonArray;
+import elemental.json.JsonObject;
import elemental.json.impl.JsonUtil;
/**
@@ -284,43 +284,13 @@ public class UidlWriter implements Serializable {
}
});
- List<String> scriptDependencies = new ArrayList<>();
- List<String> styleDependencies = new ArrayList<>();
-
- for (Class<? extends ClientConnector> class1 : newConnectorTypes) {
- JavaScript[] jsAnnotations = class1
- .getAnnotationsByType(JavaScript.class);
- if (jsAnnotations != null) {
- for (JavaScript jsAnnotation : jsAnnotations) {
- for (String uri : jsAnnotation.value()) {
- scriptDependencies.add(
- manager.registerDependency(uri, class1));
- }
- }
- }
-
- StyleSheet[] styleAnnotations = class1
- .getAnnotationsByType(StyleSheet.class);
- if (styleAnnotations != null) {
- for (StyleSheet styleAnnotation : styleAnnotations) {
- for (String uri : styleAnnotation.value()) {
- styleDependencies.add(
- manager.registerDependency(uri, class1));
- }
- }
- }
- }
-
- // Include script dependencies in output if there are any
- if (!scriptDependencies.isEmpty()) {
- writer.write(", \"scriptDependencies\": "
- + JsonUtil.stringify(toJsonArray(scriptDependencies)));
- }
+ List<Dependency> dependencies = Dependency
+ .findDependencies(newConnectorTypes, manager);
- // Include style dependencies in output if there are any
- if (!styleDependencies.isEmpty()) {
- writer.write(", \"styleDependencies\": "
- + JsonUtil.stringify(toJsonArray(styleDependencies)));
+ // Include dependencies in output if there are any
+ if (!dependencies.isEmpty()) {
+ writer.write(", \"dependencies\": "
+ + JsonUtil.stringify(toJsonArray(dependencies)));
}
session.getDragAndDropService().printJSONResponse(writer);
@@ -339,10 +309,14 @@ public class UidlWriter implements Serializable {
}
}
- private JsonArray toJsonArray(List<String> list) {
+ private JsonArray toJsonArray(List<Dependency> list) {
JsonArray result = Json.createArray();
for (int i = 0; i < list.size(); i++) {
- result.set(i, list.get(i));
+ JsonObject dep = Json.createObject();
+ Dependency dependency = list.get(i);
+ dep.put("type", dependency.getType().name());
+ dep.put("url", dependency.getUrl());
+ result.set(i, dep);
}
return result;
diff --git a/server/src/main/java/com/vaadin/ui/Dependency.java b/server/src/main/java/com/vaadin/ui/Dependency.java
new file mode 100644
index 0000000000..8c82dc0275
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/Dependency.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2000-2016 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.ui;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vaadin.annotations.HtmlImport;
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.StyleSheet;
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.LegacyCommunicationManager;
+
+/**
+ * Represents a stylesheet or JavaScript to include on the page.
+ *
+ * @author Vaadin Ltd
+ */
+public class Dependency implements Serializable {
+ /**
+ * The type of dependency.
+ */
+ public enum Type {
+ STYLESHEET(StyleSheet.class), //
+ JAVASCRIPT(JavaScript.class), //
+ HTMLIMPORT(HtmlImport.class);
+
+ private Class<? extends Annotation> annotationType;
+
+ private Type(Class<? extends Annotation> annotationType) {
+ this.annotationType = annotationType;
+ }
+ }
+
+ private final Type type;
+ private final String url;
+
+ /**
+ * Creates a new dependency of the given type, to be loaded from the given
+ * URL.
+ * <p>
+ * The URL is passed through the translation mechanism before loading, so
+ * custom protocols such as "vaadin://" can be used.
+ *
+ * @param type
+ * the type of dependency, not <code>null</code>
+ * @param url
+ * the URL to load the dependency from, not <code>null</code>
+ */
+ public Dependency(Type type, String url) {
+ if (url == null) {
+ throw new IllegalArgumentException("url cannot be null");
+ }
+ assert type != null;
+ this.type = type;
+ this.url = url;
+ }
+
+ /**
+ * Gets the untranslated URL for the dependency.
+ *
+ * @return the URL for the dependency
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Gets the type of the dependency.
+ *
+ * @return the type of the dependency
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Finds all the URLs defined for the given class using annotations for the
+ * given type, registers the URLs to the communication manager and adds the
+ * registered dependencies to the given list.
+ *
+ * @param type
+ * the type of dependencies to look for
+ * @param cls
+ * the class to scan
+ * @param manager
+ * a reference to the communication manager which tracks
+ * dependencies
+ * @param dependencies
+ * the list to add registered dependencies to
+ *
+ * @return a stream of resource URLs in the order defined by the annotations
+ */
+ @SuppressWarnings("deprecation")
+ private static void findAndRegisterResources(Type type,
+ Class<? extends ClientConnector> cls,
+ LegacyCommunicationManager manager, List<Dependency> dependencies) {
+ Annotation[] annotations = cls
+ .getAnnotationsByType(type.annotationType);
+ if (annotations != null) {
+ for (Annotation annotation : annotations) {
+ String[] resources;
+ if (annotation instanceof StyleSheet) {
+ resources = ((StyleSheet) annotation).value();
+ } else if (annotation instanceof JavaScript) {
+ resources = ((JavaScript) annotation).value();
+ } else if (annotation instanceof HtmlImport) {
+ resources = ((HtmlImport) annotation).value();
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown annotation type: "
+ + annotation.annotationType().getName());
+ }
+
+ for (String resource : resources) {
+ String url = manager.registerDependency(resource, cls);
+ dependencies.add(new Dependency(type, url));
+ }
+ }
+ }
+ }
+
+ /**
+ * Finds all the URLs defined for the given classes, registers the URLs to
+ * the communication manager and returns the registered dependencies.
+ * <p>
+ * The returned collection contains all types of dependencies for each class
+ * in the given list in the order the classes are in the list, i.e. all
+ * dependencies for the first class before all dependencies for the next
+ * class.
+ * <p>
+ * JavaScript dependencies are returned before HTML imports.
+ *
+ * @param connectorTypes
+ * the collection of connector classes to scan
+ * @param manager
+ * a reference to the communication manager which tracks
+ * dependencies
+ * @return
+ */
+ @SuppressWarnings("deprecation")
+ public static List<Dependency> findDependencies(
+ List<Class<? extends ClientConnector>> connectorTypes,
+ LegacyCommunicationManager manager) {
+ List<Dependency> dependencies = new ArrayList<>();
+
+ for (Class<? extends ClientConnector> connectorType : connectorTypes) {
+ findAndRegisterResources(Type.JAVASCRIPT, connectorType, manager,
+ dependencies);
+ findAndRegisterResources(Type.HTMLIMPORT, connectorType, manager,
+ dependencies);
+ findAndRegisterResources(Type.STYLESHEET, connectorType, manager,
+ dependencies);
+ }
+
+ return dependencies;
+ }
+
+}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java b/uitest/src/main/java/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
index 3377fc4bfa..3b2e49e05e 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/javascriptcomponent/JavaScriptPreloading.java
@@ -16,13 +16,15 @@
package com.vaadin.tests.components.javascriptcomponent;
import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.JavaScriptComponentState;
-import com.vaadin.tests.components.AbstractReindeerTestUI;
+import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.tests.util.Log;
import com.vaadin.ui.AbstractJavaScriptComponent;
-public class JavaScriptPreloading extends AbstractReindeerTestUI {
+@Widgetset("com.vaadin.DefaultWidgetSet")
+public class JavaScriptPreloading extends AbstractTestUI {
public static class JsLabelState extends JavaScriptComponentState {
public String xhtml;
diff --git a/uitest/src/main/java/com/vaadin/tests/htmlimport/HtmlImportUI.java b/uitest/src/main/java/com/vaadin/tests/htmlimport/HtmlImportUI.java
new file mode 100644
index 0000000000..ee6104fded
--- /dev/null
+++ b/uitest/src/main/java/com/vaadin/tests/htmlimport/HtmlImportUI.java
@@ -0,0 +1,49 @@
+package com.vaadin.tests.htmlimport;
+
+import com.vaadin.annotations.HtmlImport;
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Label;
+
+@JavaScript("webcomponents-lite.min.js")
+@JavaScript("ui.js")
+@HtmlImport("ui.html")
+@Widgetset("com.vaadin.DefaultWidgetSet")
+public class HtmlImportUI extends AbstractTestUI {
+
+ @HtmlImport("label.html")
+ @JavaScript("label.js")
+ public static class LabelWithImports extends Label {
+
+ public LabelWithImports(String text) {
+ super(text);
+ }
+ }
+
+ @HtmlImport("label2.html")
+ @JavaScript("label2.js")
+ public static class Label2WithImports extends LabelWithImports {
+
+ public Label2WithImports(String text) {
+ super(text);
+ }
+ }
+
+ @HtmlImport("labelX.html")
+ @JavaScript("labelX.js")
+ public static class LabelXWithImports extends LabelWithImports {
+
+ public LabelXWithImports(String text) {
+ super(text);
+ }
+ }
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ addComponent(new Label2WithImports("Foo"));
+ addComponent(new LabelXWithImports("Foo"));
+ }
+
+}
diff --git a/uitest/src/main/resources/com/vaadin/tests/components/ui/uiDependency.js b/uitest/src/main/resources/com/vaadin/tests/components/ui/uiDependency.js
index 053a03f41d..1972f40e1d 100644
--- a/uitest/src/main/resources/com/vaadin/tests/components/ui/uiDependency.js
+++ b/uitest/src/main/resources/com/vaadin/tests/components/ui/uiDependency.js
@@ -31,4 +31,4 @@
document.getElementById("statusBox").innerHTML = status;
}
-})(); \ No newline at end of file
+})();
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.html b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.html
new file mode 100644
index 0000000000..ef95fb8570
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+<script>
+ (function() {
+ window.logMessage("label.html");
+ })();
+</script>
+</head>
+</html>
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.js b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.js
new file mode 100644
index 0000000000..d97f27b6e8
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label.js
@@ -0,0 +1,3 @@
+(function() {
+ window.logMessage("label.js");
+})();
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.html b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.html
new file mode 100644
index 0000000000..0bdbda4e38
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+<script>
+ (function() {
+ window.logMessage("label2.html");
+ })();
+</script>
+</head>
+</html>
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.js b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.js
new file mode 100644
index 0000000000..5e9103e14e
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/label2.js
@@ -0,0 +1,3 @@
+(function() {
+ window.logMessage("label2.js");
+})();
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.html b/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.html
new file mode 100644
index 0000000000..a8427be0b5
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+<script>
+ (function() {
+ window.logMessage("labelX.html");
+ })();
+</script>
+</head>
+</html>
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.js b/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.js
new file mode 100644
index 0000000000..507863361c
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/labelX.js
@@ -0,0 +1,3 @@
+(function() {
+ window.logMessage("labelX.js");
+})();
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.html b/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.html
new file mode 100644
index 0000000000..2dd4b4ae4d
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+<script>
+ (function() {
+ window.logMessage("ui.html");
+ })();
+</script>
+</head>
+</html>
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.js b/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.js
new file mode 100644
index 0000000000..24a8405ab0
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/ui.js
@@ -0,0 +1,14 @@
+(function() {
+ window.logMessage = function(msg) {
+ if (!window.document.getElementById("clientlog")) {
+ var d = document.createElement("div");
+ d.id = "clientlog";
+ document.body.insertBefore(d, document.body.firstChild);
+ }
+ var d = document.createElement("div");
+ d.innerText = msg;
+ d.className = "message";
+ window.document.getElementById("clientlog").appendChild(d);
+
+ }
+})();
diff --git a/uitest/src/main/resources/com/vaadin/tests/htmlimport/webcomponents-lite.min.js b/uitest/src/main/resources/com/vaadin/tests/htmlimport/webcomponents-lite.min.js
new file mode 100644
index 0000000000..2e98cb0e59
--- /dev/null
+++ b/uitest/src/main/resources/com/vaadin/tests/htmlimport/webcomponents-lite.min.js
@@ -0,0 +1,12 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.7.23
+!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents-lite.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,o=e.split("=");o[0]&&(t=o[0].match(/wc-(.+)/))&&(n[t[1]]=o[1]||!0)}),t)for(var o,r=0;o=t.attributes[r];r++)"src"!==o.name&&(n[o.name]=o.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),function(e){"use strict";function t(e){return void 0!==h[e]}function n(){s.call(this),this._isInvalid=!0}function o(e){return""==e&&n.call(this),e.toLowerCase()}function r(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,63,96].indexOf(t)==-1?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,96].indexOf(t)==-1?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",l=0,u="",w=!1,_=!1,g=[];e:for(;(e[l-1]!=p||0==l)&&!this._isInvalid;){var b=e[l];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}u="",d="no scheme";continue}u+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))u+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}u="",l=0,d="no scheme";continue}if(this._scheme=u,u="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=r(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[l+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[l+1],E=e[l+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){w&&(c("@ already seen."),u+="%40"),w=!0;for(var L=0;L<u.length;L++){var N=u[L];if("\t"!=N&&"\n"!=N&&"\r"!=N)if(":"!=N||null!==this._password){var M=r(N);null!==this._password?this._password+=M:this._username+=M}else this._password="";else c("Invalid whitespace in authority.")}u=""}else{if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){l-=u.length,u="",d="host";continue}u+=b}break;case"file host":if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){2!=u.length||!m.test(u[0])||":"!=u[1]&&"|"!=u[1]?0==u.length?d="relative path start":(this._host=o.call(this,u),u="",d="relative path start"):d="relative path";continue}"\t"==b||"\n"==b||"\r"==b?c("Invalid whitespace in file host."):u+=b;break;case"host":case"hostname":if(":"!=b||_){if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){if(this._host=o.call(this,u),u="",d="relative path start",a)break e;continue}"\t"!=b&&"\n"!=b&&"\r"!=b?("["==b?_=!0:"]"==b&&(_=!1),u+=b):c("Invalid code point in host/hostname: "+b)}else if(this._host=o.call(this,u),u="",d="port","hostname"==a)break e;break;case"port":if(/[0-9]/.test(b))u+=b;else{if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b||a){if(""!=u){var T=parseInt(u,10);T!=h[this._scheme]&&(this._port=T+""),u=""}if(a)break e;d="relative path start";continue}"\t"==b||"\n"==b||"\r"==b?c("Invalid code point in port: "+b):n.call(this)}break;case"relative path start":if("\\"==b&&c("'\\' not allowed in path."),d="relative path","/"!=b&&"\\"!=b)continue;break;case"relative path":if(p!=b&&"/"!=b&&"\\"!=b&&(a||"?"!=b&&"#"!=b))"\t"!=b&&"\n"!=b&&"\r"!=b&&(u+=r(b));else{"\\"==b&&c("\\ not allowed in relative path.");var O;(O=f[u.toLowerCase()])&&(u=O),".."==u?(this._path.pop(),"/"!=b&&"\\"!=b&&this._path.push("")):"."==u&&"/"!=b&&"\\"!=b?this._path.push(""):"."!=u&&("file"==this._scheme&&0==this._path.length&&2==u.length&&m.test(u[0])&&"|"==u[1]&&(u=u[0]+":"),this._path.push(u)),u="","?"==b?(this._query="?",d="query"):"#"==b&&(this._fragment="#",d="fragment")}break;case"query":a||"#"!=b?p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._query+=i(b)):(this._fragment="#",d="fragment");break;case"fragment":p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._fragment+=b)}l++}}function s(){this._scheme="",this._schemeData="",this._username="",this._password=null,this._host="",this._port="",this._path=[],this._query="",this._fragment="",this._isInvalid=!1,this._isRelative=!1}function c(e,t){void 0===t||t instanceof c||(t=new c(String(t))),this._url=e,s.call(this);var n=e.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g,"");a.call(this,n,null,t)}var d=!1;if(!e.forceJURL)try{var l=new URL("b","http://a");l.pathname="c%20d",d="http://a/c%20d"===l.href}catch(u){}if(!d){var h=Object.create(null);h.ftp=21,h.file=0,h.gopher=70,h.http=80,h.https=443,h.ws=80,h.wss=443;var f=Object.create(null);f["%2e"]=".",f[".%2e"]="..",f["%2e."]="..",f["%2e%2e"]="..";var p=void 0,m=/[a-zA-Z]/,v=/[a-zA-Z0-9\+\-\.]/;c.prototype={toString:function(){return this.href},get href(){if(this._isInvalid)return this._url;var e="";return""==this._username&&null==this._password||(e=this._username+(null!=this._password?":"+this._password:"")+"@"),this.protocol+(this._isRelative?"//"+e+this.host:"")+this.pathname+this._query+this._fragment},set href(e){s.call(this),a.call(this,e)},get protocol(){return this._scheme+":"},set protocol(e){this._isInvalid||a.call(this,e+":","scheme start")},get host(){return this._isInvalid?"":this._port?this._host+":"+this._port:this._host},set host(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"host")},get hostname(){return this._host},set hostname(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"hostname")},get port(){return this._port},set port(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"port")},get pathname(){return this._isInvalid?"":this._isRelative?"/"+this._path.join("/"):this._schemeData},set pathname(e){!this._isInvalid&&this._isRelative&&(this._path=[],a.call(this,e,"relative path start"))},get search(){return this._isInvalid||!this._query||"?"==this._query?"":this._query},set search(e){!this._isInvalid&&this._isRelative&&(this._query="?","?"==e[0]&&(e=e.slice(1)),a.call(this,e,"query"))},get hash(){return this._isInvalid||!this._fragment||"#"==this._fragment?"":this._fragment},set hash(e){this._isInvalid||(this._fragment="#","#"==e[0]&&(e=e.slice(1)),a.call(this,e,"fragment"))},get origin(){var e;if(this._isInvalid||!this._scheme)return"";switch(this._scheme){case"data":case"file":case"javascript":case"mailto":return"null"}return e=this.host,e?this._scheme+"://"+e:""}};var w=e.URL;w&&(c.createObjectURL=function(e){return w.createObjectURL.apply(w,arguments)},c.revokeObjectURL=function(e){w.revokeObjectURL(e)}),e.URL=c}}(self),"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r<o.length;r++){var i=o[r],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++y}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function c(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function d(e,t){return E=new s(e,t)}function l(e){return L?L:(L=c(E),L.oldValue=e,L)}function u(){E=L=void 0}function h(e){return e===L||e===E}function f(e,t){return e===t?e:L&&h(e)?L:null}function p(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var m,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var w=[],_=String(Math.random());window.addEventListener("message",function(e){if(e.data===_){var t=w;w=[],t.forEach(function(e){e()})}}),m=function(e){w.push(e),window.postMessage(_,"*")}}var g=!1,b=[],y=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var o=v.get(e);o||v.set(e,o=[]);for(var r,i=0;i<o.length;i++)if(o[i].observer===this){r=o[i],r.removeListeners(),r.options=t;break}r||(r=new p(this,e,t),o.push(r),this.nodes_.push(e)),r.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var o=t[n];if(o.observer===this){o.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var E,L;p.prototype={enqueue:function(e){var n=this.observer.records_,o=n.length;if(n.length>0){var r=n[o-1],i=f(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,o=e.target,r=new d("attributes",o);r.attributeName=t,r.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(o,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?l(a):r});break;case"DOMCharacterDataModified":var o=e.target,r=d("characterData",o),a=e.prevValue;i(o,function(e){if(e.characterData)return e.characterDataOldValue?l(a):r});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,c,h=e.target;"DOMNodeInserted"===e.type?(s=[h],c=[]):(s=[],c=[h]);var f=h.previousSibling,p=h.nextSibling,r=d("childList",e.target.parentNode);r.addedNodes=s,r.removedNodes=c,r.previousSibling=f,r.nextSibling=p,i(e.relatedNode,function(e){if(e.childList)return r})}u()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(){function e(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case" ":return"&nbsp;"}}function t(t){return t.replace(u,e)}var n="undefined"==typeof HTMLTemplateElement;/Trident/.test(navigator.userAgent)&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}();var o=function(){if(!n){var e=document.createElement("template"),t=document.createElement("template");t.content.appendChild(document.createElement("div")),e.content.appendChild(t);var o=e.cloneNode(!0);return 0===o.content.childNodes.length||0===o.content.firstChild.content.childNodes.length}}(),r="template",i=function(){};if(n){var a=document.implementation.createHTMLDocument("template"),s=!0,c=document.createElement("style");c.textContent=r+"{display:none;}";var d=document.head;d.insertBefore(c,d.firstElementChild),i.prototype=Object.create(HTMLElement.prototype),i.decorate=function(e){if(!e.content){e.content=a.createDocumentFragment();for(var n;n=e.firstChild;)e.content.appendChild(n);if(e.cloneNode=function(e){return i.cloneNode(this,e)},s)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(a.body.innerHTML=e,i.bootstrap(a);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;a.body.firstChild;)this.content.appendChild(a.body.firstChild)},configurable:!0})}catch(o){s=!1}i.bootstrap(e.content)}},i.bootstrap=function(e){for(var t,n=e.querySelectorAll(r),o=0,a=n.length;o<a&&(t=n[o]);o++)i.decorate(t)},document.addEventListener("DOMContentLoaded",function(){i.bootstrap(document)});var l=document.createElement;document.createElement=function(){"use strict";var e=l.apply(document,arguments);return"template"===e.localName&&i.decorate(e),e};var u=/[&\u00A0<>]/g}if(n||o){var h=Node.prototype.cloneNode;i.cloneNode=function(e,t){var n=h.call(e,!1);return this.decorate&&this.decorate(n),t&&(n.content.appendChild(h.call(e.content,!0)),this.fixClonedDom(n.content,e.content)),n},i.fixClonedDom=function(e,t){if(t.querySelectorAll)for(var n,o,i=t.querySelectorAll(r),a=e.querySelectorAll(r),s=0,c=a.length;s<c;s++)o=i[s],n=a[s],this.decorate&&this.decorate(o),n.parentNode.replaceChild(o.cloneNode(!0),n)};var f=document.importNode;Node.prototype.cloneNode=function(e){var t=h.call(this,e);return e&&i.fixClonedDom(t,this),t},document.importNode=function(e,t){if(e.localName===r)return i.cloneNode(e,t);var n=f.call(document,e,t);return t&&i.fixClonedDom(n,e),n},o&&(HTMLTemplateElement.prototype.cloneNode=function(e){return i.cloneNode(this,e)})}n&&(window.HTMLTemplateElement=i)}(),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,o(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===w}function o(e,t){if(n(t))e&&e();else{var r=function(){"complete"!==t.readyState&&t.readyState!==w||(t.removeEventListener(_,r),o(e,t))};t.addEventListener(_,r)}}function r(e){e.target.__loaded=!0}function i(e,t){function n(){c==d&&e&&e({allImports:s,loadedImports:l,errorImports:u})}function o(e){r(e),l.push(this),c++,n()}function i(e){u.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,d=s.length,l=[],u=[];if(d)for(var h,f=0;f<d&&(h=s[f]);f++)a(h)?(l.push(this),c++,n()):(h.addEventListener("load",o),h.addEventListener("error",i));else n()}function a(e){return u?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)c(t)&&d(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function d(e){var t=e["import"];t?r({target:e}):(e.addEventListener("load",r),e.addEventListener("error",r))}var l="import",u=Boolean(l in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),f=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=f(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(p,"_currentScript",m);var v=/Trident/.test(navigator.userAgent),w=v?"complete":"interactive",_="readystatechange";u&&(new MutationObserver(function(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,o=t.length;n<o&&(e=t[n]);n++)d(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=l,e.useNative=u,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},o=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=o}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,o={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,o=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,o),e},resolveUrlsInCssText:function(e,o,r){var i=this.replaceUrls(e,r,o,t);return i=this.replaceUrls(i,r,o,n)},replaceUrls:function(e,t,n,o){return e.replace(o,function(e,o,r,i){var a=r.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,o+"'"+a+"'"+i})}};e.path=o}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,o,r){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}o.call(r,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,o=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};o.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,o){if(n.load&&console.log("fetch",e,o),e)if(e.match(/^data:/)){var r=e.split(","),i=r[0],a=r[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,o,null,a)}.bind(this),0)}else{var s=function(t,n,r){this.receive(e,o,t,n,r)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,o,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,o,r){this.cache[e]=o;for(var i,a=this.pending[e],s=0,c=a.length;s<c&&(i=a[s]);s++)this.onload(e,i,o,n,r),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=o}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===l}function n(e){var t=o(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function o(e){return e.textContent+r(e)}function r(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,o=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+o+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,d=e.isIE,l=e.IMPORT_LINK_TYPE,u="link[rel="+l+"]",h={documentSelectors:u,importsSelectors:[u,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,o=function(r){e.removeEventListener("load",o),e.removeEventListener("error",o),t&&t(r),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",o),e.addEventListener("error",o),d&&"style"===e.localName){var r=!1;if(e.textContent.indexOf("@import")==-1)r=!0;else if(e.sheet){r=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;c<s&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(r=r&&Boolean(i.styleSheet))}r&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var o=document.createElement("script");o.__importElement=t,o.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(o,function(t){o.parentNode&&o.parentNode.removeChild(o),e.currentScript=null}),this.addElementToDocument(o)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var o,r=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=r.length;i<a&&(o=r[i]);i++)if(!this.isParsed(o))return this.hasResource(o)?t(o)?this.nextToParseInDoc(o.__doc,o):o:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=h,e.IMPORT_SELECTOR=u}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function o(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function r(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var r=n.createElement("base");r.setAttribute("href",t),n.baseURI||o(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(r),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,l=e.Observer,u=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,o,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=o,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:r(o,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}u.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),u.parseNext()},loadedAll:function(){u.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new l,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,o={added:function(e){for(var o,r,i,a,s=0,c=e.length;s<c&&(a=e[s]);s++)o||(o=a.ownerDocument,r=t.isParsed(o)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&r&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&r.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&r.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=o.added.bind(o);var r=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(o)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var o=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return!!t(e)||void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),s=0,c=r.length;s<c&&(o=r[s]);s++)o["import"]&&i(o["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return!!e.upgrade(t,n)||void(n&&a(t))}function o(e,t){g(e,function(e){if(n(e,t))return!0})}function r(e){L.push(e),E||(E=!0,setTimeout(i))}function i(){E=!1;for(var e,t=L,n=0,o=t.length;n<o&&(e=t[n]);n++)e();L=[]}function a(e){y?r(function(){s(e);
+}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){d(e),g(e,function(e){d(e)})}function d(e){y?r(function(){l(e)}):l(e)}function l(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function u(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function h(e){if(e.shadowRoot&&!e.shadowRoot.__watched){_.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function f(e,n){if(_.dom){var o=n[0];if(o&&"childList"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=u(e);n.forEach(function(e){"childList"===e.type&&(N(e.addedNodes,function(e){e.localName&&t(e,a)}),N(e.removedNodes,function(e){e.localName&&c(e)}))}),_.dom&&console.groupEnd()}function p(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(f(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(f.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),_.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),_.dom&&console.groupEnd()}function w(e){b(e,v)}var _=e.flags,g=e.forSubtree,b=e.forDocumentTree,y=window.MutationObserver._isPolyfilled&&_["throttle-attached"];e.hasPolyfillMutations=y,e.hasThrottledAttached=y;var E=!1,L=[],N=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=h,e.upgradeDocumentTree=w,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=p}),window.CustomElements.addModule(function(e){function t(t,o){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i["extends"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),s=0;i=a[s];s++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var c=o||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(r(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(d(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c["extends"]&&(c["extends"]=c["extends"].toLowerCase()),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),l(c.__name,c),c.ctor=u(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&v(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t<y.length;t++)if(e===y[t])return!0}function i(e){var t=d(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],o=0;t=e.ancestry[o];o++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function s(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var o,r=e.prototype,i=!1;r;)r==t&&(i=!0),o=Object.getPrototypeOf(r),o&&(r.__proto__=o),r=o;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function c(e){return _(N(e.tag),e)}function d(e){if(e)return E[e.toLowerCase()]}function l(e,t){E[e]=t}function u(e){return function(){return c(e)}}function h(e,t,n){return e===L?f(t,n):M(e,t)}function f(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=d(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var o;return t?(o=f(e),o.setAttribute("is",t),o):(o=N(e),e.indexOf("-")>=0&&g(o,HTMLElement),o)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return w(e),e}}var m,v=(e.isIE,e.upgradeDocumentTree),w=e.upgradeAll,_=e.upgradeWithDefinition,g=e.implementPrototype,b=e.useNative,y=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],E={},L="http://www.w3.org/1999/xhtml",N=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||b?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=E,e["instanceof"]=m,e.reservedTagList=y,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file
diff --git a/uitest/src/test/java/com/vaadin/tests/htmlimport/HtmlImportUITest.java b/uitest/src/test/java/com/vaadin/tests/htmlimport/HtmlImportUITest.java
new file mode 100644
index 0000000000..b830552c27
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/htmlimport/HtmlImportUITest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2016 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.htmlimport;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.parallel.BrowserUtil;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class HtmlImportUITest extends MultiBrowserTest {
+
+ @Override
+ public List<DesiredCapabilities> getBrowsersToTest() {
+
+ List<DesiredCapabilities> browsers = getBrowsersExcludingPhantomJS();
+ browsers.add(PHANTOMJS2());
+
+ return browsers.stream().filter(dc -> {
+ // Won't work on Firefox 24, will work when testing is done on a
+ // modern Firefox
+ if (BrowserUtil.isFirefox(dc) && dc.getVersion().equals("24")) {
+ return false;
+ }
+
+ return true;
+
+ }).collect(Collectors.toList());
+ }
+
+ @Test
+ public void importsLoadedAfterJs() {
+ openTestURL();
+ WebElement log = findElement(By.id("clientlog")); // Defined by ui.js
+
+ List<WebElement> messages = log.findElements(By.className("message"));
+ // Assert.assertEquals("Some log messages are missing or extra", 3,
+ // messages.size());
+
+ // JS before HTML, UI deps in bootstrap, rest dynamically
+
+ // ui.js just sets up the logging
+ Assert.assertEquals("ui.html", messages.get(0).getText());
+
+ // Apparently Chrome does not guarantee that "label.js" is executed
+ // before "label.html", at least in the way we are loading HTML and JS.
+ // Therefore, this just checks the order of the import statements
+
+ List<WebElement> headContents = findElements(By.xpath("//head/*"));
+ Map<String, Integer> htmlImportIndexes = new HashMap<String, Integer>();
+ Map<String, Integer> jsIndexes = new HashMap<String, Integer>();
+
+ for (int i = 0; i < headContents.size(); i++) {
+ WebElement e = headContents.get(i);
+ if (e.getTagName().equalsIgnoreCase("link")
+ && e.getAttribute("rel").equalsIgnoreCase("import")) {
+ // HTML import
+ String href = e.getAttribute("href");
+ String file = href.substring(href.lastIndexOf('/') + 1);
+ Assert.assertFalse("Multiple HTML imports for " + file,
+ htmlImportIndexes.containsKey(file));
+ htmlImportIndexes.put(file, i);
+ } else if (e.getTagName().equalsIgnoreCase("script")) {
+ // JS
+ String src = e.getAttribute("src");
+ String file = src.substring(src.lastIndexOf('/') + 1);
+ Assert.assertFalse("Multiple script tags for " + file,
+ jsIndexes.containsKey(file));
+ jsIndexes.put(file, i);
+ }
+ }
+
+ // label.* + label2.* are from super + sub class loaded in
+ // that defined order
+ // labelX.* are on another component so it can come before or after
+
+ int superJsIndex = jsIndexes.get("label.js");
+ int superHtmlIndex = htmlImportIndexes.get("label.html");
+ int subJsIndex = jsIndexes.get("label2.js");
+ int subHtmlIndex = htmlImportIndexes.get("label2.html");
+ int otherJsIndex = jsIndexes.get("labelX.js");
+ int otherHtmlIndex = htmlImportIndexes.get("labelX.html");
+
+ Assert.assertTrue("super js should be before super html",
+ superJsIndex < superHtmlIndex);
+
+ Assert.assertTrue("super dependencies should be before sub js",
+ superHtmlIndex < subJsIndex);
+
+ Assert.assertTrue("sub js should be before sub html",
+ subJsIndex < subHtmlIndex);
+ }
+}