### Eclipse Workspace Patch 1.0
#P sgi
Index: sites/all/modules/date/date_popup/date_popup.module
===================================================================
--- sites/all/modules/date/date_popup/date_popup.module	(revision 97)
+++ sites/all/modules/date/date_popup/date_popup.module	(working copy)
@@ -22,7 +22,7 @@
 
 /**
  * Load needed files.
- * 
+ *
  * Play nice with jQuery UI.
  */
 function date_popup_add() {
@@ -78,11 +78,13 @@
  * returned CSS class.
  * @param $settings
  *   The settings array to pass to the jQuery function.
+ * @param $daysettings
+ *   The settings array to pass to the jQuery function specifically for the beforeDayShow callback.
  * @returns
  *   The CSS id to assign to the element that should have
  * $func($settings) invoked on it.
  */
-function date_popup_js_settings_id($id, $func, $settings) {
+function date_popup_js_settings_id($id, $func, $settings, $daysettings) {
   static $js_added = FALSE;
   static $id_count = array();
 
@@ -107,12 +109,13 @@
   }
 
 // It looks like we need the additional id_count for this to
-// work correctly when there are multiple values.  
+// work correctly when there are multiple values.
 //  $return_id = "$id-$func-popup";
   $return_id = "$id-$func-popup-". $id_count[$id]++;
   $js_settings['datePopup'][$return_id] = array(
     'func' => $func,
-    'settings' => $settings
+    'settings' => $settings,
+    'daysettings' => $daysettings,
   );
   drupal_add_js($js_settings, 'setting');
   return $return_id;
@@ -151,6 +154,31 @@
  *   The number of years to go back and forward in a year selector,
  *   default is -3:+3 (3 back and 3 forward).
  *
+ * #date_disallow and #date_allow
+ *   Together these define what dates can be made unselectable in
+ *   a date popup. If both are  'all' all dates remain selectable.
+ *   If neither are set to 'all' all dates are unselectable, this
+ *   is because that choice makes no sense.
+ *
+ *   To define a date or range of dates as allowed/disallowed define
+ *   an array as follows:
+ *
+ *   array(
+ *   	'YYYY-MM-DD' => array('aClass', 'aTip', 'yyyy-mm-dd'),
+ *   	... repeat as required
+ *   )
+ *
+ *   aClass will be inserted as a class for the date or dates specified
+ *   by this definition. Likewise 'aTip' will be a pop-up hint on the date
+ *   or range of dates (this is per the 'beforeShowDay' callback function.
+ *   For both these insert an empty string if not required.
+ *
+ *   The second date (if specified) gives the end date (inclusive)
+ *   of a date range specified by this line of the array.You can have
+ *   as many definition lines as you wish. The definitions apply only
+ *   to the element they are assigned to, different dates can be applied
+ *   to different elements in the same form (as you would expect).
+ *
  */
 function date_popup_element_info() {
   return array(
@@ -165,6 +193,8 @@
       '#process' => array('date_popup_element_process'),
       '#value_callback' => 'date_popup_element_value_callback',
       '#theme_wrappers' => array('date_popup'),
+      '#date_disallow' => 'all',
+      '#date_allow' => 'all',
       ),
     );
 }
@@ -195,10 +225,10 @@
   $has_time = date_has_time($granularity);
 
   $date = NULL;
-  $return = $has_time ? array('date' => '', 'time' => '') : array('date' => ''); 
+  $return = $has_time ? array('date' => '', 'time' => '') : array('date' => '');
   if ($input !== FALSE) {
     $return = $input;
-    $date = date_popup_input_date($element, $input);  
+    $date = date_popup_input_date($element, $input);
   }
   elseif (!empty($element['#default_value'])) {
     $date = date_default_date($element);
@@ -254,7 +284,7 @@
     'nextText' => t('Next', array(), array('context' => 'date_nav')) .'&gt;',
     'currentText' => t('Today', array(), array('context' => 'date_nav')),
     'changeMonth' => TRUE,
-    'changeYear' => TRUE,    
+    'changeYear' => TRUE,
     'clearText' => t('Clear'),
     'closeText' => t('Close'),
     'firstDay' => intval(variable_get('date_first_day', 1)),
@@ -272,10 +302,15 @@
     'yearRange' => $year_range,
     // Custom setting, will be expanded in Drupal.behaviors.date_popup()
     'fromTo' => isset($fromto),
-    );    
+    );
 
+  $daysettings = array(
+    'allow' => date_popup_js_showdate_daysettings($element['#date_allow']),
+    'disallow' => date_popup_js_showdate_daysettings($element['#date_disallow']),
+  );
+
   // Create a unique id for each set of custom settings.
-  $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings);
+  $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings, $daysettings);
 
   // Manually build this element and set the value - this will prevent corrupting
   // the parent value
@@ -283,7 +318,7 @@
   $sub_element = array(
     '#type' => 'textfield',
     '#default_value' => $element['#value']['date'],
-    '#id' => $id,    
+    '#id' => $id,
     '#input' => FALSE,
     '#size' => !empty($element['#size']) ? $element['#size'] : 20,
     '#maxlength' => !empty($element['#maxlength']) ? $element['#maxlength'] : 30,
@@ -298,6 +333,34 @@
 }
 
 /**
+ * Compile the date structure supplied in either #date_allow or
+ * #date_disallow into a form we can easily pass into the JS.
+ *
+ * Three s in classs is deliberate to avoid using a JS reserved word.
+ */
+function date_popup_js_showdate_daysettings($dates) {
+  if ($dates=='all') {
+    return array('all');
+  }
+
+  // Make sure the dates in ascending order (must be YYYY-MM-DD format)
+  // but convert them to milliseconds here so we don't have to keep
+  // doing it in the JS routine, which would be very slow.
+  ksort($dates);
+  $daysettings = array();
+  foreach ($dates as $date => $data) {
+    list($class, $tip, $enddate) = $data;
+    $daysettings[] = array(
+      'start' => strtotime($date)*1000,
+  	  'classs' => $class,
+      'tip' => $tip,
+      'enddate' => $enddate ? strtotime($enddate)*1000 : 0,
+    );
+  }
+  return $daysettings;
+}
+
+/**
  * Process the time portion of the element.
  */
 function date_popup_process_time(&$element) {
@@ -316,7 +379,7 @@
 
   // Create a unique id for each set of custom settings.
   $id = date_popup_js_settings_id($element['#id'], 'timeEntry', $settings);
-  
+
   // Manually build this element and set the value - this will prevent corrupting
   // the parent value
   $parents = array_merge($element['#parents'], array('time'));
@@ -339,11 +402,11 @@
 
 /**
  * Massage the input values back into a single date.
- * 
+ *
  * When used as a Views widget, the validation step always gets triggered,
  * even with no form submission. Before form submission $element['#value']
  * contains a string, after submission it contains an array.
- * 
+ *
  */
 function date_popup_validate($element, &$form_state) {
   if (is_string($element['#value'])) {
@@ -373,11 +436,11 @@
       // Set message on both date and time to get them highlighted properly.
       $message = t('Field %field is required.', array('%field' => $label));
       if (!empty($date_granularity)) {
-        form_set_error($error_field .'][date', $message);  
+        form_set_error($error_field .'][date', $message);
         $message = ' ';
       }
       if (!empty($time_granularity)) {
-        form_set_error($error_field .'][time', $message);  
+        form_set_error($error_field .'][time', $message);
         return;
       }
     }
@@ -397,11 +460,11 @@
     // Set message on both date and time to get them highlighted properly.
     $message = t('Field %field is invalid.', array('%field' => $label));
     if (!empty($element['#date_granularity'])) {
-      form_set_error($error_field .'][date', $message);  
+      form_set_error($error_field .'][date', $message);
       $message = ' ';
     }
     if (!empty($element['#time_granularity'])) {
-      form_set_error($error_field .'][time', $message);  
+      form_set_error($error_field .'][time', $message);
     }
   }
   form_set_value($element, NULL, $form_state);
@@ -409,15 +472,15 @@
 
 /**
  * Helper function for extracting a date value out of user input.
- * 
+ *
  * @param autocomplete
  *   Should we add a time value to complete the date if there is no time?
- *   Useful anytime the time value is optional. 
+ *   Useful anytime the time value is optional.
  */
 function date_popup_input_date($element, $input, $auto_complete = FALSE) {
   if (empty($input) || !is_array($input) || !array_key_exists('date', $input)) {
     return NULL;
-  }  
+  }
   date_popup_add();
   $granularity = date_format_order($element['#date_format']);
   $has_time = date_has_time($granularity);
@@ -431,9 +494,9 @@
   if (is_object($date)) {
     $date->limitGranularity($granularity);
     if ($date->validGranularity($granularity, $flexible)) {
-      date_increment_round($date, $element['#date_increment']); 
+      date_increment_round($date, $element['#date_increment']);
       return $date;
-    }        
+    }
   }
   return NULL;
 }
@@ -450,13 +513,13 @@
 
 /**
  * Format options array.
- * 
+ *
  * TODO Remove any formats not supported by the widget, if any.
  */
 function date_popup_formats() {
   $formats = str_replace('i', 'i:s', array_keys(system_get_date_formats('short')));
   $formats = drupal_map_assoc($formats);
-  return $formats;  
+  return $formats;
 }
 
 /**
@@ -465,8 +528,8 @@
  * @param string $format
  *   a normal date format string, like Y-m-d
  * @return string
- *   A format string in popup format, like YMD-, for the 
- *   earlier 'calendar' version, or m/d/Y for the later 'datepicker' 
+ *   A format string in popup format, like YMD-, for the
+ *   earlier 'calendar' version, or m/d/Y for the later 'datepicker'
  *   version.
  */
 function date_popup_format_to_popup($format) {
@@ -513,7 +576,7 @@
     'g' => 'h',
     'a' => 'A',
     ' a' => 'A',
-    ' A' => 'A',   
+    ' A' => 'A',
     );
 }
 
@@ -544,7 +607,7 @@
   $element = $vars['element'];
   $output = '';
   $class = 'container-inline-date form-item';
-  // Add #date_float to allow date parts to float together on the same line. 
+  // Add #date_float to allow date parts to float together on the same line.
   if (empty($element['#date_float'])) {
     $class .= ' date-clear-block';
   }
Index: sites/all/modules/date/date_popup/date_popup.js
===================================================================
--- sites/all/modules/date/date_popup/date_popup.js	(revision 97)
+++ sites/all/modules/date/date_popup/date_popup.js	(working copy)
@@ -5,34 +5,79 @@
  */
 (function ($) {
 Drupal.behaviors.date_popup = {
+    self : null,
+    dayChecks : null,
+    
   attach: function (context) {
-  for (var id in Drupal.settings.datePopup) {
-    $('#'+ id).bind('focus', Drupal.settings.datePopup[id], function(e) {
-      if (!$(this).hasClass('date-popup-init')) {
-        var datePopup = e.data;
-        // Explicitely filter the methods we accept.
-        switch (datePopup.func) {
-          case 'datepicker':
-            $(this)
-              .datepicker(datePopup.settings)
-              .addClass('date-popup-init')
-            $(this).click(function(){
-              $(this).focus();
-            });
-            break;
-
-          case 'timeEntry':
-            $(this)
-              .timeEntry(datePopup.settings)
-              .addClass('date-popup-init')
-            $(this).click(function(){
-              $(this).focus();
-            });
-            break;
+    self = this;
+    
+    for (var id in Drupal.settings.datePopup) {
+      $('#'+ id).bind('focus', Drupal.settings.datePopup[id], function(e) {
+        if (!$(this).hasClass('date-popup-init')) {
+          var datePopup = e.data;
+          // Explicitely filter the methods we accept.
+          switch (datePopup.func) {
+            case 'datepicker':
+              // Assign the function to approve the date
+              datePopup.settings.beforeShowDay = self.checkDay;
+              
+              $(this)
+                .datepicker(datePopup.settings)
+                .addClass('date-popup-init')
+                .click(function(){
+                  dayChecks = datePopup.daysettings;
+                  $(this).focus();
+                });
+              break;
+  
+            case 'timeEntry':
+              $(this)
+                .timeEntry(datePopup.settings)
+                .addClass('date-popup-init')
+                .click(function(){
+                  $(this).focus();
+                });
+              break;
+          }
         }
+      });
+    }
+  },
+  
+  checkDay: function (date) {
+    if (dayChecks.disallow[0]=='all' && dayChecks.allow[0]=='all') {
+      result = [true, '', ''];
+    }
+    else if (dayChecks.disallow[0]=='all') {
+      result = self.allowDay(date, dayChecks.allow);
+    }
+    else if (dayChecks.allow[0]=='all') {
+      result = self.allowDay(date, dayChecks.disallow);
+      result[0] = !result[0];
+    }
+    else {
+      result = [false, '', ''];
+    }
+    return result;
+  },
+  
+  allowDay: function(date, days) {
+    var result = [false, '', ''];
+    var daytime = date.getTime();
+    for (var i in days) {
+      var day = days[i];
+      if (day.enddate) {
+        if (daytime>=day.start && daytime<=day.enddate) {
+          result = [true, day.classs, day.tip];
+          break;
+        }
       }
-    });
+      else if (day.start==daytime) {
+        result = [true, day.classs, day.tip];
+        break;
+      }
+    }
+    return result;
   }
-  }
 };
 })(jQuery);
\ No newline at end of file
