Danil Annenkov avatar Danil Annenkov committed 9727067 Draft Merge

Merge

Comments (0)

Files changed (23)

 	@mv ${BUILD_DIR}/fullcalendar-${VER} ${BUILD_DIR}/fullcalendar
 	
 	@mkdir -p ${DIST_DIR}
-	@mv ${BUILD_DIR}/fullcalendar-${VER}.zip ${DIST_DIR}
+	@mv ${BUILD_DIR}/fullcalendar-${VER}.zip ${DIST_DIR}/fullcalendar-resourceviews-${VER}.zip
 	@echo "done."
 
 clean:
 Fork of  Full-sized drag & drop event calendar with Resource views (jQuery plugin) http://tux.fi/~jarnok/fullcalendar-resourceviews/
+
 This fork adds "summary" column with own fetcher and renderer.
 
 Original readme:
 
 
-FullCalendar - Full-sized drag & drop event calendar
+FullCalendar ResourceViews - Full-sized drag & drop event calendar with horizontal multiuser views
 ====================================================
 
 Development and testing
+version 1.5.3.3 (11/04/12)
+	- Implemented better dynamic calendar height in resourceview demo 
+	- Fixed issue in daySelection when used non resource view.
+	- Added boolean option refetchResources which determines if resources are refetched every time view changes. Default false.
+	- Resource object handles boolean value readonly which determines if resource is readonly. Default false.
+
+version 1.5.3.2 (10/21/12)
+	- Fixed a bug where day was not calculated correctly when used weekends false in resourceDay view
+	- Fixed a bug where external events broke if were dropped outside the calendar element
+	- Loading callback function returns view object now
+
+version 1.5.3.1 (10/10/12)
+	- Version numbering for resourceviews: first is fullcalendar version, then resource views version
+	- Fixed event dropping revert function
+	- dayclick callback returns resource information
 
 version 1.5.3 (2/6/12)
 	- fixed dragging issue with jQuery UI 1.8.16 (issue 1168)

demos/json-resources.php

+<?php 
+header('Content-type: application/json');
+echo '[{"name":"Resource 2","id":"resource2"},{"name":"Resource 1","id":"resource1"}]';
+?>

demos/resourceView.html

 					start: new Date(y, m, d, 12, 15),
 					end: new Date(y, m, d, 14, 45),
 					allDay: false,
-					resource: 'resource1'
+					resource: ['resource1','resource2']
 				},				
 				{
 					title: 'Meeting',
 			return h;
 		}
 		
-		$(window).resize(function() {
-			$('#calendar').fullCalendar('option', 'height', calcCalendarHeight());
+		windowResize: function( view ) {
+				calendar.fullCalendar('option', 'height', $(window).height() - 40);
+			}
 		});
 	});
 
 	t.destroy = destroy;
 	t.refetchEvents = refetchEvents;
 	t.reportEvents = reportEvents;
+	t.refetchResources = refetchResources;
 	t.reportEventChange = reportEventChange;
 	t.rerenderEvents = rerenderEvents;
 	t.changeView = changeView;
 	var fetchEvents = t.fetchEvents;
         var fetchSummary = t.fetchSummary;        
 	
+	// fetch resources
+	ResourceManager.call(t, options);
+	var fetchResources = t.fetchResources;
 	
 	// locals
 	var _element = element[0];
 	function updateEvents(forceRender) {
 		if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
 			refetchEvents();
+			if (options['refetchResources']) refetchResources(); // refetch resources every time new events are loaded
 		}
 		else if (forceRender) {
 			rerenderEvents();
         function refetchSummary() {
                 fetchSummary();
         }
+	function refetchResources() {
+		fetchResources(false, currentView);
+
+		// we have to destroy all view instances and recreate current one
+		viewInstances = [];
+		
+		// remove current view from display
+		currentView.element.remove();
+		
+		// create current view again
+		currentView = viewInstances[currentView.name] = new fcViews[currentView.name](
+					absoluteViewElement =
+						$("<div class='fc-view fc-view-" + currentView.name + "' style='position:absolute'/>")
+							.appendTo(content),
+					t // the calendar object
+				);
+		// let's render the new view		
+		renderView();
+	}
 	
 	// called when event data arrives
 	function reportEvents(_events) {

src/EventManager.js

 	
 	function pushLoading() {
 		if (!loadingLevel++) {
-			trigger('loading', null, true);
+			trigger('loading', null, true, getView());
 		}
 	}
 	
 	
 	function popLoading() {
 		if (!--loadingLevel) {
-			trigger('loading', null, false);
+			trigger('loading', null, false, getView());
 		}
 	}
 	

src/ResourceManager.js

+/*
+ * Responsible for resources.  Resource source object is anything that provides
+ * data about resources.  It can be function, a JSON object or URL to a JSON 
+ * feed.
+*/
+
+
+function ResourceManager(options) {
+    var t = this;
+	
+    // exports
+    t.fetchResources = fetchResources;
+
+    // local
+    var sources = [];  // source array
+    var cache;  // cached resources
+    
+    _addResourceSources(options['resources']);
+
+
+    /**
+     * ----------------------------------------------------------------
+     * Categorize and add the provided sources
+     * ----------------------------------------------------------------
+     */
+    function _addResourceSources(_sources) {
+        var source = {};
+        
+        if ($.isFunction(_sources)) {
+            // is it a function?
+            source = {
+                resources: _sources
+            };
+            sources.push(source);
+        } else if (typeof _sources == 'string') {
+            // is it a URL string?
+            source = {
+                url: _sources
+            };
+            sources.push(source);
+        } else if (typeof _sources == 'object') {
+            // is it json object?
+            for (var i=0; i<_sources.length; i++) {
+                var s = _sources[i];
+                normalizeSource(s);
+                source = {
+                    resources: s
+                };
+                sources.push(source);
+            }
+        }
+    }
+
+
+    /**
+     * ----------------------------------------------------------------
+     * Fetch resources from source array
+     * ----------------------------------------------------------------
+     */
+    function fetchResources(useCache, currentView) {
+        // if useCache is not defined, default to true
+        useCache = typeof useCache !== 'undefined' ? useCache : true;
+        
+        if (cache != undefined && useCache) {
+            // get from cache
+            return cache;
+        } else {
+            // do a fetch resource from source, rebuild cache
+            cache = [];
+            var len = sources.length;
+            for (var i = 0; i < len; i++) {
+                var resources = _fetchResourceSource(sources[i], currentView);
+                cache = cache.concat(resources);
+            }
+            return cache;
+        }
+    }
+    
+    
+    /**
+     * ----------------------------------------------------------------
+     * Fetch resources from each source.  If source is a function, call
+     * the function and return the resource.  If source is a URL, get
+     * the data via synchronized ajax call.  If the source is an
+     * object, return it as is.
+     * ----------------------------------------------------------------
+     */
+    function _fetchResourceSource(source, currentView) {
+        var resources = source.resources;
+       
+        if (resources) {
+            if ($.isFunction(resources)) {
+                return resources();
+            }
+        } else {
+            var url = source.url;
+            if (url) {
+                var data={};
+                if (typeof currentView == 'object') {
+                    var startParam = options.startParam;
+                    var endParam = options.endParam;
+                    if (startParam) {
+                        data[startParam] = Math.round(+currentView.visStart / 1000);
+                    }
+                    if (endParam) {
+                        data[endParam] = Math.round(+currentView.visEnd / 1000);
+                    }
+                }
+
+                $.ajax($.extend({}, source, {
+                    data: data,
+                    dataType: 'json',
+                    cache: false,
+                    success: function(res) {
+                        res = res || [];
+                        resources = res;
+                    },
+                    error: function() {
+                        alert("ajax error getting json from "+url);
+                    },
+                    async: false  // too much work coordinating callbacks so dumb it down
+                }));
+            }
+        }
+        return resources;
+    }
+    
+    
+    /**
+     * ----------------------------------------------------------------
+     * normalize the source object
+     * ----------------------------------------------------------------
+     */
+    function normalizeSource(source) {
+        if (source.className) {
+            if (typeof source.className == 'string') {
+                source.className = source.className.split(/\s+/);
+            }
+        }else{
+            source.className = [];
+        }
+        var normalizers = fc.sourceNormalizers;
+        for (var i=0; i<normalizers.length; i++) {
+            normalizers[i](source);
+        }
+    }
+}
 js('Header.js');
 js('EventManager.js');
 js('SummaryManager.js');
+js('ResourceManager.js');
 js('date_util.js');
 js('util.js');
 

src/common/DayEventRenderer.js

 		var skinCss;
 		var html = '';
 		var viewName = getViewName();
+		var weekends = opt('weekends'), weekendTestDate, weekendSumColStart, weekendSumColEnd;
 
 		// calculate desired position/dimensions, create html
 		for (i=0; i<segCnt; i++) {
 				}
 				
 				if (viewName == 'resourceMonth') {
-					// hack for resourceMonth view
-					// What is we have view which lists 4 week from now on, how can we get right columns?
+					// for resourceMonth view
 					leftCol = seg.start.getDate()-1;
 					rightCol = seg.end.getDate()-2;
 
+					if(!weekends) {
+						// Drop out weekends
+						weekendSumColStart=0	
+						weekendSumColEnd=0
+						
+						for(var j=0; j<=leftCol; j++) {
+							weekendTestDate = addDays(cloneDate(t.visStart), j);
+							
+							if(weekendTestDate.getDay() == 0 || weekendTestDate.getDay() == 6) {
+								weekendSumColStart++;
+							}
+						}
+						leftCol -= weekendSumColStart;
+						
+						if (seg.start.getDay() == 6 || seg.start.getDay() == 0) leftCol++;
+						
+						for(j=0; j<=rightCol; j++) {
+							weekendTestDate = addDays(cloneDate(t.visStart), j);
+							
+							if(weekendTestDate.getDay() == 0 || weekendTestDate.getDay() == 6) {
+								weekendSumColEnd++;
+							}
+						}
+						rightCol -= weekendSumColEnd;
+					}
+					
 					if(rightCol < 0) {
 						// end is in the next month so rightCol is the last column
 						rightCol = getColCnt()-1;
 					}
 				}
 				else if (viewName == 'resourceNextWeeks') {
+					
 					leftCol = dateCell(seg.start).col;
 					rightCol = dateCell(seg.end).col-1;
+					if(!weekends) {
+						leftCol = dateCell(seg.start).col;
+						rightCol = dateCell(addDays(cloneDate(seg.end),-1)).col;
+						if (seg.start.getDay() == 6 || seg.start.getDay() == 0) leftCol++;
+					}
 				}
 				else if (viewName == 'resourceDay') {
 					// hack for resourceDay view

src/common/SelectionManager.js

 	
 	
 	function reportSelection(startDate, endDate, allDay, ev, resource) {
+		if (typeof resource == 'object' && resource.readonly === true) {
+			return false;
+		}
+
 		selected = true;
 		trigger('select', null, startDate, endDate, allDay, ev, '', resource);
 	}
 		var hoverListener = t.getHoverListener();
 		var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
 		var row;
-		var resources = opt('resources');
+		var resources = t.getResources || [];
+		var resourceRO;
+
 		var viewName = getViewName();
 		if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
 			unselect(ev);
 			var dates;
 			hoverListener.start(function(cell, origCell) { // TODO: maybe put cellDate/cellIsAllDay info in cell
 				clearSelection();
-				if (cell && cellIsAllDay(cell)) {
+
+				if (cell) {
+					resourceRO = typeof resources[cell.row] == 'object' ? resources[cell.row].readonly : false;
+				}
+				
+
+				if (cell && cellIsAllDay(cell) && resourceRO !== true) {
 					dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp);
 					renderSelection(dates[0], dates[1], (viewName == 'resourceDay' ? false : true), cell.row);
 					row = cell.row;
 				hoverListener.stop();
 				if (dates) {
 					if (+dates[0] == +dates[1]) {
-						reportDayClick(dates[0],(viewName == 'resourceDay' ? false : true), ev);
+						reportDayClick(dates[0],(viewName == 'resourceDay' ? false : true), ev, resources[row]);
 					}
 					reportSelection(dates[0], (viewName == 'resourceDay' ? addMinutes(dates[1], opt('slotMinutes')) : dates[1]), (viewName == 'resourceDay' ? false : true), ev, resources[row]);
 				}

src/common/View.js

 	function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui, resource) {
 		var oldAllDay = event.allDay;
 		var eventId = event._id;
+		var oldResourceId = event.resource;
 		moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay, resource);
 		trigger(
 			'eventDrop',
 			allDay,
 			function() {
 				// TODO: investigate cases where this inverse technique might not work
-				moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
+				moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay,oldResourceId);
 				reportEventChange(eventId);
 			},
 			ev,
 	lazyFetching: true,
 	startParam: 'start',
 	endParam: 'end',
+
+	// resource ajax
+	refetchResources: false,
 	
 	// time formats
 	titleFormat: {
 /**
  * @preserve
- * FullCalendar v@VERSION
+ * FullCalendar ResourceViews version @VERSION
+ * http://tux.fi/~jarnok/fullcalendar-resourceviews/
  * http://arshaw.com/fullcalendar/
  *
  * Use fullcalendar.css for basic styling.
  * For event drag & drop, requires jQuery UI draggable.
  * For event resizing, requires jQuery UI resizable.
  *
+ * Copyright (c) 2012 Jarno Kurlin
  * Copyright (c) 2011 Adam Shaw
  * Dual licensed under the MIT and GPL licenses, located in
  * MIT-LICENSE.txt and GPL-LICENSE.txt respectively.

src/resource/ResourceDayView.js

 	var opt = t.opt;
 	var renderBasic = t.renderBasic;
 	var formatDates = calendar.formatDates;
-	
+	var getResources = t.getResources;
 	
 	
 	function render(date, delta) {
 		if (delta) {
 			addDays(date, delta * 1);
+			if (!opt('weekends')) skipWeekend(date, delta < 0 ? -1 : 1);
 		}
 
 		var start = addMinutes(cloneDate(date, true),parseTime(opt('minTime')));
 		var end = addMinutes(cloneDate(start), (parseTime(opt('maxTime'))-parseTime(opt('minTime'))));
 		var visStart = cloneDate(start);
 		var visEnd = cloneDate(end);
-		var weekends = opt('weekends');
-		if (!weekends) {
-			skipWeekend(visStart);
-			skipWeekend(visEnd, -1, true);
-		}
+
 		t.title = formatDates(
 			visStart,
 			addDays(cloneDate(visEnd), -1),
 		t.visEnd = visEnd;
 
 		var cols = Math.round((visEnd - visStart) / 1000 / 60 / opt('slotMinutes'));
-
-		renderBasic(opt('resources').length, opt('resources').length, cols, false);
+		renderBasic(getResources.length, getResources.length, cols, false);
 	}
 	
 	

src/resource/ResourceEventRenderer.js

 	function compileSegs(events) {
 		var rowCnt = getRowCnt(),
 			colCnt = getColCnt(),
-			resources = getResources(),
+			resources = t.getResources,
 			d1 = cloneDate(t.visStart),
-			d2 = addDays(cloneDate(d1), colCnt),
+			d2 = cloneDate(t.visEnd),
 			visEventsEnds = $.map(events, exclEndDay),
 			i, row,
 			j, level,
 			k, seg, currentResource, viewName = getViewName(),
-			segs=[];
-			
-		if (viewName == 'resourceDay') {
-			d2 = cloneDate(t.visEnd);
-			
+			l, segs=[],
+			weekends = opt('weekends'),
+			startDay, endDay, startDate, endDate;
+		
+		if (viewName == 'resourceDay') {			
 			visEventsEnds = $.map(events, function(event) {
 				return event.end || addDays(event.start, 1);
 			});
             for (j=0; j<row.length; j++) {
                 seg = row[j];
                 seg.row = i;
-                if(currentResource == seg['event'].resource) {
-                    segs.push(seg);
+                // Let's be backwards compatitle. If event resource is not array, then we convert it.
+                if (!$.isArray(seg.event.resource)) {
+                    seg.event.resource = [seg.event.resource];
+                }
+                for (l=0; l<seg.event.resource.length; l++) {
+                    startDay = seg.event.start.getDay();
+                    startDate = seg.event.start.getDate();
+                    if (seg.event.end == null) {
+                        endDay = seg.event.start.getDay();
+                        endDate = cloneDate(seg.event.start, true).getDate();
+                    }
+                    else {
+                        endDay = seg.event.end.getDay();
+                        endDate = seg.event.end.getDate();
+                    }
+
+                    // skip if weekends is set to false and this event is on weekend
+                    if(!weekends &&
+                            (startDay == 6 || startDay == 0) &&
+                            (endDay == 6 || endDay == 0) &&
+                            (startDate == endDate || addDays(cloneDate(seg.event.start),1).getDate() == endDate)
+                            ) continue;
+                    if(currentResource == seg['event'].resource) {
+                        segs.push(seg);
+                    }
                 }
             }
         }
 	
 	function draggableResourceEvent(event, eventElement) {
 		var hoverListener = getHoverListener();
-		var dayDelta, minuteDelta, resourceDelta, newResourceId, resources, viewName = getViewName();
+		var dayDelta, minuteDelta, resourceDelta, newResourceId, resources = t.getResources, viewName = getViewName(), weekendTestDate, daysToAdd, daysToDel, dayDeltaStart, dayDeltaEnd, i;
+		
+		var denyEventDragging = false;
+		$(resources).each(function(i, resource) {
+			if (resource.id == event.resource && resource.readonly) {
+				denyEventDragging = true;
+			} 
+		});
+
 		eventElement.draggable({
 			zIndex: 9,
 			delay: 50,
+			disabled: denyEventDragging,
 			opacity: opt('dragOpacity'),
 			revertDuration: opt('dragRevertDuration'),
 			start: function(ev, ui) {
 				trigger('eventDragStart', eventElement, event, ev, ui);
 				hideEvents(event, eventElement);
 				hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
-					eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
+					eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta || resources[cell.row].readonly === true);
+
 					clearOverlays();
-					if (cell) {
+					
+					if (cell && !resources[cell.row].readonly) {
 						//setOverflowHidden(true);
-						resourceDelta = rowDelta * (opt('isRTL') ? -1 : 1);
-						resources = opt('resources');
+						resourceDelta = rowDelta;
 						newResourceId = resources[cell.row].id; 
 						
 						if (viewName == 'resourceDay') {
 							);
 						}
 						else {
-							dayDelta = colDelta * (opt('isRTL') ? -1 : 1);			
+							dayDelta = dayDeltaStart = dayDeltaEnd = colDelta * (opt('isRTL') ? -1 : 1);	
+
+							// If weekends are not within, add or remove days from dayDelta. Is there a better way?
+							if (!opt('weekends') && (dayDelta > 0 || dayDelta < 0)) {
+								if (dayDelta > 0) {
+									for(i=1; i<=dayDeltaStart; i++) {
+										weekendTestDate = addDays(cloneDate(event.start), i);
+										if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaStart++;
+									}
+									
+									for(i=1; i<=dayDeltaEnd; i++) {
+										weekendTestDate = addDays(cloneDate(event.end), i);
+										if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaEnd++;
+									}
+								}
+								else {
+									for(i=-1; i>=dayDeltaStart; i--) {
+										weekendTestDate = addDays(cloneDate(event.start), i);
+										if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaStart--;
+									}
+									
+									for(i=-1; i>=dayDeltaEnd; i--) {
+										weekendTestDate = addDays(cloneDate(event.end), i);
+										if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaEnd--;
+									}
+								}
+							}	
+
 							renderDayOverlay(
-								addDays(cloneDate(event.start), dayDelta),
-								addDays(exclEndDay(event), dayDelta), 
+								addDays(cloneDate(event.start), dayDeltaStart),
+								addDays(exclEndDay(event), dayDeltaEnd), 
 								false,
 								cell.row
 							);
 					eventDrop(this, event, 0, minuteDelta, event.allDay, ev, ui, newResourceId);
 				}
 				else if (dayDelta || resourceDelta) {
+					if (!opt('weekends')) {
+						// We have to add or remove days from event.start and event.end. Is there a better way?
+						if (dayDelta > 0) {
+							daysToAdd = 0;
+							for(i=1; i<=dayDelta+daysToAdd; i++) {
+								weekendTestDate = addDays(cloneDate(event.start), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToAdd++;
+							}
+							if (daysToAdd > 0) event.start = addDays(cloneDate(event.start), daysToAdd, true);
+							
+							daysToAdd = 0;
+							for(i=1; i<=dayDelta+daysToAdd; i++) {
+								weekendTestDate = addDays(cloneDate(event.end), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToAdd++;
+							}
+							if (daysToAdd > 0) event.end = addDays(cloneDate(event.end), daysToAdd, true);
+						}
+						else {
+							daysToDel = 0;
+							for(i=-1; i>=dayDelta+daysToDel; i--) {
+								weekendTestDate = addDays(cloneDate(event.start), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToDel--;
+							}
+							if (daysToDel < 0) event.start = addDays(cloneDate(event.start), daysToDel, true);
+
+							daysToDel = 0;
+							for(i=-1; i>=dayDelta+daysToDel; i--) {
+								weekendTestDate = addDays(cloneDate(event.end), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToDel--;
+							}
+							if (daysToDel < 0) event.end = addDays(cloneDate(event.end), daysToDel, true);
+						}
+					}
 					eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui, newResourceId);
 				} else{
 					eventElement.css('filter', ''); // clear IE opacity side-effects
 		var handle = element.find('div.ui-resizable-' + direction);
 		var isResizing = false;
 		
+		// let's check if resource is readonly?
+		var denyEventResizing = false;
+		$(t.getResources).each(function(i, resource) {
+			if (resource.id == event.resource && resource.readonly) {
+				denyEventResizing = true;
+			} 
+		});
+
+		if (denyEventResizing) return false;
+
 		// TODO: look into using jquery-ui mouse widget for this stuff
 		disableTextSelection(element); // prevent native <a> selection for IE
 		element
 			var dis = rtl ? -1 : 1;
 			var dit = rtl ? colCnt-1 : 0;
 			var elementTop = element.css('top');
-			var dayDelta;
+			var dayDelta, dayDeltaStart, dayDeltaEnd;
 			var minuteDelta;
 			var helpers;
 			var eventCopy = $.extend({}, event);
 			var minCell = dateCell(event.start);
+			var newEnd;
+			var weekendTestDate;
+			
 			clearSelection();
 			$('body')
 				.css('cursor', direction + '-resize')
 						var newEnd = addMinutes(eventEnd(event), minuteDelta, true);
 					}
 					else {
-						dayDelta = (7 + c*dis+dit) - (7 + origCell.col*dis+dit);
-						var newEnd = addDays(eventEnd(event), dayDelta, true);
+						dayDelta = dayDeltaStart = dayDeltaEnd = (7 + c*dis+dit) - (7 + origCell.col*dis+dit);
+						
+						// If weekends is set to false, add or remove days from dayDelta
+						if (!opt('weekends') && (dayDelta > 0 || dayDelta < 0)) {
+							if (dayDelta > 0) {
+								for(var i=1; i<=dayDeltaEnd; i++) {
+									weekendTestDate = addDays(cloneDate(event.end), i);
+									if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaEnd++;
+								}
+							}
+							else {
+								for(i=-1; i>=dayDeltaEnd; i--) {
+									weekendTestDate = addDays(cloneDate(event.end), i);
+									if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) dayDeltaEnd--;
+								}
+							}
+						}	
+						newEnd = addDays(eventEnd(event), dayDeltaEnd, true);
+						
 					}
 					
 					if (dayDelta || minuteDelta) {
 				$('body').css('cursor', '');
 				hoverListener.stop();
 				clearOverlays();
+
 				if (dayDelta) {
+					if (!opt('weekends')) {
+						// We have to add or remove days from event.end. Is there a better way?
+						if (dayDelta > 0) {
+							var daysToAdd = 0;
+							for(var i=1; i<=dayDelta+daysToAdd; i++) {
+								weekendTestDate = addDays(cloneDate(event.end), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToAdd++;
+							}
+							if (daysToAdd > 0) event.end = addDays(cloneDate(event.end), daysToAdd, true);
+						}
+						else {
+							var daysToDel = 0;
+							for(i=-1; i>=dayDelta+daysToDel; i--) {
+								weekendTestDate = addDays(cloneDate(event.end), i);
+								if (weekendTestDate.getDay() == 6 || weekendTestDate.getDay() == 0) daysToDel--;
+							}
+							if (daysToDel < 0) event.end = addDays(cloneDate(event.end), daysToDel, true);
+						}
+					}
+				
 					eventResize(this, event, dayDelta, 0, ev);
 					// event redraw will clear helpers
 				}

src/resource/ResourceMonthView.js

 	var opt = t.opt;
 	var renderBasic = t.renderBasic;
 	var formatDates = calendar.formatDates;
-	
+	var getResources = t.getResources;
 	
 	
 	function render(date, delta) {
 			skipWeekend(visStart);
 			skipWeekend(visEnd, -1, true);
 		}
+
 		t.title = formatDates(
 			visStart,
 			addDays(cloneDate(visEnd), -1),
 		t.visStart = visStart;
 		t.visEnd = visEnd;
 		var cols = Math.round((visEnd - visStart) / (DAY_MS));
-		renderBasic(opt('resources').length, opt('resources').length, cols, false);
+		var weekendTestDate;
+		if(!weekends) {
+			// Drop out weekends from cols
+			var weekendCnt = 0;
+			for(var i=1; i<=cols; i++) {
+				weekendTestDate = addDays(cloneDate(visStart), i);
+				if(weekendTestDate.getDay() == 0 || weekendTestDate.getDay() == 6) {
+					weekendCnt++;
+				}
+			}
+			cols -= weekendCnt;
+		}
+		
+		renderBasic(getResources.length, getResources.length, cols, false);
 	}
 	
 	

src/resource/ResourceNextWeeksView.js

 	var opt = t.opt;
 	var renderBasic = t.renderBasic;
 	var formatDates = calendar.formatDates;
-	
+	var getResources = t.getResources;
 	
 	
 	function render(date, delta) {
+		var weekends = opt('weekends');
 		if (delta) {
-			addDays(date, delta * opt('numberOfWeeks') * 7);
+			addDays(date, delta * opt('numberOfWeeks') * weekends ? 7 : 5);
 		}
-		var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
+		var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + weekends ? 7 : 5) % weekends ? 7 : 5));
 		var end = addWeeks(cloneDate(start), opt('numberOfWeeks'));
 		var visStart = cloneDate(start);
 		var visEnd = cloneDate(end);
-		var weekends = opt('weekends');
 
 		if (!weekends) {
 			skipWeekend(visStart);
 			addDays(cloneDate(visEnd), -1),
 			opt('titleFormat')
 		);
+
 		t.start = start;
 		t.end = end;
 		t.visStart = visStart;
 		t.visEnd = visEnd;
-		renderBasic(opt('resources').length, opt('resources').length, weekends ? opt('numberOfWeeks') * 7 : opt('numberOfWeeks') * 5, false);
+		renderBasic(getResources.length, getResources.length, weekends ? opt('numberOfWeeks') * 7 : opt('numberOfWeeks') * 5, false);
 	}
 	
 	

src/resource/ResourceView.js

 	t.allDayBounds = allDayBounds;
 	t.getRowCnt = function() { return rowCnt };
 	t.getColCnt = function() { return colCnt };
-	t.getResources = function() { return opt('resources') };
+	t.getResources = calendar.fetchResources();
 	t.getColWidth = function() { return colWidth };
 	t.getViewName = function() { return viewName };
 	t.getDaySegmentContainer = function() { return daySegmentContainer };
 	
+	
 	// imports
 	View.call(t, element, calendar, viewName);
 	OverlayManager.call(t);
 	var clearOverlays = t.clearOverlays;
 	var daySelectionMousedown = t.daySelectionMousedown;
 	var formatDate = calendar.formatDate;
-
+	
 	
 	// locals
 	
 	var viewHeight;
 	var colWidth;
 	
-	var rowCnt, colCnt, resources;
+	var rowCnt, colCnt, getResources;
 	var coordinateGrid;
 	var hoverListener;
 	var colContentPositions;
 		var contentClass = tm + "-widget-content";
 		var i, j, id, resourceName;
 		var table;
-		var resources = opt('resources');             
-
+		var resources = t.getResources;
 		s =
-			"<table id='fc-table' class='fc-border-separate' style='width:100%' cellspacing='0'>" +
+			"<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
 			"<thead>" +
 			"<tr class='fc-first fc-last'><th class='fc-resourceName'>&nbsp;</th>";
 		for (i=0; i<colCnt; i++) {
                 }
                 s +=
 	            "</tr>" +
-		    "</thead>" +
+		        "</thead>" +
 	            "<tbody>";
 		for (i=0; i<maxRowCnt; i++) {
 			id = resources[i]['id'];
                         if (viewName == 'resourceMonth') {
                                 s += "<td class='fc-summary fc-widget-content' " + "data-resource='"+ id + "'><span></span></td>";
 			        s +=
-				"</tr>";
+				        "</tr>";
                         }
 		}
 		s +=
 		bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells
 		
 		dayBind(bodyCells);
-                var summaryCells = body.find('td.fc-summary');
+        var summaryCells = body.find('td.fc-summary');
                 
-                summaryBind(summaryCells);
+        summaryBind(summaryCells);
 		
 		daySegmentContainer =
 			$("<div style='position:absolute;z-index:8;top:0;left:0'/>")
 		var cell;
 		var date;
 		var row;
-	
+		var weekendTester;
+		var indexCorrecter=0;
+		var weekends = opt('weekends');
 		headCells.each(function(i, _cell) {
 			cell = $(_cell);
 			date = indexDate(i);
+
 			cell.html(formatDate(date, colFormat));
 			if (date.getDay() == 0 || date.getDay() == 6) {
 				cell.addClass('fc-weekend');
 			setDayID(cell, i);
 		});
 		
+		indexCorrecter=0;
 		bodyCells.each(function(i, _cell) {
 			cell = $(_cell);
 			date = indexDate(i);
 		if (viewName == 'resourceDay') {
 			rowEnd = addMinutes(cloneDate(rowStart), opt('slotMinutes')*colCnt);
 		}
+		else if (!opt('weekends')) {
+			rowEnd = cloneDate(t.visEnd);
+		}
 
 		var stretchStart = new Date(Math.max(rowStart, overlayStart));
 		var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
 					colStart = dayDiff(stretchStart, rowStart);
 					colEnd = dayDiff(stretchEnd, rowStart);
 				}
+				
+				if(!opt('weekends')) {
+					// Drop weekends off
+					var weekendSumColStart=0, weekendTestDate;				
+					for(var i=0; i<=colStart; i++) {
+						weekendTestDate = addDays(cloneDate(t.visStart), i);
+						
+						if(weekendTestDate.getDay() == 0 || weekendTestDate.getDay() == 6) {
+							weekendSumColStart++;
+						}
+					}
+					colStart -= weekendSumColStart;
+					
+					var weekendSumColEnd=0
+					for(i=0; i<=colEnd-1; i++) {
+						weekendTestDate = addDays(cloneDate(t.visStart), i);
+						
+						if(weekendTestDate.getDay() == 0 || weekendTestDate.getDay() == 6) {
+							weekendSumColEnd++;
+						}
+					}
+					colEnd -= weekendSumColEnd;
+				}
 			}
 			
 			dayBind(
 	}
 	
 	
-	function reportDayClick(date, allDay, ev) {
+	function reportDayClick(date, allDay, ev, resource) {
 		var cell = dateCell(date);
-		var _element = bodyCells[cell.row*colCnt + cell.col];
-		trigger('dayClick', _element, date, allDay, ev);
+		var _element = bodyCells[cell.col];
+		trigger('dayClick', _element, date, allDay, ev, resource);
 	}
 	
 	
 		var cell = hoverListener.stop();
 		clearOverlays();
 		if (cell) {
+			var resources = t.getResources, newResource = resources[cell.row];
 			var d = cellDate(cell);
-			trigger('drop', _dragElement, d, true, ev, ui);
+			trigger('drop', _dragElement, d, true, ev, ui, newResource);
 		}
 	}
 	
 	
 	
 	function dateCell(date) {
-		var col,year,month,day,cmpDate,cmpYear,cmpMonth,cmpDay;
+		var col,year,month,day,cmpDate,cmpYear,cmpMonth,cmpDay, weekends = opt('weekends');
 		if (viewName == 'resourceDay') {
 			col = timeOfDayCol(date);
 		}
 			year = date.getFullYear();
 			month = date.getMonth();
 			day = date.getDate();
+
 			for (var i=0; i < colCnt; i++) {
 				cmpDate = _cellDate(i);
 				cmpYear = cmpDate.getFullYear();
 					col = i;
 					break;
 				}
+				else if (cmpDate > date && !weekends) {
+					// No weekends in the calendar, this must be the right column!
+					col = i-1;
+					break;
+				}
 			};
 			
 			if (typeof col == 'undefined') {
 		if (viewName == 'resourceDay') {
 			return addMinutes(cloneDate(t.visStart), col*opt('slotMinutes'));
 		}
-		else {
-			return addDays(cloneDate(t.visStart), col*dis+dit);
+		else {	
+			if (!opt('weekends')) {
+				// no weekends
+				var dateTest, i;
+
+				for (i=0; i <= col; i++) {
+					dateTest = addDays(cloneDate(t.visStart), i);
+					
+					if (dateTest.getDay() == 6 || dateTest.getDay() == 0) {
+						// this sunday or saturday
+						col++;
+					}
+				}
+			}
+
+			return addDays(cloneDate(t.visStart), col, true);
 		}
 	}
 	

src/resource/ResourceWeekView.js

 	var opt = t.opt;
 	var renderBasic = t.renderBasic;
 	var formatDates = calendar.formatDates;
-	
+	var getResources = t.getResources;
 	
 	
 	function render(date, delta) {
 		t.end = end;
 		t.visStart = visStart;
 		t.visEnd = visEnd;
-		renderBasic(opt('resources').length, opt('resources').length, weekends ? 7 : 5, false);
+		renderBasic(getResources.length, getResources.length, weekends ? 7 : 5, false);
 	}
 	
 	

tests/json-resources.php

+<?php 
+header('Content-type: application/json');
+echo '[{"name":"Resource 3","id":"resource3"},{"name":"Resource 2","id":"resource2"},{"name":"Resource 1","id":"resource1"}]';
+?>

tests/resourceView.html

 			minTime: 7,
 			maxTime:17,
 			selectHelper: true,
-			resources: [
-				{
-					name: 'Resource Name 2',
-					id:	'resource2'
-				},
-				{
-					name: 'Resource Name 1',
-					id:	'resource1'
-				},
-				{
-					name: 'Resource Name 3',
-					id:	'resource3'
-				}
-			],
+			resources: 'json-resources.php',
 			events: [
 				{
 					title: 'Meeting',
 					start: new Date(y, m, d, 10, 30),
 					end: new Date(y, m, d+4, 11, 00),
 					allDay: false,
-					resource: 'resource1'
+					resource: ['resource1','resource3']
 				},
 					{
 					title: 'All Day Event',
-1.5.3
+1.5.3.3
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.