diff options
author | Artur <artur@vaadin.com> | 2017-01-25 11:27:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-25 11:27:49 +0200 |
commit | be694984fb35262b32c89be075e6d4a059931b62 (patch) | |
tree | d66a85f91404b5bb7f82d845a25b96ec9cebf350 | |
parent | e397ea01e5ddb0c977561c008b84c6ed7c0ef706 (diff) | |
download | vaadin-framework-be694984fb35262b32c89be075e6d4a059931b62.tar.gz vaadin-framework-be694984fb35262b32c89be075e6d4a059931b62.zip |
Support loading of HTML imports using @HtmlImport (#8301)
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 polyfills.
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"&";case"<":return"<";case">":return">";case" ":return" "}}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); + } +} |