Projet

Général

Profil

Paste
Télécharger (48,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / includes / ajax.inc @ 76597ebf

1
<?php
2

    
3
/**
4
 * @file
5
 * Functions for use with Drupal's Ajax framework.
6
 */
7

    
8
/**
9
 * @defgroup ajax Ajax framework
10
 * @{
11
 * Functions for Drupal's Ajax framework.
12
 *
13
 * Drupal's Ajax framework is used to dynamically update parts of a page's HTML
14
 * based on data from the server. Upon a specified event, such as a button
15
 * click, a callback function is triggered which performs server-side logic and
16
 * may return updated markup, which is then replaced on-the-fly with no page
17
 * refresh necessary.
18
 *
19
 * This framework creates a PHP macro language that allows the server to
20
 * instruct JavaScript to perform actions on the client browser. When using
21
 * forms, it can be used with the #ajax property.
22
 * The #ajax property can be used to bind events to the Ajax framework. By
23
 * default, #ajax uses 'system/ajax' as its path for submission and thus calls
24
 * ajax_form_callback() and a defined #ajax['callback'] function.
25
 * However, you may optionally specify a different path to request or a
26
 * different callback function to invoke, which can return updated HTML or can
27
 * also return a richer set of
28
 * @link ajax_commands Ajax framework commands @endlink.
29
 *
30
 * Standard form handling is as follows:
31
 *   - A form element has a #ajax property that includes #ajax['callback'] and
32
 *     omits #ajax['path']. See below about using #ajax['path'] to implement
33
 *     advanced use-cases that require something other than standard form
34
 *     handling.
35
 *   - On the specified element, Ajax processing is triggered by a change to
36
 *     that element.
37
 *   - The browser submits an HTTP POST request to the 'system/ajax' Drupal
38
 *     path.
39
 *   - The menu page callback for 'system/ajax', ajax_form_callback(), calls
40
 *     drupal_process_form() to process the form submission and rebuild the
41
 *     form if necessary. The form is processed in much the same way as if it
42
 *     were submitted without Ajax, with the same #process functions and
43
 *     validation and submission handlers called in either case, making it easy
44
 *     to create Ajax-enabled forms that degrade gracefully when JavaScript is
45
 *     disabled.
46
 *   - After form processing is complete, ajax_form_callback() calls the
47
 *     function named by #ajax['callback'], which returns the form element that
48
 *     has been updated and needs to be returned to the browser, or
49
 *     alternatively, an array of custom Ajax commands.
50
 *   - The page delivery callback for 'system/ajax', ajax_deliver(), renders the
51
 *     element returned by #ajax['callback'], and returns the JSON string
52
 *     created by ajax_render() to the browser.
53
 *   - The browser unserializes the returned JSON string into an array of
54
 *     command objects and executes each command, resulting in the old page
55
 *     content within and including the HTML element specified by
56
 *     #ajax['wrapper'] being replaced by the new content returned by
57
 *     #ajax['callback'], using a JavaScript animation effect specified by
58
 *     #ajax['effect'].
59
 *
60
 * A simple example of basic Ajax use from the
61
 * @link http://drupal.org/project/examples Examples module @endlink follows:
62
 * @code
63
 * function main_page() {
64
 *   return drupal_get_form('ajax_example_simplest');
65
 * }
66
 *
67
 * function ajax_example_simplest($form, &$form_state) {
68
 *   $form = array();
69
 *   $form['changethis'] = array(
70
 *     '#type' => 'select',
71
 *     '#options' => array(
72
 *       'one' => 'one',
73
 *       'two' => 'two',
74
 *       'three' => 'three',
75
 *     ),
76
 *     '#ajax' => array(
77
 *       'callback' => 'ajax_example_simplest_callback',
78
 *       'wrapper' => 'replace_textfield_div',
79
 *      ),
80
 *   );
81

    
82
 *   // This entire form element will be replaced with an updated value.
83
 *   $form['replace_textfield'] = array(
84
 *     '#type' => 'textfield',
85
 *     '#title' => t("The default value will be changed"),
86
 *     '#description' => t("Say something about why you chose") . "'" .
87
 *       (!empty($form_state['values']['changethis'])
88
 *       ? $form_state['values']['changethis'] : t("Not changed yet")) . "'",
89
 *     '#prefix' => '<div id="replace_textfield_div">',
90
 *     '#suffix' => '</div>',
91
 *   );
92
 *   return $form;
93
 * }
94
 *
95
 * function ajax_example_simplest_callback($form, $form_state) {
96
 *   // The form has already been submitted and updated. We can return the replaced
97
 *   // item as it is.
98
 *   return $form['replace_textfield'];
99
 * }
100
 * @endcode
101
 *
102
 * In the above example, the 'changethis' element is Ajax-enabled. The default
103
 * #ajax['event'] is 'change', so when the 'changethis' element changes,
104
 * an Ajax call is made. The form is submitted and reprocessed, and then the
105
 * callback is called. In this case, the form has been automatically
106
 * built changing $form['replace_textfield']['#description'], so the callback
107
 * just returns that part of the form.
108
 *
109
 * To implement Ajax handling in a form, add '#ajax' to the form
110
 * definition of a field. That field will trigger an Ajax event when it is
111
 * clicked (or changed, depending on the kind of field). #ajax supports
112
 * the following parameters (either 'path' or 'callback' is required at least):
113
 * - #ajax['callback']: The callback to invoke to handle the server side of the
114
 *   Ajax event, which will receive a $form and $form_state as arguments, and
115
 *   returns a renderable array (most often a form or form fragment), an HTML
116
 *   string, or an array of Ajax commands. If returning a renderable array or
117
 *   a string, the value will replace the original element named in
118
 *   #ajax['wrapper'], and
119
 *   theme_status_messages()
120
 *   will be prepended to that
121
 *   element. (If the status messages are not wanted, return an array
122
 *   of Ajax commands instead.)
123
 *   #ajax['wrapper']. If an array of Ajax commands is returned, it will be
124
 *   executed by the calling code.
125
 * - #ajax['path']: The menu path to use for the request. This is often omitted
126
 *   and the default is used. This path should map
127
 *   to a menu page callback that returns data using ajax_render(). Defaults to
128
 *   'system/ajax', which invokes ajax_form_callback(), eventually calling
129
 *   the function named in #ajax['callback']. If you use a custom
130
 *   path, you must set up the menu entry and handle the entire callback in your
131
 *   own code.
132
 * - #ajax['wrapper']: The CSS ID of the area to be replaced by the content
133
 *   returned by the #ajax['callback'] function. The content returned from
134
 *   the callback will replace the entire element named by #ajax['wrapper'].
135
 *   The wrapper is usually created using #prefix and #suffix properties in the
136
 *   form. Note that this is the wrapper ID, not a CSS selector. So to replace
137
 *   the element referred to by the CSS selector #some-selector on the page,
138
 *   use #ajax['wrapper'] = 'some-selector', not '#some-selector'.
139
 * - #ajax['effect']: The jQuery effect to use when placing the new HTML.
140
 *   Defaults to no effect. Valid options are 'none', 'slide', or 'fade'.
141
 * - #ajax['speed']: The effect speed to use. Defaults to 'slow'. May be
142
 *   'slow', 'fast' or a number in milliseconds which represents the length
143
 *   of time the effect should run.
144
 * - #ajax['event']: The JavaScript event to respond to. This is normally
145
 *   selected automatically for the type of form widget being used, and
146
 *   is only needed if you need to override the default behavior.
147
 * - #ajax['prevent']: A JavaScript event to prevent when 'event' is triggered.
148
 *   Defaults to 'click' for #ajax on #type 'submit', 'button', and
149
 *   'image_button'. Multiple events may be specified separated by spaces.
150
 *   For example, when binding #ajax behaviors to form buttons, pressing the
151
 *   ENTER key within a textfield triggers the 'click' event of the form's first
152
 *   submit button. Triggering Ajax in this situation leads to problems, like
153
 *   breaking autocomplete textfields. Because of that, Ajax behaviors are bound
154
 *   to the 'mousedown' event on form buttons by default. However, binding to
155
 *   'mousedown' rather than 'click' means that it is possible to trigger a
156
 *   click by pressing the mouse, holding the mouse button down until the Ajax
157
 *   request is complete and the button is re-enabled, and then releasing the
158
 *   mouse button. For this case, 'prevent' can be set to 'click', so an
159
 *   additional event handler is bound to prevent such a click from triggering a
160
 *   non-Ajax form submission. This also prevents a textfield's ENTER press
161
 *   triggering a button's non-Ajax form submission behavior.
162
 * - #ajax['method']: The jQuery method to use to place the new HTML.
163
 *   Defaults to 'replaceWith'. May be: 'replaceWith', 'append', 'prepend',
164
 *   'before', 'after', or 'html'. See the
165
 *   @link http://api.jquery.com/category/manipulation/ jQuery manipulators documentation @endlink
166
 *   for more information on these methods.
167
 * - #ajax['progress']: Choose either a throbber or progress bar that is
168
 *   displayed while awaiting a response from the callback, and add an optional
169
 *   message. Possible keys: 'type', 'message', 'url', 'interval'.
170
 *   More information is available in the
171
 *   @link forms_api_reference.html Form API Reference @endlink
172
 *
173
 * In addition to using Form API for doing in-form modification, Ajax may be
174
 * enabled by adding classes to buttons and links. By adding the 'use-ajax'
175
 * class to a link, the link will be loaded via an Ajax call. When using this
176
 * method, the href of the link can contain '/nojs/' as part of the path. When
177
 * the Ajax framework makes the request, it will convert this to '/ajax/'.
178
 * The server is then able to easily tell if this request was made through an
179
 * actual Ajax request or in a degraded state, and respond appropriately.
180
 *
181
 * Similarly, submit buttons can be given the class 'use-ajax-submit'. The
182
 * form will then be submitted via Ajax to the path specified in the #action.
183
 * Like the ajax-submit class above, this path will have '/nojs/' replaced with
184
 * '/ajax/' so that the submit handler can tell if the form was submitted
185
 * in a degraded state or not.
186
 *
187
 * When responding to Ajax requests, the server should do what it needs to do
188
 * for that request, then create a commands array. This commands array will
189
 * be converted to a JSON object and returned to the client, which will then
190
 * iterate over the array and process it like a macro language.
191
 *
192
 * Each command item is an associative array which will be converted to a
193
 * command object on the JavaScript side. $command_item['command'] is the type
194
 * of command, e.g. 'alert' or 'replace', and will correspond to a method in the
195
 * Drupal.ajax[command] space. The command array may contain any other data that
196
 * the command needs to process, e.g. 'method', 'selector', 'settings', etc.
197
 *
198
 * Commands are usually created with a couple of helper functions, so they
199
 * look like this:
200
 * @code
201
 *   $commands = array();
202
 *   // Replace the content of '#object-1' on the page with 'some html here'.
203
 *   $commands[] = ajax_command_replace('#object-1', 'some html here');
204
 *   // Add a visual "changed" marker to the '#object-1' element.
205
 *   $commands[] = ajax_command_changed('#object-1');
206
 *   // Menu 'page callback' and #ajax['callback'] functions are supposed to
207
 *   // return render arrays. If returning an Ajax commands array, it must be
208
 *   // encapsulated in a render array structure.
209
 *   return array('#type' => 'ajax', '#commands' => $commands);
210
 * @endcode
211
 *
212
 * When returning an Ajax command array, it is often useful to have
213
 * status messages rendered along with other tasks in the command array.
214
 * In that case the the Ajax commands array may be constructed like this:
215
 * @code
216
 *   $commands = array();
217
 *   $commands[] = ajax_command_replace(NULL, $output);
218
 *   $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
219
 *   return array('#type' => 'ajax', '#commands' => $commands);
220
 * @endcode
221
 *
222
 * See @link ajax_commands Ajax framework commands @endlink
223
 */
224

    
225
/**
226
 * Renders a commands array into JSON.
227
 *
228
 * @param $commands
229
 *   A list of macro commands generated by the use of ajax_command_*()
230
 *   functions.
231
 */
232
function ajax_render($commands = array()) {
233
  // Ajax responses aren't rendered with html.tpl.php, so we have to call
234
  // drupal_get_css() and drupal_get_js() here, in order to have new files added
235
  // during this request to be loaded by the page. We only want to send back
236
  // files that the page hasn't already loaded, so we implement simple diffing
237
  // logic using array_diff_key().
238
  foreach (array('css', 'js') as $type) {
239
    // It is highly suspicious if $_POST['ajax_page_state'][$type] is empty,
240
    // since the base page ought to have at least one JS file and one CSS file
241
    // loaded. It probably indicates an error, and rather than making the page
242
    // reload all of the files, instead we return no new files.
243
    if (empty($_POST['ajax_page_state'][$type])) {
244
      $items[$type] = array();
245
    }
246
    else {
247
      $function = 'drupal_add_' . $type;
248
      $items[$type] = $function();
249
      drupal_alter($type, $items[$type]);
250
      // @todo Inline CSS and JS items are indexed numerically. These can't be
251
      //   reliably diffed with array_diff_key(), since the number can change
252
      //   due to factors unrelated to the inline content, so for now, we strip
253
      //   the inline items from Ajax responses, and can add support for them
254
      //   when drupal_add_css() and drupal_add_js() are changed to use a hash
255
      //   of the inline content as the array key.
256
      foreach ($items[$type] as $key => $item) {
257
        if (is_numeric($key)) {
258
          unset($items[$type][$key]);
259
        }
260
      }
261
      // Ensure that the page doesn't reload what it already has.
262
      $items[$type] = array_diff_key($items[$type], $_POST['ajax_page_state'][$type]);
263
    }
264
  }
265

    
266
  // Render the HTML to load these files, and add AJAX commands to insert this
267
  // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
268
  // data from being altered again, as we already altered it above. Settings are
269
  // handled separately, afterwards.
270
  if (isset($items['js']['settings'])) {
271
    unset($items['js']['settings']);
272
  }
273
  $styles = drupal_get_css($items['css'], TRUE);
274
  $scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
275
  $scripts_header = drupal_get_js('header', $items['js'], TRUE);
276

    
277
  $extra_commands = array();
278
  if (!empty($styles)) {
279
    $extra_commands[] = ajax_command_add_css($styles);
280
  }
281
  if (!empty($scripts_header)) {
282
    $extra_commands[] = ajax_command_prepend('head', $scripts_header);
283
  }
284
  if (!empty($scripts_footer)) {
285
    $extra_commands[] = ajax_command_append('body', $scripts_footer);
286
  }
287
  if (!empty($extra_commands)) {
288
    $commands = array_merge($extra_commands, $commands);
289
  }
290

    
291
  // Now add a command to merge changes and additions to Drupal.settings.
292
  $scripts = drupal_add_js();
293
  if (!empty($scripts['settings'])) {
294
    $settings = $scripts['settings'];
295
    array_unshift($commands, ajax_command_settings(drupal_array_merge_deep_array($settings['data']), TRUE));
296
  }
297

    
298
  // Allow modules to alter any Ajax response.
299
  drupal_alter('ajax_render', $commands);
300

    
301
  return drupal_json_encode($commands);
302
}
303

    
304
/**
305
 * Gets a form submitted via #ajax during an Ajax callback.
306
 *
307
 * This will load a form from the form cache used during Ajax operations. It
308
 * pulls the form info from $_POST.
309
 *
310
 * @return
311
 *   An array containing the $form, $form_state, $form_id, $form_build_id and an
312
 *   initial list of Ajax $commands. Use the list() function to break these
313
 *   apart:
314
 *   @code
315
 *     list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
316
 *   @endcode
317
 */
318
function ajax_get_form() {
319
  $form_state = form_state_defaults();
320

    
321
  $form_build_id = $_POST['form_build_id'];
322

    
323
  // Get the form from the cache.
324
  $form = form_get_cache($form_build_id, $form_state);
325
  if (!$form) {
326
    // If $form cannot be loaded from the cache, the form_build_id in $_POST
327
    // must be invalid, which means that someone performed a POST request onto
328
    // system/ajax without actually viewing the concerned form in the browser.
329
    // This is likely a hacking attempt as it never happens under normal
330
    // circumstances, so we just do nothing.
331
    watchdog('ajax', 'Invalid form POST data.', array(), WATCHDOG_WARNING);
332
    drupal_exit();
333
  }
334

    
335
  // When a page level cache is enabled, the form-build id might have been
336
  // replaced from within form_get_cache. If this is the case, it is also
337
  // necessary to update it in the browser by issuing an appropriate Ajax
338
  // command.
339
  $commands = array();
340
  if (isset($form['#build_id_old']) && $form['#build_id_old'] != $form['#build_id']) {
341
    // If the form build ID has changed, issue an Ajax command to update it.
342
    $commands[] = ajax_command_update_build_id($form);
343
    $form_build_id = $form['#build_id'];
344
  }
345

    
346
  // Since some of the submit handlers are run, redirects need to be disabled.
347
  $form_state['no_redirect'] = TRUE;
348

    
349
  // When a form is rebuilt after Ajax processing, its #build_id and #action
350
  // should not change.
351
  // @see drupal_rebuild_form()
352
  $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
353
  $form_state['rebuild_info']['copy']['#action'] = TRUE;
354

    
355
  // The form needs to be processed; prepare for that by setting a few internal
356
  // variables.
357
  $form_state['input'] = $_POST;
358
  $form_id = $form['#form_id'];
359

    
360
  return array($form, $form_state, $form_id, $form_build_id, $commands);
361
}
362

    
363
/**
364
 * Menu callback; handles Ajax requests for the #ajax Form API property.
365
 *
366
 * This rebuilds the form from cache and invokes the defined #ajax['callback']
367
 * to return an Ajax command structure for JavaScript. In case no 'callback' has
368
 * been defined, nothing will happen.
369
 *
370
 * The Form API #ajax property can be set both for buttons and other input
371
 * elements.
372
 *
373
 * This function is also the canonical example of how to implement
374
 * #ajax['path']. If processing is required that cannot be accomplished with
375
 * a callback, re-implement this function and set #ajax['path'] to the
376
 * enhanced function.
377
 *
378
 * @see system_menu()
379
 */
380
function ajax_form_callback() {
381
  list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
382
  drupal_process_form($form['#form_id'], $form, $form_state);
383

    
384
  // We need to return the part of the form (or some other content) that needs
385
  // to be re-rendered so the browser can update the page with changed content.
386
  // Since this is the generic menu callback used by many Ajax elements, it is
387
  // up to the #ajax['callback'] function of the element (may or may not be a
388
  // button) that triggered the Ajax request to determine what needs to be
389
  // rendered.
390
  if (!empty($form_state['triggering_element'])) {
391
    $callback = $form_state['triggering_element']['#ajax']['callback'];
392
  }
393
  if (!empty($callback) && function_exists($callback)) {
394
    $result = $callback($form, $form_state);
395

    
396
    if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
397
      // Turn the response into a #type=ajax array if it isn't one already.
398
      $result = array(
399
        '#type' => 'ajax',
400
        '#commands' => ajax_prepare_response($result),
401
      );
402
    }
403

    
404
    $result['#commands'] = array_merge($commands, $result['#commands']);
405

    
406
    return $result;
407
  }
408
}
409

    
410
/**
411
 * Theme callback for Ajax requests.
412
 *
413
 * Many different pages can invoke an Ajax request to system/ajax or another
414
 * generic Ajax path. It is almost always desired for an Ajax response to be
415
 * rendered using the same theme as the base page, because most themes are built
416
 * with the assumption that they control the entire page, so if the CSS for two
417
 * themes are both loaded for a given page, they may conflict with each other.
418
 * For example, Bartik is Drupal's default theme, and Seven is Drupal's default
419
 * administration theme. Depending on whether the "Use the administration theme
420
 * when editing or creating content" checkbox is checked, the node edit form may
421
 * be displayed in either theme, but the Ajax response to the Field module's
422
 * "Add another item" button should be rendered using the same theme as the rest
423
 * of the page. Therefore, system_menu() sets the 'theme callback' for
424
 * 'system/ajax' to this function, and it is recommended that modules
425
 * implementing other generic Ajax paths do the same.
426
 *
427
 * @see system_menu()
428
 * @see file_menu()
429
 */
430
function ajax_base_page_theme() {
431
  if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
432
    $theme = $_POST['ajax_page_state']['theme'];
433
    $token = $_POST['ajax_page_state']['theme_token'];
434

    
435
    // Prevent a request forgery from giving a person access to a theme they
436
    // shouldn't be otherwise allowed to see. However, since everyone is allowed
437
    // to see the default theme, token validation isn't required for that, and
438
    // bypassing it allows most use-cases to work even when accessed from the
439
    // page cache.
440
    if ($theme === variable_get('theme_default', 'bartik') || drupal_valid_token($token, $theme)) {
441
      return $theme;
442
    }
443
  }
444
}
445

    
446
/**
447
 * Packages and sends the result of a page callback as an Ajax response.
448
 *
449
 * This function is the equivalent of drupal_deliver_html_page(), but for Ajax
450
 * requests. Like that function, it:
451
 * - Adds needed HTTP headers.
452
 * - Prints rendered output.
453
 * - Performs end-of-request tasks.
454
 *
455
 * @param $page_callback_result
456
 *   The result of a page callback. Can be one of:
457
 *   - NULL: to indicate no content.
458
 *   - An integer menu status constant: to indicate an error condition.
459
 *   - A string of HTML content.
460
 *   - A renderable array of content.
461
 *
462
 * @see drupal_deliver_html_page()
463
 */
464
function ajax_deliver($page_callback_result) {
465
  // Browsers do not allow JavaScript to read the contents of a user's local
466
  // files. To work around that, the jQuery Form plugin submits forms containing
467
  // a file input element to an IFRAME, instead of using XHR. Browsers do not
468
  // normally expect JSON strings as content within an IFRAME, so the response
469
  // must be customized accordingly.
470
  // @see http://malsup.com/jquery/form/#file-upload
471
  // @see Drupal.ajax.prototype.beforeSend()
472
  $iframe_upload = !empty($_POST['ajax_iframe_upload']);
473

    
474
  // Emit a Content-Type HTTP header if none has been added by the page callback
475
  // or by a wrapping delivery callback.
476
  if (is_null(drupal_get_http_header('Content-Type'))) {
477
    if (!$iframe_upload) {
478
      // Standard JSON can be returned to a browser's XHR object, and to
479
      // non-browser user agents.
480
      // @see http://www.ietf.org/rfc/rfc4627.txt?number=4627
481
      drupal_add_http_header('Content-Type', 'application/json; charset=utf-8');
482
    }
483
    else {
484
      // Browser IFRAMEs expect HTML. With most other content types, Internet
485
      // Explorer presents the user with a download prompt.
486
      drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
487
    }
488
  }
489

    
490
  // Print the response.
491
  $commands = ajax_prepare_response($page_callback_result);
492
  $json = ajax_render($commands);
493
  if (!$iframe_upload) {
494
    // Standard JSON can be returned to a browser's XHR object, and to
495
    // non-browser user agents.
496
    print $json;
497
  }
498
  else {
499
    // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
500
    // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
501
    // links. This corrupts the JSON response. Protect the integrity of the
502
    // JSON data by making it the value of a textarea.
503
    // @see http://malsup.com/jquery/form/#file-upload
504
    // @see http://drupal.org/node/1009382
505
    print '<textarea>' . $json . '</textarea>';
506
  }
507

    
508
  // Perform end-of-request tasks.
509
  ajax_footer();
510
}
511

    
512
/**
513
 * Converts the return value of a page callback into an Ajax commands array.
514
 *
515
 * @param $page_callback_result
516
 *   The result of a page callback. Can be one of:
517
 *   - NULL: to indicate no content.
518
 *   - An integer menu status constant: to indicate an error condition.
519
 *   - A string of HTML content.
520
 *   - A renderable array of content.
521
 *
522
 * @return
523
 *   An Ajax commands array that can be passed to ajax_render().
524
 */
525
function ajax_prepare_response($page_callback_result) {
526
  $commands = array();
527
  if (!isset($page_callback_result)) {
528
    // Simply delivering an empty commands array is sufficient. This results
529
    // in the Ajax request being completed, but nothing being done to the page.
530
  }
531
  elseif (is_int($page_callback_result)) {
532
    switch ($page_callback_result) {
533
      case MENU_NOT_FOUND:
534
        $commands[] = ajax_command_alert(t('The requested page could not be found.'));
535
        break;
536

    
537
      case MENU_ACCESS_DENIED:
538
        $commands[] = ajax_command_alert(t('You are not authorized to access this page.'));
539
        break;
540

    
541
      case MENU_SITE_OFFLINE:
542
        $commands[] = ajax_command_alert(filter_xss_admin(variable_get('maintenance_mode_message',
543
          t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))));
544
        break;
545
    }
546
  }
547
  elseif (is_array($page_callback_result) && isset($page_callback_result['#type']) && ($page_callback_result['#type'] == 'ajax')) {
548
    // Complex Ajax callbacks can return a result that contains an error message
549
    // or a specific set of commands to send to the browser.
550
    $page_callback_result += element_info('ajax');
551
    $error = $page_callback_result['#error'];
552
    if (isset($error) && $error !== FALSE) {
553
      if ((empty($error) || $error === TRUE)) {
554
        $error = t('An error occurred while handling the request: The server received invalid input.');
555
      }
556
      $commands[] = ajax_command_alert($error);
557
    }
558
    else {
559
      $commands = $page_callback_result['#commands'];
560
    }
561
  }
562
  else {
563
    // Like normal page callbacks, simple Ajax callbacks can return HTML
564
    // content, as a string or render array. This HTML is inserted in some
565
    // relationship to #ajax['wrapper'], as determined by which jQuery DOM
566
    // manipulation method is used. The method used is specified by
567
    // #ajax['method']. The default method is 'replaceWith', which completely
568
    // replaces the old wrapper element and its content with the new HTML.
569
    $html = is_string($page_callback_result) ? $page_callback_result : drupal_render($page_callback_result);
570
    $commands[] = ajax_command_insert(NULL, $html);
571
    // Add the status messages inside the new content's wrapper element, so that
572
    // on subsequent Ajax requests, it is treated as old content.
573
    $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
574
  }
575

    
576
  return $commands;
577
}
578

    
579
/**
580
 * Performs end-of-Ajax-request tasks.
581
 *
582
 * This function is the equivalent of drupal_page_footer(), but for Ajax
583
 * requests.
584
 *
585
 * @see drupal_page_footer()
586
 */
587
function ajax_footer() {
588
  // Even for Ajax requests, invoke hook_exit() implementations. There may be
589
  // modules that need very fast Ajax responses, and therefore, run Ajax
590
  // requests with an early bootstrap.
591
  if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')) {
592
    module_invoke_all('exit');
593
  }
594

    
595
  // Commit the user session. See above comment about the possibility of this
596
  // function running without session.inc loaded.
597
  if (function_exists('drupal_session_commit')) {
598
    drupal_session_commit();
599
  }
600
}
601

    
602
/**
603
 * Form element processing handler for the #ajax form property.
604
 *
605
 * @param $element
606
 *   An associative array containing the properties of the element.
607
 *
608
 * @return
609
 *   The processed element.
610
 *
611
 * @see ajax_pre_render_element()
612
 */
613
function ajax_process_form($element, &$form_state) {
614
  $element = ajax_pre_render_element($element);
615
  if (!empty($element['#ajax_processed'])) {
616
    $form_state['cache'] = TRUE;
617
  }
618
  return $element;
619
}
620

    
621
/**
622
 * Adds Ajax information about an element to communicate with JavaScript.
623
 *
624
 * If #ajax['path'] is set on an element, this additional JavaScript is added
625
 * to the page header to attach the Ajax behaviors. See ajax.js for more
626
 * information.
627
 *
628
 * @param $element
629
 *   An associative array containing the properties of the element.
630
 *   Properties used:
631
 *   - #ajax['event']
632
 *   - #ajax['prevent']
633
 *   - #ajax['path']
634
 *   - #ajax['options']
635
 *   - #ajax['wrapper']
636
 *   - #ajax['parameters']
637
 *   - #ajax['effect']
638
 *
639
 * @return
640
 *   The processed element with the necessary JavaScript attached to it.
641
 */
642
function ajax_pre_render_element($element) {
643
  // Skip already processed elements.
644
  if (isset($element['#ajax_processed'])) {
645
    return $element;
646
  }
647
  // Initialize #ajax_processed, so we do not process this element again.
648
  $element['#ajax_processed'] = FALSE;
649

    
650
  // Nothing to do if there is neither a callback nor a path.
651
  if (!(isset($element['#ajax']['callback']) || isset($element['#ajax']['path']))) {
652
    return $element;
653
  }
654

    
655
  // Add a reasonable default event handler if none was specified.
656
  if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {
657
    switch ($element['#type']) {
658
      case 'submit':
659
      case 'button':
660
      case 'image_button':
661
        // Pressing the ENTER key within a textfield triggers the click event of
662
        // the form's first submit button. Triggering Ajax in this situation
663
        // leads to problems, like breaking autocomplete textfields, so we bind
664
        // to mousedown instead of click.
665
        // @see http://drupal.org/node/216059
666
        $element['#ajax']['event'] = 'mousedown';
667
        // Retain keyboard accessibility by setting 'keypress'. This causes
668
        // ajax.js to trigger 'event' when SPACE or ENTER are pressed while the
669
        // button has focus.
670
        $element['#ajax']['keypress'] = TRUE;
671
        // Binding to mousedown rather than click means that it is possible to
672
        // trigger a click by pressing the mouse, holding the mouse button down
673
        // until the Ajax request is complete and the button is re-enabled, and
674
        // then releasing the mouse button. Set 'prevent' so that ajax.js binds
675
        // an additional handler to prevent such a click from triggering a
676
        // non-Ajax form submission. This also prevents a textfield's ENTER
677
        // press triggering this button's non-Ajax form submission behavior.
678
        if (!isset($element['#ajax']['prevent'])) {
679
          $element['#ajax']['prevent'] = 'click';
680
        }
681
        break;
682

    
683
      case 'password':
684
      case 'textfield':
685
      case 'textarea':
686
        $element['#ajax']['event'] = 'blur';
687
        break;
688

    
689
      case 'radio':
690
      case 'checkbox':
691
      case 'select':
692
        $element['#ajax']['event'] = 'change';
693
        break;
694

    
695
      case 'link':
696
        $element['#ajax']['event'] = 'click';
697
        break;
698

    
699
      default:
700
        return $element;
701
    }
702
  }
703

    
704
  // Attach JavaScript settings to the element.
705
  if (isset($element['#ajax']['event'])) {
706
    $element['#attached']['library'][] = array('system', 'jquery.form');
707
    $element['#attached']['library'][] = array('system', 'drupal.ajax');
708

    
709
    $settings = $element['#ajax'];
710

    
711
    // Assign default settings.
712
    $settings += array(
713
      'path' => 'system/ajax',
714
      'options' => array(),
715
    );
716

    
717
    // @todo Legacy support. Remove in Drupal 8.
718
    if (isset($settings['method']) && $settings['method'] == 'replace') {
719
      $settings['method'] = 'replaceWith';
720
    }
721

    
722
    // Change path to URL.
723
    $settings['url'] = url($settings['path'], $settings['options']);
724
    unset($settings['path'], $settings['options']);
725

    
726
    // Add special data to $settings['submit'] so that when this element
727
    // triggers an Ajax submission, Drupal's form processing can determine which
728
    // element triggered it.
729
    // @see _form_element_triggered_scripted_submission()
730
    if (isset($settings['trigger_as'])) {
731
      // An element can add a 'trigger_as' key within #ajax to make the element
732
      // submit as though another one (for example, a non-button can use this
733
      // to submit the form as though a button were clicked). When using this,
734
      // the 'name' key is always required to identify the element to trigger
735
      // as. The 'value' key is optional, and only needed when multiple elements
736
      // share the same name, which is commonly the case for buttons.
737
      $settings['submit']['_triggering_element_name'] = $settings['trigger_as']['name'];
738
      if (isset($settings['trigger_as']['value'])) {
739
        $settings['submit']['_triggering_element_value'] = $settings['trigger_as']['value'];
740
      }
741
      unset($settings['trigger_as']);
742
    }
743
    elseif (isset($element['#name'])) {
744
      // Most of the time, elements can submit as themselves, in which case the
745
      // 'trigger_as' key isn't needed, and the element's name is used.
746
      $settings['submit']['_triggering_element_name'] = $element['#name'];
747
      // If the element is a (non-image) button, its name may not identify it
748
      // uniquely, in which case a match on value is also needed.
749
      // @see _form_button_was_clicked()
750
      if (isset($element['#button_type']) && empty($element['#has_garbage_value'])) {
751
        $settings['submit']['_triggering_element_value'] = $element['#value'];
752
      }
753
    }
754

    
755
    // Convert a simple #ajax['progress'] string into an array.
756
    if (isset($settings['progress']) && is_string($settings['progress'])) {
757
      $settings['progress'] = array('type' => $settings['progress']);
758
    }
759
    // Change progress path to a full URL.
760
    if (isset($settings['progress']['path'])) {
761
      $settings['progress']['url'] = url($settings['progress']['path']);
762
      unset($settings['progress']['path']);
763
    }
764

    
765
    $element['#attached']['js'][] = array(
766
      'type' => 'setting',
767
      'data' => array('ajax' => array($element['#id'] => $settings)),
768
    );
769

    
770
    // Indicate that Ajax processing was successful.
771
    $element['#ajax_processed'] = TRUE;
772
  }
773
  return $element;
774
}
775

    
776
/**
777
 * @} End of "defgroup ajax".
778
 */
779

    
780
/**
781
 * @defgroup ajax_commands Ajax framework commands
782
 * @{
783
 * Functions to create various Ajax commands.
784
 *
785
 * These functions can be used to create arrays for use with the
786
 * ajax_render() function.
787
 */
788

    
789
/**
790
 * Creates a Drupal Ajax 'alert' command.
791
 *
792
 * The 'alert' command instructs the client to display a JavaScript alert
793
 * dialog box.
794
 *
795
 * This command is implemented by Drupal.ajax.prototype.commands.alert()
796
 * defined in misc/ajax.js.
797
 *
798
 * @param $text
799
 *   The message string to display to the user.
800
 *
801
 * @return
802
 *   An array suitable for use with the ajax_render() function.
803
 */
804
function ajax_command_alert($text) {
805
  return array(
806
    'command' => 'alert',
807
    'text' => $text,
808
  );
809
}
810

    
811
/**
812
 * Creates a Drupal Ajax 'insert' command using the method in #ajax['method'].
813
 *
814
 * This command instructs the client to insert the given HTML using whichever
815
 * jQuery DOM manipulation method has been specified in the #ajax['method']
816
 * variable of the element that triggered the request.
817
 *
818
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
819
 * defined in misc/ajax.js.
820
 *
821
 * @param $selector
822
 *   A jQuery selector string. If the command is a response to a request from
823
 *   an #ajax form element then this value can be NULL.
824
 * @param $html
825
 *   The data to use with the jQuery method.
826
 * @param $settings
827
 *   An optional array of settings that will be used for this command only.
828
 *
829
 * @return
830
 *   An array suitable for use with the ajax_render() function.
831
 */
832
function ajax_command_insert($selector, $html, $settings = NULL) {
833
  return array(
834
    'command' => 'insert',
835
    'method' => NULL,
836
    'selector' => $selector,
837
    'data' => $html,
838
    'settings' => $settings,
839
  );
840
}
841

    
842
/**
843
 * Creates a Drupal Ajax 'insert/replaceWith' command.
844
 *
845
 * The 'insert/replaceWith' command instructs the client to use jQuery's
846
 * replaceWith() method to replace each element matched matched by the given
847
 * selector with the given HTML.
848
 *
849
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
850
 * defined in misc/ajax.js.
851
 *
852
 * @param $selector
853
 *   A jQuery selector string. If the command is a response to a request from
854
 *   an #ajax form element then this value can be NULL.
855
 * @param $html
856
 *   The data to use with the jQuery replaceWith() method.
857
 * @param $settings
858
 *   An optional array of settings that will be used for this command only.
859
 *
860
 * @return
861
 *   An array suitable for use with the ajax_render() function.
862
 *
863
 * See
864
 * @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
865
 */
866
function ajax_command_replace($selector, $html, $settings = NULL) {
867
  return array(
868
    'command' => 'insert',
869
    'method' => 'replaceWith',
870
    'selector' => $selector,
871
    'data' => $html,
872
    'settings' => $settings,
873
  );
874
}
875

    
876
/**
877
 * Creates a Drupal Ajax 'insert/html' command.
878
 *
879
 * The 'insert/html' command instructs the client to use jQuery's html()
880
 * method to set the HTML content of each element matched by the given
881
 * selector while leaving the outer tags intact.
882
 *
883
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
884
 * defined in misc/ajax.js.
885
 *
886
 * @param $selector
887
 *   A jQuery selector string. If the command is a response to a request from
888
 *   an #ajax form element then this value can be NULL.
889
 * @param $html
890
 *   The data to use with the jQuery html() method.
891
 * @param $settings
892
 *   An optional array of settings that will be used for this command only.
893
 *
894
 * @return
895
 *   An array suitable for use with the ajax_render() function.
896
 *
897
 * @see http://docs.jquery.com/Attributes/html#val
898
 */
899
function ajax_command_html($selector, $html, $settings = NULL) {
900
  return array(
901
    'command' => 'insert',
902
    'method' => 'html',
903
    'selector' => $selector,
904
    'data' => $html,
905
    'settings' => $settings,
906
  );
907
}
908

    
909
/**
910
 * Creates a Drupal Ajax 'insert/prepend' command.
911
 *
912
 * The 'insert/prepend' command instructs the client to use jQuery's prepend()
913
 * method to prepend the given HTML content to the inside each element matched
914
 * by the given selector.
915
 *
916
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
917
 * defined in misc/ajax.js.
918
 *
919
 * @param $selector
920
 *   A jQuery selector string. If the command is a response to a request from
921
 *   an #ajax form element then this value can be NULL.
922
 * @param $html
923
 *   The data to use with the jQuery prepend() method.
924
 * @param $settings
925
 *   An optional array of settings that will be used for this command only.
926
 *
927
 * @return
928
 *   An array suitable for use with the ajax_render() function.
929
 *
930
 * @see http://docs.jquery.com/Manipulation/prepend#content
931
 */
932
function ajax_command_prepend($selector, $html, $settings = NULL) {
933
  return array(
934
    'command' => 'insert',
935
    'method' => 'prepend',
936
    'selector' => $selector,
937
    'data' => $html,
938
    'settings' => $settings,
939
  );
940
}
941

    
942
/**
943
 * Creates a Drupal Ajax 'insert/append' command.
944
 *
945
 * The 'insert/append' command instructs the client to use jQuery's append()
946
 * method to append the given HTML content to the inside of each element matched
947
 * by the given selector.
948
 *
949
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
950
 * defined in misc/ajax.js.
951
 *
952
 * @param $selector
953
 *   A jQuery selector string. If the command is a response to a request from
954
 *   an #ajax form element then this value can be NULL.
955
 * @param $html
956
 *   The data to use with the jQuery append() method.
957
 * @param $settings
958
 *   An optional array of settings that will be used for this command only.
959
 *
960
 * @return
961
 *   An array suitable for use with the ajax_render() function.
962
 *
963
 * @see http://docs.jquery.com/Manipulation/append#content
964
 */
965
function ajax_command_append($selector, $html, $settings = NULL) {
966
  return array(
967
    'command' => 'insert',
968
    'method' => 'append',
969
    'selector' => $selector,
970
    'data' => $html,
971
    'settings' => $settings,
972
  );
973
}
974

    
975
/**
976
 * Creates a Drupal Ajax 'insert/after' command.
977
 *
978
 * The 'insert/after' command instructs the client to use jQuery's after()
979
 * method to insert the given HTML content after each element matched by
980
 * the given selector.
981
 *
982
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
983
 * defined in misc/ajax.js.
984
 *
985
 * @param $selector
986
 *   A jQuery selector string. If the command is a response to a request from
987
 *   an #ajax form element then this value can be NULL.
988
 * @param $html
989
 *   The data to use with the jQuery after() method.
990
 * @param $settings
991
 *   An optional array of settings that will be used for this command only.
992
 *
993
 * @return
994
 *   An array suitable for use with the ajax_render() function.
995
 *
996
 * @see http://docs.jquery.com/Manipulation/after#content
997
 */
998
function ajax_command_after($selector, $html, $settings = NULL) {
999
  return array(
1000
    'command' => 'insert',
1001
    'method' => 'after',
1002
    'selector' => $selector,
1003
    'data' => $html,
1004
    'settings' => $settings,
1005
  );
1006
}
1007

    
1008
/**
1009
 * Creates a Drupal Ajax 'insert/before' command.
1010
 *
1011
 * The 'insert/before' command instructs the client to use jQuery's before()
1012
 * method to insert the given HTML content before each of elements matched by
1013
 * the given selector.
1014
 *
1015
 * This command is implemented by Drupal.ajax.prototype.commands.insert()
1016
 * defined in misc/ajax.js.
1017
 *
1018
 * @param $selector
1019
 *   A jQuery selector string. If the command is a response to a request from
1020
 *   an #ajax form element then this value can be NULL.
1021
 * @param $html
1022
 *   The data to use with the jQuery before() method.
1023
 * @param $settings
1024
 *   An optional array of settings that will be used for this command only.
1025
 *
1026
 * @return
1027
 *   An array suitable for use with the ajax_render() function.
1028
 *
1029
 * @see http://docs.jquery.com/Manipulation/before#content
1030
 */
1031
function ajax_command_before($selector, $html, $settings = NULL) {
1032
  return array(
1033
    'command' => 'insert',
1034
    'method' => 'before',
1035
    'selector' => $selector,
1036
    'data' => $html,
1037
    'settings' => $settings,
1038
  );
1039
}
1040

    
1041
/**
1042
 * Creates a Drupal Ajax 'remove' command.
1043
 *
1044
 * The 'remove' command instructs the client to use jQuery's remove() method
1045
 * to remove each of elements matched by the given selector, and everything
1046
 * within them.
1047
 *
1048
 * This command is implemented by Drupal.ajax.prototype.commands.remove()
1049
 * defined in misc/ajax.js.
1050
 *
1051
 * @param $selector
1052
 *   A jQuery selector string. If the command is a response to a request from
1053
 *   an #ajax form element then this value can be NULL.
1054
 *
1055
 * @return
1056
 *   An array suitable for use with the ajax_render() function.
1057
 *
1058
 * @see http://docs.jquery.com/Manipulation/remove#expr
1059
 */
1060
function ajax_command_remove($selector) {
1061
  return array(
1062
    'command' => 'remove',
1063
    'selector' => $selector,
1064
  );
1065
}
1066

    
1067
/**
1068
 * Creates a Drupal Ajax 'changed' command.
1069
 *
1070
 * This command instructs the client to mark each of the elements matched by the
1071
 * given selector as 'ajax-changed'.
1072
 *
1073
 * This command is implemented by Drupal.ajax.prototype.commands.changed()
1074
 * defined in misc/ajax.js.
1075
 *
1076
 * @param $selector
1077
 *   A jQuery selector string. If the command is a response to a request from
1078
 *   an #ajax form element then this value can be NULL.
1079
 * @param $asterisk
1080
 *   An optional CSS selector which must be inside $selector. If specified,
1081
 *   an asterisk will be appended to the HTML inside the $asterisk selector.
1082
 *
1083
 * @return
1084
 *   An array suitable for use with the ajax_render() function.
1085
 */
1086
function ajax_command_changed($selector, $asterisk = '') {
1087
  return array(
1088
    'command' => 'changed',
1089
    'selector' => $selector,
1090
    'asterisk' => $asterisk,
1091
  );
1092
}
1093

    
1094
/**
1095
 * Creates a Drupal Ajax 'css' command.
1096
 *
1097
 * The 'css' command will instruct the client to use the jQuery css() method
1098
 * to apply the CSS arguments to elements matched by the given selector.
1099
 *
1100
 * This command is implemented by Drupal.ajax.prototype.commands.css()
1101
 * defined in misc/ajax.js.
1102
 *
1103
 * @param $selector
1104
 *   A jQuery selector string. If the command is a response to a request from
1105
 *   an #ajax form element then this value can be NULL.
1106
 * @param $argument
1107
 *   An array of key/value pairs to set in the CSS for the selector.
1108
 *
1109
 * @return
1110
 *   An array suitable for use with the ajax_render() function.
1111
 *
1112
 * @see http://docs.jquery.com/CSS/css#properties
1113
 */
1114
function ajax_command_css($selector, $argument) {
1115
  return array(
1116
    'command' => 'css',
1117
    'selector' => $selector,
1118
    'argument' => $argument,
1119
  );
1120
}
1121

    
1122
/**
1123
 * Creates a Drupal Ajax 'settings' command.
1124
 *
1125
 * The 'settings' command instructs the client either to use the given array as
1126
 * the settings for ajax-loaded content or to extend Drupal.settings with the
1127
 * given array, depending on the value of the $merge parameter.
1128
 *
1129
 * This command is implemented by Drupal.ajax.prototype.commands.settings()
1130
 * defined in misc/ajax.js.
1131
 *
1132
 * @param $argument
1133
 *   An array of key/value pairs to add to the settings. This will be utilized
1134
 *   for all commands after this if they do not include their own settings
1135
 *   array.
1136
 * @param $merge
1137
 *   Whether or not the passed settings in $argument should be merged into the
1138
 *   global Drupal.settings on the page. By default (FALSE), the settings that
1139
 *   are passed to Drupal.attachBehaviors will not include the global
1140
 *   Drupal.settings.
1141
 *
1142
 * @return
1143
 *   An array suitable for use with the ajax_render() function.
1144
 */
1145
function ajax_command_settings($argument, $merge = FALSE) {
1146
  return array(
1147
    'command' => 'settings',
1148
    'settings' => $argument,
1149
    'merge' => $merge,
1150
  );
1151
}
1152

    
1153
/**
1154
 * Creates a Drupal Ajax 'data' command.
1155
 *
1156
 * The 'data' command instructs the client to attach the name=value pair of
1157
 * data to the selector via jQuery's data cache.
1158
 *
1159
 * This command is implemented by Drupal.ajax.prototype.commands.data()
1160
 * defined in misc/ajax.js.
1161
 *
1162
 * @param $selector
1163
 *   A jQuery selector string. If the command is a response to a request from
1164
 *   an #ajax form element then this value can be NULL.
1165
 * @param $name
1166
 *   The name or key (in the key value pair) of the data attached to this
1167
 *   selector.
1168
 * @param $value
1169
 *   The value of the data. Not just limited to strings can be any format.
1170
 *
1171
 * @return
1172
 *   An array suitable for use with the ajax_render() function.
1173
 *
1174
 * @see http://docs.jquery.com/Core/data#namevalue
1175
 */
1176
function ajax_command_data($selector, $name, $value) {
1177
  return array(
1178
    'command' => 'data',
1179
    'selector' => $selector,
1180
    'name' => $name,
1181
    'value' => $value,
1182
  );
1183
}
1184

    
1185
/**
1186
 * Creates a Drupal Ajax 'invoke' command.
1187
 *
1188
 * The 'invoke' command will instruct the client to invoke the given jQuery
1189
 * method with the supplied arguments on the elements matched by the given
1190
 * selector. Intended for simple jQuery commands, such as attr(), addClass(),
1191
 * removeClass(), toggleClass(), etc.
1192
 *
1193
 * This command is implemented by Drupal.ajax.prototype.commands.invoke()
1194
 * defined in misc/ajax.js.
1195
 *
1196
 * @param $selector
1197
 *   A jQuery selector string. If the command is a response to a request from
1198
 *   an #ajax form element then this value can be NULL.
1199
 * @param $method
1200
 *   The jQuery method to invoke.
1201
 * @param $arguments
1202
 *   (optional) A list of arguments to the jQuery $method, if any.
1203
 *
1204
 * @return
1205
 *   An array suitable for use with the ajax_render() function.
1206
 */
1207
function ajax_command_invoke($selector, $method, array $arguments = array()) {
1208
  return array(
1209
    'command' => 'invoke',
1210
    'selector' => $selector,
1211
    'method' => $method,
1212
    'arguments' => $arguments,
1213
  );
1214
}
1215

    
1216
/**
1217
 * Creates a Drupal Ajax 'restripe' command.
1218
 *
1219
 * The 'restripe' command instructs the client to restripe a table. This is
1220
 * usually used after a table has been modified by a replace or append command.
1221
 *
1222
 * This command is implemented by Drupal.ajax.prototype.commands.restripe()
1223
 * defined in misc/ajax.js.
1224
 *
1225
 * @param $selector
1226
 *   A jQuery selector string.
1227
 *
1228
 * @return
1229
 *   An array suitable for use with the ajax_render() function.
1230
 */
1231
function ajax_command_restripe($selector) {
1232
  return array(
1233
    'command' => 'restripe',
1234
    'selector' => $selector,
1235
  );
1236
}
1237

    
1238
/**
1239
 * Creates a Drupal Ajax 'update_build_id' command.
1240
 *
1241
 * This command updates the value of a hidden form_build_id input element on a
1242
 * form. It requires the form passed in to have keys for both the old build ID
1243
 * in #build_id_old and the new build ID in #build_id.
1244
 *
1245
 * The primary use case for this Ajax command is to serve a new build ID to a
1246
 * form served from the cache to an anonymous user, preventing one anonymous
1247
 * user from accessing the form state of another anonymous users on Ajax enabled
1248
 * forms.
1249
 *
1250
 * @param $form
1251
 *   The form array representing the form whose build ID should be updated.
1252
 */
1253
function ajax_command_update_build_id($form) {
1254
  return array(
1255
    'command' => 'updateBuildId',
1256
    'old' => $form['#build_id_old'],
1257
    'new' => $form['#build_id'],
1258
  );
1259
}
1260

    
1261
/**
1262
 * Creates a Drupal Ajax 'add_css' command.
1263
 *
1264
 * This method will add css via ajax in a cross-browser compatible way.
1265
 *
1266
 * This command is implemented by Drupal.ajax.prototype.commands.add_css()
1267
 * defined in misc/ajax.js.
1268
 *
1269
 * @param $styles
1270
 *   A string that contains the styles to be added.
1271
 *
1272
 * @return
1273
 *   An array suitable for use with the ajax_render() function.
1274
 *
1275
 * @see misc/ajax.js
1276
 */
1277
function ajax_command_add_css($styles) {
1278
  return array(
1279
    'command' => 'add_css',
1280
    'data' => $styles,
1281
  );
1282
}