### 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')) .'>', '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