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
038        /**
039         * @return if the script should be loaded and executed asynchronously
040         */
041        public boolean isAsync()
042        {
043                return async;
044        }
045
046        public AbstractJavaScriptReferenceHeaderItem setAsync(boolean async)
047        {
048                this.async = async;
049                return this;
050        }
051
052        /**
053         * @return if the execution of a script should be deferred (delayed) until after the page has been loaded.
054         */
055        public boolean isDefer()
056        {
057                return defer;
058        }
059
060        public AbstractJavaScriptReferenceHeaderItem setDefer(boolean defer)
061        {
062                this.defer = defer;
063                return this;
064        }
065
066        /**
067         * @return the optional value of the charset attribute of the script tag
068         */
069        public String getCharset()
070        {
071                return charset;
072        }
073
074        public AbstractJavaScriptReferenceHeaderItem setCharset(String charset)
075        {
076                this.charset = charset;
077                return this;
078        }
079
080        @Override
081        public CrossOrigin getCrossOrigin()
082        {
083                return crossOrigin;
084        }
085        
086        @Override
087        public AbstractJavaScriptReferenceHeaderItem setCrossOrigin(CrossOrigin crossOrigin)
088        {
089                this.crossOrigin = crossOrigin;
090                return this;
091        }
092        
093        @Override
094        public String getIntegrity()
095        {
096                return integrity;
097        }
098        
099        @Override
100        public AbstractJavaScriptReferenceHeaderItem setIntegrity(String integrity)
101        {
102                this.integrity = integrity;
103                return this;
104        }
105
106        protected final void internalRenderJavaScriptReference(Response response, String url)
107        {
108                Args.notEmpty(url, "url");
109
110                AttributeMap attributes = new AttributeMap();
111                attributes.putAttribute(JavaScriptUtils.ATTR_TYPE, "text/javascript");
112                attributes.putAttribute(JavaScriptUtils.ATTR_ID, getId());
113                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_DEFER, defer);
114                // XXX this attribute is not necessary for modern browsers
115                attributes.putAttribute("charset", charset);
116                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_ASYNC, async);
117                attributes.putAttribute(JavaScriptUtils.ATTR_SCRIPT_SRC, url);
118                attributes.putAttribute(JavaScriptUtils.ATTR_CSP_NONCE, getNonce());
119                attributes.putAttribute(JavaScriptUtils.ATTR_CROSS_ORIGIN, getCrossOrigin() == null ? null : getCrossOrigin().getRealName());
120                attributes.putAttribute(JavaScriptUtils.ATTR_INTEGRITY, getIntegrity());
121                JavaScriptUtils.writeScript(response, attributes);
122        }
123
124        @Override
125        public boolean equals(Object o)
126        {
127                if (this == o)
128                        return true;
129                if (o == null || getClass() != o.getClass())
130                        return false;
131                if (!super.equals(o))
132                        return false;
133                AbstractJavaScriptReferenceHeaderItem that = (AbstractJavaScriptReferenceHeaderItem)o;
134                return async == that.async &&
135                                defer == that.defer &&
136                                Objects.equals(charset, that.charset);
137        }
138
139        @Override
140        public int hashCode()
141        {
142                // Not using `Objects.hash` for performance reasons
143                int result = super.hashCode();
144                result = 31 * result + (async ? 1 : 0);
145                result = 31 * result + (defer ? 1 : 0);
146                result = 31 * result + (charset != null ? charset.hashCode() : 0);
147                return result;
148        }
149}