text: /*
   * jQuery.weekCalendar v2.0-dev
   *
   * for support join us at the google group:
   *    - http://groups.google.com/group/jquery-week-calendar
   * have a look to the wiki for documentation:
   *    - http://wiki.github.com/themouette/jquery-week-calendar/
   * something went bad ? report an issue:
   *    - http://github.com/themouette/jquery-week-calendar/issues
   * get the last version on github:
   *    - http://github.com/themouette/jquery-week-calendar
   *
   * Copyright (c) 2009 Rob Monie
   * Copyright (c) 2010 Julien MUETTON
   * Dual licensed under the MIT and GPL licenses:
   *    http://www.opensource.org/licenses/mit-license.php
   *    http://www.gnu.org/licenses/gpl.html
   *
   * If you're after a monthly calendar plugin, check out this one :
   * http://arshaw.com/fullcalendar/
   */
  
  (function($) {
    // check the jquery version
    var _v = $.fn.jquery.split('.'),
        _jQuery14OrLower = (10 * _v[0] + _v[1]) < 15;
  
    $.widget('ui.weekCalendar', (function() {
      var _currentAjaxCall, _hourLineTimeout;
  
      return {
        options: {
          date: new Date(),
          timeFormat: null,
          dateFormat: 'M d, Y',
          alwaysDisplayTimeMinutes: true,
          use24Hour: false,
          daysToShow: 7,
          minBodyHeight: 100,
          firstDayOfWeek: function(calendar) {
                    if ($(calendar).weekCalendar('option', 'daysToShow') != 5) {
                      return 0;
                    } else {
                      //workweek
                      return 1;
                    }
                }, // 0 = Sunday, 1 = Monday, 2 = Tuesday, ... , 6 = Saturday
          useShortDayNames: false,
          timeSeparator: ' to ',
          startParam: 'start',
          endParam: 'end',
          businessHours: {start: 8, end: 18, limitDisplay: false},
          newEventText: 'New Event',
          timeslotHeight: 20,
          defaultEventLength: 2,
          timeslotsPerHour: 4,
          minDate: null,
          maxDate: null,
          showHeader: true,
          buttons: true,
          buttonText: {
            today: 'today',
            lastWeek: 'previous',
            nextWeek: 'next'
          },
          switchDisplay: {},
          scrollToHourMillis: 500,
          allowEventDelete: false,
          allowCalEventOverlap: false,
          overlapEventsSeparate: false,
          totalEventsWidthPercentInOneColumn: 100,
          readonly: false,
          allowEventCreation: true,
          hourLine: false,
          deletable: function(calEvent, element) {
            return true;
          },
          draggable: function(calEvent, element) {
            return true;
          },
          resizable: function(calEvent, element) {
            return false;
          },
          eventClick: function(calEvent, element, dayFreeBusyManager, 
                                                        calendar, clickEvent) {
          },
          eventRender: function(calEvent, element) {
            return element;
          },
          eventAfterRender: function(calEvent, element) {
            return element;
          },
          eventRefresh: function(calEvent, element) {
            return element;
          },
          eventDrag: function(calEvent, element) {
          },
          eventDrop: function(calEvent, element) {
          },
          eventResize: function(calEvent, element) {
          },
          eventNew: function(calEvent, element, dayFreeBusyManager, 
                                                      calendar, mouseupEvent) {
          },
          eventMouseover: function(calEvent, $event) {
          },
          eventMouseout: function(calEvent, $event) {
          },
          eventDelete: function(calEvent, element, dayFreeBusyManager, 
                                                        calendar, clickEvent) {
              calendar.weekCalendar('removeEvent',calEvent.id);
  	},
          calendarBeforeLoad: function(calendar) {
          },
          calendarAfterLoad: function(calendar) {
          },
          noEvents: function() {
          },
          eventHeader: function(calEvent, calendar) {
            var options = calendar.weekCalendar('option');
            var one_hour = 3600000;
            var displayTitleWithTime = calEvent.end.getTime() - calEvent.start.getTime() <= (one_hour / options.timeslotsPerHour);
            if (displayTitleWithTime) {
              return calendar.weekCalendar(
                          'formatTime', calEvent.start) +
                          ': ' + calEvent.title;
            } else {
              return calendar.weekCalendar(
                          'formatTime', calEvent.start) +
                      options.timeSeparator +
                      calendar.weekCalendar(
                          'formatTime', calEvent.end);
            }
          },
          eventBody: function(calEvent, calendar) {
            return calEvent.title;
          },
          shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
          longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
          shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
          longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
          /* multi-users options */
          /**
           * the available users for calendar.
           * if you want to display users separately, enable the
           * showAsSeparateUsers option.
           * if you provide a list of user and do not enable showAsSeparateUsers
           * option, then only the events that belongs to one or several of
           * given users will be displayed
           * @type {array}
           */
          users: [],
          /**
           * should the calendar be displayed with separate column for each
           * users.
           * note that this option does nothing if you do not provide at least
           * one user.
           * @type {boolean}
           */
          showAsSeparateUsers: true,
          /**
           * callback used to read user id from a user object.
           * @param {Object} user the user to retrieve the id from.
           * @param {number} index the user index from user list.
           * @param {jQuery} calendar the calendar object.
           * @return {int|String} the user id.
           */
          getUserId: function(user, index, calendar) {
            return index;
          },
          /**
           * callback used to read user name from a user object.
           * @param {Object} user the user to retrieve the name from.
           * @param {number} index the user index from user list.
           * @param {jQuery} calendar the calendar object.
           * @return {String} the user name.
           */
          getUserName: function(user, index, calendar) {
            return user;
          },
          /**
           * reads the id(s) of user(s) for who the event should be displayed.
           * @param {Object} calEvent the calEvent to read informations from.
           * @param {jQuery} calendar the calendar object.
           * @return {number|String|Array} the user id(s) to appened events for.
           */
          getEventUserId: function(calEvent, calendar) {
            return calEvent.userId;
          },
          /**
           * sets user id(s) to the calEvent
           * @param {Object} calEvent the calEvent to set informations to.
           * @param {jQuery} calendar the calendar object.
           * @return {Object} the calEvent with modified user id.
           */
          setEventUserId: function(userId, calEvent, calendar) {
            calEvent.userId = userId;
            return calEvent;
          },
          /* freeBusy options */
          /**
           * should the calendar display freebusys ?
           * @type {boolean}
           */
          displayFreeBusys: false,
          /**
           * read the id(s) for who the freebusy is available
           * @param {Object} calEvent the calEvent to read informations from.
           * @param {jQuery} calendar the calendar object.
           * @return {number|String|Array} the user id(s) to appened events for.
           */
          getFreeBusyUserId: function(calFreeBusy, calendar) {
            return calFreeBusy.userId;
          },
          /**
           * the default freeBusy object, used to manage default state
           * @type {Object}
           */
          defaultFreeBusy: {free: false},
          /**
           * function used to display the freeBusy element
           * @type {Function}
           * @param {Object} freeBusy the freeBusy timeslot to render.
           * @param {jQuery} $freeBusy the freeBusy HTML element.
           * @param {jQuery} calendar the calendar element.
           */
          freeBusyRender: function(freeBusy, $freeBusy, calendar) {
            if (!freeBusy.free) {
              $freeBusy.addClass('free-busy-busy');
            }
            else {
              $freeBusy.addClass('free-busy-free');
            }
            return $freeBusy;
          },
          /* other options */
          /**
           * true means start on first day of week, false means starts on
           * startDate.
           * @param {jQuery} calendar the calendar object.
           * @type {Function|bool}
           */
          startOnFirstDayOfWeek: function(calendar) {
            return $(calendar).weekCalendar('option', 'daysToShow') >= 5;
          },
          /**
           * should the columns be rendered alternatively using odd/even
           * class
           * @type {boolean}
           */
          displayOddEven: false,
          textSize: 13,
          /**
           * the title attribute for the calendar. possible placeholders are:
           * <ul>
           *  <li>%start%</li>
           *  <li>%end%</li>
           *  <li>%date%</li>
           * </ul>
           * @type {Function|string}
           * @param {number} option daysToShow.
           * @return {String} the title attribute for the calendar.
           */
          title: '%start% - %end%',
          /**
           * default options to pass to callback
           * you can pass a function returning an object or a litteral object
           * @type {object|function(#calendar)}
           */
          jsonOptions: {},
          headerSeparator: '<br />',
          /**
            * returns formatted header for day display
            * @type {function(date,calendar)}
            */
          getHeaderDate: null,
          preventDragOnEventCreation: false,
          /**
            * the event on which to bind calendar resize
            * @type {string}
            */
          resizeEvent: 'resize.weekcalendar'
        },
  
        /***********************
         * Initialise calendar *
         ***********************/
        _create: function() {
          var self = this;
          self._computeOptions();
          self._setupEventDelegation();
          self._renderCalendar();
          self._loadCalEvents();
          self._resizeCalendar();
          self._scrollToHour(self.options.date.getHours(), true);
  
          if (this.options.resizeEvent) {
            $(window).unbind(this.options.resizeEvent);
            $(window).bind(this.options.resizeEvent, function() {
              self._resizeCalendar();
            });
          }
  
        },
  
        /********************
         * public functions *
         ********************/
        /*
         * Refresh the events for the currently displayed week.
         */
        refresh: function() {
          //reload with existing week
          this._loadCalEvents(this.element.data('startDate'));
        },
  
        /*
         * Clear all events currently loaded into the calendar
         */
        clear: function() {
          this._clearCalendar();
        },
  
        /*
         * Go to this week
         */
        today: function() {
          this._clearCalendar();
          this._loadCalEvents(new Date());
        },
  
        /*
         * Go to the previous week relative to the currently displayed week
         */
        prevWeek: function() {
          //minus more than 1 day to be sure we're in previous week - account for daylight savings or other anomolies
          var newDate = new Date(this.element.data('startDate').getTime() - (MILLIS_IN_WEEK / 6));
          this._clearCalendar();
          this._loadCalEvents(newDate);
        },
  
        /*
          * Go to the next week relative to the currently displayed week
          */
        nextWeek: function() {
            //add 8 days to be sure of being in prev week - allows for daylight savings or other anomolies
            var newDate = new Date(this.element.data('startDate').getTime() + MILLIS_IN_WEEK + MILLIS_IN_DAY);
            this._clearCalendar();
            this._loadCalEvents(newDate);
        },
  
        /*
          * Reload the calendar to whatever week the date passed in falls on.
          */
        gotoWeek: function(date) {
            this._clearCalendar();
            this._loadCalEvents(date);
        },
  
        /*
          * Reload the calendar to whatever week the date passed in falls on.
          */
        gotoDate: function(date) {
            this._clearCalendar();
            this._loadCalEvents(date);
        },
  
        /**
          * change the number of days to show
          */
        setDaysToShow: function(daysToShow) {
            var self = this;
            var hour = self._getCurrentScrollHour();
            self.options.daysToShow = daysToShow;
            $(self.element).html('');
            self._renderCalendar();
            self._loadCalEvents();
            self._resizeCalendar();
            self._scrollToHour(hour, false);
  
            if (this.options.resizeEvent) {
              $(window).unbind(this.options.resizeEvent);
              $(window).bind(this.options.resizeEvent, function() {
                self._resizeCalendar();
              });
          }
        },
  
        /*
          * Remove an event based on it's id
          */
        removeEvent: function(eventId) {
  
            var self = this;
  
            self.element.find('.wc-cal-event').each(function() {
              if ($(this).data('calEvent').id === eventId) {
                  $(this).remove();
                  return false;
              }
            });
  
            //this could be more efficient rather than running on all days regardless...
            self.element.find('.wc-day-column-inner').each(function() {
              self._adjustOverlappingEvents($(this));
            });
        },
  
        /*
          * Removes any events that have been added but not yet saved (have no id).
          * This is useful to call after adding a freshly saved new event.
          */
        removeUnsavedEvents: function() {
  
            var self = this;
  
            self.element.find('.wc-new-cal-event').each(function() {
              $(this).remove();
            });
  
            //this could be more efficient rather than running on all days regardless...
            self.element.find('.wc-day-column-inner').each(function() {
              self._adjustOverlappingEvents($(this));
            });
        },
  
        /*
          * update an event in the calendar. If the event exists it refreshes
          * it's rendering. If it's a new event that does not exist in the calendar
          * it will be added.
          */
        updateEvent: function(calEvent) {
            this._updateEventInCalendar(calEvent);
        },
  
        /*
          * Returns an array of timeslot start and end times based on
          * the configured grid of the calendar. Returns in both date and
          * formatted time based on the 'timeFormat' config option.
          */
        getTimeslotTimes: function(date) {
            var options = this.options;
            var firstHourDisplayed = options.businessHours.limitDisplay ? options.businessHours.start : 0;
            var startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), firstHourDisplayed);
  
            var times = [],
                startMillis = startDate.getTime();
            for (var i = 0; i < options.timeslotsPerDay; i++) {
              var endMillis = startMillis + options.millisPerTimeslot;
              times[i] = {
                  start: new Date(startMillis),
                  startFormatted: this.formatTime(new Date(startMillis), options.timeFormat),
                  end: new Date(endMillis),
                  endFormatted: this.formatTime(new Date(endMillis), options.timeFormat)
              };
              startMillis = endMillis;
            }
            return times;
        },
  
        formatDate: function(date, format) {
            if (format) {
              return this._formatDate(date, format);
            } else {
              return this._formatDate(date, this.options.dateFormat);
            }
        },
  
        formatTime: function(date, format) {
            if (format) {
              return this._formatDate(date, format);
            } else if (this.options.timeFormat) {
              return this._formatDate(date, this.options.timeFormat);
            } else if (this.options.use24Hour) {
              return this._formatDate(date, 'H:i');
            } else {
              return this._formatDate(date, 'h:i a');
            }
        },
  
        serializeEvents: function() {
          var self = this;
          var calEvents = [];
  
          self.element.find('.wc-cal-event').each(function() {
            calEvents.push($(this).data('calEvent'));
          });
          return calEvents;
        },
  
        next: function() {
          if (this._startOnFirstDayOfWeek()) {
            return this.nextWeek();
          }
          var newDate = new Date(this.element.data('startDate').getTime());
          newDate.setDate(newDate.getDate() + this.options.daysToShow);
  
          this._clearCalendar();
          this._loadCalEvents(newDate);
        },
  
        prev: function() {
          if (this._startOnFirstDayOfWeek()) {
            return this.prevWeek();
          }
          var newDate = new Date(this.element.data('startDate').getTime());
          newDate.setDate(newDate.getDate() - this.options.daysToShow);
  
          this._clearCalendar();
          this._loadCalEvents(newDate);
        },
        getCurrentFirstDay: function() {
          return this._dateFirstDayOfWeek(this.options.date || new Date());
        },
        getCurrentLastDay: function() {
          return this._addDays(this.getCurrentFirstDay(), this.options.daysToShow - 1);
        },
  
        /*********************
          * private functions *
          *********************/
        _setOption: function(key, value) {
          var self = this;
          if (self.options[key] != value) {
            // event callback change, no need to re-render the events
            if (key == 'beforeEventNew') {
              self.options[key] = value;
              return;
            }
  
            // this could be made more efficient at some stage by caching the
            // events array locally in a store but this should be done in conjunction
            // with a proper binding model.
  
            var currentEvents = self.element.find('.wc-cal-event').map(function() {
              return $(this).data('calEvent');
            });
  
            var newOptions = {};
            newOptions[key] = value;
            self._renderEvents({events: currentEvents, options: newOptions}, self.element.find('.wc-day-column-inner'));
          }
        },
  
        // compute dynamic options based on other config values
        _computeOptions: function() {
          var options = this.options;
          if (options.businessHours.limitDisplay) {
            options.timeslotsPerDay = options.timeslotsPerHour * (options.businessHours.end - options.businessHours.start);
            options.millisToDisplay = (options.businessHours.end - options.businessHours.start) * 3600000; // 60 * 60 * 1000
            options.millisPerTimeslot = options.millisToDisplay / options.timeslotsPerDay;
          } else {
            options.timeslotsPerDay = options.timeslotsPerHour * 24;
            options.millisToDisplay = MILLIS_IN_DAY;
            options.millisPerTimeslot = MILLIS_IN_DAY / options.timeslotsPerDay;
          }
        },
  
        /*
          * Resize the calendar scrollable height based on the provided function in options.
          */
        _resizeCalendar: function() {
          var options = this.options;
          if (options && $.isFunction(options.height)) {
            var calendarHeight = options.height(this.element);
            var headerHeight = this.element.find('.wc-header').outerHeight();
            var navHeight = this.element.find('.wc-toolbar').outerHeight();
            var scrollContainerHeight = Math.max(calendarHeight - navHeight - headerHeight, options.minBodyHeight);
            var timeslotHeight = this.element.find('.wc-time-slots').outerHeight();
            this.element.find('.wc-scrollable-grid').height(scrollContainerHeight);
            if (timeslotHeight <= scrollContainerHeight) {
              this.element.find('.wc-scrollbar-shim').width(0);
            }
            else {
              this.element.find('.wc-scrollbar-shim').width(this._findScrollBarWidth());
            }
            this._trigger('resize', this.element);
          }
        },
  
        _findScrollBarWidth: function() {
          var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body');
          var child = parent.children();
          var width = child.innerWidth() - child.height(99).innerWidth();
          parent.remove();
          return width || /* default to 16 that is the average */ 16;
        },
  
        /*
          * configure calendar interaction events that are able to use event
          * delegation for greater efficiency
          */
        _setupEventDelegation: function() {
          var self = this;
          var options = this.options;
  
          this.element.click(function(event) {
            var $target = $(event.target),
                freeBusyManager;
  
            // click is disabled
            if ($target.data('preventClick')) {
              return;
            }
  
            var $calEvent = $target.hasClass('wc-cal-event') ?
              $target :
              $target.parents('.wc-cal-event');
            if (!$calEvent.length || !$calEvent.data('calEvent')) {
              return;
            }
  
            freeBusyManager = self.getFreeBusyManagerForEvent($calEvent.data('calEvent'));
  
            if (options.allowEventDelete && $target.hasClass('wc-cal-event-delete')) {
              options.eventDelete($calEvent.data('calEvent'), $calEvent, freeBusyManager, self.element, event);
            } else {
              options.eventClick($calEvent.data('calEvent'), $calEvent, freeBusyManager, self.element, event);
            }
          }).mouseover(function(event) {
            var $target = $(event.target);
            var $calEvent = $target.hasClass('wc-cal-event') ?
              $target :
              $target.parents('.wc-cal-event');
  
            if (!$calEvent.length || !$calEvent.data('calEvent')) {
              return;
            }
  
            if (self._isDraggingOrResizing($calEvent)) {
              return;
            }
  
            options.eventMouseover($calEvent.data('calEvent'), $calEvent, event);
          }).mouseout(function(event) {
            var $target = $(event.target);
            var $calEvent = $target.hasClass('wc-cal-event') ?
              $target :
              $target.parents('.wc-cal-event');
  
            if (!$calEvent.length || !$calEvent.data('calEvent')) {
              return;
            }
  
            if (self._isDraggingOrResizing($calEvent)) {
              return;
            }
  
            options.eventMouseout($calEvent.data('calEvent'), $calEvent, event);
          });
        },
  
        /**
         * check if a ui draggable or resizable is currently being dragged or
         * resized.
         */
        _isDraggingOrResizing: function($target) {
          return $target.hasClass('ui-draggable-dragging') ||
                 $target.hasClass('ui-resizable-resizing');
        },
  
        /*
          * Render the main calendar layout
          */
        _renderCalendar: function() {
          var $calendarContainer, $weekDayColumns;
          var self = this;
          var options = this.options;
  
          $calendarContainer = $('<div class=\"ui-widget wc-container\">').appendTo(self.element);
  
          //render the different parts
            // nav links
          self._renderCalendarButtons($calendarContainer);
            // header
          self._renderCalendarHeader($calendarContainer);
            // body
          self._renderCalendarBody($calendarContainer);
  
          $weekDayColumns = $calendarContainer.find('.wc-day-column-inner');
          $weekDayColumns.each(function(i, val) {
              if (!options.readonly) {
                self._addDroppableToWeekDay($(this));
                if (options.allowEventCreation) {
                  self._setupEventCreationForWeekDay($(this));
                }
              }
          });
        },
  
        /**
          * render the nav buttons on top of the calendar
          */
        _renderCalendarButtons: function($calendarContainer) {
          var self = this, options = this.options;
          if ( !options.showHeader ) return;
          if (options.buttons) {
              var calendarNavHtml = '';
  
              calendarNavHtml += '<div class=\"ui-widget-header wc-toolbar\">';
                calendarNavHtml += '<div class=\"wc-display\"></div>';
                calendarNavHtml += '<div class=\"wc-nav\">';
                  calendarNavHtml += '<button class=\"wc-prev\">' + options.buttonText.lastWeek + '</button>';
                  calendarNavHtml += '<button class=\"wc-today\">' + options.buttonText.today + '</button>';
                  calendarNavHtml += '<button class=\"wc-next\">' + options.buttonText.nextWeek + '</button>';
                calendarNavHtml += '</div>';
                calendarNavHtml += '<h1 class=\"wc-title\"></h1>';
              calendarNavHtml += '</div>';
  
              $(calendarNavHtml).appendTo($calendarContainer);
  
              $calendarContainer.find('.wc-nav .wc-today')
                .button({
                  icons: {primary: 'ui-icon-home'}})
                .click(function() {
                      self.today();
                      return false;
                    });
  
              $calendarContainer.find('.wc-nav .wc-prev')
                .button({
                  text: false,
                  icons: {primary: 'ui-icon-seek-prev'}})
                .click(function() {
                    self.element.weekCalendar('prev');
                    return false;
                  });
  
              $calendarContainer.find('.wc-nav .wc-next')
                .button({
                  text: false,
                  icons: {primary: 'ui-icon-seek-next'}})
                .click(function() {
                    self.element.weekCalendar('next');
                    return false;
                  });
  
              // now add buttons to switch display
              if (this.options.switchDisplay && $.isPlainObject(this.options.switchDisplay)) {
                var $container = $calendarContainer.find('.wc-display');
                $.each(this.options.switchDisplay, function(label, option) {
                        var _id = 'wc-switch-display-' + option;
                        var _input = $('<input type="radio" id="' + _id + '" name="wc-switch-display" class="wc-switch-display"/>');
                        var _label = $('<label for="' + _id + '"></label>');
                        _label.html(label);
                        _input.val(option);
                        if (parseInt(self.options.daysToShow, 10) === parseInt(option, 10)) {
                          _input.attr('checked', 'checked');
                        }
                        $container
                          .append(_input)
                          .append(_label);
                      });
                $container.find('input').change(function() {
                    self.setDaysToShow(parseInt($(this).val(), 10));
                  });
              }
              $calendarContainer.find('.wc-nav, .wc-display').buttonset();
              var _height = $calendarContainer.find('.wc-nav').outerHeight();
              $calendarContainer.find('.wc-title')
                .height(_height)
                .css('line-height', _height + 'px');
          }else{
              var calendarNavHtml = '';
              calendarNavHtml += '<div class=\"ui-widget-header wc-toolbar\">';
                calendarNavHtml += '<h1 class=\"wc-title\"></h1>';
              calendarNavHtml += '</div>';
              $(calendarNavHtml).appendTo($calendarContainer);
  
          }
        },
  
        /**
          * render the calendar header, including date and user header
          */
        _renderCalendarHeader: function($calendarContainer) {
          var self = this, options = this.options,
              showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
              rowspan = '', colspan = '', calendarHeaderHtml;
  
          if (showAsSeparatedUser) {
            rowspan = ' rowspan=\"2\"';
            colspan = ' colspan=\"' + options.users.length + '\" ';
          }
  
          //first row
          calendarHeaderHtml = '<div class=\"ui-widget-content wc-header\">';
          calendarHeaderHtml += '<table><tbody><tr><td class=\"wc-time-column-header\"></td>';
          for (var i = 1; i <= options.daysToShow; i++) {
            calendarHeaderHtml += '<td class=\"wc-day-column-header wc-day-' + i + '\"' + colspan + '></td>';
          }
          calendarHeaderHtml += '<td class=\"wc-scrollbar-shim\"' + rowspan + '></td></tr>';
  
          //users row
          if (showAsSeparatedUser) {
            calendarHeaderHtml += '<tr><td class=\"wc-time-column-header\"></td>';
            var uLength = options.users.length,
                _headerClass = '';
  
            for (var i = 1; i <= options.daysToShow; i++) {
              for (var j = 0; j < uLength; j++) {
                _headerClass = [];
                if (j == 0) {
                  _headerClass.push('wc-day-column-first');
                }
                if (j == uLength - 1) {
                  _headerClass.push('wc-day-column-last');
                }
                if (!_headerClass.length) {
                  _headerClass = 'wc-day-column-middle';
                }
                else {
                  _headerClass = _headerClass.join(' ');
                }
                calendarHeaderHtml += '<td class=\"' + _headerClass + ' wc-user-header wc-day-' + i + ' wc-user-' + self._getUserIdFromIndex(j) + '\">';
  //              calendarHeaderHtml+=    "<div class=\"wc-user-header wc-day-" + i + " wc-user-" + self._getUserIdFromIndex(j) +"\" >";
                calendarHeaderHtml += self._getUserName(j);
  //              calendarHeaderHtml+=    "</div>";
                calendarHeaderHtml += '</td>';
              }
            }
            calendarHeaderHtml += '</tr>';
          }
          //close the header
          calendarHeaderHtml += '</tbody></table></div>';
  
          $(calendarHeaderHtml).appendTo($calendarContainer);
        },
  
        /**
          * render the calendar body.
          * Calendar body is composed of several distinct parts.
          * Each part is displayed in a separated row to ease rendering.
          * for further explanations, see each part rendering function.
          */
        _renderCalendarBody: function($calendarContainer) {
          var self = this, options = this.options,
              showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
              $calendarBody, $calendarTableTbody;
          // create the structure
          $calendarBody = '<div class=\"wc-scrollable-grid\">';
          $calendarBody += '<table class=\"wc-time-slots\">';
          $calendarBody += '<tbody>';
          $calendarBody += '</tbody>';
          $calendarBody += '</table>';
          $calendarBody += '</div>';
          $calendarBody = $($calendarBody);
          $calendarTableTbody = $calendarBody.find('tbody');
  
          self._renderCalendarBodyTimeSlots($calendarTableTbody);
          self._renderCalendarBodyOddEven($calendarTableTbody);
          self._renderCalendarBodyFreeBusy($calendarTableTbody);
          self._renderCalendarBodyEvents($calendarTableTbody);
  
          $calendarBody.appendTo($calendarContainer);
  
          //set the column height
          $calendarContainer.find('.wc-full-height-column').height(options.timeslotHeight * options.timeslotsPerDay);
          //set the timeslot height
          $calendarContainer.find('.wc-time-slot').height(options.timeslotHeight - 1); //account for border
          //init the time row header height
          /**
    TODO    if total height for an hour is less than 11px, there is a display problem.
            Find a way to handle it
          */
          $calendarContainer.find('.wc-time-header-cell').css({
            height: (options.timeslotHeight * options.timeslotsPerHour) - 11,
            padding: 5
          });
          //add the user data to every impacted column
          if (showAsSeparatedUser) {
            for (var i = 0, uLength = options.users.length; i < uLength; i++) {
              $calendarContainer.find('.wc-user-' + self._getUserIdFromIndex(i))
                    .data('wcUser', options.users[i])
                    .data('wcUserIndex', i)
                    .data('wcUserId', self._getUserIdFromIndex(i));
            }
          }
        },
  
        /**
          * render the timeslots separation
          */
        _renderCalendarBodyTimeSlots: function($calendarTableTbody) {
          var options = this.options,
              renderRow, i, j,
              showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
              start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
              end = (options.businessHours.limitDisplay ? options.businessHours.end : 24),
              rowspan = 1;
  
          //calculate the rowspan
          if (options.displayOddEven) { rowspan += 1; }
          if (options.displayFreeBusys) { rowspan += 1; }
          if (rowspan > 1) {
            rowspan = ' rowspan=\"' + rowspan + '\"';
          }
          else {
            rowspan = '';
          }
  
          renderRow = '<tr class=\"wc-grid-row-timeslot\">';
          renderRow += '<td class=\"wc-grid-timeslot-header\"' + rowspan + '></td>';
          renderRow += '<td colspan=\"' + options.daysToShow * (showAsSeparatedUser ? options.users.length : 1) + '\">';
          renderRow += '<div class=\"wc-no-height-wrapper wc-time-slot-wrapper\">';
          renderRow += '<div class=\"wc-time-slots\">';
  
          for (i = start; i < end; i++) {
            for (j = 0; j < options.timeslotsPerHour - 1; j++) {
              renderRow += '<div class=\"wc-time-slot\"></div>';
            }
            renderRow += '<div class=\"wc-time-slot wc-hour-end\"></div>';
          }
  
          renderRow += '</div>';
          renderRow += '</div>';
          renderRow += '</td>';
          renderRow += '</tr>';
  
          $(renderRow).appendTo($calendarTableTbody);
        },
  
        /**
          * render the odd even columns
          */
        _renderCalendarBodyOddEven: function($calendarTableTbody) {
          if (this.options.displayOddEven) {
            var options = this.options,
                renderRow = '<tr class=\"wc-grid-row-oddeven\">',
                showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
                oddEven,
                // let's take advantage of the jquery ui framework
                oddEvenClasses = {'odd': 'wc-column-odd', 'even': 'ui-state-hover wc-column-even'};
  
            //now let's display oddEven placeholders
            for (var i = 1; i <= options.daysToShow; i++) {
                if (!showAsSeparatedUser) {
                  oddEven = (oddEven == 'odd' ? 'even' : 'odd');
                  renderRow += '<td class=\"wc-day-column day-' + i + '\">';
                  renderRow += '<div class=\"wc-no-height-wrapper wc-oddeven-wrapper\">';
                  renderRow += '<div class=\"wc-full-height-column ' + oddEvenClasses[oddEven] + '\"></div>';
                  renderRow += '</div>';
                  renderRow += '</td>';
                }
                else {
                  var uLength = options.users.length;
                  for (var j = 0; j < uLength; j++) {
                      oddEven = (oddEven == 'odd' ? 'even' : 'odd');
                      renderRow += '<td class=\"wc-day-column day-' + i + '\">';
                      renderRow += '<div class=\"wc-no-height-wrapper wc-oddeven-wrapper\">';
                      renderRow += '<div class=\"wc-full-height-column ' + oddEvenClasses[oddEven] + '\" ></div>';
                      renderRow += '</div>';
                      renderRow += '</td>';
                  }
              }
            }
            renderRow += '</tr>';
  
            $(renderRow).appendTo($calendarTableTbody);
          }
        },
  
        /**
          * render the freebusy placeholders
          */
        _renderCalendarBodyFreeBusy: function($calendarTableTbody) {
          if (this.options.displayFreeBusys) {
            var self = this, options = this.options,
                renderRow = '<tr class=\"wc-grid-row-freebusy\">',
                showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
            renderRow += '</td>';
  
            //now let's display freebusy placeholders
            for (var i = 1; i <= options.daysToShow; i++) {
              if (options.displayFreeBusys) {
                if (!showAsSeparatedUser) {
                  renderRow += '<td class=\"wc-day-column day-' + i + '\">';
                  renderRow += '<div class=\"wc-no-height-wrapper wc-freebusy-wrapper\">';
                  renderRow += '<div class=\"wc-full-height-column wc-column-freebusy wc-day-' + i + '\"></div>';
                  renderRow += '</div>';
                  renderRow += '</td>';
                }
                else {
                  var uLength = options.users.length;
                  for (var j = 0; j < uLength; j++) {
                      renderRow += '<td class=\"wc-day-column day-' + i + '\">';
                      renderRow += '<div class=\"wc-no-height-wrapper wc-freebusy-wrapper\">';
                      renderRow += '<div class=\"wc-full-height-column wc-column-freebusy wc-day-' + i;
                      renderRow += ' wc-user-' + self._getUserIdFromIndex(j) + '\">';
                      renderRow += '</div>';
                      renderRow += '</div>';
                      renderRow += '</td>';
                  }
                }
              }
            }
  
            renderRow += '</tr>';
  
            $(renderRow).appendTo($calendarTableTbody);
          }
        },
  
        /**
          * render the calendar body for event placeholders
          */
        _renderCalendarBodyEvents: function($calendarTableTbody) {
          var self = this, options = this.options,
              renderRow,
              showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
              start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
              end = (options.businessHours.limitDisplay ? options.businessHours.end : 24);
          renderRow = '<tr class=\"wc-grid-row-events\">';
          renderRow += '<td class=\"wc-grid-timeslot-header\">';
          for (var i = start; i < end; i++) {
            var bhClass = (options.businessHours.start <= i && options.businessHours.end > i) ? 'ui-state-active wc-business-hours' : 'ui-state-default';
            renderRow += '<div class=\"wc-hour-header ' + bhClass + '\">';
            if (options.use24Hour) {
              renderRow += '<div class=\"wc-time-header-cell\">' + self._24HourForIndex(i) + '</div>';
            }
            else {
              renderRow += '<div class=\"wc-time-header-cell\">' + self._hourForIndex(i) + '<span class=\"wc-am-pm\">' + self._amOrPm(i) + '</span></div>';
            }
            renderRow += '</div>';
          }
          renderRow += '</td>';
  
          //now let's display events placeholders
          var _columnBaseClass = 'ui-state-default wc-day-column';
          for (var i = 1; i <= options.daysToShow; i++) {
            if (!showAsSeparatedUser) {
              renderRow += '<td class=\"' + _columnBaseClass + ' wc-day-column-first wc-day-column-last day-' + i + '\">';
              renderRow += '<div class=\"wc-full-height-column wc-day-column-inner day-' + i + '\"></div>';
              renderRow += '</td>';
            }
            else {
              var uLength = options.users.length;
              var columnclass;
              for (var j = 0; j < uLength; j++) {
                columnclass = [];
                if (j == 0) {
                  columnclass.push('wc-day-column-first');
                }
                if (j == uLength - 1) {
                  columnclass.push('wc-day-column-last');
                }
                if (!columnclass.length) {
                  columnclass = 'wc-day-column-middle';
                }
                else {
                  columnclass = columnclass.join(' ');
                }
                renderRow += '<td class=\"' + _columnBaseClass + ' ' + columnclass + ' day-' + i + '\">';
                renderRow += '<div class=\"wc-full-height-column wc-day-column-inner day-' + i;
                renderRow += ' wc-user-' + self._getUserIdFromIndex(j) + '\">';
                renderRow += '</div>';
                renderRow += '</td>';
              }
            }
          }
  
          renderRow += '</tr>';
  
          $(renderRow).appendTo($calendarTableTbody);
        },
  
        /*
          * setup mouse events for capturing new events
          */
        _setupEventCreationForWeekDay: function($weekDay) {
            var self = this;
            var options = this.options;
            $weekDay.mousedown(function(event) {
              var $target = $(event.target);
              if ($target.hasClass('wc-day-column-inner')) {
  
                  var $newEvent = $('<div class=\"wc-cal-event wc-new-cal-event wc-new-cal-event-creating\"></div>');
  
                  $newEvent.css({lineHeight: (options.timeslotHeight - 2) + 'px', fontSize: (options.timeslotHeight / 2) + 'px'});
                  $target.append($newEvent);
  
                  var columnOffset = $target.offset().top;
                  var clickY = event.pageY - columnOffset;
                  var clickYRounded = (clickY - (clickY % options.timeslotHeight)) / options.timeslotHeight;
                  var topPosition = clickYRounded * options.timeslotHeight;
                  $newEvent.css({top: topPosition});
  
                  if (!options.preventDragOnEventCreation) {
                    $target.bind('mousemove.newevent', function(event) {
                      $newEvent.show();
                      $newEvent.addClass('ui-resizable-resizing');
                      var height = Math.round(event.pageY - columnOffset - topPosition);
                      var remainder = height % options.timeslotHeight;
                      //snap to closest timeslot
                      if (remainder < 0) {
                          var useHeight = height - remainder;
                          $newEvent.css('height', useHeight < options.timeslotHeight ? options.timeslotHeight : useHeight);
                      } else {
                          $newEvent.css('height', height + (options.timeslotHeight - remainder));
                      }
                    }).mouseup(function() {
                      $target.unbind('mousemove.newevent');
                      $newEvent.addClass('ui-corner-all');
                    });
                  }
              }
  
            }).mouseup(function(event) {
              var $target = $(event.target);
  
              var $weekDay = $target.closest('.wc-day-column-inner');
              var $newEvent = $weekDay.find('.wc-new-cal-event-creating');
  
              if ($newEvent.length) {
                  var createdFromSingleClick = !$newEvent.hasClass('ui-resizable-resizing');
  
                  //if even created from a single click only, default height
                  if (createdFromSingleClick) {
                    $newEvent.css({height: options.timeslotHeight * options.defaultEventLength}).show();
                  }
                  var top = parseInt($newEvent.css('top'));
                  var eventDuration = self._getEventDurationFromPositionedEventElement($weekDay, $newEvent, top);
  
                  $newEvent.remove();
                  var newCalEvent = {start: eventDuration.start, end: eventDuration.end, title: options.newEventText};
                  var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
  
                  if (showAsSeparatedUser) {
                    newCalEvent = self._setEventUserId(newCalEvent, $weekDay.data('wcUserId'));
                  }
                  else if (!options.showAsSeparateUsers && options.users && options.users.length == 1) {
                    newCalEvent = self._setEventUserId(newCalEvent, self._getUserIdFromIndex(0));
                  }
  
                  var freeBusyManager = self.getFreeBusyManagerForEvent(newCalEvent);
  
                  var $renderedCalEvent = self._renderEvent(newCalEvent, $weekDay);
  
                  if (!options.allowCalEventOverlap) {
                    self._adjustForEventCollisions($weekDay, $renderedCalEvent, newCalEvent, newCalEvent);
                    self._positionEvent($weekDay, $renderedCalEvent);
                  } else {
                    self._adjustOverlappingEvents($weekDay);
                  }
  
                  var proceed = self._trigger('beforeEventNew', event, {
                    'calEvent': newCalEvent,
                    'createdFromSingleClick': createdFromSingleClick,
                    'calendar': self.element
                  });
    							if (proceed) {
  									options.eventNew(newCalEvent, $renderedCalEvent, freeBusyManager, self.element, event);
  								}
  								else {
  									$($renderedCalEvent).remove();
  								}
              }
            });
        },
  
        /*
          * load calendar events for the week based on the date provided
          */
        _loadCalEvents: function(dateWithinWeek) {
  
            var date, weekStartDate, weekEndDate, $weekDayColumns;
            var self = this;
            var options = this.options;
            date = this._fixMinMaxDate(dateWithinWeek || options.date);
            // if date is not provided
            // or was not set
            // or is different than old one
            if ((!date || !date.getTime) ||
                (!options.date || !options.date.getTime) ||
                date.getTime() != options.date.getTime()
            ) {
              // trigger the changedate event
              this._trigger('changedate', this.element, date);
            }
            this.options.date = date;
            weekStartDate = self._dateFirstDayOfWeek(date);
            weekEndDate = self._dateLastMilliOfWeek(date);
  
            options.calendarBeforeLoad(self.element);
  
            self.element.data('startDate', weekStartDate);
            self.element.data('endDate', weekEndDate);
  
            $weekDayColumns = self.element.find('.wc-day-column-inner');
  
            self._updateDayColumnHeader($weekDayColumns);
  
            //load events by chosen means
            if (typeof options.data == 'string') {
              if (options.loading) {
                options.loading(true);
              }
              if (_currentAjaxCall) {
                // first abort current request.
                if (!_jQuery14OrLower) {
                  _currentAjaxCall.abort();
                } else {
                  // due to the fact that jquery 1.4 does not detect a request was
                  // aborted, we need to replace the onreadystatechange and
                  // execute the "complete" callback.
                  _currentAjaxCall.onreadystatechange = null;
                  _currentAjaxCall.abort();
                  _currentAjaxCall = null;
                  if (options.loading) {
                    options.loading(false);
                  }
                }
              }
              var jsonOptions = self._getJsonOptions();
              jsonOptions[options.startParam || 'start'] = Math.round(weekStartDate.getTime() / 1000);
              jsonOptions[options.endParam || 'end'] = Math.round(weekEndDate.getTime() / 1000);
              _currentAjaxCall = $.ajax({
                url: options.data,
                data: jsonOptions,
                dataType: 'json',
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                  // only prevent error with jQuery 1.5
                  // see issue #34. thanks to dapplebeforedawn
                  // (https://github.com/themouette/jquery-week-calendar/issues#issue/34)
                  // for 1.5+, aborted request mean errorThrown == 'abort'
                  // for prior version it means !errorThrown && !XMLHttpRequest.status
                  // fixes #55
                  if (errorThrown != 'abort' && XMLHttpRequest.status != 0) {
                    alert('unable to get data, error:' + textStatus);
                  }
                },
                success: function(data) {
                  self._renderEvents(data, $weekDayColumns);
                },
                complete: function() {
                  _currentAjaxCall = null;
                  if (options.loading) {
                    options.loading(false);
                  }
                }
              });
            }
            else if ($.isFunction(options.data)) {
              options.data(weekStartDate, weekEndDate,
                    function(data) {
                        self._renderEvents(data, $weekDayColumns);
                    });
            }
            else if (options.data) {
                  self._renderEvents(options.data, $weekDayColumns);
              }
  
            self._disableTextSelect($weekDayColumns);
        },
  
        /**
         * Draws a thin line which indicates the current time.
         */
        _drawCurrentHourLine: function() {
          var d = new Date(),
              options = this.options,
              businessHours = options.businessHours;
  
          // first, we remove the old hourline if it exists
          $('.wc-hourline', this.element).remove();
  
          // the line does not need to be displayed
          if (businessHours.limitDisplay && d.getHours() > businessHours.end) {
            return;
          }
  
          // then we recreate it
          var paddingStart = businessHours.limitDisplay ? businessHours.start : 0;
          var nbHours = d.getHours() - paddingStart + d.getMinutes() / 60;
          var positionTop = nbHours * options.timeslotHeight * options.timeslotsPerHour;
          var lineWidth = $('.wc-scrollable-grid .wc-today', this.element).width() + 3;
  
          $('.wc-scrollable-grid .wc-today', this.element).append(
            $('<div>', {
              'class': 'wc-hourline',
              style: 'top: ' + positionTop + 'px; width: ' + lineWidth + 'px'
            })
          );
        },
  
        /*
          * update the display of each day column header based on the calendar week
          */
        _updateDayColumnHeader: function($weekDayColumns) {
            var self = this;
            var options = this.options;
            var currentDay = self._cloneDate(self.element.data('startDate'));
            var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
            var todayClass = 'ui-state-active wc-today';
  
            self.element.find('.wc-header td.wc-day-column-header').each(function(i, val) {
              $(this).html(self._getHeaderDate(currentDay));
              if (self._isToday(currentDay)) {
                  $(this).addClass(todayClass);
              } else {
                  $(this).removeClass(todayClass);
              }
              currentDay = self._addDays(currentDay, 1);
  
            });
  
            currentDay = self._cloneDate(self.element.data('startDate'));
            if (showAsSeparatedUser)
            {
              self.element.find('.wc-header td.wc-user-header').each(function(i, val) {
                if (self._isToday(currentDay)) {
                    $(this).addClass(todayClass);
                } else {
                    $(this).removeClass(todayClass);
                }
                currentDay = ((i + 1) % options.users.length) ? currentDay : self._addDays(currentDay, 1);
              });
            }
  
            currentDay = self._cloneDate(self.element.data('startDate'));
  
            $weekDayColumns.each(function(i, val) {
  
              $(this).data('startDate', self._cloneDate(currentDay));
              $(this).data('endDate', new Date(currentDay.getTime() + (MILLIS_IN_DAY)));
              if (self._isToday(currentDay)) {
                  $(this).parent()
                      .addClass(todayClass)
                      .removeClass('ui-state-default');
              } else {
                  $(this).parent()
                      .removeClass(todayClass)
                      .addClass('ui-state-default');
              }
  
              if (!showAsSeparatedUser || !((i + 1) % options.users.length)) {
                currentDay = self._addDays(currentDay, 1);
              }
            });
  
            //now update the freeBusy placeholders
            if (options.displayFreeBusys) {
              currentDay = self._cloneDate(self.element.data('startDate'));
              self.element.find('.wc-grid-row-freebusy .wc-column-freebusy').each(function(i, val) {
                $(this).data('startDate', self._cloneDate(currentDay));
                $(this).data('endDate', new Date(currentDay.getTime() + (MILLIS_IN_DAY)));
                if (!showAsSeparatedUser || !((i + 1) % options.users.length)) {
                  currentDay = self._addDays(currentDay, 1);
                }
              });
            }
  
            // now update the calendar title
            if (this.options.title) {
              var date = this.options.date,
                  start = self._cloneDate(self.element.data('startDate')),
                  end = self._dateLastDayOfWeek(new Date(this._cloneDate(self.element.data('endDate')).getTime() - (MILLIS_IN_DAY))),
                  title = this._getCalendarTitle(),
                  date_format = options.dateFormat;
  
              // replace the placeholders contained in the title
              title = title.replace('%start%', self._formatDate(start, date_format));
              title = title.replace('%end%', self._formatDate(end, date_format));
              title = title.replace('%date%', self._formatDate(date, date_format));
  
              $('.wc-toolbar .wc-title', self.element).html(title);
            }
            //self._clearFreeBusys();
        },
  
        /**
         * Gets the calendar raw title.
         */
        _getCalendarTitle: function() {
          if ($.isFunction(this.options.title)) {
            return this.options.title(this.options.daysToShow);
          }
  
          return this.options.title || '';
        },
  
        /**
         * Render the events into the calendar
         */
        _renderEvents: function(data, $weekDayColumns) {
          var self = this;
          var options = this.options;
          var eventsToRender, nbRenderedEvents = 0;
  
          if (data.options) {
            var updateLayout = false;
            // update options
            $.each(data.options, function(key, value) {
              if (value !== options[key]) {
                options[key] = value;
                updateLayout = updateLayout || $.ui.weekCalendar.updateLayoutOptions[key];
              }
            });
  
            self._computeOptions();
  
            if (updateLayout) {
              var hour = self._getCurrentScrollHour();
              self.element.empty();
              self._renderCalendar();
              $weekDayColumns = self.element.find('.wc-time-slots .wc-day-column-inner');
              self._updateDayColumnHeader($weekDayColumns);
              self._resizeCalendar();
              self._scrollToHour(hour, false);
            }
          }
          this._clearCalendar();
  
          if ($.isArray(data)) {
            eventsToRender = self._cleanEvents(data);
          } else if (data.events) {
            eventsToRender = self._cleanEvents(data.events);
            self._renderFreeBusys(data);
          }
  
          $.each(eventsToRender, function(i, calEvent) {
            // render a multi day event as various event :
            // thanks to http://github.com/fbeauchamp/jquery-week-calendar
            var initialStart = new Date(calEvent.start);
            var initialEnd = new Date(calEvent.end);
            var maxHour = self.options.businessHours.limitDisplay ? self.options.businessHours.end : 24;
            var minHour = self.options.businessHours.limitDisplay ? self.options.businessHours.start : 0;
            var start = new Date(initialStart);
            var startDate = self._formatDate(start, 'Ymd');
            var endDate = self._formatDate(initialEnd, 'Ymd');
            var $weekDay;
            var isMultiday = false;
  
            while (startDate < endDate) {
              calEvent.start = start;
  
              // end of this virual calEvent is set to the end of the day
              calEvent.end.setFullYear(start.getFullYear());
              calEvent.end.setDate(start.getDate());
              calEvent.end.setMonth(start.getMonth());
              calEvent.end.setHours(maxHour, 0, 0);
  
              if (($weekDay = self._findWeekDayForEvent(calEvent, $weekDayColumns))) {
                self._renderEvent(calEvent, $weekDay);
                nbRenderedEvents += 1;
              }
  
              // start is set to the begin of the new day
              start.setDate(start.getDate() + 1);
              start.setHours(minHour, 0, 0);
  
              startDate = self._formatDate(start, 'Ymd');
              isMultiday = true;
            }
  
            if (start <= initialEnd) {
              calEvent.start = start;
              calEvent.end = initialEnd;
  
              if (((isMultiday && calEvent.start.getTime() != calEvent.end.getTime()) || !isMultiday) && ($weekDay = self._findWeekDayForEvent(calEvent, $weekDayColumns))) {
                self._renderEvent(calEvent, $weekDay);
                nbRenderedEvents += 1;
              }
            }
  
            // put back the initial start date
            calEvent.start = initialStart;
          });
  
          $weekDayColumns.each(function() {
            self._adjustOverlappingEvents($(this));
          });
  
          options.calendarAfterLoad(self.element);
  
          _hourLineTimeout && clearInterval(_hourLineTimeout);
  
          if (options.hourLine) {
            self._drawCurrentHourLine();
  
            _hourLineTimeout = setInterval(function() {
              self._drawCurrentHourLine();
            }, 60 * 1000); // redraw the line each minute
          }
  
          !nbRenderedEvents && options.noEvents();
        },
  
        /*
          * Render a specific event into the day provided. Assumes correct
          * day for calEvent date
          */
        _renderEvent: function(calEvent, $weekDay) {
            var self = this;
            var options = this.options;
            if (calEvent.start.getTime() > calEvent.end.getTime()) {
              return; // can't render a negative height
            }
  
            var eventClass, eventHtml, $calEventList, $modifiedEvent;
  
            eventClass = calEvent.id ? 'wc-cal-event' : 'wc-cal-event wc-new-cal-event';
            eventHtml = '<div class=\"' + eventClass + ' ui-corner-all\">';
            eventHtml += '<div class=\"wc-time ui-corner-top\"></div>';
            eventHtml += '<div class=\"wc-title\"></div></div>';
  
            $weekDay.each(function() {
              var $calEvent = $(eventHtml);
              $modifiedEvent = options.eventRender(calEvent, $calEvent);
              $calEvent = $modifiedEvent ? $modifiedEvent.appendTo($(this)) : $calEvent.appendTo($(this));
              $calEvent.css({lineHeight: (options.textSize + 2) + 'px', fontSize: options.textSize + 'px'});
  
              self._refreshEventDetails(calEvent, $calEvent);
              self._positionEvent($(this), $calEvent);
  
              //add to event list
              if ($calEventList) {
                $calEventList = $calEventList.add($calEvent);
              }
              else {
                $calEventList = $calEvent;
              }
            });
            $calEventList.show();
  
            if (!options.readonly && options.resizable(calEvent, $calEventList)) {
              self._addResizableToCalEvent(calEvent, $calEventList, $weekDay);
            }
            if (!options.readonly && options.draggable(calEvent, $calEventList)) {
              self._addDraggableToCalEvent(calEvent, $calEventList);
            }
            options.eventAfterRender(calEvent, $calEventList);
  
            return $calEventList;
  
        },
        addEvent: function() {
          return this._renderEvent.apply(this, arguments);
        },
  
        _adjustOverlappingEvents: function($weekDay) {
            var self = this;
            if (self.options.allowCalEventOverlap) {
              var groupsList = self._groupOverlappingEventElements($weekDay);
              $.each(groupsList, function() {
                  var curGroups = this;
                  $.each(curGroups, function(groupIndex) {
                    var curGroup = this;
  
                    // do we want events to be displayed as overlapping
                    if (self.options.overlapEventsSeparate) {
                        var newWidth = self.options.totalEventsWidthPercentInOneColumn / curGroups.length;
                        var newLeft = groupIndex * newWidth;
                    } else {
                        // TODO what happens when the group has more than 10 elements
                        var newWidth = self.options.totalEventsWidthPercentInOneColumn - ((curGroups.length - 1) * 10);
                        var newLeft = groupIndex * 10;
                    }
                    $.each(curGroup, function() {
                        // bring mouseovered event to the front
                        if (!self.options.overlapEventsSeparate) {
                          $(this).bind('mouseover.z-index', function() {
                              var $elem = $(this);
                              $.each(curGroup, function() {
                                $(this).css({'z-index': '1'});
                              });
                              $elem.css({'z-index': '3'});
                          });
                        }
                        $(this).css({width: newWidth + '%', left: newLeft + '%', right: 0});
                    });
                  });
              });
            }
        },
  
  
        /*
          * Find groups of overlapping events
          */
        _groupOverlappingEventElements: function($weekDay) {
            var $events = $weekDay.find('.wc-cal-event:visible');
            var sortedEvents = $events.sort(function(a, b) {
              return $(a).data('calEvent').start.getTime() - $(b).data('calEvent').start.getTime();
            });
  
            var lastEndTime = new Date(0, 0, 0);
            var groups = [];
            var curGroups = [];
            var $curEvent;
            $.each(sortedEvents, function() {
              $curEvent = $(this);
              //checks, if the current group list is not empty, if the overlapping is finished
              if (curGroups.length > 0) {
                  if (lastEndTime.getTime() <= $curEvent.data('calEvent').start.getTime()) {
                    //finishes the current group list by adding it to the resulting list of groups and cleans it
  
                    groups.push(curGroups);
                    curGroups = [];
                  }
              }
  
              //finds the first group to fill with the event
              for (var groupIndex = 0; groupIndex < curGroups.length; groupIndex++) {
                  if (curGroups[groupIndex].length > 0) {
                    //checks if the event starts after the end of the last event of the group
                    if (curGroups[groupIndex][curGroups[groupIndex].length - 1].data('calEvent').end.getTime() <= $curEvent.data('calEvent').start.getTime()) {
                        curGroups[groupIndex].push($curEvent);
                        if (lastEndTime.getTime() < $curEvent.data('calEvent').end.getTime()) {
                          lastEndTime = $curEvent.data('calEvent').end;
                        }
                        return;
                    }
                  }
              }
              //if not found, creates a new group
              curGroups.push([$curEvent]);
              if (lastEndTime.getTime() < $curEvent.data('calEvent').end.getTime()) {
                  lastEndTime = $curEvent.data('calEvent').end;
              }
            });
            //adds the last groups in result
            if (curGroups.length > 0) {
              groups.push(curGroups);
            }
            return groups;
        },
  
  
        /*
          * find the weekday in the current calendar that the calEvent falls within
          */
        _findWeekDayForEvent: function(calEvent, $weekDayColumns) {
  
            var $weekDay,
                options = this.options,
                showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
                user_ids = this._getEventUserId(calEvent);
  
            if (!$.isArray(user_ids)) {
              user_ids = [user_ids];
            }
  
            $weekDayColumns.each(function(index, curDay) {
              if ($(this).data('startDate').getTime() <= calEvent.start.getTime() &&
                    $(this).data('endDate').getTime() >= calEvent.end.getTime() &&
                    (!showAsSeparatedUser || $.inArray($(this).data('wcUserId'), user_ids) !== -1)
              ) {
                  if ($weekDay) {
                  $weekDay = $weekDay.add($(curDay));
                  }
                  else {
                  $weekDay = $(curDay);
                  }
              }
            });
  
            return $weekDay;
        },
  
        /*
          * update the events rendering in the calendar. Add if does not yet exist.
          */
        _updateEventInCalendar: function(calEvent) {
            var self = this;
            self._cleanEvent(calEvent);
  
            if (calEvent.id) {
              self.element.find('.wc-cal-event').each(function() {
                  if ($(this).data('calEvent').id === calEvent.id || $(this).hasClass('wc-new-cal-event')) {
                    $(this).remove();
                //     return false;
                  }
              });
            }
  
            var $weekDays = self._findWeekDayForEvent(calEvent, self.element.find('.wc-grid-row-events .wc-day-column-inner'));
            if ($weekDays) {
              $weekDays.each(function(index, weekDay) {
                var $weekDay = $(weekDay);
                var $calEvent = self._renderEvent(calEvent, $weekDay);
                self._adjustForEventCollisions($weekDay, $calEvent, calEvent, calEvent);
                self._refreshEventDetails(calEvent, $calEvent);
                self._positionEvent($weekDay, $calEvent);
                self._adjustOverlappingEvents($weekDay);
              });
            }
        },
  
        /*
          * Position the event element within the weekday based on it's start / end dates.
          */
        _positionEvent: function($weekDay, $calEvent) {
            var options = this.options;
            var calEvent = $calEvent.data('calEvent');
            var pxPerMillis = $weekDay.height() / options.millisToDisplay;
            var firstHourDisplayed = options.businessHours.limitDisplay ? options.businessHours.start : 0;
            var startMillis = this._getDSTdayShift(calEvent.start).getTime() - this._getDSTdayShift(new Date(calEvent.start.getFullYear(), calEvent.start.getMonth(), calEvent.start.getDate(), firstHourDisplayed)).getTime();
            var eventMillis = this._getDSTdayShift(calEvent.end).getTime() - this._getDSTdayShift(calEvent.start).getTime();
            var pxTop = pxPerMillis * startMillis;
            var pxHeight = pxPerMillis * eventMillis;
            //var pxHeightFallback = pxPerMillis * (60 / options.timeslotsPerHour) * 60 * 1000;
        $calEvent.css({top: pxTop, height: pxHeight || (pxPerMillis * 3600000 / options.timeslotsPerHour)});
        },
  
        /*
          * Determine the actual start and end times of a calevent based on it's
          * relative position within the weekday column and the starting hour of the
          * displayed calendar.
          */
        _getEventDurationFromPositionedEventElement: function($weekDay, $calEvent, top) {
            var options = this.options;
            var startOffsetMillis = options.businessHours.limitDisplay ? options.businessHours.start * 3600000 : 0;
            var start = new Date($weekDay.data('startDate').getTime() + startOffsetMillis + Math.round(top / options.timeslotHeight) * options.millisPerTimeslot);
            var end = new Date(start.getTime() + ($calEvent.height() / options.timeslotHeight) * options.millisPerTimeslot);
            return {start: this._getDSTdayShift(start, -1), end: this._getDSTdayShift(end, -1)};
        },
  
        /*
          * If the calendar does not allow event overlap, adjust the start or end date if necessary to
          * avoid overlapping of events. Typically, shortens the resized / dropped event to it's max possible
          * duration  based on the overlap. If no satisfactory adjustment can be made, the event is reverted to
          * it's original location.
          */
        _adjustForEventCollisions: function($weekDay, $calEvent, newCalEvent, oldCalEvent, maintainEventDuration) {
            var options = this.options;
  
            if (options.allowCalEventOverlap) {
              return;
            }
            var adjustedStart, adjustedEnd;
            var self = this;
  
            $weekDay.find('.wc-cal-event').not($calEvent).each(function() {
              var currentCalEvent = $(this).data('calEvent');
  
              //has been dropped onto existing event overlapping the end time
              if (newCalEvent.start.getTime() < currentCalEvent.end.getTime() &&
                    newCalEvent.end.getTime() >= currentCalEvent.end.getTime()) {
  
                  adjustedStart = currentCalEvent.end;
              }
  
  
              //has been dropped onto existing event overlapping the start time
              if (newCalEvent.end.getTime() > currentCalEvent.start.getTime() &&
                    newCalEvent.start.getTime() <= currentCalEvent.start.getTime()) {
  
                  adjustedEnd = currentCalEvent.start;
              }
              //has been dropped inside existing event with same or larger duration
              if (oldCalEvent.resizable == false ||
                    (newCalEvent.end.getTime() <= currentCalEvent.end.getTime() &&
                      newCalEvent.start.getTime() >= currentCalEvent.start.getTime())) {
  
                  adjustedStart = oldCalEvent.start;
                  adjustedEnd = oldCalEvent.end;
                  return false;
              }
  
            });
  
  
            newCalEvent.start = adjustedStart || newCalEvent.start;
  
            if (adjustedStart && maintainEventDuration) {
              newCalEvent.end = new Date(adjustedStart.getTime() + (oldCalEvent.end.getTime() - oldCalEvent.start.getTime()));
              self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, oldCalEvent);
            } else {
              newCalEvent.end = adjustedEnd || newCalEvent.end;
            }
  
  
            //reset if new cal event has been forced to zero size
            if (newCalEvent.start.getTime() >= newCalEvent.end.getTime()) {
              newCalEvent.start = oldCalEvent.start;
              newCalEvent.end = oldCalEvent.end;
            }
  
            $calEvent.data('calEvent', newCalEvent);
        },
  
        /**
         * Add draggable capabilities to an event
         */
        _addDraggableToCalEvent: function(calEvent, $calEvent) {
          var options = this.options;
  
          $calEvent.draggable({
            handle: '.wc-time',
            containment: 'div.wc-time-slots',
            snap: '.wc-day-column-inner',
            snapMode: 'inner',
            snapTolerance: options.timeslotHeight - 1,
            revert: 'invalid',
            opacity: 0.5,
            grid: [$calEvent.outerWidth() + 1, options.timeslotHeight],
            start: function(event, ui) {
              var $calEvent = ui.draggable || ui.helper;
              options.eventDrag(calEvent, $calEvent);
            }
          });
        },
  
        /*
          * Add droppable capabilites to weekdays to allow dropping of calEvents only
          */
        _addDroppableToWeekDay: function($weekDay) {
            var self = this;
            var options = this.options;
            $weekDay.droppable({
              accept: '.wc-cal-event',
              drop: function(event, ui) {
                  var $calEvent = ui.draggable;
                  var top = Math.round(parseInt(ui.position.top));
                  var eventDuration = self._getEventDurationFromPositionedEventElement($weekDay, $calEvent, top);
                  var calEvent = $calEvent.data('calEvent');
                  var newCalEvent = $.extend(true, {}, calEvent, {start: eventDuration.start, end: eventDuration.end});
                  var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
                  if (showAsSeparatedUser) {
                    // we may have dragged the event on column with a new user.
                    // nice way to handle that is:
                    //  - get the newly dragged on user
                    //  - check if user is part of the event
                    //  - if yes, nothing changes, if not, find the old owner to remove it and add new one
                    var newUserId = $weekDay.data('wcUserId');
                    var userIdList = self._getEventUserId(calEvent);
                    var oldUserId = $(ui.draggable.parents('.wc-day-column-inner').get(0)).data('wcUserId');
                    if (!$.isArray(userIdList)) {
                      userIdList = [userIdList];
                    }
                    if ($.inArray(newUserId, userIdList) == -1) {
                      // remove old user
                      var _index = $.inArray(oldUserId, userIdList);
                      userIdList.splice(_index, 1);
                      // add new user ?
                      if ($.inArray(newUserId, userIdList) == -1) {
                        userIdList.push(newUserId);
                      }
                    }
                    newCalEvent = self._setEventUserId(newCalEvent, ((userIdList.length == 1) ? userIdList[0] : userIdList));
                  }
                  self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, calEvent, true);
                  var $weekDayColumns = self.element.find('.wc-day-column-inner');
  
                  //trigger drop callback
                  options.eventDrop(newCalEvent, calEvent, $calEvent);
  
                  var $newEvent = self._renderEvent(newCalEvent, self._findWeekDayForEvent(newCalEvent, $weekDayColumns));
                  $calEvent.hide();
  
                  $calEvent.data('preventClick', true);
  
                  var $weekDayOld = self._findWeekDayForEvent($calEvent.data('calEvent'), self.element.find('.wc-time-slots .wc-day-column-inner'));
  
                  if ($weekDayOld.data('startDate') != $weekDay.data('startDate')) {
                    self._adjustOverlappingEvents($weekDayOld);
                  }
                  self._adjustOverlappingEvents($weekDay);
  
                  setTimeout(function() {
                    $calEvent.remove();
                  }, 1000);
  
              }
            });
        },
  
        /*
          * Add resizable capabilities to a calEvent
          */
        _addResizableToCalEvent: function(calEvent, $calEvent, $weekDay) {
            var self = this;
            var options = this.options;
            $calEvent.resizable({
              grid: options.timeslotHeight,
              containment: $weekDay,
              handles: 's',
              minHeight: options.timeslotHeight,
              stop: function(event, ui) {
                  var $calEvent = ui.element;
                  var newEnd = new Date($calEvent.data('calEvent').start.getTime() + Math.max(1, Math.round(ui.size.height / options.timeslotHeight)) * options.millisPerTimeslot);
                  if (self._needDSTdayShift($calEvent.data('calEvent').start, newEnd))
              newEnd = self._getDSTdayShift(newEnd, -1);
                  var newCalEvent = $.extend(true, {}, calEvent, {start: calEvent.start, end: newEnd});
                  self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, calEvent);
  
                  //trigger resize callback
                  options.eventResize(newCalEvent, calEvent, $calEvent);
                  self._refreshEventDetails(newCalEvent, $calEvent);
                  self._positionEvent($weekDay, $calEvent);
                  self._adjustOverlappingEvents($weekDay);
                  $calEvent.data('preventClick', true);
                  setTimeout(function() {
                    $calEvent.removeData('preventClick');
                  }, 500);
              }
            });
            $('.ui-resizable-handle', $calEvent).text('=');
        },
  
        /*
          * Refresh the displayed details of a calEvent in the calendar
          */
        _refreshEventDetails: function(calEvent, $calEvent) {
  	  var suffix = '';
  	  if (!this.options.readonly &&
  		 this.options.allowEventDelete &&
  		 this.options.deletable(calEvent,$calEvent)) {
  	      suffix = '<div class="wc-cal-event-delete ui-icon ui-icon-close"></div>';
  	  }
            $calEvent.find('.wc-time').html(this.options.eventHeader(calEvent, this.element) + suffix);
            $calEvent.find('.wc-title').html(this.options.eventBody(calEvent, this.element));
            $calEvent.data('calEvent', calEvent);
            this.options.eventRefresh(calEvent, $calEvent);
        },
  
        /*
          * Clear all cal events from the calendar
          */
        _clearCalendar: function() {
            this.element.find('.wc-day-column-inner div').remove();
            this._clearFreeBusys();
        },
  
        /*
          * Scroll the calendar to a specific hour
          */
        _scrollToHour: function(hour, animate) {
            var self = this;
            var options = this.options;
            var $scrollable = this.element.find('.wc-scrollable-grid');
            var slot = hour;
            if (self.options.businessHours.limitDisplay) {
              if (hour <= self.options.businessHours.start) {
                  slot = 0;
              } else if (hour >= self.options.businessHours.end) {
                  slot = self.options.businessHours.end - self.options.businessHours.start - 1;
              } else {
                  slot = hour - self.options.businessHours.start;
              }
            }
  
            var $target = this.element.find('.wc-grid-timeslot-header .wc-hour-header:eq(' + slot + ')');
  
            $scrollable.animate({scrollTop: 0}, 0, function() {
              var targetOffset = $target.offset().top;
              var scroll = targetOffset - $scrollable.offset().top - $target.outerHeight();
              if (animate) {
                $scrollable.animate({scrollTop: scroll}, options.scrollToHourMillis);
              }
              else {
                $scrollable.animate({scrollTop: scroll}, 0);
              }
            });
        },
  
        /*
          * find the hour (12 hour day) for a given hour index
          */
        _hourForIndex: function(index) {
            if (index === 0) { //midnight
              return 12;
            } else if (index < 13) { //am
              return index;
            } else { //pm
              return index - 12;
            }
        },
  
        _24HourForIndex: function(index) {
            if (index === 0) { //midnight
              return '00:00';
            } else if (index < 10) {
              return '0' + index + ':00';
            } else {
              return index + ':00';
            }
        },
  
        _amOrPm: function(hourOfDay) {
            return hourOfDay < 12 ? 'AM' : 'PM';
        },
  
        _isToday: function(date) {
            var clonedDate = this._cloneDate(date);
            this._clearTime(clonedDate);
            var today = new Date();
            this._clearTime(today);
            return today.getTime() === clonedDate.getTime();
        },
  
        /*
         * Clean events to ensure correct format
         */
        _cleanEvents: function(events) {
            var self = this;
            $.each(events, function(i, event) {
              self._cleanEvent(event);
            });
            return events;
        },
  
        /*
         * Clean specific event
         */
        _cleanEvent: function(event) {
            if (event.date) {
              event.start = event.date;
            }
            event.start = this._cleanDate(event.start);
            event.end = this._cleanDate(event.end);
            if (!event.end) {
              event.end = this._addDays(this._cloneDate(event.start), 1);
            }
        },
  
        /*
         * Disable text selection of the elements in different browsers
         */
        _disableTextSelect: function($elements) {
            $elements.each(function() {
              if ($.browser.mozilla) {//Firefox
                  $(this).css('MozUserSelect', 'none');
              } else if ($.browser.msie) {//IE
                  $(this).bind('selectstart', function() {
                    return false;
                  });
              } else {//Opera, etc.
                  $(this).mousedown(function() {
                    return false;
                  });
              }
            });
        },
  
        /*
         * returns the date on the first millisecond of the week
         */
        _dateFirstDayOfWeek: function(date) {
          var self = this;
          var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
          var adjustedDate = new Date(midnightCurrentDate);
          adjustedDate.setDate(adjustedDate.getDate() - self._getAdjustedDayIndex(midnightCurrentDate));
  
          return adjustedDate;
        },
  
        /*
         * returns the date on the first millisecond of the last day of the week
         */
        _dateLastDayOfWeek: function(date) {
          var self = this;
          var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
          var adjustedDate = new Date(midnightCurrentDate);
          var daysToAdd = (self.options.daysToShow - 1 - self._getAdjustedDayIndex(midnightCurrentDate));
          adjustedDate.setDate(adjustedDate.getDate() + daysToAdd);
  
          return adjustedDate;
        },
  
        /**
         * fix the date if it is not within given options
         * minDate and maxDate
         */
        _fixMinMaxDate: function(date) {
          var minDate, maxDate;
          date = this._cleanDate(date);
  
          // not less than minDate
          if (this.options.minDate) {
            minDate = this._cleanDate(this.options.minDate);
            // midnight on minDate
            minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
            if (date.getTime() < minDate.getTime()) {
              this._trigger('reachedmindate', this.element, date);
            }
            date = this._cleanDate(Math.max(date.getTime(), minDate.getTime()));
          }
  
          // not more than maxDate
          if (this.options.maxDate) {
            maxDate = this._cleanDate(this.options.maxDate);
            // apply correction for max date if not startOnFirstDayOfWeek
            // to make sure no further date is displayed.
            // otherwise, the complement will still be shown
            if (!this._startOnFirstDayOfWeek()) {
              var day = maxDate.getDate() - this.options.daysToShow + 1;
              maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), day);
            }
            // microsecond before midnight on maxDate
            maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999);
            if (date.getTime() > maxDate.getTime()) {
              this._trigger('reachedmaxdate', this.element, date);
            }
            date = this._cleanDate(Math.min(date.getTime(), maxDate.getTime()));
          }
  
          return date;
        },
  
        /*
          * gets the index of the current day adjusted based on options
          */
        _getAdjustedDayIndex: function(date) {
            if (!this._startOnFirstDayOfWeek()) {
              return 0;
            }
  
            var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
            var currentDayOfStandardWeek = midnightCurrentDate.getDay();
            var days = [0, 1, 2, 3, 4, 5, 6];
            this._rotate(days, this._firstDayOfWeek());
            return days[currentDayOfStandardWeek];
        },
  
        _firstDayOfWeek: function() {
          if ($.isFunction(this.options.firstDayOfWeek)) {
            return this.options.firstDayOfWeek(this.element);
          }
          return this.options.firstDayOfWeek;
        },
  
        /*
          * returns the date on the last millisecond of the week
          */
        _dateLastMilliOfWeek: function(date) {
            var lastDayOfWeek = this._dateLastDayOfWeek(date);
            lastDayOfWeek = this._cloneDate(lastDayOfWeek);
            lastDayOfWeek.setDate(lastDayOfWeek.getDate() + 1);
            return lastDayOfWeek;
  
        },
  
        /*
          * Clear the time components of a date leaving the date
          * of the first milli of day
          */
        _clearTime: function(d) {
            d.setHours(0);
            d.setMinutes(0);
            d.setSeconds(0);
            d.setMilliseconds(0);
            return d;
        },
  
        /*
          * add specific number of days to date
          */
        _addDays: function(d, n, keepTime) {
            d.setDate(d.getDate() + n);
            if (keepTime) {
              return d;
            }
            return this._clearTime(d);
        },
  
        /*
          * Rotate an array by specified number of places.
          */
        _rotate: function(a /*array*/, p /* integer, positive integer rotate to the right, negative to the left... */) {
            for (var l = a.length, p = (Math.abs(p) >= l && (p %= l), p < 0 && (p += l), p), i, x; p; p = (Math.ceil(l / p) - 1) * p - l + (l = p)) {
              for (i = l; i > p; x = a[--i], a[i] = a[i - p], a[i - p] = x) {}
            }
            return a;
        },
  
        _cloneDate: function(d) {
            return new Date(d.getTime());
        },
  
        /**
         * Return a Date instance for different representations.
         * Valid representations are:
         *  * timestamps
         *  * Date objects
         *  * textual representations (only these accepted by the Date
         *    constructor)
         *
         *  @return {Date} The clean date object.
         */
        _cleanDate: function(d) {
            if (typeof d === 'string') {
              // if is numeric
              if (!isNaN(Number(d))) {
                return this._cleanDate(parseInt(d, 10));
              }
  
              // this is a human readable date
              return Date.parse(d) || new Date(d);
            }
  
            if (typeof d == 'number') {
              return new Date(d);
            }
  
            return d;
        },
  
        /*
          * date formatting is adapted from
          * http://jacwright.com/projects/javascript/date_format
          */
        _formatDate: function(date, format) {
          var returnStr = '';
          for (var i = 0; i < format.length; i++) {
            var curChar = format.charAt(i);
            if (i != 0 && format.charAt(i - 1) == '\\') {
              returnStr += curChar;
            }
            else if (this._replaceChars[curChar]) {
              returnStr += this._replaceChars[curChar](date, this);
            } else if (curChar != '\\') {
              returnStr += curChar;
            }
          }
          return returnStr;
        },
  
        _replaceChars: {
        // Day
        d: function(date) { return (date.getDate() < 10 ? '0' : '') + date.getDate(); },
        D: function(date, calendar) { return calendar.options.shortDays[date.getDay()]; },
        j: function(date) { return date.getDate(); },
        l: function(date, calendar) { return calendar.options.longDays[date.getDay()]; },
        N: function(date) { var _d = date.getDay(); return _d ? _d : 7; },
        S: function(date) { return (date.getDate() % 10 == 1 && date.getDate() != 11 ? 'st' : (date.getDate() % 10 == 2 && date.getDate() != 12 ? 'nd' : (date.getDate() % 10 == 3 && date.getDate() != 13 ? 'rd' : 'th'))); },
        w: function(date) { return date.getDay(); },
        z: function(date) { var d = new Date(date.getFullYear(), 0, 1); return Math.ceil((date - d) / 86400000); }, // Fixed now
        // Week
        W: function(date) { var d = new Date(date.getFullYear(), 0, 1); return Math.ceil((((date - d) / 86400000) + d.getDay() + 1) / 7); }, // Fixed now
        // Month
        F: function(date, calendar) { return calendar.options.longMonths[date.getMonth()]; },
        m: function(date) { return (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1); },
        M: function(date, calendar) { return calendar.options.shortMonths[date.getMonth()]; },
        n: function(date) { return date.getMonth() + 1; },
        t: function(date) { var d = date; return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate() }, // Fixed now, gets #days of date
        // Year
        L: function(date) { var year = date.getFullYear(); return (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)); },  // Fixed now
        o: function(date) { var d = new Date(date.valueOf()); d.setDate(d.getDate() - ((date.getDay() + 6) % 7) + 3); return d.getFullYear();}, //Fixed now
        Y: function(date) { return date.getFullYear(); },
        y: function(date) { return ('' + date.getFullYear()).substr(2); },
        // Time
        a: function(date) { return date.getHours() < 12 ? 'am' : 'pm'; },
        A: function(date) { return date.getHours() < 12 ? 'AM' : 'PM'; },
        B: function(date) { return Math.floor((((date.getUTCHours() + 1) % 24) + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600) * 1000 / 24); }, // Fixed now
        g: function(date) { return date.getHours() % 12 || 12; },
        G: function(date) { return date.getHours(); },
        h: function(date) { return ((date.getHours() % 12 || 12) < 10 ? '0' : '') + (date.getHours() % 12 || 12); },
        H: function(date) { return (date.getHours() < 10 ? '0' : '') + date.getHours(); },
        i: function(date) { return (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); },
        s: function(date) { return (date.getSeconds() < 10 ? '0' : '') + date.getSeconds(); },
        u: function(date) { var m = date.getMilliseconds(); return (m < 10 ? '00' : (m < 100 ? '0' : '')) + m; },
        // Timezone
        e: function(date) { return 'Not Yet Supported'; },
        I: function(date) { return 'Not Yet Supported'; },
        O: function(date) { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + '00'; },
        P: function(date) { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + ':00'; }, // Fixed now
        T: function(date) { var m = date.getMonth(); date.setMonth(0); var result = date.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1'); date.setMonth(m); return result;},
        Z: function(date) { return -date.getTimezoneOffset() * 60; },
        // Full Date/Time
        c: function(date, calendar) { return calendar._formatDate(date, 'Y-m-d\\TH:i:sP'); }, // Fixed now
        r: function(date, calendar) { return calendar._formatDate(date, 'D, d M Y H:i:s O'); },
        U: function(date) { return date.getTime() / 1000; }
        },
  
        /* USER MANAGEMENT FUNCTIONS */
  
        getUserForId: function(id) {
          return $.extend({}, this.options.users[this._getUserIndexFromId(id)]);
        },
  
        /**
          * return the user name for header
          */
        _getUserName: function(index) {
            var self = this;
            var options = this.options;
            var user = options.users[index];
            if ($.isFunction(options.getUserName)) {
            return options.getUserName(user, index, self.element);
            }
            else {
            return user;
            }
        },
        /**
          * return the user id for given index
          */
        _getUserIdFromIndex: function(index) {
            var self = this;
            var options = this.options;
            if ($.isFunction(options.getUserId)) {
            return options.getUserId(options.users[index], index, self.element);
            }
            return index;
        },
        /**
          * returns the associated user index for given ID
          */
        _getUserIndexFromId: function(id) {
            var self = this;
            var options = this.options;
            for (var i = 0; i < options.users.length; i++) {
              if (self._getUserIdFromIndex(i) == id) {
                return i;
              }
            }
            return 0;
        },
        /**
          * return the user ids for given calEvent.
          * default is calEvent.userId field.
          */
        _getEventUserId: function(calEvent) {
            var self = this;
            var options = this.options;
            if (options.showAsSeparateUsers && options.users && options.users.length) {
              if ($.isFunction(options.getEventUserId)) {
              return options.getEventUserId(calEvent, self.element);
              }
              return calEvent.userId;
            }
            return [];
        },
        /**
          * sets the event user id on given calEvent
          * default is calEvent.userId field.
          */
        _setEventUserId: function(calEvent, userId) {
            var self = this;
            var options = this.options;
            if ($.isFunction(options.setEventUserId)) {
              return options.setEventUserId(userId, calEvent, self.element);
            }
            calEvent.userId = userId;
            return calEvent;
        },
        /**
          * return the user ids for given freeBusy.
          * default is freeBusy.userId field.
          */
        _getFreeBusyUserId: function(freeBusy) {
          var self = this;
          var options = this.options;
          if ($.isFunction(options.getFreeBusyUserId)) {
            return options.getFreeBusyUserId(freeBusy.getOption(), self.element);
          }
          return freeBusy.getOption('userId');
        },
  
        /* FREEBUSY MANAGEMENT */
  
        /**
          * ckean the free busy managers and remove all the freeBusy
          */
        _clearFreeBusys: function() {
          if (this.options.displayFreeBusys) {
            var self = this,
                options = this.options,
                $freeBusyPlaceholders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy');
            $freeBusyPlaceholders.each(function() {
              $(this).data('wcFreeBusyManager', new FreeBusyManager({
                  start: self._cloneDate($(this).data('startDate')),
                  end: self._cloneDate($(this).data('endDate')),
                  defaultFreeBusy: options.defaultFreeBusy || {}
              }));
            });
            self.element.find('.wc-grid-row-freebusy .wc-freebusy').remove();
          }
        },
        /**
          * retrieve placeholders for given freebusy
          */
        _findWeekDaysForFreeBusy: function(freeBusy, $weekDays) {
          var $returnWeekDays,
              options = this.options,
              showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
              self = this,
              userList = self._getFreeBusyUserId(freeBusy);
          if (!$.isArray(userList)) {
            userList = userList != 'undefined' ? [userList] : [];
          }
          if (!$weekDays) {
            $weekDays = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy');
          }
          $weekDays.each(function() {
            var manager = $(this).data('wcFreeBusyManager'),
                has_overlap = manager.isWithin(freeBusy.getStart()) ||
                                manager.isWithin(freeBusy.getEnd()) ||
                                freeBusy.isWithin(manager.getStart()) ||
                                freeBusy.isWithin(manager.getEnd()),
                userId = $(this).data('wcUserId');
            if (has_overlap && (!showAsSeparatedUser || ($.inArray(userId, userList) != -1))) {
              $returnWeekDays = $returnWeekDays ? $returnWeekDays.add($(this)) : $(this);
            }
          });
          return $returnWeekDays;
        },
  
        /**
          * used to render all freeBusys
          */
        _renderFreeBusys: function(freeBusys) {
          if (this.options.displayFreeBusys) {
            var self = this,
                $freeBusyPlaceholders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
                freebusysToRender;
            //insert freebusys to dedicated placeholders freebusy managers
            if ($.isArray(freeBusys)) {
              freebusysToRender = self._cleanFreeBusys(freeBusys);
            } else if (freeBusys.freebusys) {
              freebusysToRender = self._cleanFreeBusys(freeBusys.freebusys);
            }
            else {
              freebusysToRender = [];
            }
  
            $.each(freebusysToRender, function(index, freebusy) {
                var $placeholders = self._findWeekDaysForFreeBusy(freebusy, $freeBusyPlaceholders);
                if ($placeholders) {
                  $placeholders.each(function() {
                    var manager = $(this).data('wcFreeBusyManager');
                    manager.insertFreeBusy(new FreeBusy(freebusy.getOption()));
                    $(this).data('wcFreeBusyManager', manager);
                  });
                }
              });
  
            //now display freebusys on  place holders
            self._refreshFreeBusys($freeBusyPlaceholders);
          }
        },
        /**
          * refresh freebusys for given placeholders
          */
        _refreshFreeBusys: function($freeBusyPlaceholders) {
          if (this.options.displayFreeBusys && $freeBusyPlaceholders) {
            var self = this,
                options = this.options,
                start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
                end = (options.businessHours.limitDisplay ? options.businessHours.end : 24);
  
            $freeBusyPlaceholders.each(function() {
                var $placehoder = $(this);
                var s = self._cloneDate($placehoder.data('startDate')),
                    e = self._cloneDate(s);
                s.setHours(start);
                e.setHours(end);
                $placehoder.find('.wc-freebusy').remove();
                $.each($placehoder.data('wcFreeBusyManager').getFreeBusys(s, e), function() {
                    self._renderFreeBusy(this, $placehoder);
                  });
              });
          }
        },
        /**
          * render a freebusy item on dedicated placeholders
          */
        _renderFreeBusy: function(freeBusy, $freeBusyPlaceholder) {
          if (this.options.displayFreeBusys) {
            var self = this,
                options = this.options,
                freeBusyHtml = '<div class="wc-freebusy"></div>';
  
            var $fb = $(freeBusyHtml);
            $fb.data('wcFreeBusy', new FreeBusy(freeBusy.getOption()));
            this._positionFreeBusy($freeBusyPlaceholder, $fb);
            $fb = options.freeBusyRender(freeBusy.getOption(), $fb, self.element);
            if ($fb) {
              $fb.appendTo($freeBusyPlaceholder);
            }
          }
        },
        /*
          * Position the freebusy element within the weekday based on it's start / end dates.
          */
        _positionFreeBusy: function($placeholder, $freeBusy) {
            var options = this.options;
            var freeBusy = $freeBusy.data('wcFreeBusy');
            var pxPerMillis = $placeholder.height() / options.millisToDisplay;
            var firstHourDisplayed = options.businessHours.limitDisplay ? options.businessHours.start : 0;
            var startMillis = freeBusy.getStart().getTime() - new Date(freeBusy.getStart().getFullYear(), freeBusy.getStart().getMonth(), freeBusy.getStart().getDate(), firstHourDisplayed).getTime();
            var eventMillis = freeBusy.getEnd().getTime() - freeBusy.getStart().getTime();
            var pxTop = pxPerMillis * startMillis;
            var pxHeight = pxPerMillis * eventMillis;
            $freeBusy.css({top: pxTop, height: pxHeight});
        },
        /*
          * Clean freebusys to ensure correct format
          */
        _cleanFreeBusys: function(freebusys) {
            var self = this,
                freeBusyToReturn = [];
            if (!$.isArray(freebusys)) {
              var freebusys = [freebusys];
            }
            $.each(freebusys, function(i, freebusy) {
              freeBusyToReturn.push(new FreeBusy(self._cleanFreeBusy(freebusy)));
            });
            return freeBusyToReturn;
        },
  
        /*
          * Clean specific freebusy
          */
        _cleanFreeBusy: function(freebusy) {
            if (freebusy.date) {
              freebusy.start = freebusy.date;
            }
            freebusy.start = this._cleanDate(freebusy.start);
            freebusy.end = this._cleanDate(freebusy.end);
            return freebusy;
        },
  
        /**
          * retrives the first freebusy manager matching demand.
          */
        getFreeBusyManagersFor: function(date, users) {
          var calEvent = {
            start: date,
            end: date
          };
          this._setEventUserId(calEvent, users);
          return this.getFreeBusyManagerForEvent(calEvent);
        },
        /**
          * retrives the first freebusy manager for given event.
          */
        getFreeBusyManagerForEvent: function(newCalEvent) {
          var self = this,
              options = this.options,
              freeBusyManager;
          if (options.displayFreeBusys) {
            var $freeBusyPlaceHoders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
                freeBusy = new FreeBusy({start: newCalEvent.start, end: newCalEvent.end}),
                showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
                userId = showAsSeparatedUser ? self._getEventUserId(newCalEvent) : null;
            if (!$.isArray(userId)) {
              userId = [userId];
            }
            $freeBusyPlaceHoders.each(function() {
              var manager = $(this).data('wcFreeBusyManager'),
                  has_overlap = manager.isWithin(freeBusy.getEnd()) ||
                                  manager.isWithin(freeBusy.getEnd()) ||
                                  freeBusy.isWithin(manager.getStart()) ||
                                  freeBusy.isWithin(manager.getEnd());
              if (has_overlap && (!showAsSeparatedUser || $.inArray($(this).data('wcUserId'), userId) != -1)) {
                freeBusyManager = $(this).data('wcFreeBusyManager');
                return false;
              }
            });
          }
          return freeBusyManager;
        },
        /**
          * appends the freebusys to replace the old ones.
          * @param {array|object} freeBusys freebusy(s) to apply.
          */
        updateFreeBusy: function(freeBusys) {
          var self = this,
              options = this.options;
          if (options.displayFreeBusys) {
            var $toRender,
                $freeBusyPlaceHoders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
                _freeBusys = self._cleanFreeBusys(freeBusys);
  
            $.each(_freeBusys, function(index, _freeBusy) {
  
              var $weekdays = self._findWeekDaysForFreeBusy(_freeBusy, $freeBusyPlaceHoders);
              //if freebusy has a placeholder
              if ($weekdays && $weekdays.length) {
                $weekdays.each(function(index, day) {
                  var manager = $(day).data('wcFreeBusyManager');
                  manager.insertFreeBusy(_freeBusy);
                  $(day).data('wcFreeBusyManager', manager);
                });
                $toRender = $toRender ? $toRender.add($weekdays) : $weekdays;
              }
            });
            self._refreshFreeBusys($toRender);
          }
        },
  
        /* NEW OPTIONS MANAGEMENT */
  
        /**
          * checks wether or not the calendar should be displayed starting on first day of week
          */
        _startOnFirstDayOfWeek: function() {
          return jQuery.isFunction(this.options.startOnFirstDayOfWeek) ? this.options.startOnFirstDayOfWeek(this.element) : this.options.startOnFirstDayOfWeek;
        },
  
        /**
          * finds out the current scroll to apply it when changing the view
          */
        _getCurrentScrollHour: function() {
            var self = this;
            var options = this.options;
            var $scrollable = this.element.find('.wc-scrollable-grid');
            var scroll = $scrollable.scrollTop();
            if (self.options.businessHours.limitDisplay) {
              scroll = scroll + options.businessHours.start * options.timeslotHeight * options.timeslotsPerHour;
            }
            return Math.round(scroll / (options.timeslotHeight * options.timeslotsPerHour)) + 1;
        },
        _getJsonOptions: function() {
          if ($.isFunction(this.options.jsonOptions)) {
              return $.extend({}, this.options.jsonOptions(this.element));
          }
          if ($.isPlainObject(this.options.jsonOptions)) {
            return $.extend({}, this.options.jsonOptions);
          }
          return {};
        },
        _getHeaderDate: function(date) {
          var options = this.options;
          if (options.getHeaderDate && $.isFunction(options.getHeaderDate))
          {
            return options.getHeaderDate(date, this.element);
          }
          var dayName = options.useShortDayNames ? options.shortDays[date.getDay()] : options.longDays[date.getDay()];
          return dayName + (options.headerSeparator) + this._formatDate(date, options.dateFormat);
        },
  
  
  
        /**
          * returns corrected date related to DST problem
          */
        _getDSTdayShift: function(date, shift) {
        var start = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0);
        var offset1 = start.getTimezoneOffset();
        var offset2 = date.getTimezoneOffset();
            if (offset1 == offset2)
          return date;
        shift = shift ? shift : 1;
        return new Date(date.getTime() - shift * (offset1 > offset2 ? -1 : 1) * (Math.max(offset1, offset2) - Math.min(offset1, offset2)) * 60000);
        },
        _needDSTdayShift: function(date1, date2) {
        return date1.getTimezoneOffset() != date2.getTimezoneOffset();
        }
  
  
  
      }; // end of widget function return
      })() //end of widget function closure execution
    ); // end of $.widget("ui.weekCalendar"...
  
      $.extend($.ui.weekCalendar, {
        version: '2.0-dev',
        updateLayoutOptions: {
          startOnFirstDayOfWeek: true,
          firstDayOfWeek: true,
          daysToShow: true,
          displayOddEven: true,
          timeFormat: true,
          dateFormat: true,
          use24Hour: true,
          useShortDayNames: true,
          businessHours: true,
          timeslotHeight: true,
          timeslotsPerHour: true,
          buttonText: true,
          height: true,
          shortMonths: true,
          longMonths: true,
          shortDays: true,
          longDays: true,
          textSize: true,
          users: true,
          showAsSeparateUsers: true,
          displayFreeBusys: true
        }
      });
  
      var MILLIS_IN_DAY = 86400000;
      var MILLIS_IN_WEEK = MILLIS_IN_DAY * 7;
  
      /* FREE BUSY MANAGERS */
      var FreeBusyProto = {
        getStart: function() {return this.getOption('start')},
        getEnd: function() {return this.getOption('end')},
        getOption: function() {
            if (!arguments.length) { return this.options }
            if (typeof(this.options[arguments[0]]) !== 'undefined') {
              return this.options[arguments[0]];
            }
            else if (typeof(arguments[1]) !== 'undefined') {
              return arguments[1];
            }
            return null;
          },
        setOption: function(key, value) {
            if (arguments.length == 1) {
              $.extend(this.options, arguments[0]);
              return this;
            }
            this.options[key] = value;
            return this;
          },
        isWithin: function(dateTime) {return Math.floor(dateTime.getTime() / 1000) >= Math.floor(this.getStart().getTime() / 1000) && Math.floor(dateTime.getTime() / 1000) <= Math.floor(this.getEnd().getTime() / 1000)},
        isValid: function() {return this.getStart().getTime() < this.getEnd().getTime()}
      };
  
      /**
        * @constructor
        * single user freebusy manager.
        */
      var FreeBusy = function(options) {
        this.options = $.extend({}, options || {});
      };
      $.extend(FreeBusy.prototype, FreeBusyProto);
  
      var FreeBusyManager = function(options) {
        this.options = $.extend({
            defaultFreeBusy: {}
          }, options || {});
        this.freeBusys = [];
        this.freeBusys.push(new FreeBusy($.extend({
              start: this.getStart(),
              end: this.getEnd()
            }, this.options.defaultFreeBusy)));
      };
      $.extend(FreeBusyManager.prototype, FreeBusyProto, {
        /**
          * return matching freeBusys.
          * if you do not pass any argument, returns all freebusys.
          * if you only pass a start date, only matchinf freebusy will be returned.
          * if you pass 2 arguments, then all freebusys available within the time period will be returned
          * @param {Date} start [optionnal] if you do not pass end date, will return the freeBusy within which this date falls.
          * @param {Date} end [optionnal] the date where to stop the search.
          * @return {Array} an array of FreeBusy matching arguments.
          */
        getFreeBusys: function() {
            switch (arguments.length) {
              case 0:
                return this.freeBusys;
              case 1:
                var freeBusy = [];
                var start = arguments[0];
                if (!this.isWithin(start)) {
                  return freeBusy;
                }
                $.each(this.freeBusys, function() {
                  if (this.isWithin(start)) {
                    freeBusy.push(this);
                  }
                  if (Math.floor(this.getEnd().getTime() / 1000) > Math.floor(start.getTime() / 1000)) {
                    return false;
                  }
                });
                return freeBusy;
              default:
                //we assume only 2 first args are revealants
                var freeBusy = [];
                var start = arguments[0], end = arguments[1];
                var tmpFreeBusy = new FreeBusy({start: start, end: end});
                if (end.getTime() < start.getTime() || this.getStart().getTime() > end.getTime() || this.getEnd().getTime() < start.getTime()) {
                  return freeBusy;
                }
                $.each(this.freeBusys, function() {
                  if (this.getStart().getTime() >= end.getTime()) {
                    return false;
                  }
                  if (tmpFreeBusy.isWithin(this.getStart()) && tmpFreeBusy.isWithin(this.getEnd())) {
                    freeBusy.push(this);
                  }
                  else if (this.isWithin(tmpFreeBusy.getStart()) && this.isWithin(tmpFreeBusy.getEnd())) {
                    var _f = new FreeBusy(this.getOption());
                    _f.setOption('end', tmpFreeBusy.getEnd());
                    _f.setOption('start', tmpFreeBusy.getStart());
                    freeBusy.push(_f);
                  }
                  else if (this.isWithin(tmpFreeBusy.getStart()) && this.getStart().getTime() < start.getTime()) {
                    var _f = new FreeBusy(this.getOption());
                    _f.setOption('start', tmpFreeBusy.getStart());
                    freeBusy.push(_f);
                  }
                  else if (this.isWithin(tmpFreeBusy.getEnd()) && this.getEnd().getTime() > end.getTime()) {
                    var _f = new FreeBusy(this.getOption());
                    _f.setOption('end', tmpFreeBusy.getEnd());
                    freeBusy.push(_f);
                  }
                });
                return freeBusy;
            }
          },
        insertFreeBusy: function(freeBusy) {
            var freeBusy = new FreeBusy(freeBusy.getOption());
            //first, if inserted freebusy is bigger than manager
            if (freeBusy.getStart().getTime() < this.getStart().getTime()) {
              freeBusy.setOption('start', this.getStart());
            }
            if (freeBusy.getEnd().getTime() > this.getEnd().getTime()) {
              freeBusy.setOption('end', this.getEnd());
            }
            var start = freeBusy.getStart(), end = freeBusy.getEnd(),
                startIndex = 0, endIndex = this.freeBusys.length - 1,
                newFreeBusys = [];
            var pushNewFreeBusy = function(_f) {if (_f.isValid()) newFreeBusys.push(_f);};
  
            $.each(this.freeBusys, function(index) {
              //within the loop, we have following vars:
              // curFreeBusyItem: the current iteration freeBusy, part of manager freeBusys list
              // start: the insterted freeBusy start
              // end: the inserted freebusy end
              var curFreeBusyItem = this;
              if (curFreeBusyItem.isWithin(start) && curFreeBusyItem.isWithin(end)) {
                /*
                  we are in case where inserted freebusy fits in curFreeBusyItem:
                  curFreeBusyItem:    *-----------------------------*
                  freeBusy:                *-------------*
                  obviously, start and end indexes are this item.
                */
                startIndex = index;
                endIndex = index;
                if (start.getTime() == curFreeBusyItem.getStart().getTime() && end.getTime() == curFreeBusyItem.getEnd().getTime()) {
                  /*
                    in this case, inserted freebusy is exactly curFreeBusyItem:
                    curFreeBusyItem:    *-----------------------------*
                    freeBusy:            *-----------------------------*
  
                    just replace curFreeBusyItem with freeBusy.
                  */
                  var _f1 = new FreeBusy(freeBusy.getOption());
                  pushNewFreeBusy(_f1);
                }
                else if (start.getTime() == curFreeBusyItem.getStart().getTime()) {
                  /*
                    in this case inserted freebusy starts with curFreeBusyItem:
                    curFreeBusyItem:    *-----------------------------*
                    freeBusy:            *--------------*
  
                    just replace curFreeBusyItem with freeBusy AND the rest.
                  */
                  var _f1 = new FreeBusy(freeBusy.getOption());
                  var _f2 = new FreeBusy(curFreeBusyItem.getOption());
                  _f2.setOption('start', end);
                  pushNewFreeBusy(_f1);
                  pushNewFreeBusy(_f2);
                }
                else if (end.getTime() == curFreeBusyItem.getEnd().getTime()) {
                  /*
                    in this case inserted freebusy ends with curFreeBusyItem:
                    curFreeBusyItem:    *-----------------------------*
                    freeBusy:                          *--------------*
  
                    just replace curFreeBusyItem with before part AND freeBusy.
                  */
                  var _f1 = new FreeBusy(curFreeBusyItem.getOption());
                  _f1.setOption('end', start);
                  var _f2 = new FreeBusy(freeBusy.getOption());
                  pushNewFreeBusy(_f1);
                  pushNewFreeBusy(_f2);
                }
                else {
                  /*
                    in this case inserted freebusy is within curFreeBusyItem:
                    curFreeBusyItem:    *-----------------------------*
                    freeBusy:                    *--------------*
  
                    just replace curFreeBusyItem with before part AND freeBusy AND the rest.
                  */
                  var _f1 = new FreeBusy(curFreeBusyItem.getOption());
                  var _f2 = new FreeBusy(freeBusy.getOption());
                  var _f3 = new FreeBusy(curFreeBusyItem.getOption());
                  _f1.setOption('end', start);
                  _f3.setOption('start', end);
                  pushNewFreeBusy(_f1);
                  pushNewFreeBusy(_f2);
                  pushNewFreeBusy(_f3);
                }
                /*
                  as work is done, no need to go further.
                  return false
                */
                return false;
              }
              else if (curFreeBusyItem.isWithin(start) && curFreeBusyItem.getEnd().getTime() != start.getTime()) {
                /*
                  in this case, inserted freebusy starts within curFreeBusyItem:
                  curFreeBusyItem:    *----------*
                  freeBusy:                *-------------------*
  
                  set start index AND insert before part, we'll insert freebusy later
                */
                if (curFreeBusyItem.getStart().getTime() != start.getTime()) {
                  var _f1 = new FreeBusy(curFreeBusyItem.getOption());
                  _f1.setOption('end', start);
                  pushNewFreeBusy(_f1);
                }
                startIndex = index;
              }
              else if (curFreeBusyItem.isWithin(end) && curFreeBusyItem.getStart().getTime() != end.getTime()) {
                /*
                  in this case, inserted freebusy starts within curFreeBusyItem:
                  curFreeBusyItem:                  *----------*
                  freeBusy:            *-------------------*
  
                  set end index AND insert freebusy AND insert after part if needed
                */
                pushNewFreeBusy(new FreeBusy(freeBusy.getOption()));
                if (end.getTime() < curFreeBusyItem.getEnd().getTime()) {
                  var _f1 = new FreeBusy(curFreeBusyItem.getOption());
                  _f1.setOption('start', end);
                  pushNewFreeBusy(_f1);
                }
                endIndex = index;
                return false;
              }
            });
            //now compute arguments
            var tmpFB = this.freeBusys;
            this.freeBusys = [];
  
            if (startIndex) {
              this.freeBusys = this.freeBusys.concat(tmpFB.slice(0, startIndex));
            }
            this.freeBusys = this.freeBusys.concat(newFreeBusys);
            if (endIndex < tmpFB.length) {
              this.freeBusys = this.freeBusys.concat(tmpFB.slice(endIndex + 1));
            }
  /*          if(start.getDate() == 1){
            console.info('insert from '+freeBusy.getStart() +' to '+freeBusy.getEnd());
              console.log('index from '+ startIndex + ' to ' + endIndex);
              var str = [];
              $.each(tmpFB, function(i){str.push(i + ": " + this.getStart().getHours() + ' > ' + this.getEnd().getHours() + ' ' + (this.getOption('free') ? 'free' : 'busy'))});
              console.log(str.join('\n'));
  
              console.log('insert');
              var str = [];
              $.each(newFreeBusys, function(i){str.push(this.getStart().getHours() + ' > ' + this.getEnd().getHours())});
              console.log(str.join(', '));
  
              console.log('results');
              var str = [];
              $.each(this.freeBusys, function(i){str.push(i + ": " + this.getStart().getHours() + ' > ' + this.getEnd().getHours()  + ' ' + (this.getOption('free') ? 'free' :'busy'))});
              console.log(str.join('\n'));
            }*/
            return this;
          }
      });
  
  })(jQuery);
  