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;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.wicket.Component;
023import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
024import org.apache.wicket.markup.head.IHeaderResponse;
025import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
026import org.apache.wicket.util.lang.Args;
027import org.apache.wicket.util.lang.Checks;
028import org.apache.wicket.util.string.Strings;
029import org.danekja.java.util.function.serializable.SerializableConsumer;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * An ajax behavior that is attached to a certain client-side (usually javascript) event, such as
035 * click, change, keydown, etc.
036 * <p>
037 * Example:
038 * 
039 * <pre>
040 *         WebMarkupContainer div=new WebMarkupContainer(...);
041 *         div.setOutputMarkupId(true);
042 *         div.add(new AjaxEventBehavior(&quot;click&quot;) {
043 *             protected void onEvent(AjaxRequestTarget target) {
044 *                 System.out.println(&quot;ajax here!&quot;);
045 *             }
046 *         }
047 * </pre>
048 * 
049 * This behavior will be linked to the <em>click</em> javascript event of the div WebMarkupContainer
050 * represents, and so anytime a user clicks this div the {@link #onEvent(AjaxRequestTarget)} of the
051 * behavior is invoked.
052 *
053 * <p>
054 * <strong>Note</strong>: {@link #getEvent()} method cuts any <em>on</em> prefix from the given event name(s).
055 * This is being done for easier migration of applications coming from Wicket 1.5.x where Wicket used
056 * inline attributes like 'onclick=...'. If the application needs to use custom events with names starting with
057 * <em>on</em> then {@link #getEvent()} should be overridden.
058 * </p>
059 *
060 * @since 1.2
061 * 
062 * @author Igor Vaynberg (ivaynberg)
063 * @see #onEvent(AjaxRequestTarget)
064 */
065public abstract class AjaxEventBehavior extends AbstractDefaultAjaxBehavior
066{
067        private static final Logger LOGGER = LoggerFactory.getLogger(AjaxEventBehavior.class);
068
069        private static final long serialVersionUID = 1L;
070
071        private static final char EVENT_NAME_SEPARATOR = ' ';
072
073        private final String event;
074
075        /**
076         * Construct.
077         * 
078         * @param event
079         *      the event this behavior will be attached to
080         */
081        public AjaxEventBehavior(String event)
082        {
083                Args.notEmpty(event, "event");
084
085                if ("inputchange".equals(event))
086                {
087                        // TODO Wicket 10 remove (see WICKET-6667)
088                        event = "input";
089                        LOGGER.warn("Since version 9.0.0 Wicket no longer supports 'inputchange' events, please use 'input' instead");
090                }
091
092                this.event = event;
093        }
094
095        @Override
096        public void renderHead(final Component component, final IHeaderResponse response)
097        {
098                super.renderHead(component, response);
099
100                if (component.isEnabledInHierarchy())
101                {
102                        CharSequence js = getCallbackScript(component);
103
104                        response.render(OnDomReadyHeaderItem.forScript(js.toString()));
105                }
106        }
107
108        @Override
109        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
110        {
111                super.updateAjaxAttributes(attributes);
112
113                String evt = getEvent();
114                Checks.notEmpty(evt, "getEvent() should return non-empty event name(s)");
115                attributes.setEventNames(evt);
116        }
117
118        /**
119         * @return event
120         *      the event this behavior is attached to
121         */
122        public String getEvent()
123        {
124                if (event.indexOf(EVENT_NAME_SEPARATOR) == -1)
125                {
126                        return event;
127                }
128
129                String[] splitEvents = Strings.split(event, EVENT_NAME_SEPARATOR);
130                List<String> cleanedEvents = new ArrayList<>(splitEvents.length);
131                for (String evt : splitEvents)
132                {
133                        evt = evt.trim();
134                        if (!Strings.isEmpty(evt))
135                        {
136                                cleanedEvents.add(evt);
137                        }
138                }
139
140                return Strings.join(" ", cleanedEvents);
141        }
142
143        @Override
144        protected final void respond(final AjaxRequestTarget target)
145        {
146                onEvent(target);
147        }
148
149        /**
150         * Listener method for the ajax event
151         * 
152         * @param target
153         *      the current request handler
154         */
155        protected abstract void onEvent(final AjaxRequestTarget target);
156
157        /**
158         * Creates an {@link AjaxEventBehavior} based on lambda expressions
159         * 
160         * @param eventName
161         *            the event name
162         * @param onEvent
163         *            the {@code SerializableConsumer} which accepts the {@link AjaxRequestTarget}
164         * @return the {@link AjaxEventBehavior}
165         */
166        public static AjaxEventBehavior onEvent(String eventName, SerializableConsumer<AjaxRequestTarget> onEvent)
167        {
168                Args.notNull(onEvent, "onEvent");
169
170                return new AjaxEventBehavior(eventName)
171                {
172                        private static final long serialVersionUID = 1L;
173
174                        @Override
175                        protected void onEvent(AjaxRequestTarget target)
176                        {
177                                onEvent.accept(target);
178                        }
179                };
180        }
181}