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;
018
019import java.util.Objects;
020
021import org.apache.wicket.core.util.string.JavaScriptUtils;
022import org.apache.wicket.markup.html.CrossOrigin;
023import org.apache.wicket.request.Response;
024import org.apache.wicket.util.lang.Args;
025import org.apache.wicket.util.value.AttributeMap;
026
027/**
028 * A {@link org.apache.wicket.markup.head.HeaderItem} that renders a JavaScript reference.
029 */
030public abstract class AbstractJavaScriptReferenceHeaderItem extends JavaScriptHeaderItem implements ISubresourceHeaderItem
031{
032        private boolean async;
033        private boolean defer;
034        private String charset;
035        private CrossOrigin crossOrigin;
036        private String integrity;
037        private JavaScriptReferenceType type = JavaScriptReferenceType.TEXT_JAVASCRIPT;
038
039        /**
040         * @return if the script should be loaded and executed asynchronously
041         */
042        public boolean isAsync()
043        {
044                return async;
045        }
046
047        public AbstractJavaScriptReferenceHeaderItem setAsync(boolean async)
048        {
049                this.async = async;
050                return this;
051        }
052
053        /**
054         * @return if the execution of a script should be deferred (delayed) until after the page has been loaded.
055         */
056        public boolean isDefer()
057        {
058                return defer;
059        }
060
061        public AbstractJavaScriptReferenceHeaderItem setDefer(boolean defer)
062        {
063                this.defer = defer;
064                return this;
065        }
066
067        /**
068         * @return the optional value of the charset attribute of the script tag
069         */
070        public String getCharset()
071        {
072                return charset;
073        }
074
075        public AbstractJavaScriptReferenceHeaderItem setCharset(String charset)
076        {
077                this.charset = charset;
078                return this;
079        }
080
081        @Override
082        public CrossOrigin getCrossOrigin()
083        {
084                return crossOrigin;
085        }
086        
087        @Override
088        public AbstractJavaScriptReferenceHeaderItem setCrossOrigin(CrossOrigin crossOrigin)
089        {
090                this.crossOrigin = crossOrigin;
091                return this;
092        }
093        
094        @Override
095        public String getIntegrity()
096        {
097                return integrity;
098        }
099        
100        @Override
101        public AbstractJavaScriptReferenceHeaderItem setIntegrity(String integrity)
102        {
103                this.integrity = integrity;
104                return this;
105        }
106
107        public JavaScriptReferenceType getType() {
108                return type;
109        }
110
111        public AbstractJavaScriptReferenceHeaderItem setType(final JavaScriptReferenceType type) {
112                this.type = type;
113                return this;
114        }
115
116        protected final void internalRenderJavaScriptReference(Response response, String url)
117        {
118                Args.notEmpty(url, "url");
119                final AttributeMap attributes = createAttributeMap(url);
120                JavaScriptUtils.writeScript(response, attributes);
121        }
122
123        final AttributeMap createAttributeMap(final String url) {
124                final AttributeMap attributes = new AttributeMap();
125                final JavaScriptReferenceType type = getType();
126                if (type != null) {
127                        attributes.putAttribute(JavaScriptUtils.ATTR_TYPE, type.getType());
128                }
129                attributes.putAttribute(JavaScriptUtils.ATTR_ID, getId());
130                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_DEFER, defer);
131                // XXX this attribute is not necessary for modern browsers
132                attributes.putAttribute("charset", charset);
133                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_ASYNC, async);
134                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_SRC, url);
135                attributes.putAttribute(JavaScriptUtils.ATTR_CSP_NONCE, getNonce());
136                attributes.putAttribute(JavaScriptUtils.ATTR_CROSS_ORIGIN, getCrossOrigin() == null ? null : getCrossOrigin().getRealName());
137                attributes.putAttribute(JavaScriptUtils.ATTR_INTEGRITY, getIntegrity());
138                return attributes;
139        }
140
141        @Override
142        public boolean equals(Object o)
143        {
144                if (this == o)
145                        return true;
146                if (o == null || getClass() != o.getClass())
147                        return false;
148                if (!super.equals(o))
149                        return false;
150                AbstractJavaScriptReferenceHeaderItem that = (AbstractJavaScriptReferenceHeaderItem)o;
151                return async == that.async &&
152                                defer == that.defer &&
153                                Objects.equals(charset, that.charset) &&
154                                Objects.equals(type, that.type);
155        }
156
157        @Override
158        public int hashCode()
159        {
160                // Not using `Objects.hash` for performance reasons
161                int result = super.hashCode();
162                result = 31 * result + (async ? 1 : 0);
163                result = 31 * result + (defer ? 1 : 0);
164                result = 31 * result + (charset != null ? charset.hashCode() : 0);
165                result = 31 * result + (type != null ? type.hashCode() : 0);
166                return result;
167        }
168}