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.<Serializable> 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.<Serializable> 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                        buildSrcSetAttribute(tag, getSrcSetModel());
137                }
138                else
139                {
140                        checkComponentTag(tag, "img");
141                        buildSrcAttribute(tag, getDefaultModel());
142                        buildSrcSetAttribute(tag, getSrcSetModel());
143                }
144
145                buildSizesAttribute(tag);
146
147                CrossOrigin crossOrigin = getCrossOrigin();
148                if (crossOrigin != null && CrossOrigin.NO_CORS != crossOrigin)
149                {
150                        tag.put("crossOrigin", crossOrigin.getRealName());
151                }
152        }
153
154        /**
155         * Builds the src attribute
156         *
157         * @param tag
158         *            the component tag
159         * @param srcModel
160         *            the model containing the src URL
161         */
162        protected void buildSrcAttribute(final ComponentTag tag, IModel<?> srcModel)
163        {
164                tag.put("src", String.valueOf(srcModel.getObject()));
165        }
166
167        /**
168         * Builds the srcset attribute if multiple models are found as varargs
169         *
170         * @param tag
171         *            the component tag
172         * @param srcSetModel
173         *            the models containing the src set URLs
174         */
175        protected void buildSrcSetAttribute(final ComponentTag tag,
176                IModel<List<Serializable>> srcSetModel)
177        {
178                int srcSetPosition = 0;
179                List<Serializable> srcSetItems = srcSetModel.getObject();
180                for (Serializable srcSet : srcSetItems)
181                {
182                        String srcset = tag.getAttribute("srcset");
183                        String xValue = "";
184
185                        // If there are xValues set process them in the applied order to the srcset
186                        // attribute.
187                        if (xValues != null)
188                        {
189                                xValue = xValues.size() > srcSetPosition && xValues.get(srcSetPosition) != null
190                                        ? " " + xValues.get(srcSetPosition) : "";
191                        }
192                        tag.put("srcset", (srcset != null ? srcset + ", " : "") + srcSet + xValue);
193                        srcSetPosition++;
194                }
195        }
196
197        /**
198         * builds the sizes attribute of the img tag
199         *
200         * @param tag
201         *            the component tag
202         */
203        protected void buildSizesAttribute(final ComponentTag tag)
204        {
205                // if no sizes have been set then don't build the attribute
206                if (sizes == null)
207                {
208                        return;
209                }
210                String sizes = "";
211                for (String size : this.sizes)
212                {
213                        sizes += size + ",";
214                }
215                int lastIndexOf = sizes.lastIndexOf(",");
216                if (lastIndexOf != -1)
217                {
218                        sizes = sizes.substring(0, lastIndexOf);
219                }
220                if (!sizes.isEmpty())
221                {
222                        tag.put("sizes", sizes);
223                }
224        }
225
226        /**
227         * @param values
228         *            the x values to be used in the srcset
229         */
230        public void setXValues(String... values)
231        {
232                if (xValues == null)
233                {
234                        xValues = new ArrayList<>();
235                }
236                else
237                {
238                        xValues.clear();
239                }
240                xValues.addAll(Arrays.asList(values));
241        }
242
243        /**
244         * Removes all sizes values. The corresponding tag will not be rendered anymore.
245         */
246        public void removeSizes()
247        {
248                if (sizes != null)
249                {
250                        sizes.clear();
251                }
252        }
253
254        /**
255         * @param sizes
256         *            the sizes to be used in the size
257         */
258        public void setSizes(String... sizes)
259        {
260                if (this.sizes == null)
261                {
262                        this.sizes = new ArrayList<>();
263                }
264                else
265                {
266                        this.sizes.clear();
267                }
268                this.sizes.addAll(Arrays.asList(sizes));
269        }
270
271        /**
272         * Removes all x values from the image src set.
273         */
274        public void removeXValues()
275        {
276                if (xValues != null)
277                {
278                        xValues.clear();
279                }
280        }
281
282        /**
283         * Gets the cross-origin settings
284         * 
285         * @see org.apache.wicket.markup.html.image.Image#setCrossOrigin(CrossOrigin)
286         *
287         * @return the cross-origins settings
288         */
289        public CrossOrigin getCrossOrigin()
290        {
291                return crossOrigin;
292        }
293
294        /**
295         * Sets the cross-origin settings
296         * 
297         * @see org.apache.wicket.markup.html.image.Image#setCrossOrigin(CrossOrigin)
298         * @param crossOrigin
299         *            the cross-origins settings to set
300         */
301        public void setCrossOrigin(CrossOrigin crossOrigin)
302        {
303                this.crossOrigin = crossOrigin;
304        }
305
306        /**
307         * Gets a list of models containing the src set values
308         * 
309         * @return a list of models containing the src set values
310         */
311        public IModel<List<Serializable>> getSrcSetModel()
312        {
313                return srcSetModel;
314        }
315
316        /**
317         * Sets the source set model
318         * 
319         * @param srcSetModel
320         *            the model of a list of src set entries
321         */
322        public void setSrcSetModel(IModel<List<Serializable>> srcSetModel)
323        {
324                this.srcSetModel = srcSetModel;
325        }
326
327        /**
328         * Detaches the srcSetModels
329         */
330        @Override
331        protected void onDetach()
332        {
333                if (srcSetModel != null)
334                {
335                        srcSetModel.detach();
336                }
337                super.onDetach();
338        }
339}