diff --git fullcalendar.info fullcalendar.info
index 0fd8e51..71de27f 100644
--- fullcalendar.info
+++ fullcalendar.info
@@ -5,3 +5,4 @@ core = 6.x
 package = "Views"
 dependencies[] = views
 dependencies[] = date
\ No newline at end of file
+dependencies[] = jquery_ui
diff --git fullcalendar.module fullcalendar.module
index ac42759..18b571d 100644
--- fullcalendar.module
+++ fullcalendar.module
@@ -15,9 +15,50 @@ function fullcalendar_views_api() {
   );
 }
 
+/**
+ * Implementation of hook_init().
+ */
 function fullcalendar_init() {
-  drupal_add_css(drupal_get_path('module', 'fullcalendar') .'/fullcalendar.css', 'module');
-  drupal_add_js(drupal_get_path('module', 'fullcalendar') .'/fullcalendar.js', 'module');
+  $path = drupal_get_path('module', 'fullcalendar');
+
+  drupal_add_css($path . '/fullcalendar.css', 'module');
+  drupal_add_css($path . '/fullcalendar.custom.css', 'module');
+  drupal_add_js($path . '/fullcalendar.js', 'module');
+
+  // We need some jQuery UI files.
+  $files = array('ui.draggable', 'ui.droppable', 'ui.resizable', 'effects.highlight');
+  jquery_ui_add($files);
+}
+
+/**
+ * Implementation of hook_perm().
+ *
+ * @return array An array of valid permissions for the fullcalendar module
+ */
+function fullcalendar_perm() {
+  return array('update any fullcalendar event');
+}
+
+/**
+ * Implementation of hook_menu().
+ *
+ * @return An array of menu items.
+ */
+function fullcalendar_menu() {
+  $items = array();
+
+  $items['fullcalendar/ajax/update/%/%node'] = array(
+    'title' => 'Update event',
+    'description' => 'Save the updated event datetime.',
+    'page callback' => 'fullcalendar_update',
+    'page arguments' => array(3, 4),
+    'access callback' => '_fullcalendar_update_access',
+    'access arguments' => array(4),
+    'type' => MENU_CALLBACK,
+    'file' => 'fullcalendar.update.inc',
+  );
+
+  return $items;
 }
 
 //Pass through to template_preprocess_views_view_fields so that fields will be available to template file
@@ -25,17 +66,6 @@ function fullcalendar_init() {
 //  template_preprocess_views_view_fields($vars);
 //}
 
-function fullcalendar_set_display_times($node, $field_name, &$vars){
-  //$vars['start'] = $node->{$field_name}[0]['value'];
-  //$vars['end'] = $node->{$field_name}[0]['value2'];
-  
-  //Pass times through date modules date_formatter_process function to translate them to the right display times
-  $dfp_info = array('#node' => $node, '#field_name' => $field_name, '#formatter' => NULL, '#item' => $node->{$field_name}[0]);
-  $dfp = date_formatter_process($dfp_info);
-  $vars['start'] = $dfp['value']['local']['datetime'];
-  $vars['end'] = $dfp['value2']['local']['datetime'];
-}
-
 /**
  * Implementation of hook_theme().
  */
@@ -69,13 +99,13 @@ function template_preprocess_views_view_node_fullcalendar(&$vars) {
   $options = $vars['options'];
 
   // Make sure the variables are defined.
-  $vars['title'] = '';
-  $vars['start'] = '';
-  $vars['end'] = '';
   $vars['url'] = '';
   $vars['node'] = '';
-  $vars['allDay'] = FALSE;
   $vars['className'] = '';
+  $vars['data'] = array(); // Contains the start, end & allDay values.
+  $vars['editable'] = FALSE;
+
+  $index = 0; // Used to create $vars['data'] variable.
   
   $nid = $vars['row']->{$vars['field_alias']};
     if (!is_numeric($nid)) {
@@ -83,67 +113,149 @@ function template_preprocess_views_view_node_fullcalendar(&$vars) {
   }
 
   $node = node_load($nid);
-  
-
   if (empty($node)) {
     return;
   }
   
+  // Allow resize/drag/drop of an event if user has proper permissions.
+  if (_fullcalendar_update_access($node)) {
+    $vars['editable'] = TRUE;
+  }
+
   $vars['className'] = theme('fullcalendar_classname', $node);
   $vars['node'] = $node;
-  $vars['title'] = $node->title;
-  $vars['url'] = url('node/'. $nid);
-  //echo str_replace(" ","&nbsp;",str_replace("\n","<br>",htmlspecialchars(print_r($node,true))));
-  if ($options['fullcalendar_url_field']) {
+
+  if (!empty($options['fullcalendar_url_field'])) {
     if (isset($node->{$options['fullcalendar_url_field']}[0]['value'])) {
       $vars['url'] = $node->{$options['fullcalendar_url_field']}[0]['value'];
     }
+  } else {
+    $vars['url'] = url('node/'. $nid);
   }
   
-  //store field info in a static so it doesn't have to be loaded for every row
-  static $fields_info;
-  if (!(is_array($fields_info))) {
-    $fields_info = array();
-  }
-  
-  //find datetime field (strip tags in case one of the wysiwyg's adds tags)
+  // Find datetime field (strip tags in case one of the wysiwyg's adds tags).
   $date_fields = trim(strip_tags($options['fullcalendar_date_fields']));
   if ($date_fields) {
     $date_fields = explode("\n", $date_fields);
     foreach ($date_fields as $date_field) {
       if ($date_field == 'node_created') {
-        $vars['start'] = format_date($node->created, 'custom', 'Y-m-j H:i:s');
+        $vars['data'][$index]['field'] = $date_field;
+        $vars['data'][$index++]['start'] = format_date($node->created, 'custom', 'Y-m-j H:i:s');
         break;
       }
       if ($date_field == 'node_changed') {
-        $vars['start'] = format_date($node->changed, 'custom', 'Y-m-j H:i:s');
+        $vars['data'][$index]['field'] = $date_field;
+        $vars['data'][$index++]['start'] = format_date($node->changed, 'custom', 'Y-m-j H:i:s');
+        break;
+      }
+      // If field exists and it is a date or datetime.
+      if (isset($node->$date_field)) {
+        switch ($node->{$date_field}[0]['date_type']) {
+          case 'date':
+          case 'datetime':
+          case 'datestamp':
+            foreach ($node->$date_field as $key => $values) {
+              $vars['data'][$index]['field'] = $date_field;
+              $vars['data'][$index]['index'] = $key;
+
+              if ($field = content_fields($key)) {
+                $vars['allDay'][$index]['allDay'] = _fullcalendar_is_all_day($field, $values);
+              }
+              else {
+                $vars['allDay'][$index]['allDay'] = FALSE;
+              }
+              _fullcalendar_set_display_times($node, $date_field, $index++, $vars);
+            }
         break;
       }
-      //if field exists and it is a date or datetime
-      if ((isset($node->{$date_field})) && in_array($node->{$date_field}[0]['date_type'], array('datetime', 'date', 'datestamp'))) {
-        fullcalendar_set_display_times($node, $date_field, &$vars);
         break;
       }
     }
   }
-  //if no specified datetime field then search all fields and use first date or datetime field
+  // Dynamically find the field(s) with date/datetime values.
   else {
-    $node_ary = (array) $node;
-    foreach ($node_ary as $key => $value) {
-      if (preg_match('/field_.*/', $key)) {
-        if ((isset($value[0]['date_type'])) and (($value[0]['date_type'] == 'datetime') or ($value[0]['date_type'] == 'date'))) {
+    $fields = content_fields();
+    $node_a = (array) $node;
+    foreach ($node_a as $field_name => $node_values) {
+      if (preg_match('/field_.*/', $field_name) && isset($node_values[0]['date_type'])) {
+        // Check first element; can have > 1 value, but the date_type will be the same for all those values.
+        switch ($node_values[0]['date_type']) {
+          case 'date':
+          case 'datetime':
+          case 'datestamp':
+            foreach ($node_values as $key_index => $values) {
+              if (isset($fields[$field_name])) {
+                // If no end date is set, copy start date for processing.
+                if (empty($values['value2'])) {
+                  $values['value2'] = $values['value'];
+                }
 
-          // If no end date is set, copy start date for processing
-          if (empty($value[0]['value2'])) {
-            $value[0]['value2'] = $value[0]['value'];
+                $vars['data'][$index]['field'] = $field_name;
+                $vars['data'][$index]['index'] = $key_index;
+                $vars['allDay'][$index]['allDay'] = _fullcalendar_is_all_day($field, $values);
+
+                _fullcalendar_set_display_times($node, $field_name, $index++, $vars);
+              }
+            }
+            break;
+        }
+      }
+    }
+  }
           }
         
-          if ($field = content_fields($key)) {
+/**
+ * Pass times through date modules date_formatter_process function to
+ * translate them to the right display times.
+ *
+ * @param object $node
+ * @param string $field_name
+ * @param integer $index
+ * @param array $vars
+ */
+function _fullcalendar_set_display_times($node, $field_name, $index, &$vars) {
+  $dfp_info = array(
+    '#node' => $node,
+    '#field_name' => $field_name,
+    '#formatter' => NULL,
+    '#item' => $node->{$field_name}[$index],
+  );
+  $dfp = date_formatter_process($dfp_info);
+  $vars['data'][$index]['start'] = $dfp['value']['local']['datetime'];
+  $vars['data'][$index]['end'] = $dfp['value2']['local']['datetime'];
+}
+
+/**
+ * Check if the user has access to update the given fullcalendar event.
+ *
+ * @param object $node
+ * @return bool
+ */
+function _fullcalendar_update_access($node) {
+  global $user;
+
+  if (!empty($node) && (user_access('administer nodes')
+      || user_access('update any fullcalendar event')
+      || user_access('edit any ' . $node->type . ' content')
+      || (user_access('edit own ' . $node->type . ' content') && $node->uid == $user->uid))) {
+        return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+ * Determine if the event should be flaged as an ALL DAY event.
+ *
+ * @param string $field
+ * @param array $values
+ */
+function _fullcalendar_is_all_day($field, $values) {
             // Get DateTime objects with timezone support
-            $date1 = date_create($value[0]['value'], timezone_open($value[0]['timezone_db']));
-            date_timezone_set($date1, timezone_open($value[0]['timezone']));
-            $date2 = date_create($value[0]['value2'], timezone_open($value[0]['timezone_db']));
-            date_timezone_set($date2, timezone_open($value[0]['timezone']));
+  $date1 = date_create($values['start'], timezone_open($values['timezone_db']));
+  date_timezone_set($date1, timezone_open($values['timezone']));
+  $date2 = date_create($values['end'], timezone_open($values['timezone_db']));
+  date_timezone_set($date2, timezone_open($values['timezone']));
 
             // Get the max increment for minutes and seconds
             $increment = isset($field['widget']['increment']) ? $field['widget']['increment'] : 1;
@@ -175,15 +287,6 @@ function template_preprocess_views_view_node_fullcalendar(&$vars) {
                 $max_comp = FALSE;
             }
 
-            // If granularity did not include time, set to All Day
-            $fields_info[$key]['allDay'] = (date_has_time(date_granularity($field))) ? ($min_comp && $max_comp) : TRUE;
-          }
-          
-          $vars['allDay'] = $fields_info[$key]['allDay'];
-          
-          fullcalendar_set_display_times($node, $key, &$vars);
-        }
-      }
-    }
-  }
+  // If granularity did not include time, event is ALL DAY.
+  return (date_has_time(date_granularity($field))) ? ($min_comp && $max_comp) : TRUE;
 }
diff --git views-view-fullcalendar.tpl.php views-view-fullcalendar.tpl.php
index 6bcea78..266e2e4 100644
--- views-view-fullcalendar.tpl.php
+++ views-view-fullcalendar.tpl.php
@@ -17,6 +17,7 @@
  */
  
 ?>
+<div id="fullcalendar_status"></div>
 <div id="fullcalendar"></div>
 <div id="fullcalendar_content">
 <?php
@@ -26,7 +27,7 @@ for ($i = 0; $i < count($rows); $i++) {
 ?>
 </div>
 <script type="text/javascript">
-$(document).ready(function() {
+Drupal.behaviors.fullCalendar = function(context) {
     $('#fullcalendar_content').hide(); //hide the failover display
     $('#fullcalendar').fullCalendar({
         defaultView: '<?php echo $options['fullcalendar_view']; ?>',
@@ -36,15 +37,19 @@ $(document).ready(function() {
           center: '<?php echo $options['fullcalendar_header_center']; ?>',
           right: '<?php echo $options['fullcalendar_header_right']; ?>'
         },
-        <?php if($options['fullcalendar_url_colorbox']){ ?>
         eventClick: function(calEvent, jsEvent, view) {
-          //test for colorbox
+      <?php if ($options['fullcalendar_url_colorbox']): ?>
+      // Open in colorbox if exists, else open in new window.
           if($.colorbox){
             $.colorbox({href:calEvent.url,iframe:true, width:'80%', height: '80%'});
-            return false;
+      } else {
+        window.open(calEvent.url);
           }
+      <?php else: ?>
+      window.open(calEvent.url);
+      <?php endif; ?>
+      return false;
         },
-        <?php } ?>
         <?php if (!empty($options['fullcalendar_defaultyear'])): ?>
           year: <?php echo $options['fullcalendar_defaultyear']; ?>,
         <?php endif; ?>
@@ -62,21 +67,54 @@ $(document).ready(function() {
           var events = [];
 
           $('.fullcalendar_event').each(function() {
-              event_details = $(this).find('.fullcalendar_event_details');
+        $(this).find('.fullcalendar_event_details').each(function() {
               events.push({
-                  title: $(event_details).attr('title'),
-                  start: $(event_details).attr('start'),
-                  end: $(event_details).attr('end'),
-                  url: $(event_details).attr('href'),
-                  allDay: ($(event_details).attr('allDay') == '1'),
-                  className: $(event_details).attr('cn'),
+            field: $(this).attr('field'),
+            index: $(this).attr('index'),
+            nid: $(this).attr('nid'),
+            title: $(this).attr('title'),
+            start: $(this).attr('start'),
+            end: $(this).attr('end'),
+            url: $(this).attr('href'),
+            allDay: ($(this).attr('allDay') == '1'),
+            className: $(this).attr('cn'),
+            editable: $(this).attr('editable')
+          });
               });
           });
 
           callback(events);
+    },
+    eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc) {
+      $.post('/fullcalendar/ajax/update/drop/'+ event.nid,
+        'field=' + event.field + '&index=' + event.index + '&day_delta=' + dayDelta + '&minute_delta=' + minuteDelta + '&all_day=' + allDay,
+        fullcalendarUpdate);
+      return false;
+    },
+    eventResize: function(event, dayDelta, minuteDelta, revertFunc) {
+      $.post('/fullcalendar/ajax/update/resize/'+ event.nid,
+          'field=' + event.field + '&index=' + event.index + '&day_delta=' + dayDelta + '&minute_delta=' + minuteDelta,
+        fullcalendarUpdate);
+      return false;
         }
     });
+
+  var fullcalendarUpdate = function(response) {
+    var result = Drupal.parseJson(response);
+    if ($('#fullcalendar_status').text() == '') {
+      $('#fullcalendar_status').html(result.msg).slideDown();
+    } else {
+      $('#fullcalendar_status').html(result.msg).effect('highlight', {}, 5000);
+    }
+    return false;
+  }
+
+  $('.fullcalendar_status_close').live('click', function() {
+    $('#fullcalendar_status').slideUp();
+    return false;
+  });
+
     //trigger a window resize so that calendar will redraw itself as it loads funny in some browsers occasionally
     $(window).resize();
-});
+};
 </script>
diff --git views-view-node-fullcalendar.tpl.php views-view-node-fullcalendar.tpl.php
index 84cdd90..f824fad 100644
--- views-view-node-fullcalendar.tpl.php
+++ views-view-node-fullcalendar.tpl.php
@@ -5,11 +5,13 @@
  * View to display the fullcalendar rows (events)
  *
  * Variables available:
+ * - $node: The node object.
  * - $url: The url for the event
- * - $title : The event node's title
- * - $allDay : If the event is all day (does not include hour and minute granularity)
- * - $start : When the event start
- * - $end : When the event ends
+ * - $data['field']: The field that contains the event date and time.
+ * - $data['index']: The index of the event date and time (to support multiple values).
+ * - $data['allDay']: If the event is all day (does not include hour and minute granularity).
+ * - $data['start'] : When the event starts.
+ * - $data['end'] : When the event ends.
  * - $className : The node type that the event came from
  *
  * Note that if you use className for the event's className attribute then you'll get weird results from jquery!
@@ -17,8 +19,18 @@
  
 ?>
 <div class="fullcalendar_event">
-  <a class="fullcalendar_event_details" cn="<?php echo $className; ?>" href="<?php echo $url; ?>" title="<?php echo $title; ?>" allDay="<?php echo $allDay; ?>" start="<?php echo $start; ?>" end="<?php echo $end; ?>"><?php echo $title; ?></a> : <?php echo format_date(strtotime($start)); ?>
-  <?php if((!$allDay) and ($end)){ ?>
-    to <?php echo format_date(strtotime($end)); ?>
-  <?php } ?>
+  <?php foreach ($data as $row): ?>
+    <a class="fullcalendar_event_details" cn="<?php echo $className; ?>" href="<?php echo $url; ?>"
+      field="<?php echo $row['field']; ?>" index="<?php echo $row['index']; ?>"
+      nid="<?php echo $node->nid; ?>" title="<?php echo $node->title; ?>"
+      allDay="<?php echo $row['allDay']; ?>" start="<?php echo $row['start']; ?>" end="<?php echo $row['end']; ?>"
+      editable="<?php echo $editable; ?>"><?php echo $node->title; ?></a>
+
+    : <?php echo format_date(strtotime($row['start'])); ?>
+
+    <?php if (!$row['allDay'] && !empty($row['end'])): ?>
+      to <?php echo format_date(strtotime($row['end'])); ?>
+    <?php endif; ?>
+
+  <?php endforeach; ?>
 </div>
\ No newline at end of file
