BTMash

Blob of contradictions

JQuery UI Datepicker: highlighting dates with events

Sat, 10/22/2011 - 00:32 -- btmash
A picture of the jquery ui datepicker highlighting days with events.

Since the last time I posted on using the JQuery UI Datepicker for event navigation, we launched the site that I built out this functionality for (if you are interested, visit REDCAT). A few months ago, we received a feature request to extend the set of functionality by displaying (or highlighting) the list of events that occur in a given month. People have responded very favorably to the switch but this was a feature that had been lacking since the move over from using the Calendar to the custom datepicker. Since my module did not address this (and we were not using calendar which provides some of this functionality), it was time to add a little bit more custom code.

We'll start by first generating javascript object data that can be used to figure out when events are occurring. On my end, I had a few set facts.

  • We are using a SQL storage engine. No need to worry about how to integrate what I need with MongoDB or the like (atleast not until a few years down the line)
  • We know the fields which contain the event data. This is also not likely to change given that the theater only holds certain types of events and as such some fields are 'fixed' as far as where dates would be held.
  • For now, there aren't a massive number of events / event dates (maybe a thousand over the past 3 years that the site has been live).

Based on the facts above, I decided that for now, I will do a SQL query that retrieves a list of all the dates in the table. I could do http requests to only get dates in a particular month or other funky stuff. But for now, I am dealing with a fairly small dataset and could get by on that. In the event that it grows to a much larger state or performance is hampered, we can try something different in the future (which I already have some ideas for ^_^). I've decided to also cache the data for easy retrieval in the future.

/**
* Retrieve a list of dates.
*/
function _my_calendar_customizations_retrieve_event_dates() {
  static $dates;

  if (!isset($dates)){
    $dates = array();
    $cached_dates = cache_get('my_calendar_customizations_event_dates');
    if (!empty($cached_dates)) {
      $dates = $cached_dates->data;
    }
    else {
      $default_timezone = new DateTimeZone(date_default_timezone(FALSE));
     
      $field_info = field_info_field('field_content_date_time');
      $table_info = $field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT'];
      foreach ($table_info as $key => $values) {
        $table_name = $key;
        $table_column = $values['value'];
      }
     
      $results = db_query("SELECT $table_column FROM {$table_name} WHERE entity_type = 'node' AND bundle = 'my_calendar_event' ORDER BY $table_column ASC");
      foreach ($results as $result) {
        $date_object = new DateObject($result->$table_column, 'UTC');
        $date_object->setTimezone($default_timezone);
        $year = date_format($date_object, 'Y');
        $month = date_format($date_object, 'm');
        $day = date_format($date_object, 'd');
        $dates[$year][$month][$day] = $day;
      }
      cache_set('my_calendar_customizations_event_dates', $dates, 'cache', CACHE_TEMPORARY);
    }
  }
  return $dates;
}

And now that we have a list of dates, let's go back to our original block view and add in the following javascript setting:

drupal_add_js(array('my_calendar_event_base_uri' => $calendar_uri, 'my_calendar_event_dates' => _my_calendar_customizations_retrieve_event_dates()), 'setting');

So we now have a list of all the event dates as a Drupal js setting that the JQuery Datepicker will have access to when the calendar month is loaded and/or changes. But we need to tie this all in for that period as well.
I first made a function called my_calendar_customizations_update_month in my_calendar_customizations.js

  function my_calendar_customizations_update_month() {
    Drupal.settings.my_calendar_customizations_current_year = year = Drupal.settings.my_calendar_customizations_current_date.substring(0, 4);
    Drupal.settings.my_calendar_customizations_current_month = month = Drupal.settings.my_calendar_customizations_current_date.substring(5);
    var dates = Drupal.settings.my_calendar_event_dates;
    if (dates[year] != undefined && dates[year][month] != undefined) {
     
      $('.ui-datepicker-calendar td').not('.ui-datepicker-other-month').each(function(index, value) {
        var year = Drupal.settings.my_calendar_customizations_current_year;
        var month = Drupal.settings.my_calendar_customizations_current_month;
        var dates = Drupal.settings.my_calendar_event_dates;
        var date = my_calendar_customizations_pad(index + 1);
        if (dates[year][month][date] != undefined) {
          $(this).addClass('has-event');
        }
      });
    }

And my_calendar_customizations.js just calls this function when the datepicker is first loaded and when the datepicker month is changed by the user. So we now have a functioning datepicker displaying all events in a given month when a user clicks to browse. The page size does not grow considerably (12kb gzipped vs. 8 - 10k gzipped before), performance is zippy, and we don't have to guess if something is happening on a particular day. Again, pieces of this might have been possible with the calendar module...but usage of the jquery datepicker has so far allowed for better date browsing and an overall better experience for the user.