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' => '«', 'nextText' => '»', @@ -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.