Index: includes/view.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/view.inc,v
retrieving revision 1.165
diff -u -p -r1.165 view.inc
--- includes/view.inc	25 Jun 2009 22:06:55 -0000	1.165
+++ includes/view.inc	27 Aug 2009 22:46:33 -0000
@@ -28,6 +28,7 @@ class view extends views_db_object {
   var $build_info = array();
 
   var $use_ajax = FALSE;
+  var $ajax_refresh_interval = 0;
 
   // Where the results of a query will go.
   var $result = array();
@@ -133,6 +134,14 @@ class view extends views_db_object {
   }
 
   /**
+   * The interval, in seconds, at which AJAX views should automatically
+   * refresh themselves. If set to zero (none), no AJAX refreshing will occur.
+   */
+  function set_ajax_refresh_interval($interval) {
+    $this->ajax_refresh_interval = $interval;
+  }
+
+  /**
    * Set the exposed filters input to an array. If unset they will be taken
    * from $_GET when the time comes.
    */
Index: js/ajax_view.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/js/ajax_view.js,v
retrieving revision 1.19
diff -u -p -r1.19 ajax_view.js
--- js/ajax_view.js	26 Jul 2009 15:07:25 -0000	1.19
+++ js/ajax_view.js	27 Aug 2009 22:46:33 -0000
@@ -9,6 +9,18 @@
 Drupal.Views.Ajax = Drupal.Views.Ajax || {};
 
 /**
+ * Helper function to return the (potentially nested) ajax callback.
+ */
+Drupal.Views.Ajax.getAjaxPath = function () {
+  var ajaxPath = Drupal.settings.views.ajax_path;
+  // If there are multiple views this might've ended up showing up multiple times.
+  if (ajaxPath.constructor.toString().indexOf("Array") != -1) {
+    ajaxPath = ajaxPath[0];
+  }
+  return ajaxPath;
+}
+    
+/**
  * An ajax responder that accepts a packet of JSON data and acts appropriately.
  *
  * The following fields control behavior.
@@ -37,16 +49,53 @@ Drupal.Views.Ajax.ajaxViewResponse = fun
 };
 
 /**
+ * Initiate an ajax refresh of a view.
+ */
+Drupal.Views.Ajax.ajaxViewRefresh = function(domId, selector) {
+  var target = $(selector).get(0);
+  var viewData = Drupal.settings.views.ajaxViews[domId];
+  // Fetch the current view data.
+  $.extend(
+    viewData,
+    Drupal.settings.ajaxViewsCurrent[domId]
+  );
+  var ajaxPath = Drupal.Views.Ajax.getAjaxPath();
+  $.ajax({
+    url: ajaxPath,
+    type: 'GET',
+    data: viewData,
+    success: function(response) {
+      $(this).removeClass('views-throbbing');
+      // Scroll to the top of the view. This will allow users
+      // to browse newly loaded content after e.g. clicking a pager
+      // link.
+      var offset = $(target).offset();
+      // Only scroll upward
+      if (offset.top - 10 < $(window).scrollTop()) {
+        $('html,body').animate({scrollTop: (offset.top - 10)}, 500);
+      }
+      // Call all callbacks.
+      if (response.__callbacks) {
+        $.each(response.__callbacks, function(i, callback) {
+          eval(callback)(target, response);
+        });
+      }
+    },
+    error: function() { $(this).removeClass('views-throbbing'); alert(Drupal.t("An error occurred at @path.", {'@path': ajaxPath})); },
+    dataType: 'json'
+  });
+};
+
+/**
  * Ajax behavior for views. 
  */
 Drupal.behaviors.ViewsAjaxView = function() {
   if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) {
-    var ajax_path = Drupal.settings.views.ajax_path;
-    // If there are multiple views this might've ended up showing up multiple times.
-    if (ajax_path.constructor.toString().indexOf("Array") != -1) {
-      ajax_path = ajax_path[0];
-    }
-    $.each(Drupal.settings.views.ajaxViews, function(i, settings) {
+    // Set an object for storing the current view. Do so directly off Drupal.settings to avoid
+    // losing data when new data are merged into the existing Drupal.settings object.
+    Drupal.settings.ajaxViewsCurrent = Drupal.settings.ajaxViewsCurrent || {};
+    var ajaxPath = Drupal.Views.Ajax.getAjaxPath();
+    $.each(Drupal.settings.views.ajaxViews, function(view_dom_id, settings) {
       var view = '.view-dom-id-' + settings.view_dom_id;
       if (!$(view).size()) {
         // Backward compatibility: if 'views-view.tpl.php' is old and doesn't
@@ -68,15 +117,18 @@ Drupal.behaviors.ViewsAjaxView = functio
         // ajaxSubmit doesn't accept a data argument, so we have to
         // pass additional fields this way.
         $.each(settings, function(key, setting) {
-          $(form).append('<input type="hidden" name="'+ key + '" value="'+ setting +'"/>');
+          $(form).append('<input class="views-hidden" type="hidden" name="'+ key + '" value="'+ setting +'"/>');
         });
       })
       .addClass('views-processed')
       .submit(function () {
         $('input[type=submit], button', this).after('<span class="views-throbbing">&nbsp</span>');
+        // Cache the data being sent so that it can be used in subsequent refreshes.
+        var fieldsQueryString = $(this).formSerialize();
+        Drupal.settings.ajaxViewsCurrent[settings.view_dom_id] = Drupal.Views.parseQueryString(fieldsQueryString);
         var object = this;
         $(this).ajaxSubmit({
-          url: ajax_path,
+          url: ajaxPath,
           type: 'GET',
           success: function(response) {
             // Call all callbacks.
@@ -87,7 +139,7 @@ Drupal.behaviors.ViewsAjaxView = functio
               $('.views-throbbing', object).remove();
             }
           },
-          error: function() { alert(Drupal.t("An error occurred at @path.", {'@path': ajax_path})); $('.views-throbbing', object).remove(); },
+          error: function() { alert(Drupal.t("An error occurred at @path.", {'@path': ajaxPath})); $('.views-throbbing', object).remove(); },
           dataType: 'json'
         });
 
@@ -103,53 +155,36 @@ Drupal.behaviors.ViewsAjaxView = functio
           return !$(this).parents('.view').size();
         })
         .each(function() {
-          // Set a reference that will work in subsequent calls.
-          var target = this;
+          // If this view is set to automatically refresh, schedule refreshing.
+          // We only need to set the timeout once because it will be reset
+          // when the new view loads and this behavior reattaches.
+          if (settings.ajax_refresh_interval && parseInt(settings.ajax_refresh_interval)) {
+            var timeoutId = window.setTimeout('Drupal.Views.Ajax.ajaxViewRefresh("' + settings.view_dom_id + '", "' + view + '")', parseInt(settings.ajax_refresh_interval));
+            $(window).unload(function () {clearTimeout(timeoutId);});
+          }
           $(this)
             .addClass('views-processed')
             // Process pager, tablesort, and attachment summary links.
             .find('ul.pager > li > a, th.views-field a, .attachment .views-summary a')
-            .each(function () {
-              var viewData = {};
-              // Construct an object using the settings defaults and then overriding
-              // with data specific to the link.
+            .click(function () {
+              // Override the settings defaults with data specific to the link.
               $.extend(
-                viewData,
-                settings,
+                Drupal.settings.views.ajaxViews[settings.view_dom_id],
                 Drupal.Views.parseQueryString($(this).attr('href')),
                 // Extract argument data from the URL.
                 Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path)
               );
-              $(this).click(function () {
-                $(this).addClass('views-throbbing');
-                $.ajax({
-                  url: ajax_path,
-                  type: 'GET',
-                  data: viewData,
-                  success: function(response) {
-                    $(this).removeClass('views-throbbing');
-                    // Scroll to the top of the view. This will allow users
-                    // to browse newly loaded content after e.g. clicking a pager
-                    // link.
-                    var offset = $(target).offset();
-                    // Only scroll upward
-                    if (offset.top - 10 < $(window).scrollTop()) {
-                      $('html,body').animate({scrollTop: (offset.top - 10)}, 500);
-                    }
-                    // Call all callbacks.
-                    if (response.__callbacks) {
-                      $.each(response.__callbacks, function(i, callback) {
-                        eval(callback)(target, response);
-                      });
-                    }
-                  },
-                  error: function() { $(this).removeClass('views-throbbing'); alert(Drupal.t("An error occurred at @path.", {'@path': ajax_path})); },
-                  dataType: 'json'
-                });
-
-                return false;
-              });
-            }); // .each function () {
+              // Cache the current view data, including the new settings extracted from the link.
+              Drupal.settings.ajaxViewsCurrent[settings.view_dom_id] = Drupal.settings.views.ajaxViews[settings.view_dom_id];
+              // Cancel any pending refresh.
+              if (timeoutId) {
+                clearTimeout(timeoutId);
+              }
+              $(this).addClass('views-throbbing');
+              // Refresh the view.
+              Drupal.Views.Ajax.ajaxViewRefresh(settings.view_dom_id, view);
+              return false;
+            });
       }); // $view.filter().each
     }); // .each Drupal.settings.views.ajaxViews
   } // if
Index: plugins/views_plugin_display.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/views_plugin_display.inc,v
retrieving revision 1.25
diff -u -p -r1.25 views_plugin_display.inc
--- plugins/views_plugin_display.inc	26 Jun 2009 00:23:42 -0000	1.25
+++ plugins/views_plugin_display.inc	27 Aug 2009 22:46:34 -0000
@@ -163,7 +163,7 @@ class views_plugin_display extends views
       'header' => array('header', 'header_format', 'header_empty'),
       'footer' => array('footer', 'footer_format', 'footer_empty'),
       'empty' => array('empty', 'empty_format'),
-      'use_ajax' => array('use_ajax'),
+      'use_ajax' => array('use_ajax', 'ajax_refresh_interval'),
       'items_per_page' => array('items_per_page', 'offset', 'use_pager', 'pager_element'),
       'use_pager' => array('items_per_page', 'offset', 'use_pager', 'pager_element'),
       'use_more' => array('use_more', 'use_more_text'),
@@ -235,6 +235,7 @@ class views_plugin_display extends views
           'empty_format' => TRUE,
 
           'use_ajax' => TRUE,
+          'ajax_refresh_interval' => TRUE,
           'items_per_page' => TRUE,
           'offset' => TRUE,
           'use_pager' => TRUE,
@@ -322,6 +323,9 @@ class views_plugin_display extends views
       'use_ajax' => array(
         'default' => FALSE,
       ),
+      'ajax_refresh_interval' => array(
+        'default' => 0,
+      ),
       'items_per_page' => array(
         'default' => 10,
       ),
@@ -669,7 +673,7 @@ class views_plugin_display extends views
         'category' => 'basic',
         'title' => t('Use AJAX'),
         'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
-        'desc' => t('Change whether or not this display will use AJAX.'),
+        'desc' => t('Change whether and how this display will use AJAX.'),
       );
     }
 
@@ -860,6 +864,14 @@ class views_plugin_display extends views
           '#options' => array(1 => t('Yes'), 0 => t('No')),
           '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
         );
+        $period = drupal_map_assoc(array(1, 2, 3, 5, 7, 10, 15, 20, 30, 45, 60, 180, 300), 'format_interval');
+        $form['ajax_refresh_interval'] = array(
+          '#type' => 'select',
+          '#title' => t('Refresh'),
+          '#description' => t('Set an interval at which this view should automatically refresh, or "None" for no automatic refreshing.'),
+          '#options' => array(0 => t('None')) + $period,
+          '#default_value' => $this->get_option('ajax_refresh_interval'),
+        );
         break;
       case 'use_pager':
         $form['#title'] .= t('Use a pager for this view');
@@ -1479,6 +1491,7 @@ class views_plugin_display extends views
         break;
       case 'use_ajax':
         $this->set_option($section, (bool)$form_state['values'][$section]);
+        $this->set_option('ajax_refresh_interval', intval($form_state['values']['ajax_refresh_interval']));
         break;
       case 'use_pager':
         $this->set_option($section, $form_state['values'][$section]);
@@ -1758,6 +1771,7 @@ class views_plugin_display extends views
    */
   function pre_execute() {
     $this->view->set_use_ajax($this->use_ajax());
+    $this->view->set_ajax_refresh_interval($this->get_option('ajax_refresh_interval'));
     // Copy pager information from the display.
     $this->view->set_use_pager($this->use_pager());
     $this->view->set_pager_element($this->get_option('pager_element'));
Index: theme/theme.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/theme/theme.inc,v
retrieving revision 1.79
diff -u -p -r1.79 theme.inc
--- theme/theme.inc	11 Aug 2009 22:52:21 -0000	1.79
+++ theme/theme.inc	27 Aug 2009 22:46:35 -0000
@@ -134,7 +134,7 @@ function template_preprocess_views_view(
       'views' => array(
         'ajax_path' => url('views/ajax'),
         'ajaxViews' => array(
-          array(
+          $vars['dom_id'] => array(
             'view_name' => $view->name,
             'view_display_id' => $view->current_display,
             'view_args' => implode('/', $view->args),
@@ -146,6 +146,8 @@ function template_preprocess_views_view(
             // To fit multiple views on a page, the programmer may have
             // overridden the display's pager_element.
             'pager_element' => $view->pager['element'],
+            // Data are in seconds, but the Javascript expects milliseconds.
+            'ajax_refresh_interval' => $view->ajax_refresh_interval * 1000,
           ),
         ),
       ),
