diff --git a/date_popup/date_popup.js b/date_popup/date_popup.js
index b13b1e5..a2b4551 100644
--- a/date_popup/date_popup.js
+++ b/date_popup/date_popup.js
@@ -1,33 +1,49 @@
-
 /**
  * Attaches the calendar behavior to all required fields
  */
 Drupal.behaviors.date_popup = 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;
+  var datePopup = Drupal.settings.datePopup
+
+  $('input.date-popup-date:not(.date-popup-processed)', context).each(function() {
+    var inputid = $(this).addClass('date-popup-processed')[0].id;
+    if (datePopup[inputid]) {
+      var settings = datePopup[inputid].settings;
+      var func = datePopup[inputid].func;
+      
+      // Explicitely filter the methods we accept.
+      if (func == 'datepicker') {
+        // make minDate and maxDate JS Date objects when they are integers
+        if (typeof settings.minDate == 'number') {
+          settings.minDate = new Date(settings.minDate * 1000);
+        }
+        if (typeof settings.maxDate == 'number') {
+          settings.maxDate = new Date(settings.maxDate * 1000);
+        }
+        
+        if (settings.weekdayDisable.length > 0 || settings.dayDisable.length > 0) {
+          settings.beforeShowDay = function(date) {
+            var dayDisabled = jQuery.inArray(date.getTime() / 1000, settings.dayDisable) != -1;
+            var weekdayDisabled = jQuery.inArray(date.getDay(), settings.weekdayDisable) != -1;
 
-          case 'timeEntry':
-            $(this)
-              .timeEntry(datePopup.settings)
-              .addClass('date-popup-init')
-            $(this).click(function(){
-              $(this).focus();
-            });
-            break;
+            return [!(dayDisabled || weekdayDisabled), '', ''];
+          }
         }
+        
+        $(this).datepicker(settings);
       }
-    });
-  }
-};
+    }
+  });
+  
+  $('input.date-popup-time:not(.date-popup-processed)', context).each(function() {
+	var inputid = $(this).addClass('date-popup-processed')[0].id;
+	if (datePopup[inputid]) {
+	  var settings = datePopup[inputid].settings;
+	  var func = datePopup[inputid].func;
+	  
+	  // Explicitely filter the methods we accept.
+	  if (func == 'timeEntry') {
+        $(this).timeEntry(settings);
+	  }
+    }
+  });
+};
\ No newline at end of file
diff --git a/date_popup/date_popup.module b/date_popup/date_popup.module
index f219a72..1b0b5fc 100644
--- a/date_popup/date_popup.module
+++ b/date_popup/date_popup.module
@@ -189,6 +189,27 @@ function date_popup_theme() {
  *   The number of years to go back and forward in a year selector,
  *   default is -3:+3 (3 back and 3 forward).
  *
+ * #date_date_min
+ *   The minimum selectable date. Give a timestamp or string
+ *   that is parseable by strtotime.
+ *
+ * #date_date_max
+ *   The maximum selectable date. Give a timestamp or string
+ *   that is parseable by strtotime.
+ *
+ * #date_weekday_disable
+ *   Unselectable weekdays. Give an array of integers, where 0 is
+ *   Sunday, 1 is Monday, etc.
+ *
+ * #date_day_disable
+ *   Unselectable days. Give an array of timestamps or strings
+ *   that are parseable by strtotime.
+ * 
+ * #date_datepicker_settings
+ *   Additional ui.datepicker settings can be supplied in this 
+ *   attribute. See http://docs.jquery.com/UI/Datepicker
+ *   Example:
+ *     array('changeFirstDay' => FALSE)
  */
 function date_popup_elements() {
   return array(
@@ -199,6 +220,11 @@ function date_popup_elements() {
       '#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
       '#date_increment' => 1,
       '#date_year_range' => '-3:+3',
+  	  '#date_date_min' => NULL,
+      '#date_date_max' => NULL,
+      '#date_weekday_disable' => array(),
+      '#date_day_disable' => array(),
+      '#date_datepicker_settings' => array(),
       '#process' => array('date_popup_process'),
       ),
     );
@@ -267,6 +293,20 @@ function date_popup_process_date(&$element, $edit = NULL, $date = NULL) {
   $range = date_range_years($element['#date_year_range'], $date);
   $year_range = date_range_string($range);
 
+  // Preprocess minDate and maxDate
+  $element['#date_date_min'] = date_popup_parse_date($element['#date_date_min']);
+  $element['#date_date_max'] = date_popup_parse_date($element['#date_date_max']);
+
+  // Preprocess the disabled days
+  $disabledDays = array();
+  foreach($element['#date_day_disable'] as $day) {
+    $day = date_popup_parse_date($day);
+    if ($day !== NULL) {
+      $disabledDays[] = $day;
+    }
+  }
+  $element['#date_day_disable'] = $disabledDays;
+
   $settings = array(
     'prevText' => '&laquo;',
     'nextText' => '&raquo;',
@@ -283,18 +323,37 @@ function date_popup_process_date(&$element, $edit = NULL, $date = NULL) {
     'monthNamesShort' => array_values(date_month_names_abbr(TRUE)),
     //'buttonImage' => base_path() . drupal_get_path('module', 'date_api') ."/images/calendar.png",
     //'buttonImageOnly' => TRUE,
+  	'showOn' => 'button',
+  	'mandatory' => isset($element['#required']) && $element['#required'],
+    'showButtonPanel' => TRUE,
     'autoPopUp' => 'focus',
     'closeAtTop' => FALSE,
     'speed' => 'immediate',
     'dateFormat' => date_popup_format_to_popup($date_format, 'datepicker'),
     'yearRange' => $year_range,
+    'minDate' => $element['#date_date_min'],
+    'maxDate' => $element['#date_date_max'],
+    'hideIfNoPrevNext' => TRUE,
     // Custom setting, will be expanded in Drupal.behaviors.date_popup()
     'fromTo' => isset($fromto),
+    'weekdayDisable' => $element['#date_weekday_disable'],
+    'dayDisable' => $disabledDays,
     );    
 
+  // Additional attributes override defaults
+  foreach ($element['#date_datepicker_settings'] as $key => $value) {
+    $settings[$key] = $value;
+  }
+
   // Create a unique id for each set of custom settings.
   $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings);
-  
+  $attributes = $element['#attributes'];
+  if (isset($attributes['class'])) {
+    $attributes['class'] .= ' date-popup-date';
+  } else {
+    $attributes['class'] = 'date-popup-date';
+  }
+
   // Manually build this element and set the value - this will prevent corrupting
   // the parent value
   $parents = array_merge($element['#parents'], array('date'));
@@ -305,7 +364,7 @@ function date_popup_process_date(&$element, $edit = NULL, $date = NULL) {
     '#input' => FALSE,
     '#size' => !empty($element['#size']) ? $element['#size'] : 20,
     '#maxlength' => !empty($element['#maxlength']) ? $element['#maxlength'] : 30,
-    '#attributes' => $element['#attributes'],
+    '#attributes' => $attributes,
     '#parents' => $parents,
     '#name' => array_shift($parents) . '['. implode('][', $parents) .']'
   );
@@ -348,7 +407,8 @@ function date_popup_process_time(&$element, $edit = NULL, $date = NULL) {
     '#size' => 10,
     '#maxlength' => 10,
     '#parents' => $parents,
-    '#name' => array_shift($parents) . '['. implode('][', $parents) .']'
+    '#name' => array_shift($parents) . '['. implode('][', $parents) .']',
+    '#attributes' => array('class' => 'date-popup-time'),
   );
   $sub_element['#value'] = $sub_element['#default_value'];
 
@@ -400,8 +460,29 @@ function date_popup_validate($element, &$form_state) {
 
   // If the created date is valid, set it.
   if (!empty($value)) {
-    form_set_value($element, $value, $form_state);
-    return;
+    // check restrictions
+    $valuestamp = strtotime($value);
+    $message = '';
+    if ($element['#date_date_min'] !== NULL && $valuestamp < $element['#date_date_min']) {
+      $message = t('Field %field is invalid.', array('%field' => $label));
+    }
+    elseif ($element['#date_date_max'] !== NULL && $valuestamp > $element['#date_date_max']) {
+      $message = t('Field %field is invalid.', array('%field' => $label));
+    }
+    elseif (in_array(date('w', $valuestamp), $element['#date_weekday_disable'])) {
+      $message = t('Field %field is invalid.', array('%field' => $label));
+    }
+    elseif (in_array($valuestamp, $element['#date_day_disable'])) {
+      $message = t('Field %field is invalid.', array('%field' => $label));
+    }
+    
+    if ($message != '') {
+      form_set_error($error_field .'][date', $message);
+    }
+    else {
+      form_set_value($element, $value, $form_state);
+      return;
+    }
   }
   else {
     // Set message on both date and time to get them highlighted properly.
@@ -580,6 +661,31 @@ function datepicker_format_replacements() {
 }
 
 /**
+ * Parses a date to the following rules:
+ * - An int is returned as-is
+ * - A string is converter to an int if it contains only numbers
+ * - Other strings are parsed with strtotime and the result
+ *   is returned if it is valid
+ * - Otherwise: NULL is returned
+ */
+function date_popup_parse_date($input) {
+  if (is_int($input)) {
+    return $input;
+  }
+  if (is_string($input) && preg_match('#^\d+$#', $input)) {
+    return intval($input);
+  }
+  if (is_string($input)) {
+    $tmp = strtotime($input);
+    if ($tmp !== FALSE && $tmp !== -1) {
+      return $tmp;
+    }
+  }
+
+  return NULL;
+}
+
+/**
  * Format a date popup element.
  *
  * Use a class that will float date and time next to each other.
