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.html.image;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.List;
024
025import org.apache.wicket.markup.ComponentTag;
026import org.apache.wicket.markup.html.CrossOrigin;
027import org.apache.wicket.markup.html.WebComponent;
028import org.apache.wicket.model.IModel;
029import org.apache.wicket.model.Model;
030
031/**
032 * A component to display external images. The src / srcSet information are hold in models
033 * 
034 * @see org.apache.wicket.markup.html.image.Image
035 * 
036 * @author Tobias Soloschenko
037 * @author Sebastien Briquet
038 * @author Sven Meier
039 * @author Martin Grigorov
040 *
041 */
042public class ExternalImage extends WebComponent
043{
044
045        private static final long serialVersionUID = 1L;
046
047        /** The x values to be used within the srcset */
048        private List<String> xValues = null;
049
050        /** The sizes of the responsive images */
051        private List<String> sizes = null;
052
053        /**
054         * Cross origin settings
055         */
056        private CrossOrigin crossOrigin = null;
057
058        private IModel<List<Serializable>> srcSetModel;
059
060        /**
061         * Creates an external image
062         * 
063         * @param id
064         *            the component id
065         */
066        public ExternalImage(String id)
067        {
068                this(id, null, Model.ofList(Collections.emptyList()));
069        }
070
071        /**
072         * Creates an external image
073         * 
074         * @param id
075         *            the component id
076         * @param src
077         *            the source URL
078         */
079        public ExternalImage(String id, Serializable src)
080        {
081                this(id, Model.of(src), Model.ofList(Collections.<Serializable> emptyList()));
082        }
083
084        /**
085         * Creates an external image
086         * 
087         * @param id
088         *            the component id
089         * @param src
090         *            the source URL
091         * @param srcSet
092         *            a list of URLs placed in the srcSet attribute
093         */
094        public ExternalImage(String id, Serializable src, List<Serializable> srcSet)
095        {
096                this(id, Model.of(src), Model.ofList(srcSet));
097        }
098
099        /**
100         * Creates an external image
101         * 
102         * @param id
103         *            the component id
104         * @param srcModel
105         *            the model source URL
106         */
107        public ExternalImage(String id, IModel<Serializable> srcModel)
108        {
109                this(id, srcModel, Model.ofList(Collections.emptyList()));
110        }
111
112        /**
113         * Creates an external image
114         * 
115         * @param id
116         *            the component id
117         * @param srcModel
118         *            the model source URL
119         * @param srcSetModel
120         *            a model list of URLs placed in the srcSet attribute
121         */
122        public ExternalImage(String id, IModel<Serializable> srcModel,
123                IModel<List<Serializable>> srcSetModel)
124        {
125                super(id, srcModel);
126                this.srcSetModel = srcSetModel;
127        }
128
129        @Override
130        protected void onComponentTag(ComponentTag tag)
131        {
132                super.onComponentTag(tag);
133
134                if (!"source".equals(tag.getName()))
135                {
136                        checkComponentTag(tag, "img");
137                        buildSrcAttribute(tag, getDefaultModel());
138                }
139                buildSrcSetAttribute(tag, getSrcSetModel());
140
141                buildSizesAttribute(tag);
142
143                CrossOrigin crossOrigin = getCrossOrigin();
144                if (crossOrigin != null && CrossOrigin.NO_CORS != crossOrigin)
145                {
146                        tag.put("crossOrigin", crossOrigin.getRealName());
147                }
148        }
149
150        /**
151         * Builds the src attribute
152         *
153         * @param tag
154         *            the component tag
155         * @param srcModel
156         *            the model containing the src URL
157         */
158        protected void buildSrcAttribute(final ComponentTag tag, IModel<?> srcModel)
159        {
160                tag.put("src", String.valueOf(srcModel.getObject()));
161        }
162
163        /**
164         * Builds the srcset attribute if multiple models are found as varargs
165         *
166         * @param tag
167         *            the component tag
168         * @param srcSetModel
169         *            the models containing the src set URLs
170         */
171        protected void buildSrcSetAttribute(final ComponentTag tag,
172                IModel<List<Serializable>> srcSetModel)
173        {
174                int srcSetPosition = 0;
175                List<Serializable> srcSetItems = srcSetModel.getObject();
176                for (Serializable srcSet : srcSetItems)
177                {
178                        String srcset = tag.getAttribute("srcset");
179                        String xValue = "";
180
181                        // If there are xValues set process them in the applied order to the srcset
182                        // attribute.
183                        if (xValues != null)
184                        {
185                                xValue = xValues.size() > srcSetPosition && xValues.get(srcSetPosition) != null
186                                        ? " " + xValues.get(srcSetPosition) : "";
187                        }
188                        tag.put("srcset", (srcset != null ? srcset + ", " : "") + srcSet + xValue);
189                        srcSetPosition++;
190                }
191        }
192
193        /**
194         * builds the sizes attribute of the img tag
195         *
196         * @param tag
197         *            the component tag
198         */
199        protected void buildSizesAttribute(final ComponentTag tag)
200        {
201                // if no sizes have been set then don't build the attribute
202                if (sizes == null)
203                {
204                        return;
205                }
206                String sizes = "";
207                for (String size : this.sizes)
208                {
209                        sizes += size + ",";
210                }
211                int lastIndexOf = sizes.lastIndexOf(",");
212                if (lastIndexOf != -1)
213                {
214                        sizes = sizes.substring(0, lastIndexOf);
215                }
216                if (!sizes.isEmpty())
217                {
218                        tag.put("sizes", sizes);
219                }
220        }
221
222        /**
223         * @param values
224         *            the x values to be used in the srcset
225         */
226        public void setXValues(String... values)
227        {
228                if (xValues == null)
229                {
230                        xValues = new ArrayList<>();
231                }
232                else
233                {
234                        xValues.clear();
235                }
236                xValues.addAll(Arrays.asList(values));
237        }
238
239        /**
240         * Removes all sizes values. The corresponding tag will not be rendered anymore.
241         */
242        public void removeSizes()
243        {
244                if (sizes != null)
245                {
246                        sizes.clear();
247                }
248        }
249
250        /**
251         * @param sizes
252         *            the sizes to be used in the size
253         */
254        public void setSizes(String... sizes)
255        {
256                if (this.sizes == null)
257                {
258                        this.sizes = new ArrayList<>();
259                }
260                else
261                {
262                        this.sizes.clear();
263                }
264                this.sizes.addAll(Arrays.asList(sizes));
265        }
266
267        /**
268         * Removes all x values from the image src set.
269         */
270        public void removeXValues()
271        {
272                if (xValues != null)
273                {
274                        xValues.clear();
275                }
276        }
277
278        /**
279         * Gets the cross origin settings
280         * 
281         * @see org.apache.wicket.markup.html.image.Image#setCrossOrigin(CrossOrigin)
282         *
283         * @return the cross origins settings
284         */
285        public CrossOrigin getCrossOrigin()
286        {
287                return crossOrigin;
288        }
289
290        /**
291         * Sets the cross origin settings
292         * 
293         * @see org.apache.wicket.markup.html.image.Image#setCrossOrigin(CrossOrigin)
294         * @param crossOrigin
295         *            the cross origins settings to set
296         */
297        public void setCrossOrigin(CrossOrigin crossOrigin)
298        {
299                this.crossOrigin = crossOrigin;
300        }
301
302        /**
303         * Gets a list of models containing the src set values
304         * 
305         * @return a list of models containing the src set values
306         */
307        public IModel<List<Serializable>> getSrcSetModel()
308        {
309                return srcSetModel;
310        }
311
312        /**
313         * Sets the source set model
314         * 
315         * @param srcSetModel
316         *            the model of a list of src set entries
317         */
318        public void setSrcSetModel(IModel<List<Serializable>> srcSetModel)
319        {
320                this.srcSetModel = srcSetModel;
321        }
322
323        /**
324         * Detaches the srcSetModels
325         */
326        @Override
327        protected void onDetach()
328        {
329                if (srcSetModel != null)
330                {
331                        srcSetModel.detach();
332                }
333                super.onDetach();
334        }
335}