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.ajax.markup.html;
018
019import java.util.Optional;
020import org.apache.wicket.Component;
021
022import org.apache.wicket.ajax.AjaxEventBehavior;
023import org.apache.wicket.ajax.AjaxRequestTarget;
024import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
025import org.apache.wicket.markup.ComponentTag;
026import org.apache.wicket.markup.html.link.Link;
027import org.apache.wicket.model.IModel;
028
029/**
030 * An ajax link that will degrade to a normal request if ajax is not available or javascript is
031 * disabled.
032 * 
033 * <p>
034 * If JavaScript is enabled then the registered JavaScript event 'click' handler will be used,
035 * otherwise the 'href' attribute if the markup element is an &lt;a&gt;, &lt;area&gt; or
036 * &lt;link&gt;. AjaxFallbackLink doesn't fallback if the markup element is none of the three above.
037 * </p>
038 * 
039 * @since 1.2
040 * 
041 * @author Igor Vaynberg (ivaynberg)
042 * @param <T>
043 *            type of model object
044 */
045public abstract class AjaxFallbackLink<T> extends Link<T>
046{
047        /** */
048        private static final long serialVersionUID = 1L;
049
050        /**
051         * Construct.
052         * 
053         * @param id
054         */
055        public AjaxFallbackLink(final String id)
056        {
057                this(id, null);
058        }
059
060        /**
061         * Construct.
062         * 
063         * @param id
064         * @param model
065         */
066        public AjaxFallbackLink(final String id, final IModel<T> model)
067        {
068                super(id, model);
069
070        }
071
072        @Override
073        protected void onInitialize()
074        {
075                super.onInitialize();
076                add(newAjaxEventBehavior("click"));
077        }
078
079        /**
080         * @param event
081         *            the name of the default event on which this link will listen to
082         * @return the ajax behavior which will be executed when the user clicks the link
083         */
084        protected AjaxEventBehavior newAjaxEventBehavior(String event)
085        {
086                return new AjaxEventBehavior(event)
087                {
088                        private static final long serialVersionUID = 1L;
089
090                        @Override
091                        protected void onEvent(AjaxRequestTarget target)
092                        {
093                                onClick(Optional.of(target));
094                        }
095
096                        @Override
097                        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
098                        {
099                                super.updateAjaxAttributes(attributes);
100                                attributes.setPreventDefault(true);
101                                AjaxFallbackLink.this.updateAjaxAttributes(attributes);
102                        }
103                        
104                        @Override
105                        public boolean getStatelessHint(Component component)
106                        {                               
107                                return AjaxFallbackLink.this.getStatelessHint();
108                        }
109                };
110        }
111
112        /**
113         * @param attributes
114         */
115        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
116        {
117        }
118
119        @Override
120        public final void onClick()
121        {
122                onClick(Optional.empty());
123        }
124
125        /**
126         * Callback for the onClick event. If ajax failed and this event was generated via a normal link
127         * the target argument will be null
128         * 
129         * @param target
130         *            ajax target if this linked was invoked using ajax, null otherwise
131         */
132        public abstract void onClick(final Optional<AjaxRequestTarget> target);
133
134        /**
135         * Checks if the tag supports href: a, area or link.
136         * 
137         * @param tag
138         *            the component tag
139         */
140        @Override
141        protected void onComponentTag(ComponentTag tag)
142        {
143                super.onComponentTag(tag);
144
145                String tagName = tag.getName();
146                if (isEnabledInHierarchy() &&
147                        !("a".equalsIgnoreCase(tagName) || "area".equalsIgnoreCase(tagName) || "link".equalsIgnoreCase(tagName)))
148                {
149                        String msg = String.format(
150                                "%s must be used only with <a>, <area> or <link> markup elements. "
151                                        + "The fallback functionality doesn't work for other markup elements. "
152                                        + "Component path: %s, markup element: <%s>.",
153                                AjaxFallbackLink.class.getSimpleName(), getClassRelativePath(), tagName);
154                        findMarkupStream().throwMarkupException(msg);
155                }
156        }
157
158        @Override
159        protected boolean useJSEventBindingWhenNeeded()
160        {
161                // AjaxFallbackLink uses Ajax event binding.
162                return false;
163        }
164}