(function(angular) {

  /**
   * buildArray(size)
   * Helper method for array creation. Needs to be checked regarding
   * functionality in IE.
   * @TODO optimize for speed?
   */
  var buildArray = function(size) {
    var array = [];
    for(var index = 0; index < size; index ++) {
      array.push(index);
    }    
    return array;
  };

  /**
   *  This is an angularJS Calendar directive
   *
   *  Possible parameters:
   *
   *  <calendar
   *    events      what events should be visible in this calendar
   *    view        "hour", "day", "week", "month", "events"
   *    date        date of the calendar, if week or month - view is
   *                used, it will be changed to the first day of the week
   *    format-day  format of the labels for each day [default: 'dddd' or 'dddd, t\\he Do of MMMM']
   *    format-time format of the time label in the hour or events view [default: 'H a']
   *    format-week format of the weeknumber for the week view [default: 'wo']
   *    format ???
   *
   */
  var SECONDS_OF_A_DAY  = 24*60*60
    , SECONDS_MINIMAL   = 60 * 30
    , SLOT_WIDTH        = 10
    , PLUNKER           = true
    , DEFAULT_TIMEOUT   = 0
    , CALENDAR_HEIGHT   = 1080
    , MOMENT_HOURS      = buildArray(24).map(function(index) {
        return moment().hours(index).minutes(0).seconds(0)
      })

  /**
   * sortEventByStartAndDuration(a, b)
   * Simple Sorting function, which sort the values a and b by start time,
   */
  var sortEventByStartAndDuration = function sortEventByStartAndDuration(a, b) {
    return a.start.diff(b.start) || b.end.diff(b.start) - a.end.diff(a.start);
  };

  /**
   * filterEventDuringDateRangeFactory(start, end) [function(event)]
   * The filter addresses four different case:
   * - event starts after item but item ends after event starts
   * - item starts after event but also item start after event ends
   * - item starts before event ends and also item ends after event start
   * - the item-event does not take place during the event-event
   */
  var filterEventDuringDateRangeFactory = function(start, end) {
    return function(event) {
      if(event.start.diff(start) < 0 && event.end.diff(start) > 0)
        return true;
      if(event.start.diff(start) > 0 && event.end.diff(start) < 0)
        return true;
      if(event.end.diff(start) > 0 && event.start.diff(end) < 0)
        return true;
      return false;
    };
  };

  /**
   * Verify if event is longer than 24hours.
   */
  var isWholeDayEvent = function(event) {    
    return event.end.diff(event.start, 'seconds') >= SECONDS_OF_A_DAY;
  };

  /**
   * colspanFactory()
   * 
   */
  var colspanify = function colspanify(days, events) {
    // return empty array if no days or events were given
    if((!days.length) || (!events.length)) return;
    var result    = []
      , slots     = []
      , length    = days.length
      , slot      = 0
      , firstDay  = days[0].clone().sod();
    
    events.map(function(event) {
      var eventStart  = event.start.clone().sod()
        , eventEnd    = event.end.clone().sod()
        , slot        = event.slot - 1;
        
      current = slots[slot] = (slot in result ? slots[slot] : 0);

      result[slot] = result[slot] || [];

      var start  = Math.min(length, Math.max(0, eventStart.diff(firstDay, 'days')))
        , colspan = Math.max(1, Math.min(length - start, eventEnd.diff(eventStart, 'days') + 1));

      if(slots[slot] < start) {
        result[slot][result[slot].length] = {
          colspan: start - slots[slot]
        };
      }
      
      console.log('Calendar-Colspanify: ', colspan);
      
      slots[slot]   = start + colspan;
      result[slot][result[slot].length]  = {
        colspan : colspan
      , event   : event
      };
    });
    
    return result.map(function(list, index) {
      if(slots[index] < length)
        list[list.length] = {
          colspan: length - slots[index]
        };
      return list;
    });
  };

  /**
   * slotFactory()
   * Slotfactory creates different slots for events. That is necessary as they may overlapp
   * regarding start and end time. Before using slotFactory it is necessary to order the
   * list of events regarding time and length.
   */
  var slotFactory = function(property) {
    // [ { event: event, property: value } ]
    return function slotify(item, index, list) {
      // item = { event: event, property: value }
      var slots = list.slice(0, index)
        .filter(filterEventDuringDateRangeFactory(item.start, item.end))
        .map(function(event) {
          return event[property];
        })
        .filter(uniqueFilter)
        .sort();
      var slot = slots
        .reduce(function(result, item, index) {
          return !result && (item !== index + 1) ? index + 1 : result;
        }, undefined);
      /**
      var result = {
        event : item,
        slot  : slot || slots.length + 1
      };
      **/
      item[property] = slot || slots.length + 1;
      return item;
    };
  };

  var timeout = function(scope, fn, timeout) {
    // check if scope is set
    if(typeof scope === "function") {
      timeout = fn;
      fn      = scope;
      scope   = null;
    }
    var apply = (function(scope) {
      if(scope) return function(fn, args) {
        scope.$apply(function() {
          fn.apply(self, args);
        });
      }
      return function(fn, args) {
        fn.apply(self, args);
      }
    })(scope)

    return (function(timeout) {
      var triggered = null;
      return function() {
        var args = Array.prototype.slice.call(arguments)
          , self = this;
        if(triggered)
          clearTimeout(triggered);
        triggered = setTimeout(function() {
          triggered = null;
          apply(fn, args);
        }, timeout);
      };
    })(timeout || DEFAULT_TIMEOUT);
  };

  /**
   *  method get-start-of-week will return the first day of the week for any given day
   *
   *  @param date day
   *  @return date
   **/
  var getStartOfWeek = function startOfWeek(day) {
    // check if we have the monday-sunday problem
    if(day.clone().day(1).diff(date)>0)
      day.add('days', -7);
    return day.day(1);
  };

  /**
   *  method hover-selektor will add and remove a klass for a selector when a given element is hovered
   *
   *  @param DOM-elment element
   *  @param string selector
   *  @param string css-class
   *  @return element
   **/
  var hoverSelektor = function(element, selector, klass) {
    return element.hover( function() { angular.element(selector).addClass(klass); }
                        , function() { angular.element(selector).removeClass(klass); }
    );
  };

  /**
   *  unique-filter function to be applied with Array::filter
   **/
  var uniqueFilter = function(item, index, list) {
    return list.indexOf(item, index + 1) < 0;
  };

  var calendarDirective = angular.module('directive.calendar', []);

  calendarDirective.factory('eventEmitter', function() {
    var events    = {}
      , seperator = '::'

    var getEvent = function(event) {
      return events[event] || (events[event] = [])
    }

    var Event = function(event) { this.event = event; }

    var eventEmitter = {
      create      : function(event) {
        return new Event(event || 'event');
      }
    , publish     : function(event) {
        var args  = Array.prototype.slice.call(arguments, 1)
          , Event = getEvent(event)
          , self  = this;
        Event.map(function(handler) {
          setTimeout(function() {
            handler.apply(self, args);
          }, DEFAULT_TIMEOUT);
        });
      }
    , subscribe   : function(event, handler) {
        var Event = getEvent(event);
        Event[Event.length] = handler;
      }
    , unsubscribe : function(event, handler) {
        var Event = getEvent(event);
        Event.filter(function(item) {
          return item !== handler;
        });
      }
    , clear       : function(event) {
        events[event] = [];
      }
    }

    var curry = function(method) {
      return function(event) {
        var args = [[this.event, seperator, event].join('')].concat(Array.prototype.slice.call(arguments, 1));
        eventEmitter[method].apply(this, args);
      };
    };

    Object.keys(eventEmitter).map(function(method) {
      Event.prototype[method] = curry(method);
    });

    return eventEmitter;
  });

  /**
   * Calendar Event Service
   */
  calendarDirective.factory('eventService', ['eventEmitter', function(eventEmitter) {
      var rawDayEvents    = []
        , dayEvents       = {}
        , wholeDayEvents  = []
        , allEvents       = []
        , slotter         = slotFactory('slot')

      var addItemToList   = function(item, list) {
        if (list.indexOf(item) !== -1)
          return true;
        
    		if (!eventExists(item, list)) {
  	      var length = list.length;
  	      //slotter(item, length, list)
  	      list[length] = item;
  			}
      };
		
  		var eventExists = function(item, list) {
  			var result = false;
  			angular.forEach(list, function(event) {
  				if (item.id === event.id)
  					result = true;
  			});
  			return result;
  		};
      var EventEmitter = eventEmitter.create('eventService');

      var eventService = {
        storeEvents : function(events) {
          if(!events) return;
          dayEvents = {};
          events.map(function (event) {
            addItemToList(event, isWholeDayEvent(event) ?
              wholeDayEvents : rawDayEvents);
            addItemToList(event, allEvents);
          });
          [rawDayEvents, wholeDayEvents, allEvents].map(eventService.sortEvents);
          [rawDayEvents, wholeDayEvents].map(function(list) { list.map(slotter); });
          EventEmitter.publish('stored', events.length);
        }
      , appendEvents : function(events) {
          EventEmitter.publish('store', events);
        }
      , sortEvents : function(events) {
          return events.sort(sortEventByStartAndDuration);
        }
      , getDayEvents : function(day) {
          var index = day.clone().sod()
          if(!dayEvents[index]) {
            var filter = filterEventDuringDateRangeFactory(day.clone().sod(), day.clone().eod())
            dayEvents[index] = rawDayEvents.filter(filter);
          }
          return dayEvents[index];
        }
      , getWholeDayEvents : function(days) {
          return wholeDayEvents;
        }
      , getAllEvent : function() {
          return allEvents;
        }
      };

      EventEmitter.subscribe('store', eventService.storeEvents)

      return eventService;
    }]);


  calendarDirective.directive('wholeDayEvent', [function() {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div name="bs-calendar-event-id-{{event.id}}" class="bs-calendar-whole-day-event-container evt-{{event.id}}">'
      +           '<div class="bs-calendar-whole-day-event-container-inner color-{{event.colorId}}">'
      +             '<div class="bs-calendar-whole-day-event-content">{{event.summary}}</div>'
      +           '</div>'
      +         '</div>'
    , scope: {
        event: '='
      }
    , link: function(scope, iElement, iAttr) {
        if(scope.event !== undefined && scope.event.id)
          hoverSelektor(iElement, '[name=bs-calendar-event-id-' + scope.event.id + ']', 'hovered');
      }
    };
  }]);

  calendarDirective.directive('hourViewNow', ['$compile', function($compile) {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div>' +
                  '<div class="bs-tg-now"></div>' +
                '</div>'
    , link: function(scope, iElement, iAttribute) {
        var day           = scope.$parent.day
          , display       = 'none'
          , now           = moment()
          , start_seconds = Math.max(now.diff(day.clone().sod(), 'seconds'), 0);

          var height        = CALENDAR_HEIGHT / SECONDS_OF_A_DAY;
          if (!day.sod().diff(now.sod()))
            display = 'block';

          var nowDiv = angular.element(iElement.children("div.bs-tg-now"));
          nowDiv.css({
              display : display
            , position: 'absolute'
            , top     : start_seconds * height
          });          
      }
    };
  }]);

  /**
   *
   */
  calendarDirective.directive('hourViewEvent', [function() {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div name="bs-calendar-event-id-{{event.id}}" class="bs-calendar-event-container">'
      +           '<div class="bs-calendar-event-content color-{{event.colorId}}">'
      +             '<div class="bs-calendar-event-header">{{event.summary}}</div>'
      +             '<div class="bs-calendar-event-body">{{event.description}}</div>'
      +           '</div>'
      +         '</div>'
    , link: function postLink(scope, iElement, iAttrs) {
        var day           = scope.$parent.$parent.day
          , start_seconds = Math.max(scope.event.start.diff(day.clone().sod(), 'seconds'), 0)
          , end_seconds   = Math.max((Math.min(scope.event.end.diff(day.clone().sod(), 'seconds'), SECONDS_OF_A_DAY) - start_seconds), SECONDS_MINIMAL)
        ;
                       
        var height = CALENDAR_HEIGHT / SECONDS_OF_A_DAY;
        iElement.css({
          left    : ((scope.event.slot - 1) * SLOT_WIDTH) + '%'
        , top     : start_seconds * height
        , height  : end_seconds   * height
        });
        
        if(scope.event.id)
          hoverSelektor(iElement, '[name=bs-calendar-event-id-' + scope.event.id + ']', 'hovered');
                
      }
    };
  }]);

  /**
   * dayViewCalendarDay is responsible for the creation of the basic table layout. It allows
   * to create dynamic day-ranges to create calendars with ranges between 1 and 7 days.
   */
  calendarDirective.directive('hourViewEventContainer', ['$compile', function($compile) {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div class="bs-calendar-tg-day">'
      +           '<hour-view-now></hour-view-now>'
      +           '<hour-view-event ng-repeat="event in events"></hour-view-event>'
      +         '</div>'
    , scope: {
      events: '='
    }
    , link: function(scope, iElement, iAttr) {                
      }
    };
  }]);

  calendarDirective.directive('wholeDayView', ['eventService', function(eventService) {
    return {
      template: '<tr class="bs-calendar-header">'
      +           '<td class="bs-calendar-weeknumber">'
      +             '<div>{{days[0].format($parent.labelFormat)}}</div>'
      +           '</td>'
      +           '<td ng-repeat="day in days">'
      +             '<div class="bs-calendar-daylabel">{{day.format($parent.$parent.dayLabelFormat)}}</div>'
      +           '</td>'
      +         '</tr>'
      +         '<tr class="bs-calendar-whole-day-event-list" ng-repeat="wholeDayEvent in eventlist">'
      +           '<td class="bs-calendar-whole-day-space">&nbsp;</td>'
      +           '<td ng-repeat="event in wholeDayEvent" colspan="{{event.colspan}}">'
      +             '<whole-day-event event="event.event"></whole-day-event>'
      +           '</td>'
      +         '</tr>'
    , scope: {
        days: '=days'
      }
    , link: function(scope, iElement, iAttr) {    
        scope.$watch('days', function() {
          scope.events = eventService.getWholeDayEvents(scope.days);
          scope.eventlist = colspanify(scope.days, scope.events);
          console.log('Calendar-Whole-Days: ', scope.days);
          console.log('Calendar-Whole-Day-Events: ', scope.eventlist);
        })
      }
    };
  }]);
   
  /**
   *
   */
  calendarDirective.directive('calendarDayView', ['eventService', function(eventService) {
    return {
      template: '<table class="table bs-calendar day-view">'
      +           '<tbody whole-day-view days="days"></tbody>' 
      +           '<tbody>'
      +             '<tr>'
      +              '<td colspan="{{days.length + 1}}" class="bs-calendar-colwrapper">'
      +                '<div class="bs-calendar-spanningwrapper">'
      +                  '<div class="bs-calendar-tg-hourmarkers">'
      +                    '<div class="bs-calendar-tg-markercell" ng-repeat="hour in hours">'
      +                      '<div class="bs-calendar-tg-dualmarker"></div>'
      +                    '</div>'
      +                  '</div>'
      +                '</div>'
      +              '</td>'
      +             '</tr>'
      +             '<tr>'
      +               '<td class="bs-calendar-tg-hours">'
      +                 '<div class="bs-calendar-tg-hour-inner" ng-repeat="hour in hours">'
      +                   '<div class="bs-calendar-tg-hour-clock">'
      +                     '{{hour.format(timeFormat)}}'
      +                   '</div>'
      +                 '</div>'
      +               '</td>'      
      +               '<td ng-repeat="day in days" class="bs-calendar-tg-day-container" ng-class="{today: ((day.sod().diff(now.sod())!=0) + (numberOfDays > 1)  == 1)}">'  
      +                 '<hour-view-event-container events="day.events"></hour-view-event-container>'
      +               '</td>'
      +             '</tr>'
      +           '</tbody>'
      +         '</table>'
    , link: function postLink(scope, iElement, iAttr) {
        var attributes = scope.$parent.attributes
          , numberOfDays  = parseInt(attributes.days, 10)   || 1
          , offset        = parseInt(attributes.offset, 10) || 0
          , date          = moment(scope.$parent.date)
          , now           = scope.$parent.now;
        console.log('Calendar-Days: ', numberOfDays);
        scope.days  = [];
        scope.timeFormat  = attributes.timeFormat           || 'H a';
        scope.hours = MOMENT_HOURS;
        
        var update = timeout(scope, function() {
          var date = scope.$parent.date.clone().add('days', offset);
          scope.days = buildArray(numberOfDays).map(function(index) {
            var day     = date.clone().add('days', index);
            var events  = eventService.getDayEvents(day);
            day.events = events;
            return day;
          });
        });
        scope.$on('calendar-update', update);        
      }
    };
  }]);

  calendarDirective.factory('daysOfWeek', function() {
    return function(day) {
      var base = day.clone(),
          
          result = [];
      for (var i = 0;i<7; i++) 
        result.push(base.add('days', i).clone());
     
     return result;     
    };
  });
  /**
   *
   */
  calendarDirective.directive('calendarMonthView', ['daysOfWeek', function(daysOfWeek) {
    return {
      template: '<table class="table table-bordered table-striped bs-calendar">' 
      +           '<thead>' 
      +             '<th style="width: 15px">#</th>' 
      +             '<th ng-repeat="day in days">{{day.format("dddd")}}</th>' 
      +           '</thead>' 
      +           '<tbody>'
      +             '<tr ng-repeat="week in weeks">'
      +               '<td class="bs-calendar-month-row">{{week[1]}}</td>' 
      +               '<td ng-repeat="day in daysOfWeek[week[0]]" class="bs-calendar-month-row">{{day.format("DD")}}</td>'
      +             '</tr>' 
      +           '</tbody>'
      +         '</table>'
    , link: function(scope, iElement, iAttr) {
        var attributes = scope.$parent.attributes        
          , numberOfDays  = 7
          , numberOfWeeks = 5
          , offset        = parseInt(attributes.offset, 10) || 0
          , date          = moment(scope.$parent.date);
          
        scope.daysOfWeek = {};
        scope.days = buildArray(numberOfDays).map(function(index) {
            return date.clone().add('days', index);            
        });
        
        scope.weeks = buildArray(numberOfWeeks).map(function(index) {
          var day = date.clone().add('weeks', index);             
              scope.daysOfWeek[index] = scope.daysOfWeek[index] || daysOfWeek(day);
              return [index, day.format('w')];
        });
      }
    };
  }]);
  
  /**
   * This is the head of the calendar directive, it will determine
   * what type of calendar is show.
   * The possible views are: day, week, month, events
   * 
   * day-view:
   *  This will show a vertical list of events a specific day
   * week-view:
   *  This is an stretched day view with more than one day
   * month-view: 
   *  This is an stretched view accross n tables under each other
   * events-view:
   *  This is an view, show all upcoming events in an ordered list
   */
  calendarDirective.directive('calendar', ['eventService', function(eventService) {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div class="calendar table-container" ng-switch on="view">' 
      +           '<div ng-switch-when="day" calendar-day-view></div>'
      +           '<div ng-switch-when="month" calendar-month-view></div>'
      +           '<div ng-switch-when="events" calendar-events-view></div>'
      +         '</div>'
    , scope: {
        sourceEvents      : '=eventsource'
      , controlDate       : '=date'
      , calendarId        : '@calendarId'
      , confstartOfWeek   : '@startOfWeek'
      , confTimeFormat    : '@timeFormat'
      , confNumberOfDays  : '@numberOfDays'
      , confNumberOfWeeks : '@numberOfWeeks'
      , confDayLabelFormat: '@dayLabelFormat'
      }
    , link: function(scope, iElement, iAttrs) {
        scope.attributes    = iAttrs;
        scope.timeFormat    = iAttrs.timeFormat                   || 'H a';
        scope.dayLabelFormat= iAttrs.dayLabelFormat               || (scope.startOfWeek ? 'dddd' : 'dddd, t\\he Do of MMMM');
        scope.labelFormat   = iAttrs.labelFormat                  || 'wo';
        scope.offset        = parseInt(iAttrs.offset, 10)         || 0;
        scope.now           = moment();
        scope.showHours     = scope.numberOfWeeks === 1;
        scope.date          = moment(scope.controlDate);
        scope.weeks         = [];
        scope.timeFormat    = 'HH:mm DD.MM.YYYY';
        scope.view          = iAttrs.view;
      }
    };
  }]);
})(window.angular);
<!DOCTYPE html>
<html ng-app="myApp">
  <head lang="en">
    <meta charset="utf-8"> 
    <title>Bootstrap-AngularJS Calendar - Directive</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/1.7.2/moment.min.js"></script>
    <script src="components.js"></script>    
    <script src="app.js"></script>
    <script src="controller.js"></script>
    <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap-combined.min.css"
    rel="stylesheet">
    <link href="calendar.css" rel="stylesheet">
    <link href="calendar-colors.css" rel="stylesheet">
    <link href="style.css" rel="stylesheet">
  </head>
  <body ng-controller="Ctrl"> 
    <div class="container" style="margin-top: 20px;">   
      <!--
      <ng-include src="'event-form.html'"></ng-include>
      <ng-include src="'event-popup.html'"></ng-include>
      -->
      <tabs>
        <pane pane-title="1 Days" pane-prev="date.add('days', -1)" pane-next="date.add('days', 1)">
          <calendar
            date="date"
            view="day"
            eventsource="events" 
            time-format="H a"
            start-of-week="false"
            days="1"
            offset="0"
            calendar-id="day"></calendar>
        </pane>
        <pane pane-title="4 Days" pane-prev="date.add('days', -1)" pane-next="date.add('days', 1)">
          <calendar
            date="date"
            view="day"
            eventsource="events" 
            time-format="H a"
            start-of-week="false"
            days="4"
            offset="-2"
            calendar-id="day"></calendar>
        </pane>
        <pane pane-title="7 Days" pane-prev="date.add('weeks', -1)" pane-next="date.add('weeks', 1)">
          <calendar
            date="date"
            view="day"
            eventsource="events" 
            time-format="H a"
            start-of-week="false"
            days="7"
            offset="-3"
            calendar-id="day"></calendar>
        </pane>
        <pane pane-title="Month" pane-prev="date.add('days', -1)" pane-next="date.add('days', 1)">
          <calendar
            date="date"
            view="month"
            days="7"
            offset="-1"></calendar>
          </calendar>
        </pane>    
      </tabs>
    </div>
  </body>  
</html>
/** 
   * @ToDo:
   * Monatsansicht
   * 
   */
  var calendarDirective = angular.module('directive.calendar', []);
        
  var timeout = function(scope, fn) { return function() { 
    console.log('timeouting...', scope.$$phase)
    scope.$apply(fn);
  }; };

  var getStartOfWeek = function startOfWeek(day) {
    // check if we have the monday-sunday problem
    if(day.clone().day(1).diff(date)>0)
      day.add('days', -7);
    return day.day(1);
  };
  
  var hoverSelektor = function(element, selektor, klass) {
    element.hover( function() { angular.element(selektor).addClass(klass); }
                 , function() { angular.element(selektor).removeClass(klass); }
    );
  };
  
  var SECONDS_OF_A_DAY  = 24*60*60
    , SLOT_WIDTH        = 10
    , PLUNKER           = true;

  var dateParser = function dateParser(event) {
    
  };
  
  var updateChain = function updateChain() {
    var handler = [], length = 0;
    var update = function Update(fn) {
      handler[length] = fn;
      length ++;
      fn.apply(this, []);
    };
    update.trigger = function() {
      var self = this, args = arguments;
      angular.forEach(handler, function(fn) { fn.apply(self, args); });
    };
    return update;
  };
 
  /**
   * sortEventByStartAndDuration(a, b)
   * Simple Sorting function, which sort the values a and b by start time, 
   */
  var sortEventByStartAndDuration = function sortEventByStartAndDuration(a, b) {  
    return a.start.diff(b.start) || b.end.diff(b.start) - a.end.diff(a.start);
  };
  
  /**
   * filterEventDuringEventFactory(event)
   * The filter addresses four different case:
   * - event starts after item but item ends after event starts
   * - item starts after event but also item start after event ends
   * - item starts before event ends and also item ends after event start
   * - the item-event does not take place during the event-event
   */
  var filterEventDuringDateRangeFactory = function(start, end) {
    return function(event) {    
      if(event.start.diff(start) < 0 && event.end.diff(start) > 0) 
        return true;   
      if(event.start.diff(start) > 0 && event.end.diff(start) < 0) 
        return true;    
      if(event.end.diff(start) > 0 && event.start.diff(end) < 0) 
        return true;
      return false;
    };
  };
  
  /**
   * 
   */
  var filterWholeDayEventDateRangeFactory = function(start, end) {
    return function(event) {
      if(start.diff(event.start) >= 0 && event.end.diff(start) >= 0)
        return true;
      if(end.diff(event.start) >= 0 && event.end.diff(end) >= 0)
        return true;
      return false;
    };
  };
  
  /**
   * slotFactory()
   * Slotfactory creates different slots for events. That is necessary as they may overlapp
   * regarding start and end time. Before using slotFactory it is necessary to order the 
   * list of events regarding time and length.
   */
  var slotFactory = (function() {
    var factory = function(assigner, modifier) {
      return function slotify(item, index, list) {
        var slots = list.slice(0, index)
              .map(modifier)
              .filter(filterEventDuringDateRangeFactory(item.start, item.end))
              .map(function(event) {
                return event.slot;
              }).sort().filter(function(item, index, list) {
                return list.indexOf(item) >= index;
              })
          , slot = slots
              .reduce(function(result, item, index) {
                if(result) return result;
                if(item !== index + 1) return index + 1;
              }, undefined);
        return assigner(item, slot || slots.length + 1);
      };
    };
    var slotter = function(assigner, modifier) {
      return factory(typeof assigner === 'function' ? assigner : function(item, slot) {
        item.slot = slot;
        return item;
      }, typeof modifier === 'function' ? modifier : function(item) {
        return angular.copy(item);
      });
    };
    return slotter;
  })();

  /**
   * buildArray(size)
   * Helper method for array creation. Needs to be checked regarding
   * functionality in IE.
   */
  var buildArray = function(size) {
    var array = [];
    for(var index = 0; index < size; index ++) {
      array.push(index);
    }
    return array;
  };

  calendarDirective.directive('wholeDayEvent', [function() {
    return {
      restrict: 'E',
      replace: true,
      template: '<div name="bs-calendar-event-id-{{event.id}}" class="bs-calendar-whole-day-event-container evt-{{event.id}}">' +
                  '<div class="bs-calendar-whole-day-event-container-inner color-{{event.colorId}}">' +
                    '<div class="bs-calendar-whole-day-event-content">{{event.summary}}</div>' +
                  '</div>' +
                '</div>',
      scope: {
        event: '='
      }    
    , link: function(scope, iElement, iAttr) {
        if(scope.event.id) hoverSelektor(iElement, '[name=bs-calendar-event-id-' + scope.event.id + ']', 'hovered');
      }
    };
  }]);
  
  calendarDirective.directive('hourViewNow', ['$compile', function($compile) {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div>' +
                  '<div class="bs-tg-now"></div>' +
                '</div>'
    , link: function(scope, iElement, iAttribute) {
        var day           = scope.$parent.day
          , display       = 'none'
          , now           = moment()
          , start_seconds = Math.max(now.diff(day.clone().sod(), 'seconds'), 0)
        
        scope.$on('calendar-update', timeout(scope, function() { 
          var height        = scope.height / SECONDS_OF_A_DAY
          if (day.sod().diff(now.sod()) == 0)
            display = 'block';
                    
          var nowDiv = angular.element(iElement.children("div.bs-tg-now"));       
          nowDiv.css(x = {
              display : display
            , position: 'absolute'          
            , top     : start_seconds * height     
          });
        }));

        
      }
    };
  }]);
  
  /**
   * 
   */
  calendarDirective.directive('hourViewEvent', [function() {
    var eventcounter = 0;
    return {
      restrict: 'E'
    , replace: true
    , template: '<div name="bs-calendar-event-id-{{event.id}}" class="bs-calendar-event-container">' +
                  '<div class="bs-calendar-event-content color-{{event.colorId}}">' + 
                    '<div class="bs-calendar-event-header">{{event.summary}}</div>' +
                    '<div class="bs-calendar-event-body">{{event.description}}</div>' +
                  '</div>' +
                '</div>'
    , link: function(scope, iElement, iAttr) {
        var day           = scope.$parent.$parent.day
          , start_seconds = Math.max(scope.event.start.diff(day.clone().sod(), 'seconds'), 0)
          , end_seconds   = (Math.min(scope.event.end.diff(day.clone().sod(), 'seconds'), SECONDS_OF_A_DAY) - start_seconds)
        ;
        
        scope.$on('calendar-update', timeout(scope, function() { 
          var height        = scope.$parent.height / SECONDS_OF_A_DAY
          iElement.css({
            left    : ((scope.event.slot - 1) * SLOT_WIDTH) + '%'
          , top     : start_seconds * height
          , height  : end_seconds   * height
          });
          if(scope.event.id)
            hoverSelektor(iElement, '[name=bs-calendar-event-id-' + scope.event.id + ']', 'hovered');
        }));
      }
    };
  }]);

  /**
   * dayViewCalendarDay is responsible for the creation of the basic table layout. It allows
   * to create dynamic day-ranges to create calendars with ranges between 1 and 7 days.
   */
  calendarDirective.directive('hourViewEventContainer', ['$compile', function($compile) {
    return {
      restrict: 'E'
    , replace: true
    , template: '<div class="bs-calendar-tg-day">' +
                  '<hour-view-now></hour-view-now>' +                    
                  '<hour-view-event ng-repeat="event in events"></hour-view-event>' +
                '</div>'
    , scope: {
      events: '='
    }
    , link: function(scope, iElement, iAttr) {
        scope.height    = 0; // just some default value, should be changed before use
        scope.$on('calendar-update', function() {
          scope.height = angular.element(iElement).innerHeight();
        });
      }
    }; 
  }]);
  
  calendarDirective.directive('wholeDayView', [function() {
    return {
      require: '^calendar'
    , restrict: 'A'
    , template:
      '<tr class="bs-calendar-header">' + 
        '<td class="bs-calendar-weeknumber">' +
          '<div>{{days[0].format($parent.labelFormat)}}</div>' +
        '</td>' +
        '<td ng-repeat="day in days" ng-class="{today: ((day.sod().diff($parent.now.sod())!=0) + ($parent.numberOfDays > 1) == 1)}">' +
          '<div class="bs-calendar-daylabel">{{day.format($parent.$parent.dayLabelFormat)}}</div>' +
        '</td>' +
      '</tr>' +
      '<tr class="bs-calendar-whole-day-event-list" ng-repeat="wholeDayEvent in getEventsOfWeek()">' +
        '<td>&nbsp;</td>' + 
        '<td ng-repeat="event in wholeDayEvent.list" colspan="{{event.colspan}}">' +        
          '<whole-day-event event="event"></whole-day-event>' +
        '</td>' +
      '</tr>' 
    , scope: {
        days: '=wholeDayView'
      }
    , link: function(scope, iElement, iAttr, calendarController) {
        scope.eventCache = [];
        scope.getEventsOfWeek = function() {
          return scope.eventCache = scope.eventCache.length
            ? scope.eventCache
            : calendarController.getWholeDayEvents(scope.days);
        };
        // emit the update
        scope.$on('calendar-update', function() {
          scope.eventCache = [];
        });
      }
    };
  }]);

  /**
   * 
   */
  calendarDirective.directive('hourView', [function() {
    return {
      require: '^calendar'
    , restrict: 'A'
    , template: 
      '<tr>' +
        '<td colspan="{{days.length + 1}}" class="bs-calendar-colwrapper">' +
          '<div class="bs-calendar-spanningwrapper">' + 
            '<div class="bs-calendar-tg-hourmarkers">' +
              '<div class="bs-calendar-tg-markercell" ng-repeat="hour in hours">' +
                '<div class="bs-calendar-tg-dualmarker"></div>' +
              '</div>' +
            '</div>' +
          '</div>' + 
        '</td>' + 
      '</tr>' + 
      '<tr>' +
        '<td class="bs-calendar-tg-hours">' +
          '<div class="bs-calendar-tg-hour-inner" ng-repeat="hour in hours">' + 
            '<div class="bs-calendar-tg-hour-clock">' +
              '{{hour.format(timeFormat)}}' +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td ng-repeat="day in days" class="bs-calendar-tg-day-container" ng-class="{today: ((day.sod().diff(now.sod())!=0) + (numberOfDays > 1)  == 1)}">' +
           '{{day.format("DD.MM.YYYY")}}<hour-view-event-container events="getEventsOfDay(day)"></hour-view-event-container>' +
        '</td>' +
      '</tr>'
    , link: function(scope, iElement, iAttr, calendarController) {
        scope.eventCache = {}
        scope.getEventsOfDay = function(day) {
          var sod = day.clone().sod();
          if(sod in scope.eventCache)
            return scope.eventCache[sod];
          return scope.eventCache[sod] = calendarController.getEvents(sod, day.clone().eod());
        }
        // emit the update
        scope.$on('calendar-update', function() {
          scope.eventCache = {};
        })
      }
    };
  }]);
 
  /**
   * dayViewCalendar is responsible for the creation of the basic table layout. It allows
   * to create dynamic day-ranges to create calendars with ranges between 1 and 7 days.
   */
  calendarDirective.directive('calendar', [function() {
    return {
      restrict: 'E'
    , replace: true
    , template:
        '<div class="table-container">' +
          '<table ng-switch="weeks.length" id="{{calendarId}}" class="table table-bordered table-striped bs-calendar" ng-switch="numberOfWeeks">' +
            '<tbody whole-day-view="week" ng-repeat="week in weeks"></tbody>' +
            '<tbody hour-view="daysEventList" ng-switch-when="1" days="weeks[0]"></tbody>' +
          '</table>' +       
       '</div>'
    , scope: {
        events            : '=eventsource'
      , controlDate       : '=date'
      // config elements that will have default values
      , calendarId        : '@calendarId'
      , confstartOfWeek   : '@startOfWeek'
      , confTimeFormat    : '@timeFormat'
      , confNumberOfDays  : '@numberOfDays'
      , confNumberOfWeeks : '@numberOfWeeks'
      , confDayLabelFormat: '@dayLabelFormat'
      }
    , controller: ['$scope', function(scope, $element) {
        this.getEvents = function getEvents(start, end) {
          var wholeDayEventFilter = filterWholeDayEventDateRangeFactory(start, end);
          var events = angular.copy(scope.events)
            .sort(sortEventByStartAndDuration)
            .filter(filterEventDuringDateRangeFactory(start, end))
            .filter(function(event) {
              return !wholeDayEventFilter(event);
            })
            .map(slotFactory());
          return events;
        };
        
        this.getWholeDayEvents = function getWholeDayEvents(days) {
          var numberOfDays      = days.length
            , date              = days[0]
            // prepare everything to create the td-tr listing from the slots
            , wholeDayEventList = []
            , slots             = []
            // add-item-to-slot adds a colspan to an item
            , addItemToSlot     = function addItemToSlot(slot, start, event) {
                event         = event || {};
                event.colspan = start - slots[slot] + 1;
                slots[slot]   = start + 1;
                wholeDayEventList[slot].list[wholeDayEventList[slot].list.length] = event;
              }
            , fillWithEmpty = function fillWithEmpty(slot, number) {
                for(var current = slots[slot]; current < number; current ++) {
                  // add empty item
                  addItemToSlot(slot, current);
                }
              }
            // add-events-to-slot will add an event to a slot, and checking for
            // holes in the colspan list. it will add an empty item there
            , addEventsToSlot = function addEventsToSlot(event) {
                // slot to work with and start/end in numbers of the event
                var slot  = event.slot - 1
                  , start = Math.min(numberOfDays - 1
                            , Math.max(0
                              , event.start.clone().sod().diff(date.clone().sod(), 'days')
                            ))
                  , end   = Math.min(numberOfDays - 1
                            , Math.max(0
                              , event.end.clone().sod().diff(date.clone().sod(), 'days')
                            ));
                // if slot was not used, fill it with empty data
                if(!(slot in slots)) {
                  wholeDayEventList[slot] = { slot: slot, list: [] };
                  slots[slot]                   = 0;
                }
                // fill until we reach the start of this event
                // this works because the events are sorted
                fillWithEmpty(slot, start);
                // add event itself
                addItemToSlot(slot, end, event);
              };
          angular.copy(scope.events)
            // first sort them by start and duration
            .sort(sortEventByStartAndDuration)
            // then filter any event that is not longer then any
            // day we are looking at
            .filter(function(event) {
              return days.reduce(function(current, day) {
                return current
                || filterWholeDayEventDateRangeFactory(day.clone().sod(), day.clone().eod())(event);
              }, false);
            // then slot them, by creating a slotter which will modify 
            // the dates so we are looking only at sod and eod dates
            }).map(slotFactory(null, function(item) {
              var result = angular.copy(item);
              result.start  = result.start.clone().sod();
              result.end    = result.end.clone().eod();
              return result;
            // add all whole day events to their slots
            })).map(addEventsToSlot);
          // check each slot and fill them up
          slots.map(function(_, slot) { fillWithEmpty(slot, numberOfDays); });
          // finally return the list
          return wholeDayEventList;
        }
      }]
    , link: function(scope, iElement, iAttrs) {
        scope.numberOfWeeks = Math.max(1, parseInt(iAttrs.numberOfWeeks, 10) || 1);
        scope.numberOfDays  = Math.max(1, parseInt(iAttrs.numberOfDays, 10) || 7);
        scope.startOfWeek   = scope.$parent.$eval(iAttrs.startOfWeek); // @ check
        scope.timeFormat    = iAttrs.timeFormat                   || 'H a';
        scope.dayLabelFormat= iAttrs.dayLabelFormat               || (scope.startOfWeek ? 'dddd' : 'dddd, t\\he Do of MMMM');
        scope.labelFormat   = iAttrs.labelFormat                  || 'wo';
        scope.offset        = parseInt(iAttrs.offset, 10)         || 0;
        scope.now           = moment();

        scope.showHours     = scope.numberOfWeeks === 1;

        scope.date          = moment(scope.controlDate);
        scope.weeks         = [];
        
        // watch for changes in the control-date
        scope.$watch(function() {
          return scope.controlDate && scope.date.diff(moment(scope.controlDate));
        }, function() {
          // if we have some changes, change the internal date and update the calendar with the new data
          scope.date = moment(scope.controlDate).add('days', scope.offset);
          update();
        });
        scope.$watch(scope.events, update);
        scope.$on('update', function() {
          scope.$broadcast('calendar-update');
        });

        var update = function() {
          // get us a copy of the date to work with
          var date = scope.date.clone();
          // if the calendar should start at the start of the week:
          if(scope.startOfWeek) {
            scope.date = getStartOfWeek(scope.date);
          }
          
          if(scope.numberOfWeeks === 1) {
            scope.weeks = buildArray(scope.numberOfWeeks).map(function(week) {
              return buildArray(scope.numberOfDays).map(function(day) {
                return scope.date.clone().add('weeks', week).add('days', day + scope.offset);
              })
            })
          } else {
            scope.weeks = buildArray(scope.numberOfWeeks).map(function(week) {
              return buildArray(scope.numberOfDays).map(function(day) {
                return scope.date.clone().add('weeks', week + scope.offset).add('days', day);
              })
            })
          }
          
          // create the list of days that will be shown
          scope.days          = buildArray(scope.numberOfDays).map(function(index) {
            return scope.date.clone().add('days', index);
          });
          var end   = scope.days[scope.numberOfDays - 1].clone().eod();
          
          // create a element for each hour of the day
          scope.hours         = buildArray(24).map(function(index) {
            return scope.days[0].clone().sod().add('hours', index);
          });
          
          scope.$broadcast('calendar-update');
        };
      }         
    };
  }]);
var app = angular.module('myApp', ['components', 'directive.calendar']);

var etag = 'etag', string = 'string', boolean = 'boolean', time = 'time', integer = 'integer', date = 'date',
  datetime = 'datetime', attendee = 'attendee';

app.controller('Ctrl', ['$scope', 'eventService', function($scope, eventService) {
  console.log('------------------------------------------------------');
  		console.log('-----------new run through the program----------------');
			console.log('------------------------------------------------------');
			$scope.events = [];
			$scope.times = [];
			$scope.date = moment().add('days', 0);

			$scope.initForm = function() {
				for (var i = 0; i < 96; i++) {
					var date = moment().sod().add('minutes', i*15);
					$scope.times.push(date);
				}				
			};

			$scope.addAttendee = function() {
				$scope.calendar = $scope.calendar || {};
				$scope.calendar.attendees = $scope.calendar.attendees || [];
				$scope.calendar.attendees.push({
					"id": "3m32ic0",
					"email": $scope.calendar.attendee,
					"displayName": "John",
					"organizer": true,
					"self": false,
					"resource": false,
					"optional": true,
					"responseStatus": "none",
					"comment": "none",
					"additionalGuests": 0
				});
				$scope.calendar.attendee = '';
				console.log('add attendee was clicked');
			};
	
			$scope.removeAttendee = function(attendee) {
				var index = $scope.calendar.attendees.indexOf(attendee);
				$scope.calendar.attendees.splice(index, 1);  
				console.log('remove attendee was clicked');
			};
	 
			$scope.addEvent = function() {
				console.log('add event was clicked');
			}; 
	
			var colorArray = ['blue', 'lilac', 'turqoise', 'green', 'bold-green', 'yellow',
				'orange', 'red', 'bold-red', 'purple', 'grey'];
		
			var createEvents = function(date) {
			var   d = date.getDate(),
						m = date.getMonth(),
						y = date.getFullYear(),
						max = colorArray.length - 1,
						min = 0,
						color;
			var randColor = function() {
				return colorArray[Math.floor(Math.random() * (max - min + 1)) + min];
			};
 
			var ids = 11413,
					s, e;
			var id = function id() { 
				return ++ids; 
			};

			for (var i = 0; i < 2; i++) {
				for (var j = 0; j < 4; j++) {
					var temporaryEvent = {
						"id"          : id(),
						"start"       : s = moment().add('days', -i*j),
						"end"         : e = moment().add('days', j),
						"colorId"     : randColor(),
						"summary"     : "Event (" + (i*j+j+1) + " Days) Start: " + s.format('DD.MM') + " End: " + e.format('DD.MM'),
						"description" : "Start: " + s + " End: " + e 
					};
					$scope.events.push(temporaryEvent);
				}
			}
		    
		  for (var i = -2; i < 2; i++) {
		  	for (var j = 0; j < 4; j++) {
		      var rand = Math.floor(Math.random()*10)+1;
					var inDayEvent = {
						"id"          : id(),
						"start"       : s = moment().add('days', i).sod().add('hours', rand),
						"end"         : e = moment().add('days', i).sod().add('hours', j + rand),
						"colorId"     : randColor(),
						"summary"     : "Event (" + (i*j+j+1) + " Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
		  			"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
					};
					$scope.events.push(inDayEvent);
				}
			}

			var fiveDayEvent = {
				"id"          : id(),
				"start"       : s = moment().add('days', -2),
				"end"         : e = moment().add('days', 3),
				"colorId"     : randColor(),					
				"summary"     : "Event (5 Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
	  		"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
			};
			$scope.events.push(fiveDayEvent);

			var threeDayEvent = {
				"id"          : id(),
				"start"       : s = moment().add('days', -1),
				"end"         : e = moment().add('days', 2),
				"colorId"     : randColor(),
				"summary"     : "Event (3 Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
	  		"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
			};
			$scope.events.push(threeDayEvent);
	
			var currentWholeDayEvent = {
				"id"          : id(),
				"start"       : s = moment().sod(),
				"end"         : e = moment().eod(),
				"colorId"     : randColor(),
				"summary"     : "Event (1 Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
	  		"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
			}; 
			$scope.events.push(currentWholeDayEvent);
		
			var inDayEvent = {
				"id"          : id(),
				"start"       : s = moment().add('hours', -4),
				"end"         : e = moment().add('hours', 2),
				"colorId"     : randColor(),
				"summary"     : "Event (1 Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
	  		"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
			};
			$scope.events.push(inDayEvent);
	
			var shortInDayEvent = {
				"id"          : id(),
				"start"       : s = moment().add('hours', -1),
				"end"         : e = moment().add('hours', 1),
				"colorId"     : randColor(),
				"summary"     : "Event (1 Days) Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM'),
	  		"description" : "Start: " + s.format('DD.MM-HH:MM') + " End: " + e.format('DD.MM-HH:MM')
			};
			$scope.events.push(shortInDayEvent);  
		};
		createEvents(new Date());
			 
		eventService.appendEvents($scope.events);
}]);
/** Table Builder here we go **/
.bs-calendar {
  table-layout: fixed; 
}

.bs-calendar-head {
  margin-bottom: 5px;
}

.bs-calendar-tg-hours-head {
  width: 30px;
  text-align: center; 
}

.bs-calendar-header tr td {
  line-height: 20px;
  padding: 0;
  margin: 0;
}

.bs-calendar tbody tr.bs-calendar-header td {
  padding: 8px;
  background: #FFF;
}

.bs-calendar-header td .bs-calendar-daylabel { 
  border-bottom: 1px dotted #eee;
  border-top: 1px dotted #eee;
  margin: 0 -8px 0 -8px;
  text-align: center;
}

.bs-calendar-weeknumber {
  width: 29px;
}

.bs-calendar-weeknumber div {
  padding: 0 4px 0px 10px;
  margin: 0px -8px 0 0;
  float: right;
  border-top: 1px dotted #EEE;
  border-bottom: 1px dotted #EEE;
  border-left: 1px dotted #EEE;
  border-radius: 4px 0 0 4px;
}

.bs-calendar tbody tr td {
  padding: 0 5px 0 0;  
  font-size: 10px;
  font-weight: bold;
  text-align: right;
  vertical-align: bottom;	
}

.bs-calendar-tg-day {
  position: relative;
  height: 1030px;   
}

.bs-calendar .today {
  background-color: #F1F1F1;
}

.bs-tg-now {
  position: relative;
  left: 0;
  width: 100%;
  top: 0;
  height: 0;
  border-top: 2px solid #FF7F6E;
  overflow: hidden; 
  z-index: 3;
}

.bs-calendar-colwrapper {
  position: relative;
}

.bs-calendar-spanningwrapper {
  margin: 1px 0 0 1px;
  position: absolute;
  width: 100%;
  top: -1px;
  z-index: 1;
}

.bs-calendar th {
  border-bottom: 1px solid #DDD;
}

.bs-calendar-tg-markercell {
  height: 42px;
  border-top: 1px solid #DDD;
}

.bs-calendar-tg-day-container {
  position: relative;
}

.bs-calendar-header {
  border-top: 1px solid #DDD;
  border-left: 1px solid #DDD;
  border-right: 1px solid #DDD;
}

.bs-calendar-whole-day-event-list {
  border-right: 1px solid #DDD;
}

.bs-calendar-whole-day-event-list td:first-child {
  border-top: 0;
}

.bs-calendar-whole-day-event-container {
  opacity: .7;
  width: auto;
  padding: 2px;
  height: 17px;
  overflow: hidden;
  text-overflow: ellipsis;
  -o-text-overflow: ellipsis;
  -webkit-text-overflow: ellipsis;
}

.bs-calendar-whole-day-event-container-inner {
  height: 17px;
  overflow: hidden;
  border-radius: 2px;
  line-height: 14px;
}

.bs-calendar-whole-day-event-content {
  text-align: left;
  font-size: 1.1em;
  word-wrap: break-word;
  font-weight: bold;
  padding: 2px 4px 2px 4px;
  
  overflow: hidden;
  text-overflow: ellipsis;
  -o-text-overflow: ellipsis;
  -webkit-text-overflow: ellipsis;
  white-space: nowrap;
}

.bs-calendar-whole-day-event-container.hovered,
.bs-calendar-whole-day-event-container:hover {
  opacity: .9;
  overflow: inherit;
  z-index: 9999;
  position: relative;
}
.bs-calendar-whole-day-event-container.hovered .bs-calendar-whole-day-event-container-inner,
.bs-calendar-whole-day-event-container:hover .bs-calendar-whole-day-event-container-inner {
  height: auto;
  overflow: inherit;
}
.bs-calendar-whole-day-event-container.hovered .bs-calendar-whole-day-event-content,
.bs-calendar-whole-day-event-container:hover .bs-calendar-whole-day-event-content {
  white-space: normal;
}

.bs-calendar-whole-day-space {
  border-left: 1px solid #DDD;
  border-right: 1px solid #DDD;
  border-bottom: 1px dotted #DDD;
}

/* whole-day events end */

.bs-calendar-tg-hour-clock {
  border-bottom: 1px dotted #DDD;
  border-left: 1px dotted #DDD;
  border-radius: 0 0 0 4px;
  width: 40px;
  height: 21px;
  margin-right: -2px;
  float: right;
  padding-right: 4px;
}

.bs-calendar-tg-dualmarker {
  height: 21px;
  border-bottom: 1px dotted #DDD;
  font-size: 1px;
}

.bs-calendar-tg-hours {
  border-left: 1px solid #DDD;
  background-color: white;
  color: #555;
  padding: 1px 0 0;
  text-align: right;
  vertical-align: top;
	padding: 8px;
  width: 80px;
}

.bs-calendar-tg-hour-inner {
  padding-right: 2px;
  height: 43px;
}

.bs-calendar-event-container  {
  opacity: .7;
  width: 70%;
  overflow: hidden;
  z-index: 2;
  height: 25px;
  position: absolute;
}

.bs-calendar-event-content {
  text-align: left;
  min-height: 100%;
  height: 100%;
  word-wrap: break-word;
  font-weight: normal;
  border: 1px solid #CCC;
  border: 1px solid rgba(0, 0, 0, 0.2); 
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px; 
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  -moz-box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  -webkit-background-clip: padding-box;
  -moz-background-clip: padding; 
}

.bs-calendar-event-header {
  font-size: 1.2em;
  font-weight: bold;
  line-height: 1.2em;
  padding: 1px 2px 0 2px;  
  border-radius: 2px; 
  background-color: rgba(0, 0, 0, 0.5); 
  color: rgb(255, 255, 255);
  margin: 2px;
  
  text-align: left;
  word-wrap: break-word;
  font-weight: bold;
  
  overflow: hidden;
  text-overflow: ellipsis;
  -o-text-overflow: ellipsis;
  -webkit-text-overflow: ellipsis;
  white-space: nowrap;  
}

.bs-calendar-event-body {
  font-size: 1.1em;
  margin: 4px;
}

.bs-calendar-event-container.hovered,
.bs-calendar-event-container:hover {
  opacity: .9;
  overflow: inherit;
  z-index: 1000;
}

.bs-calendar-event-container.hovered .bs-calendar-event-content,
.bs-calendar-event-container:hover .bs-calendar-event-content {
  height: auto; 
}

.bs-calendar-event-container.hovered .bs-calendar-event-header,
.bs-calendar-event-container:hover .bs-calendar-event-header,
.bs-calendar-event-container.hovered .bs-calendar-event-body,
.bs-calendar-event-container:hover .bs-calendar-event-body {
  white-space: normal;
}

/** Month view properties **/
.bs-calendar-month-row {
  height: 120px;
}
angular.module('components', []).
  directive('tabs', function() {
    return {
      restrict: 'E',
      transclude: true,
      controller: function($scope, $element, $timeout) {
        var panes = $scope.panes = [];
        var current = null;

        $scope.select = function(pane) {         
          angular.forEach(panes, function(pane) {
            pane.selected = false;
          });
          pane.selected = true;      
          current = pane;
          $timeout(function() {
            $scope.$broadcast('calendar-update');
          }, 1);
        };
 
        this.addPane = function(pane) {
          if (panes.length === 0) $scope.select(pane);
          if (pane.paneDefault) $scope.select(pane);
          panes.push(pane);
        };
        
        $scope.prev = function() {
          current.prev();
        }
        $scope.next = function() {
          current.next();
        }
      },
      template:
      '<div class="row">' +
        '<div class="row" style="padding-bottom: 20px;">' +
          '<div class="span5 btn-group">' +
           '<button class="btn btn-primary" ng-click="prev()">Prev</button>' +
           '<button class="btn btn-primary" ng-click="next()">Next</button>' +
          '</div>' +
          '<div class="span2">' +
            '<h4>{{date.format("MMMM YYYY")}}</h4>' +
          '</div>' +
          '<div class="span5 btn-group" style="text-align: right">' +
            '<button ng-repeat="pane in panes" class="btn" ng-class="{active:pane.selected}" ng-click="select(pane)">{{pane.paneTitle}}</button>' +            
          '</div>' +     
        '</div>' +
        '<div class="row">' + 
          '<div class="tab-content" ng-transclude></div>' +
        '</div>' +
      '</div>',
      replace: true
    };
  }).
  directive('pane', [function() {
    return {
      require: '^tabs',
      restrict: 'E',
      transclude: true,
      template:
        '<div class="tab-pane" style="height: {{paneHeight}}px" ng-class="{active: selected}" ng-transclude>' +
        '</div>',
      replace: true,
      scope: {
        paneTitle: '@',
        paneDefault: '@',
        panePrev: '@',
        paneNext: '@'        
      },
      link: function(scope, element, attrs, tabsCtrl) {
        tabsCtrl.addPane(scope);
        var docHeight,
            panePos,
            paneHeight;
        panePos = angular.element('.tab-content').position();
        docHeight = $(window).height();
        paneHeight = docHeight - panePos.top - 20;
        scope.paneHeight = paneHeight;
        
        $scope = scope.$parent;
        
        scope.prev = attrs.panePrev ? function() {
          $scope.$eval(attrs.panePrev);
        } : angular.noop;
        scope.next = attrs.paneNext ? function() {
          $scope.$eval(attrs.paneNext);
        } : angular.noop;
        
        angular.element(window).bind('resize', function() {
          panePos = angular.element('.tab-content').position();
          docHeight = $(window).height();
          
          paneHeight = docHeight - panePos.top - 20;
          scope.paneHeight = paneHeight;   
          
          console.log(panePos, docHeight, paneHeight);
          scope.$digest();
        });
      }
    };
  }]);
  
body {
 height: 100%; 
}
/* Colordefinitions .color-{{colorId}} */
.color-blue {
  background-color: #5484ED;
  border-color: #5484ED;
}

.color-lilac {
  background-color: #A4BDFC;
  border-color: #A4BDFC;
}

.color-turqoise {
  background-color: #46D6DB;
  border-color: #46D6DB;
}

.color-green {
  background-color: #7AE7BF;
  border-color: #7AE7BF;
}

.color-bold-green {
  background-color: #51B749;
  border-color: #51B749;
}

.color-yellow {
  background-color: #FBD75B;
  border-color: #FBD75B;
}

.color-orange {
  background-color: #FFB878;
  border-color: #FFB878;
}

.color-red {
  background-color: #FF887C;
  border-color: #FF887C; 
}

.color-bold-red {
  background-color: #DC2127;
  border-color: #DC2127;
}

.color-purple {
  background-color: #DBADFF;
  border-color: #DBADFF;
}

.color-grey {
  background-color: #E1E1E1;
  border-color: #E1E1E1;
}



<div class="row" style="padding-top: 40px" ng-controller="CalendarController">
<div class="span12" style="padding-bottom: 20px">
  <a href="#/components/calendar" class="btn btn-warning pull-left">Return to calendar</a>
</div>
<div class="span12 form-bg">
 <form class="form-horizontal" ng-submit="addEvent()" ng-init="initForm()" name="create">
 <h3><i class="icon-pencil"></i> Add event</h3>		
    <fieldset>
      	<div class="control-group">        			
			<div class="controls">
				<div class="input-prepend">
					<input placeholder="Summary..." type="text" class="input-xxlarge" ng-model="calendar.summary" name="summary" />
				</div>
				<span ng-show="form.summary.$error" class="help-inline">{{errors.summary}}</span>
			</div>
		</div>
      	<div class="control-group">          		
			<div class="controls">
				<div class="input-prepend">
					<input placeholder="Start date..." type="text" class="input-small" style="margin-right: 3px;" ng-model="calendar.start.date" name="start.date" />						 
            		<select placeholder="Start time" ng-model="calendar.start.dateTime" class="input-small">
						<option ng-repeat="time in times">{{time.format("hh:mm")}}</option>
					</select>
            		<input placeholder="End date..." type="text" class="input-small" style="margin-right: 3px;" ng-model="calendar.end.date" name="end.date" /> 
            		<select placeholder="End time" ng-model="calendar.end.dateTime" class="input-small">
						<option ng-repeat="time in times">{{time.format("hh:mm")}}</option>
					</select>
				</div>
				<span ng-show="form.summary.$error" class="help-inline">{{errors.summary}}</span>
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="surrname">Where:</label>
			<div class="controls">
				<div class="input-prepend">
					<input placeholder="Where..." type="text" class="input-xlarge" ng-model="calendaer.location" name="location" />
				</div>
				<span ng-show="form.location.$error" class="help-inline">{{errors.location}}</span>
			</div>
			</div>
      	<div class="control-group">
  			<label class="control-label" for="name">Calendar:</label>
			<div class="controls">
				<div class="input-prepend">
					<select name="id" ng-model="calendar.id">
						<option>Calendar 1</option>
						<option>Calendar 2</option>  								
					</select>			
				</div>
				<span ng-show="form.id.$error" class="help-inline">{{errors.id}}</span>
			</div>
		</div>	
		<div class="control-group">
			<label class="control-label" for="name">Description:</label>
			<div class="controls">
				<div class="input-prepend">
					<textarea placeholder="Description..." type="text" class="input-xlarge" ng-model="calendar.description" name="description">
            		</textarea>
				</div>
				<span ng-show="form.description.$error" class="help-inline">{{errors.description}}</span>
			</div>
		</div>									
		<div class="control-group">
			<label class="control-label" for="colors">Event color:</label>
			<div class="controls">
				<div class="input-prepend">
					<ng-color-picker model-object="calendar" model-property="color"></ng-color-picker>
				</div>    						
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="reminders">Reminder:</label>
			<div class="controls">
				<div class="input-prepend">
					 <select name="reminders" class="input-small" ng-model="calendar.reminders.method">
  						<option>E-Mail</option>
						<option>Popup</option>  								
					</select>	                
             		<input placeholder="10" type="text" class="input-mini" ng-model="calendar.reminders.minutes" name="minutes" />
             		<select name="timespan" class="input-small" ng-model="calendar.reminders.length">                 
  						<option>minutes</option>
						<option>hours</option>  	
              			<option>days</option>    
					</select>
				</div>
				<span ng-show="form.reminder.$error" class="help-inline">{{errors.reminder}}</span>
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="availability">Show me as:</label>
			<div class="controls">
				<div class="input-prepend">
          			<label class="radio">
            			<input type="radio" name="optionsRadios" id="optionsRadios1" ng-model="calendar.locked" value="available" checked>
            			Available
          			</label>
          			<label class="radio">
            			<input type="radio" name="optionsRadios" id="optionsRadios2" ng-model="calendar.locked" value="busy">
            			Busy
          			</label>		
				</div>
				<span ng-show="form.locked.$error" class="help-inline">{{errors.locked}}</span>
			</div>
		</div>    
      	<div class="control-group">
  			<label class="control-label" for="confirmPassword">Privacy:</label>
			<div class="controls">
				<div class="input-prepend">
              		<label class="radio">
                		<input type="radio" name="optionsRadios2" id="optionsRadios3" ng-model="calendar.visibility" value="default" checked>
                		Default
              		</label>
              		<label class="radio">
                		<input type="radio" name="optionsRadios2" id="optionsRadios4" ng-model="calendar.visibility" value="public">
                		Public
              		</label>	
               		<label class="radio">
                		<input type="radio" name="optionsRadios2" id="optionsRadios5" ng-model="calendar.visibility" value="private">
                		Private
              		</label>  	
				</div>
				<span ng-show="form.visibility.$error" class="help-inline">{{errors.visibility}}</span>
			</div>
		</div>    		
		<div class="control-group">
			<label class="control-label" for="guest">Add guests:</label>
			<div class="controls">
				<div class="input-prepend">
					<span class="add-on"><i class="icon-user"></i></span>
					<input placeholder="Enter invitee..." type="text" class="input-large" ng-model="calendar.attendee" name="attendee" />
            		<button class="btn btn-primary" ng-click="addAttendee()">Add</button>  		  
				</div>
				<span ng-show="form.attendee.$error" class="help-inline">{{errors.attendee}}</span>
			</div>
		</div>    
      	<div class="control-group">
			<label class="control-label" for="invitees">&nbsp;</label>
			<div class="controls">
				<ul class="unstyled">
        			<li ng-repeat="attendee in calendar.attendees">                    
          				<span>{{attendee.email}}</span>
          				<a href="" ng-click="removeAttendee(attendee)"><i class="icon-remove"></i></a>
        			</li>
      			</ul>
			</div>
		</div>   
      	<div class="control-group">
  			<label class="control-label" for="surrname">Guests may:</label>
			<div class="controls">
				<label class="checkbox">
            		<input type="checkbox" ng-model="calendar.guestsCanModify"> modify event
          		</label>
          		<label class="checkbox">
            		<input type="checkbox" ng-model="calendar.guestsCanInviteOthers"> invite others
          		</label>
          		<label class="checkbox">
            		<input type="checkbox" ng-model="calendar.guestsCanSeeOtherGuests"> see guest list
          		</label>
			</div>
		</div> 
	</fieldset>		
	<div class="form-actions">
  		<button class="btn" ng-click="reset()" ng-disabled="isUnchanged(calendar)">Cancel</button>
		<button class="btn btn-primary" type="submit">Save</button>			   	            		
	</div>
</form>		
</div>
<div class="span12">
	<pre>calendar = {{calendar | json}}</pre>		
</div>
</div>
<form class="form-horizontal" ng-submit="addEvent()" name="form">   
	<div class="span9">
  <h3><i class="icon-pencil"></i> Add event</h3> 
	<fieldset>
		<div class="control-group">
			<label class="control-label" for="name">When:</label>
				<div class="controls">					  
					Thu, November 8, 6:30am - 7.30am					  					  
				</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="name">What:</label>
			<div class="controls">
				<div class="input-prepend">
					<!--<span class="add-on"><i class="icon-user"></i></span>-->
					<input placeholder="Summary..." type="text" class="input-xlarge" ng-model="calendar.summary" name="summary" />  					 	
				</div>
				<span ng-show="form.calendar.$error" class="help-inline">{{errors.calendar}}</span>
      </div>
		</div>
		<div class="control-group">
			<label class="control-label" for="name">Calendar:</label>
			<div class="controls">
				<div class="input-prepend">
					<!--<span class="add-on"><i class="icon-user"></i></span>-->
					<select name="cpu" ng-model="event.calendar">
						<option>Calendar 1</option>
						<option>Calendar 2</option>  								
					</select>			
				</div>
				<span ng-show="form.calendar.$error" class="help-inline">{{errors.calendar}}</span>
			</div>
		</div>	        
		</fieldset>
    <div class="form-actions">
  		<button class="btn" ng-click="reset()" ng-disabled="isUnchanged(calendar)">Cancel</button>
			<button class="btn btn-primary" type="submit">Save</button>		
      <button class="btn btn-inverse" type="submit">Edit</button>  	
		</div>	
  </div>
</form>  


 calendar = {
  "kind": "calendar#event",
  "etag": etag,
  "id": string,
  "created": new Date(),
  "updated": new Date(),
  "summary": "This is the summary",
  "description": "This is the description",
  "location": "This is the location",
  "visibility": "default",
  "colorId": "red",
  "creator": {
    "id": "m8doslak",
    "email": "a@b.com",
    "displayName": "Lordnox",
    "self": true
  },
  "organizer": {
    "id": "m8doslak",
    "email": "a@b.com",
    "displayName": "Lordnox",
    "self": true
  },
  "start": {
    "date": new Date(),
    "dateTime": new Date(),
    "timeZone": "CET"
  },
  "end": {
    "date": new Date(),
    "dateTime": new Date(),
    "timeZone": "CET"
  },
  "endTimeUnspecified": false,
  "recurrence": "no",
  "recurringEventId": "",
  "originalStartTime": {
    "date": new Date(),
    "dateTime": new Date(),
    "timeZone": "CET"
  },
  "iCalUID": "282mmn23nc923",
  "sequence": 0,
  "attendees": [{
      "id": "m8doslak",
      "email": "a@b.com",
      "displayName": "Lordnox",
      "self": true,
      "organizer": true,
      "resource": false,
      "optional": true,
      "responseStatus": "none",
      "comment": "(bow)",
      "additionalGuests": 0}
    ],
  "attendeesOmitted": false,
  "anyoneCanAddSelf": boolean,
  "guestsCanInviteOthers": boolean,
  "guestsCanModify": boolean,
  "guestsCanSeeOtherGuests": boolean,
  "locked": true,
  "reminders": {
    "useDefault": true,
    "overrides": [
      {
        "method": "E-Mail",
        "minutes": 15
      }
    ]
  }
}