001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.head.filter; 018 019import org.apache.wicket.ajax.AjaxRequestTarget; 020import org.apache.wicket.core.util.string.JavaScriptUtils; 021import org.apache.wicket.markup.head.AbstractJavaScriptReferenceHeaderItem; 022import org.apache.wicket.markup.head.HeaderItem; 023import org.apache.wicket.markup.head.IHeaderResponse; 024import org.apache.wicket.markup.head.IWrappedHeaderItem; 025import org.apache.wicket.markup.head.JavaScriptContentHeaderItem; 026import org.apache.wicket.markup.head.JavaScriptHeaderItem; 027import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 028import org.apache.wicket.markup.head.OnLoadHeaderItem; 029import org.apache.wicket.markup.html.DecoratingHeaderResponse; 030import org.apache.wicket.request.Response; 031import org.apache.wicket.request.cycle.RequestCycle; 032import org.apache.wicket.util.string.Strings; 033 034/** 035 * A header response which defers all {@link AbstractJavaScriptReferenceHeaderItem}s. 036 * <p> 037 * To prevent any error because of possible dependencies to referenced JavaScript files 038 * *all* {@link JavaScriptHeaderItem}s are replaced with suitable implementations that 039 * delay any execution until all deferred {@link AbstractJavaScriptReferenceHeaderItem}s 040 * have been loaded. 041 * <p> 042 * Note: This solution depends on the execution order of JavaScript in the browser: 043 * The 'DOMContentLoaded' event has to be fired <me>after</em> all deferred JavaScript 044 * resources have been loaded. This doesn't seem to be the case in all browsers, thus 045 * this class should be considered experimental. 046 * 047 * @author svenmeier 048+ */ 049public class JavaScriptDeferHeaderResponse extends DecoratingHeaderResponse 050{ 051 /** 052 * Decorate the given response. 053 * 054 * @param response 055 */ 056 public JavaScriptDeferHeaderResponse(IHeaderResponse response) 057 { 058 super(response); 059 } 060 061 @Override 062 public void render(HeaderItem item) 063 { 064 if (RequestCycle.get().find(AjaxRequestTarget.class).isEmpty()) { 065 while (item instanceof IWrappedHeaderItem) { 066 item = ((IWrappedHeaderItem)item).getWrapped(); 067 } 068 069 if (item instanceof AbstractJavaScriptReferenceHeaderItem) { 070 ((AbstractJavaScriptReferenceHeaderItem)item).setDefer(true); 071 } else if (item instanceof JavaScriptContentHeaderItem) { 072 item = new NativeOnDomContentLoadedHeaderItem(((JavaScriptContentHeaderItem)item).getJavaScript()); 073 } else if (item instanceof OnDomReadyHeaderItem) { 074 item = new NativeOnDomContentLoadedHeaderItem(((OnDomReadyHeaderItem)item).getJavaScript()); 075 } else if (item instanceof OnLoadHeaderItem) { 076 item = new NativeOnLoadHeaderItem(((OnLoadHeaderItem)item).getJavaScript()); 077 } 078 } 079 080 super.render(item); 081 } 082 083 /** 084 * A specialization that uses native "DOMContentLoaded" events without dependency to external JavaScript. 085 */ 086 private static class NativeOnDomContentLoadedHeaderItem extends OnDomReadyHeaderItem 087 { 088 private static final long serialVersionUID = 1L; 089 090 /** 091 * Construct. 092 * 093 * @param javaScript 094 */ 095 public NativeOnDomContentLoadedHeaderItem(CharSequence javaScript) 096 { 097 super(javaScript); 098 } 099 100 /** 101 * Overriden to use native {@code addEventListener('DOMContentLoaded')} instead. 102 */ 103 @Override 104 public void render(Response response) 105 { 106 CharSequence js = getJavaScript(); 107 if (Strings.isEmpty(js) == false) 108 { 109 JavaScriptUtils.writeJavaScript(response, "document.addEventListener('DOMContentLoaded', function() { " + js + "; });"); 110 } 111 } 112 } 113 114 /** 115 * A specialization that uses native "load" events without dependency to external JavaScript 116 */ 117 private static class NativeOnLoadHeaderItem extends OnLoadHeaderItem 118 { 119 private static final long serialVersionUID = 1L; 120 121 /** 122 * Construct. 123 * 124 * @param javaScript 125 */ 126 public NativeOnLoadHeaderItem(CharSequence javaScript) 127 { 128 super(javaScript); 129 } 130 131 /** 132 * Overriden to use native {@code addEventListener('load')} instead. 133 */ 134 @Override 135 public void render(Response response) 136 { 137 CharSequence js = getJavaScript(); 138 if (Strings.isEmpty(js) == false) 139 { 140 JavaScriptUtils.writeJavaScript(response, "window.addEventListener('load', function() { " + js + "; });"); 141 } 142 } 143 } 144}