Index: includes/ajax.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/ajax.inc,v retrieving revision 1.29 diff -u -p -r1.29 ajax.inc --- includes/ajax.inc 31 Mar 2010 19:34:56 -0000 1.29 +++ includes/ajax.inc 8 Apr 2010 20:26:21 -0000 @@ -193,11 +193,24 @@ * functions. */ function ajax_render($commands = array()) { + // Update the page state and determine the new CSS and JavaScript that needs + // to be loaded. If there is no prior page state, something is wrong. + drupal_static_reset('drupal_page_state_id'); + if ($prior_page_state = drupal_get_page_state()) { + $new_page_state = $prior_page_state; + drupal_update_page_state($new_page_state); + $new_css = array_diff_key($new_page_state['css'], $prior_page_state['css']); + $new_js = array_diff_key($new_page_state['js'], $prior_page_state['js']); + unset($new_js['settings']); + } + // Automatically extract any 'settings' added via drupal_add_js() and make // them the first command. $scripts = drupal_add_js(NULL, NULL); if (!empty($scripts['settings'])) { array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $scripts['settings']['data']))); + $commands[0]['styles'] = isset($new_css) ? drupal_get_css($new_css) : ''; + $commands[0]['scripts'] = isset($new_js) ? drupal_get_js('header', $new_js) : ''; } // Allow modules to alter any AJAX response. @@ -518,6 +531,7 @@ function ajax_process_form($element, &$f $form_state['cache'] = TRUE; } + drupal_page_state_enabled(TRUE); return $element; } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1145 diff -u -p -r1.1145 common.inc --- includes/common.inc 7 Apr 2010 17:30:43 -0000 1.1145 +++ includes/common.inc 8 Apr 2010 20:26:22 -0000 @@ -6647,3 +6647,79 @@ function drupal_get_updaters() { } return $updaters; } + +/** + * + */ +function drupal_page_state_id($new_id = NULL) { + $id = &drupal_static(__FUNCTION__); + if (isset($new_id)) { + $id = $new_id; + } + // Security care must always be taken when using raw $_POST data. + elseif (!isset($id) && isset($_POST['_page_state_id']) && is_string($_POST['_page_state_id']) && preg_match('/^[0-9a-f]{32}$/', $_POST['_page_state_id'])) { + $id = $_POST['_page_state_id']; + } + return $id; +} + +/** + * + */ +function drupal_get_page_state($id = NULL) { + if (!isset($id)) { + $id = drupal_page_state_id(); + } + if ($id && $cached = cache_get('page_state_' . $id, 'cache_form')) { + $page_state = $cached->data; + } + else { + $page_state = array(); + } + return $page_state; +} + +/** + * + */ +function drupal_page_state_enabled($enable = NULL) { + $enabled = &drupal_static(__FUNCTION__); + if (isset($enable)) { + $enabled = $enable; + } + elseif (!isset($enabled)) { + $enabled = (bool) drupal_page_state_id(); + } + return $enabled; +} + +/** + * + */ +function drupal_update_page_state(&$page_state = NULL) { + if (!isset($page_state)) { + $page_state = drupal_get_page_state(); + } + foreach (module_implements('page_state_update') as $module) { + $function = $module . '_page_state_update'; + $function($page_state); + } + + // There can be many users viewing pages that need their page state persisted. + // We don't want each one having a randomly generated id, as that would flood + // the cache bin with many records. By basing the id on the page state + // content, we only need as many records as there are unique states. + $page_state_id = md5(serialize($page_state)); + + // $page_state serves a similar purpose to $form_state. Therefore, persist it + // in the same cache bin with the same 6 hour life time. + cache_set('page_state_' . $page_state_id, $page_state, 'cache_form', REQUEST_TIME + 21600); + + // The browser will need to know the new page state id. + drupal_add_js(array('pageStateId' => $page_state_id), 'setting'); + + // Future calls to drupal_page_state_id() during this page request should + // retrieve the new id. + drupal_page_state_id($page_state_id); + return $page_state_id; +} Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.588 diff -u -p -r1.588 theme.inc --- includes/theme.inc 6 Apr 2010 17:56:40 -0000 1.588 +++ includes/theme.inc 8 Apr 2010 20:26:23 -0000 @@ -2390,6 +2390,13 @@ function template_process_html(&$variabl $variables['head'] = drupal_get_html_head(); $variables['css'] = drupal_add_css(); $variables['styles'] = drupal_get_css(); + + // If necessary, persist page state information to enable AJAX functionality. + // This updates a JavaScript variable, so do this before calling + // drupal_get_js(). + if (drupal_page_state_enabled()) { + drupal_update_page_state(); + } $variables['scripts'] = drupal_get_js(); } Index: misc/ajax.js =================================================================== RCS file: /cvs/drupal/drupal/misc/ajax.js,v retrieving revision 1.15 diff -u -p -r1.15 ajax.js --- misc/ajax.js 7 Apr 2010 17:30:43 -0000 1.15 +++ misc/ajax.js 8 Apr 2010 20:26:24 -0000 @@ -200,6 +200,9 @@ Drupal.ajax.prototype.beforeSubmit = fun // Disable the element that received the change. $(this.element).addClass('progress-disabled').attr('disabled', true); + // Give the server information about the page state. + form_values.push({ name: '_page_state_id', value: Drupal.settings.pageStateId }); + // Prevent duplicate HTML ids in the returned markup. // @see drupal_html_id() $('[id]').each(function () { @@ -408,6 +411,12 @@ Drupal.ajax.prototype.commands = { * Command to set the settings that will be used for other commands in this response. */ settings: function (ajax, response, status) { + if (response.styles) { + $('head').append(response.styles); + } + if (response.scripts) { + $('head').append(response.scripts); + } if (response.merge) { $.extend(true, Drupal.settings, response.settings); } Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.918 diff -u -p -r1.918 system.module --- modules/system/system.module 7 Apr 2010 16:35:03 -0000 1.918 +++ modules/system/system.module 8 Apr 2010 20:26:29 -0000 @@ -3667,3 +3667,15 @@ function system_admin_paths() { ); return $paths; } + +/** + * Implements hook_page_state_update(). + */ +function system_page_state_update(&$page_state) { + $page_state += array( + 'css' => array(), + 'js' => array(), + ); + $page_state['css'] = array_merge($page_state['css'], drupal_add_css()); + $page_state['js'] = array_merge($page_state['js'], drupal_add_js()); +}