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