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.attributes; 018 019import java.util.ArrayList; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.wicket.ajax.AjaxChannel; 025import org.apache.wicket.util.lang.Args; 026import java.time.Duration; 027 028/** 029 * Attributes of an Ajax Request. 030 * 031 * @author Matej Knopp 032 */ 033public final class AjaxRequestAttributes 034{ 035 /** 036 * The method to be used when submitting a form 037 */ 038 public enum Method 039 { 040 /** get */ 041 GET, 042 043 /** post */ 044 POST; 045 046 @Override 047 public String toString() 048 { 049 return name(); 050 } 051 } 052 053 /** 054 * The JavaScript event propagation type 055 */ 056 public enum EventPropagation 057 { 058 /** 059 * Stops the propagation of the JavaScript event to the parent of its target 060 */ 061 STOP, 062 063 /** 064 * Stops the propagation of the JavaScript event to the parent of its target and all other 065 * event listeners registered on the same target 066 */ 067 STOP_IMMEDIATE, 068 069 /** 070 * Do not stop the propagation of the JavaScript event 071 */ 072 BUBBLE 073 } 074 075 public static final String XML_DATA_TYPE = "xml"; 076 077 private boolean multipart = false; 078 079 private Method method = Method.GET; 080 081 private Duration requestTimeout; 082 083 private boolean preventDefault = false; 084 085 private EventPropagation eventPropagation = EventPropagation.BUBBLE; 086 087 /** 088 * The names of the events which will trigger the Ajax call 089 */ 090 private String[] eventNames = new String[0]; 091 092 /** 093 * The id of the for that should be submitted 094 */ 095 private String formId; 096 097 /** 098 * The id of the button/link that submitted the form 099 */ 100 private String submittingComponentName; 101 102 /** 103 * Indicates whether or not this AjaxBehavior will produce <ajax-response>. By default it will 104 * produce it but some behaviors may need to return their own response which shouldn't be 105 * processed by wicket-ajax.js 106 */ 107 private boolean wicketAjaxResponse = true; 108 109 private String dataType = XML_DATA_TYPE; 110 111 private List<IAjaxCallListener> ajaxCallListeners; 112 113 private Map<String, Object> extraParameters; 114 115 private List<CharSequence> dynamicExtraParameters; 116 117 private AjaxChannel channel; 118 119 /** 120 * Whether or not to use asynchronous XMLHttpRequest 121 */ 122 private boolean async = true; 123 124 /** 125 * The settings to use if the Ajax call should be throttled. Throttled behaviors only execute 126 * once within the given delay even though they are triggered multiple times. 127 * <p> 128 * For example, this is useful when attaching a behavior to the keypress event. It is not 129 * desirable to have an ajax call made every time the user types so we throttle that call to a 130 * desirable delay, such as once per second. This gives us a near real time ability to provide 131 * feedback without overloading the server with ajax calls. 132 */ 133 private ThrottlingSettings throttlingSettings; 134 135 /** 136 * A selector string to filter the descendants of the selected 137 * elements that trigger the event. If the selector is null or empty, 138 * the event is always triggered when it reaches the selected HTML element. 139 * 140 * @see <a href="http://api.jquery.com/on/">jQuery#on's selector</a> 141 */ 142 private CharSequence childSelector; 143 144 /** 145 * A flag indicating whether to collect (submit) the name/value pairs for all HTML form elements 146 * children of the HTML element with the JavaScript listener 147 */ 148 private boolean serializeRecursively; 149 150 /** 151 * @see #childSelector 152 * @return The selector string that filters the descendants 153 */ 154 public CharSequence getChildSelector() 155 { 156 return childSelector; 157 } 158 159 /** 160 * @see #childSelector 161 * @param childSelector 162 * The selector string that filters the descendants 163 */ 164 public void setChildSelector(CharSequence childSelector) 165 { 166 this.childSelector = childSelector; 167 } 168 169 /** 170 * Returns whether the form submit is multipart. 171 * <p> 172 * Note that for multipart AJAX requests a hidden IFRAME will be used and that can have negative 173 * impact on error detection. 174 * 175 * @return <code>true</code> if the form submit should be multipart, <code>false</code> 176 * otherwise 177 */ 178 public boolean isMultipart() 179 { 180 return multipart; 181 } 182 183 /** 184 * Determines whether the form submit is multipart. 185 * 186 * <p> 187 * Note that for multipart AJAX requests a hidden IFRAME will be used and that can have negative 188 * impact on error detection. 189 * 190 * @param multipart 191 * @return this object 192 */ 193 public AjaxRequestAttributes setMultipart(boolean multipart) 194 { 195 this.multipart = multipart; 196 return this; 197 } 198 199 /** 200 * Returns the type of the Ajax request: <code>GET</code> or <code>POST</code>. 201 * <p> 202 * For a <code>POST</code>request all URL arguments are submitted as body. This can be useful if 203 * the URL parameters are longer than maximal URL length. 204 * 205 * @return the type of the Ajax request. Default: {@linkplain Method#GET} 206 */ 207 public Method getMethod() 208 { 209 return method; 210 } 211 212 /** 213 * Sets the type of the Ajax request: <code>GET</code> or <code>POST</code>. 214 * <p> 215 * For a <code>POST</code>request all URL arguments are submitted as body. This can be useful if 216 * the URL parameters are longer than maximal URL length. 217 * 218 * @param method 219 * the type of the Ajax request 220 * @return {@code this} object for chaining 221 */ 222 public AjaxRequestAttributes setMethod(final Method method) 223 { 224 this.method = Args.notNull(method, "method"); 225 return this; 226 } 227 228 /** 229 * Returns the timeout in milliseconds for the AJAX request. This only involves the actual 230 * communication and not the processing afterwards. Can be <code>null</code> in which case the 231 * default request timeout will be used. 232 * 233 * @return request timeout or <code>null<code> for default timeout. Default: no timeout. 234 */ 235 public Duration getRequestTimeout() 236 { 237 return requestTimeout; 238 } 239 240 /** 241 * Sets the timeout in milliseconds for the AJAX request. This only involves the actual 242 * communication and not the processing afterwards. Can be <code>null</code> in which case the 243 * default request timeout will be used. 244 * 245 * @param requestTimeout 246 * @return this object 247 */ 248 public AjaxRequestAttributes setRequestTimeout(final Duration requestTimeout) 249 { 250 this.requestTimeout = requestTimeout; 251 return this; 252 } 253 254 /** 255 * @return a list of {@link IAjaxCallListener}s which will be notified during the the execution 256 * of the Ajax call. 257 */ 258 public List<IAjaxCallListener> getAjaxCallListeners() 259 { 260 if (ajaxCallListeners == null) 261 { 262 ajaxCallListeners = new ArrayList<>(); 263 } 264 return ajaxCallListeners; 265 } 266 267 /** 268 * Map that contains additional (static) URL parameters. These will be appended to the request 269 * URL. If you need more than one value for a key then use a java.util.List or an Object[] as a 270 * value of that key. 271 * 272 * @return a map with additional URL arguments 273 * @see #getDynamicExtraParameters() 274 */ 275 public Map<String, Object> getExtraParameters() 276 { 277 if (extraParameters == null) 278 { 279 extraParameters = new LinkedHashMap<>(); 280 } 281 return extraParameters; 282 } 283 284 /** 285 * Array of JavaScript functions that produce additional URL arguments. 286 * 287 * <p> 288 * If there are no multivalued parameters then the function can return a simple JavaScript 289 * object. Example: 290 * 291 * <pre> 292 * return { 293 * 'param1': document.body.tagName, 294 * 'param2': calculateParam2() 295 * } 296 * </pre> 297 * 298 * </p> 299 * <p> 300 * If there are multivalued parameters then an array of objects may be used. Example: 301 * 302 * <pre> 303 * return [ 304 * { name: 'param1', value: document.body.tagName }, 305 * { name: 'param1', value: calculateSecondValueForParam1() }, 306 * { name: 'param2', value: calculateParam2() } 307 * ] 308 * 309 * </pre> 310 * 311 * </p> 312 * 313 * @return a list of functions that produce additional URL arguments. 314 * @see #getExtraParameters() 315 */ 316 public List<CharSequence> getDynamicExtraParameters() 317 { 318 if (dynamicExtraParameters == null) 319 { 320 dynamicExtraParameters = new ArrayList<>(); 321 } 322 return dynamicExtraParameters; 323 } 324 325 /** 326 * Only applies for event behaviors. Returns whether the behavior should prevent the default event 327 * handler to be invoked. For example if the behavior is attached to a link and 328 * isPreventDefault() returns <code>true</code>, the link's 329 * URL will not be followed. If the Ajax behavior is attached to a checkbox or a radio button 330 * then the default behavior should be allowed to actually check the box or radio button, i.e. 331 * this method should return <code>false</code>. 332 * 333 * @return {@code false} if the default event handler should be invoked 334 */ 335 public boolean isPreventDefault() 336 { 337 return preventDefault; 338 } 339 340 /** 341 * Only applies for event behaviors. Determines whether the behavior should prevent the default 342 * event handler to be invoked. 343 * 344 * @see #isPreventDefault() 345 * 346 * @param preventDefault 347 * @return {@code this} object for chaining 348 * @see #isPreventDefault() 349 */ 350 public AjaxRequestAttributes setPreventDefault(boolean preventDefault) 351 { 352 this.preventDefault = preventDefault; 353 return this; 354 } 355 356 /** 357 * Only applies for event behaviors. Returns whether the behavior should allow the JavaScript 358 * event to propagate to the parent of its target. 359 */ 360 public EventPropagation getEventPropagation() 361 { 362 return eventPropagation; 363 } 364 365 /** 366 * Only applies to event behaviors. Determines whether the behavior should allow the JavaScript 367 * event to propagate to the parent of its target. 368 * 369 * @param eventPropagation 370 * the type of the stop 371 * @return {@code this} object, for chaining 372 */ 373 public AjaxRequestAttributes setEventPropagation(EventPropagation eventPropagation) 374 { 375 this.eventPropagation = Args.notNull(eventPropagation, "eventPropagation"); 376 return this; 377 } 378 379 /** 380 * @param async 381 * a flag whether to do asynchronous Ajax call or not 382 * @return {@code this} object for chaining 383 */ 384 public AjaxRequestAttributes setAsynchronous(final boolean async) 385 { 386 this.async = async; 387 return this; 388 } 389 390 /** 391 * @return whether to do asynchronous Ajax call 392 */ 393 public boolean isAsynchronous() 394 { 395 return async; 396 } 397 398 /** 399 * @return the channel to use 400 */ 401 public AjaxChannel getChannel() 402 { 403 return channel; 404 } 405 406 /** 407 * @param channel 408 * the Ajax channel to use. Pass {@code null} to use the default channel with name 409 * <em>0</em> and queueing type. 410 * @return {@code this} object for chaining 411 */ 412 public AjaxRequestAttributes setChannel(final AjaxChannel channel) 413 { 414 this.channel = channel; 415 return this; 416 } 417 418 /** 419 * @return the name(s) of the event(s) which will trigger the Ajax call 420 */ 421 public String[] getEventNames() 422 { 423 return eventNames; 424 } 425 426 /** 427 * @param eventNames 428 * the names of the events which will trigger the Ajax call 429 * @return {@code this} object for chaining 430 */ 431 public AjaxRequestAttributes setEventNames(String... eventNames) 432 { 433 Args.notNull(eventNames, "eventNames"); 434 this.eventNames = eventNames; 435 return this; 436 } 437 438 /** 439 * @return the id of the for that should be submitted 440 */ 441 public String getFormId() 442 { 443 return formId; 444 } 445 446 /** 447 * @param formId 448 * the id of the for that should be submitted 449 * @return {@code this} object for chaining 450 */ 451 public AjaxRequestAttributes setFormId(final String formId) 452 { 453 this.formId = formId; 454 return this; 455 } 456 457 /** 458 * @return the input name of the button/link that submits the form 459 */ 460 public String getSubmittingComponentName() 461 { 462 return submittingComponentName; 463 } 464 465 /** 466 * @param submittingComponentName 467 * the input name of the button/link that submits the form 468 * @return {@code this} object for chaining 469 */ 470 public AjaxRequestAttributes setSubmittingComponentName(String submittingComponentName) 471 { 472 this.submittingComponentName = submittingComponentName; 473 return this; 474 } 475 476 /** 477 * @return a flag indicating whether the Ajax response should be processed by Wicket (i.e. to 478 * replace components, execute scripts, etc.). Default: {@code true}. 479 */ 480 public boolean isWicketAjaxResponse() 481 { 482 return wicketAjaxResponse; 483 } 484 485 /** 486 * @param wicketAjaxResponse 487 * a flag indicating whether the Ajax response should be processed by Wicket (i.e. to 488 * replace components, execute scripts, etc.). 489 * @return {@code this} object for chaining 490 */ 491 public AjaxRequestAttributes setWicketAjaxResponse(final boolean wicketAjaxResponse) 492 { 493 this.wicketAjaxResponse = wicketAjaxResponse; 494 return this; 495 } 496 497 /** 498 * Returns the type of the data in the Ajax response. For example: 'xml', 'json', 'html', etc. 499 * See the documentation of jQuery.ajax() method for more information. 500 * 501 * @return the type of the data in the Ajax response. 502 */ 503 public String getDataType() 504 { 505 return dataType; 506 } 507 508 /** 509 * @param dataType 510 * the type of the data in the Ajax response. 511 * @return {@code this} object for chaining 512 */ 513 public AjaxRequestAttributes setDataType(final String dataType) 514 { 515 this.dataType = Args.notEmpty(dataType, "dataType"); 516 return this; 517 } 518 519 /** 520 * @return the settings to use when throttling is needed. 521 */ 522 public ThrottlingSettings getThrottlingSettings() 523 { 524 return throttlingSettings; 525 } 526 527 /** 528 * @param throttlingSettings 529 * the settings to use when throttling is needed. Pass {@code null} to disable 530 * throttling. 531 * @return {@code this} object for chaining 532 */ 533 public AjaxRequestAttributes setThrottlingSettings(ThrottlingSettings throttlingSettings) 534 { 535 this.throttlingSettings = throttlingSettings; 536 return this; 537 } 538 539 /** 540 * @return whether to collect (submit) the name/value pairs for all HTML form elements 541 * children of the HTML element with the JavaScript listener 542 */ 543 public boolean isSerializeRecursively() { 544 return serializeRecursively; 545 } 546 547 /** 548 * @param serializeRecursively 549 * a flag indicating whether to collect (submit) the name/value pairs for all HTML form elements 550 * children of the HTML element with the JavaScript listener 551 */ 552 public AjaxRequestAttributes setSerializeRecursively(final boolean serializeRecursively) { 553 this.serializeRecursively = serializeRecursively; 554 return this; 555 } 556}