WICKET-6498 always add event listener regardless of readyState
for Ajax requests PartialPageUpdate will pack all OnDomReady and OnLoad items into an <evaluate> block anyways
diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.java b/wicket-core/src/main/java/org/apache/wicket/Application.java
index 356c8b9..9415afa 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Application.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Application.java
@@ -41,6 +41,7 @@
import org.apache.wicket.event.IEventSink;
import org.apache.wicket.javascript.DefaultJavaScriptCompressor;
import org.apache.wicket.markup.MarkupFactory;
+import org.apache.wicket.markup.head.HeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.ResourceAggregator;
import org.apache.wicket.markup.html.IHeaderContributor;
@@ -1612,9 +1613,13 @@
/**
* Sets an {@link IHeaderResponseDecorator} that you want your application to use to decorate
* header responses.
+ * <p>
+ * Calling this method replaces the default decorator, which utilizes a {@link ResourceAggregator}:
+ * The given implementation should make sure, that it too wraps responses in a {@link ResourceAggregator},
+ * otherwise no dependencies for {@link HeaderItem}s will be resolved.
*
* @param headerResponseDecorator
- * your custom decorator
+ * your custom decorator, must not be null
*/
public final Application setHeaderResponseDecorator(final IHeaderResponseDecorator headerResponseDecorator)
{
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/head/filter/JavaScriptDeferHeaderResponse.java b/wicket-core/src/main/java/org/apache/wicket/markup/head/filter/JavaScriptDeferHeaderResponse.java
index 15dd49e..300f960 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/head/filter/JavaScriptDeferHeaderResponse.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/head/filter/JavaScriptDeferHeaderResponse.java
@@ -22,19 +22,30 @@
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.IWrappedHeaderItem;
import org.apache.wicket.markup.head.JavaScriptContentHeaderItem;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.head.OnLoadHeaderItem;
import org.apache.wicket.markup.html.DecoratingHeaderResponse;
+import org.apache.wicket.page.PartialPageUpdate;
import org.apache.wicket.request.Response;
import org.apache.wicket.util.string.Strings;
/**
* A header response that defers all {@link AbstractJavaScriptReferenceHeaderItem}s.
+ * <p>
+ * To prevent any error because of possible dependencies to referenced JavaScript files
+ * *all* {@link JavaScriptHeaderItem}s are replaced with suitable implementations that
+ * delay any execution until {@link AbstractJavaScriptReferenceHeaderItem}s have been loaded.
*
* @author svenmeier
+ */
public class JavaScriptDeferHeaderResponse extends DecoratingHeaderResponse
{
+ /**
+ * Decorate the given response.
+ *
+ * @param response
+ */
public JavaScriptDeferHeaderResponse(IHeaderResponse response)
{
super(response);
@@ -49,17 +60,23 @@
if (item instanceof AbstractJavaScriptReferenceHeaderItem) {
((AbstractJavaScriptReferenceHeaderItem)item).setDefer(true);
+ } else if (item instanceof JavaScriptContentHeaderItem) {
+ item = new NativeOnDomContentLoadedHeaderItem(((JavaScriptContentHeaderItem)item).getJavaScript());
} else if (item instanceof OnDomReadyHeaderItem) {
item = new NativeOnDomContentLoadedHeaderItem(((OnDomReadyHeaderItem)item).getJavaScript());
} else if (item instanceof OnLoadHeaderItem) {
item = new NativeOnLoadHeaderItem(((OnLoadHeaderItem)item).getJavaScript());
- } else if (item instanceof JavaScriptContentHeaderItem) {
- item = new NativeOnDomContentLoadedHeaderItem(((JavaScriptContentHeaderItem)item).getJavaScript());
}
super.render(item);
}
+ /**
+ * A specialization that uses native "DOMContentLoaded" events without dependency to external JavaScript.
+ * <p>
+ * For Ajax requests we utilize the fact, that {@link PartialPageUpdate} renders {@link #getJavaScript()} only,
+ * thus executing the JavaScript directly without any event registration.
+ */
private class NativeOnDomContentLoadedHeaderItem extends OnDomReadyHeaderItem
{
/**
@@ -72,18 +89,26 @@
super(javaScript);
}
+ /**
+ * Overriden to use native {@code addEventListener('DOMContentLoaded')} instead.
+ */
@Override
public void render(Response response)
{
CharSequence js = getJavaScript();
if (Strings.isEmpty(js) == false)
{
- JavaScriptUtils.writeJavaScript(response,
- "(function(){ var f = function() {" + js + ";};\nif ('loading' !== document.readyState) f(); else document.addEventListener('DOMContentLoaded', f); })();");
+ JavaScriptUtils.writeJavaScript(response, "document.addEventListener('DOMContentLoaded', function() { " + js + "; });");
}
}
}
+ /**
+ * A specialization that uses native "load" events without dependency to external JavaScript
+ * <p>
+ * For Ajax requests we utilize the fact, that {@link PartialPageUpdate} renders {@link #getJavaScript()} only,
+ * thus executing the JavaScript directly without any event registration.
+ */
private class NativeOnLoadHeaderItem extends OnLoadHeaderItem
{
@@ -97,15 +122,17 @@
super(javaScript);
}
+ /**
+ * Overriden to use native {@code addEventListener('load')} instead.
+ */
@Override
public void render(Response response)
{
CharSequence js = getJavaScript();
if (Strings.isEmpty(js) == false)
{
- JavaScriptUtils.writeJavaScript(response,
- "(function(){ var f = function() {" + js + ";};\nif ('complete' === document.readyState) f(); else window.addEventListener('load', f); })();");
+ JavaScriptUtils.writeJavaScript(response, "window.addEventListener('load', function() { " + js + "; });");
}
}
}
-}
\ No newline at end of file
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/IHeaderResponseDecorator.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/IHeaderResponseDecorator.java
index 3e502b9..00c9b17 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/IHeaderResponseDecorator.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/IHeaderResponseDecorator.java
@@ -19,10 +19,10 @@
import org.apache.wicket.markup.head.IHeaderResponse;
/**
- * Setting an IHeaderResponseDecorator on an application allows you to wrap any IHeaderResponse
- * created by Wicket in a separate implementation that incrementally adds functionality to the
- * IHeaderResponse that is used by all IHeaderContributor components or behaviors.
- *
+ * Setting an IHeaderResponseDecorator on an application allows you to wrap any {@link IHeaderResponse}
+ * created by Wicket in a separate implementation that adds functionality to it when used by all
+ * {@link IHeaderContributor} components or behaviors.
+ * <p>
* Everywhere that Wicket creates an instance of IHeaderResponse, it will call to your application
* and give it the opportunity to decorate that IHeaderResponse before using it.
*
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.html b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.html
new file mode 100644
index 0000000..ee4a485
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.html
@@ -0,0 +1,4 @@
+<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd" >
+ <body>
+ </body>
+</html>
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.java b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.java
new file mode 100644
index 0000000..355328b
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPage.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.wicket.markup.head.filter;
+
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.WebPage;
+
+/**
+ * @author svenmeier
+ */
+public class DeferredPage extends WebPage
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct.
+ *
+ * @param parameters
+ */
+ public DeferredPage()
+ {
+ add(new AjaxEventBehavior("click") {
+ @Override
+ protected void onEvent(AjaxRequestTarget target)
+ {
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPageExpected.html b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPageExpected.html
new file mode 100644
index 0000000..894bad2
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/DeferredPageExpected.html
@@ -0,0 +1,25 @@
+<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd" >
+ <head><script type="text/javascript" defer="defer" src="../resource/org.apache.wicket.resource.JQueryResourceReference/jquery/jquery-2.2.4.js"></script>
+<script type="text/javascript" defer="defer" src="../resource/org.apache.wicket.ajax.AbstractDefaultAjaxBehavior/res/js/wicket-ajax-jquery.js"></script>
+<script type="text/javascript" defer="defer" src="../resource/org.apache.wicket.ajax.AbstractDefaultAjaxBehavior/res/js/wicket-ajax-jquery-debug.js"></script>
+<script type="text/javascript" >
+/*<![CDATA[*/
+document.addEventListener('DOMContentLoaded', function() { Wicket.Ajax.DebugWindow.enabled=true;; });
+/*]]>*/
+</script>
+<script type="text/javascript" >
+/*<![CDATA[*/
+document.addEventListener('DOMContentLoaded', function() { Wicket.Ajax.baseUrl="wicket/bookmarkable/org.apache.wicket.markup.head.filter.DeferredPage?0";; });
+/*]]>*/
+</script>
+<script type="text/javascript" >
+/*<![CDATA[*/
+document.addEventListener('DOMContentLoaded', function() {
+Wicket.Ajax.ajax({"u":"./org.apache.wicket.markup.head.filter.DeferredPage?0-1.0-","e":"click"});;
+Wicket.Event.publish(Wicket.Event.Topic.AJAX_HANDLERS_BOUND);
+; });
+/*]]>*/
+</script>
+</head><body>
+ </body>
+</html>
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
index d8a901a..99665a7 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/filter/FilteringHeaderResponseTest.java
@@ -75,4 +75,22 @@
CharSequence realContent = headerResponse.getContent(filterName);
assertEquals(headerContent, realContent.toString());
}
-}
+
+ /**
+ * WICKET-6498 all JavaScript resources have an "defer" attribute, all other JavaScript is
+ * inside a {@code document.addEventListener('DOMContentLoaded', function() {}; } hook.
+ */
+ @Test
+ public void deferred() throws Exception
+ {
+ tester.getApplication().setHeaderResponseDecorator(new IHeaderResponseDecorator()
+ {
+ @Override
+ public IHeaderResponse decorate(IHeaderResponse response)
+ {
+ return new ResourceAggregator(new JavaScriptDeferHeaderResponse(response));
+ }
+ });
+ executeTest(DeferredPage.class, "DeferredPageExpected.html");
+ }
+}
\ No newline at end of file