From 1dba3928bbdd80a1c82a60d8dac66eeb507cd7ab Mon Sep 17 00:00:00 2001 From: Nic Newdigate Date: Tue, 31 Mar 2015 19:28:49 +0100 Subject: [PATCH 1/5] Initial resource month view --- demos/resource-views.html | 10 +- lumbar.json | 2 + src/main.css | 5 + src/resource/ResourceMonthEventRenderer.js | 138 +++++++++++++++++++++ src/resource/ResourceMonthView.js | 64 ++++++++++ 5 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/resource/ResourceMonthEventRenderer.js create mode 100644 src/resource/ResourceMonthView.js diff --git a/demos/resource-views.html b/demos/resource-views.html index 20e3f1c347..9499d96542 100644 --- a/demos/resource-views.html +++ b/demos/resource-views.html @@ -46,7 +46,7 @@ header: { left: 'prev,next today', center: 'title', - right: 'month,agendaWeek,resourceDay' + right: 'month,agendaWeek,resourceDay, resourceMonth' }, defaultView: 'resourceDay', editable: true, @@ -77,6 +77,7 @@ title: 'R1-R2: Lunch 12.15-14.45', start: new Date(y, m, d, 12, 15), end: new Date(y, m, d, 14, 45), + color: 'black', allDay: false, resources: ['resource1', 'resource2'] }, @@ -84,12 +85,14 @@ title: 'R1: All day', start: new Date(y, m, d, 10, 30), end: new Date(y, m, d, 11, 0), + color: 'black', allDay: true, resources: 'resource1' }, { title: 'R2: Meeting 11.00', start: new Date(y, m, d, 11, 0), + color: 'black', allDay: true, resources: 'resource2' }, @@ -97,6 +100,7 @@ title: 'R1/R2: Lunch 12-14', start: new Date(y, m, d, 12, 0), end: new Date(y, m, d, 14, 0), + color: 'black', allDay: false, resources: ['resource1', 'resource2'] }, @@ -105,6 +109,7 @@ title: 'R1: Lunch', start: new Date(y, m, d, 12, 0), end: new Date(y, m, d, 14, 0), + color: 'black', allDay: false, resources: ['resource1'] }, @@ -112,6 +117,7 @@ title: 'R3: Breakfast', start: new Date(y, m, d, 8, 0), end: new Date(y, m, d, 8, 30), + color: 'black', allDay: false, resources: ['resource3'] }, @@ -119,6 +125,7 @@ id: 999, title: 'Repeating Event', start: new Date(y, m, d - 3, 16, 0), + color: 'black', allDay: false, resources: 'resource2' }, @@ -126,6 +133,7 @@ id: 999, title: 'Repeating Event', start: new Date(y, m, d + 4, 16, 0), + color: 'black', allDay: false, resources: 'resource2' } diff --git a/lumbar.json b/lumbar.json index 0cfac92b09..5afb40b375 100644 --- a/lumbar.json +++ b/lumbar.json @@ -34,7 +34,9 @@ "src/agenda/AgendaEventRenderer.js", "src/resource/ResourceDayView.js", "src/resource/ResourceView.js", + "src/resource/ResourceMonthView.js", "src/resource/ResourceEventRenderer.js", + "src/resource/ResourceMonthEventRenderer.js", "src/common/View.js", "src/common/DayEventRenderer.js", "src/common/SelectionManager.js", diff --git a/src/main.css b/src/main.css index 6fd39c2df5..bbc6654384 100644 --- a/src/main.css +++ b/src/main.css @@ -114,3 +114,8 @@ html .fc, } + +.fcDayContentResource { + float:left; + height:50px; +} diff --git a/src/resource/ResourceMonthEventRenderer.js b/src/resource/ResourceMonthEventRenderer.js new file mode 100644 index 0000000000..0ae4344e88 --- /dev/null +++ b/src/resource/ResourceMonthEventRenderer.js @@ -0,0 +1,138 @@ +function ResourceEventMonthRenderer() { + var t = this; + + // imports + BasicEventRenderer.call(t); + + // exports + t.renderEvents = renderEvents; + t.dayStart = moment.duration("07:00:00"); + t.dayEnd = moment.duration("18:00:00"); + t.hoursInDay = t.dayEnd.asHours() - t.dayStart.asHours(); + t.getSegmentsForEvent = getSegmentsForEvent; + t.getCellForDateAndResource = getCellForDateAndResource; + t.resources = t.calendar.fetchResources(true, t); + + /* Rendering + ----------------------------------------------------------------------------*/ + + function renderEvents(events, modifiedEventId) { + events.forEach(function(event, eventIndex) { + var segments = t.getSegmentsForEvent(event); + + segments.forEach(function(segment, segmentIndex) { + + var divForResource = t.getCellForDateAndResource(segment.date, segmentIndex); + + if (divForResource.length > 0) { + var resourceBookingDivTable = $('
'); + divForResource.append(resourceBookingDivTable); + var resourceDivHeight = divForResource.height(); + var bookingStart = segment.startHour; + var bookingEnd = segment.endHour; + var divtop = (resourceDivHeight * (bookingStart - t.dayStart.asHours())) / (t.hoursInDay); + var divheight = (resourceDivHeight * (bookingEnd - bookingStart)) / (t.hoursInDay); + resourceBookingDivTable.attr("title", event.title + ' - ' + bookingStart + ' - ' + bookingEnd); + resourceBookingDivTable.css("top", divtop + "px"); + resourceBookingDivTable.css("height", divheight + "px"); + resourceBookingDivTable.css("width", Math.floor(100 / t.resources.length) + "%"); + resourceBookingDivTable.css("background-color", event.color); + resourceBookingDivTable.css("position", "absolute"); + resourceBookingDivTable.css("opacity","0.3"); + } + }); + }); + } + + + function getCellForDateAndResource(date, resourceIndex) { + var dataDate = '.fc-day[data-date="' + date.format("YYYY-MM-DD") + '"]'; + var resourceContent = $(dataDate).find('#resourceContent' + date.format("YYYYMMDD") + "R" + resourceIndex); + return resourceContent; + } + + function getSegmentsForEvent(event) { + var segments = []; + + var mStart = moment(event.start); + var mEnd = moment(event.end); + if (event.allDay) { + mEnd = moment(event.start).add(t.dayEnd.asHours(), 'hour'); + } + var numDays = Math.ceil(moment.duration(mEnd.diff(mStart)).asDays()); + var startDate = moment(mStart).startOf('day'); + + if (event.resources instanceof Array) { + + event.resources.forEach(function(resource, resourceIndex) { + segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate)); + }); + + } else { + segments = segments.concat(createSegmentsForResource(event.resources, 0, numDays, mStart, mEnd, startDate)); + } + return segments; + } + + function createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate) { + var results = []; + if (event.allDay) { + for (var dayIndex=0;dayIndex 1){ + // event spans multiple days + var startOfSegmentDay = moment(mStart).startOf('day'); + segment.startHour = moment.duration(moment(mStart).diff(startOfSegmentDay)).asHours(); + segment.endHour = t.dayEnd.asHours(); + } + } else + { + if (i==numDays-1) { + // segment for last day of event + segment.startHour = moment.duration(t.dayStart).asHours(); + segment.endHour = moment.duration(mEnd).asHours(); + } else + { + // segment for day between start and end of event + segment.startHour = moment.duration(t.dayStart).asHours(); + segment.endHour = moment.duration(t.dayEnd).asHours(); + } + } + + if (segment.startHour < t.dayStart.asHours()) { + segment.startHour = t.dayStart.asHours(); + } + + if (segment.endHour > t.dayEnd.asHours()) { + segment.endHour = t.dayEnd.asHours(); + } + + results.push(segment); + } + } + return results; + } + +} \ No newline at end of file diff --git a/src/resource/ResourceMonthView.js b/src/resource/ResourceMonthView.js new file mode 100644 index 0000000000..420218b237 --- /dev/null +++ b/src/resource/ResourceMonthView.js @@ -0,0 +1,64 @@ + +fcViews.resourceMonth = ResourceMonthView; + +function ResourceMonthView(element, calendar) { + var t = this; + + // exports + t.incrementDate = incrementDate; + t.render = render; + + // imports + BasicView.call(t, element, calendar, 'resourceMonth'); + ResourceEventMonthRenderer.call(t); + t.calendar.options.dayRender = dayRender; + + function incrementDate(date, delta) { + return date.clone().stripTime().add('months', delta).startOf('month'); + } + + function render(date) { + t.intervalStart = date.clone().stripTime().startOf('month'); + t.intervalEnd = t.intervalStart.clone().add('months', 1); + + t.start = t.intervalStart.clone(); + t.start = t.skipHiddenDays(t.start); // move past the first week if no visible days + t.start.startOf('week'); + t.start = t.skipHiddenDays(t.start); // move past the first invisible days of the week + + t.end = t.intervalEnd.clone(); + t.end = t.skipHiddenDays(t.end, -1, true); // move in from the last week if no visible days + t.end.add((7 - t.end.weekday()) % 7, 'days'); // move to end of week if not already + t.end = t.skipHiddenDays(t.end, -1, true); // move in from the last invisible days of the week + + var rowCnt = Math.ceil( // need to ceil in case there are hidden days + t.end.diff(t.start, 'weeks', true) // returnfloat=true + ); + if (t.opt('weekMode') == 'fixed') { + t.end.add('weeks', 6 - rowCnt); + rowCnt = 6; + } + + t.title = calendar.formatDate(t.intervalStart, t.opt('titleFormat')); + + t.renderBasic(rowCnt, t.getCellsPerWeek(), true); + } + + function dayRender(date, cell) { + var mContainer = moment(date); + var datePostfix = mContainer.format("YYYYMMDD"); + var idString = "wxx" + datePostfix; + var fcDayContent = $(cell).find('.fc-day-content'); + fcDayContent.children().empty(); + var wrapper = $('
'); + fcDayContent.children().append(wrapper); + + t.resources.forEach(function(resource, resourceIndex) { + var resourceDiv = $('
'); + resourceDiv.css("width", Math.floor(100 / t.resources.length) + "%"); + resourceDiv.css("height", "80px"); + + wrapper.append(resourceDiv); + }); + } +} \ No newline at end of file From 11ccc9af580b019d79c4c083d5ba310ddd6b7ddb Mon Sep 17 00:00:00 2001 From: Nic Newdigate Date: Tue, 31 Mar 2015 20:50:59 +0100 Subject: [PATCH 2/5] Fixed: incorrect div returned for resource --- src/resource/ResourceMonthEventRenderer.js | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/resource/ResourceMonthEventRenderer.js b/src/resource/ResourceMonthEventRenderer.js index 0ae4344e88..7c56a0f0ce 100644 --- a/src/resource/ResourceMonthEventRenderer.js +++ b/src/resource/ResourceMonthEventRenderer.js @@ -22,7 +22,7 @@ function ResourceEventMonthRenderer() { segments.forEach(function(segment, segmentIndex) { - var divForResource = t.getCellForDateAndResource(segment.date, segmentIndex); + var divForResource = t.getCellForDateAndResource(segment.date, segment.resourceIndex); if (divForResource.length > 0) { var resourceBookingDivTable = $('
'); @@ -64,16 +64,32 @@ function ResourceEventMonthRenderer() { if (event.resources instanceof Array) { - event.resources.forEach(function(resource, resourceIndex) { - segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate)); + event.resources.forEach(function(resource) { // wauhdfiasdnp iucfnapsubnvapdsfiunvb paefinv + + var resourceIndex = getResourceIndexFromId(resource, t.calendar.options.resources); + console.info("resource:" + resource +"; index:"+resourceIndex); + if (resourceIndex > -1) { + segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate)); + } }); } else { - segments = segments.concat(createSegmentsForResource(event.resources, 0, numDays, mStart, mEnd, startDate)); + var resourceIndex = getResourceIndexFromId(event.resources, t.calendar.options.resources); + segments = segments.concat(createSegmentsForResource(event.resources, resourceIndex, numDays, mStart, mEnd, startDate)); } return segments; } + function getResourceIndexFromId(resourceId, list) { + var result = -1; + list.forEach(function(resource,resourceIndex) { + if (resourceId == resource.id) { + result = resourceIndex; + } + }); + return result; + } + function createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate) { var results = []; if (event.allDay) { From 4bbb9d6641df98afe3d02cf9745e35d016ae2d93 Mon Sep 17 00:00:00 2001 From: Nic Newdigate Date: Wed, 1 Apr 2015 00:03:29 +0100 Subject: [PATCH 3/5] minimum height and passing allDay as a parameter --- src/resource/ResourceMonthEventRenderer.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/resource/ResourceMonthEventRenderer.js b/src/resource/ResourceMonthEventRenderer.js index 7c56a0f0ce..faed4b3bf9 100644 --- a/src/resource/ResourceMonthEventRenderer.js +++ b/src/resource/ResourceMonthEventRenderer.js @@ -32,6 +32,9 @@ function ResourceEventMonthRenderer() { var bookingEnd = segment.endHour; var divtop = (resourceDivHeight * (bookingStart - t.dayStart.asHours())) / (t.hoursInDay); var divheight = (resourceDivHeight * (bookingEnd - bookingStart)) / (t.hoursInDay); + if (divheight === 0) { + divheight = 2; + } resourceBookingDivTable.attr("title", event.title + ' - ' + bookingStart + ' - ' + bookingEnd); resourceBookingDivTable.css("top", divtop + "px"); resourceBookingDivTable.css("height", divheight + "px"); @@ -56,26 +59,32 @@ function ResourceEventMonthRenderer() { var mStart = moment(event.start); var mEnd = moment(event.end); + if (!mEnd.isValid()) { + mEnd = moment(mStart).add(2,'hour'); + } if (event.allDay) { mEnd = moment(event.start).add(t.dayEnd.asHours(), 'hour'); } var numDays = Math.ceil(moment.duration(mEnd.diff(mStart)).asDays()); + if (numDays === 0) { + numDays = 1; + } var startDate = moment(mStart).startOf('day'); if (event.resources instanceof Array) { - event.resources.forEach(function(resource) { // wauhdfiasdnp iucfnapsubnvapdsfiunvb paefinv + event.resources.forEach(function(resource) { var resourceIndex = getResourceIndexFromId(resource, t.calendar.options.resources); - console.info("resource:" + resource +"; index:"+resourceIndex); + //console.info("resource:" + resource +"; index:"+resourceIndex); if (resourceIndex > -1) { - segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate)); + segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate, event.allDay)); } }); } else { var resourceIndex = getResourceIndexFromId(event.resources, t.calendar.options.resources); - segments = segments.concat(createSegmentsForResource(event.resources, resourceIndex, numDays, mStart, mEnd, startDate)); + segments = segments.concat(createSegmentsForResource(event.resources, resourceIndex, numDays, mStart, mEnd, startDate, event.allDay)); } return segments; } @@ -90,9 +99,10 @@ function ResourceEventMonthRenderer() { return result; } - function createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate) { + // creates a segment per resource per day. + function createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate, allDay) { var results = []; - if (event.allDay) { + if (allDay) { for (var dayIndex=0;dayIndex Date: Thu, 2 Apr 2015 16:00:12 +0100 Subject: [PATCH 4/5] display events with no resources as using all resources by default --- demos/resource-views.html | 24 +++++++++++----------- src/resource/ResourceMonthEventRenderer.js | 15 ++++++++++---- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/demos/resource-views.html b/demos/resource-views.html index 9499d96542..4ff9975471 100644 --- a/demos/resource-views.html +++ b/demos/resource-views.html @@ -46,9 +46,9 @@ header: { left: 'prev,next today', center: 'title', - right: 'month,agendaWeek,resourceDay, resourceMonth' + right: 'month,agendaWeek,resourceDay,resourceMonth' }, - defaultView: 'resourceDay', + defaultView: 'resourceMonth', editable: true, droppable: true, resources: [ @@ -77,22 +77,22 @@ title: 'R1-R2: Lunch 12.15-14.45', start: new Date(y, m, d, 12, 15), end: new Date(y, m, d, 14, 45), - color: 'black', - allDay: false, - resources: ['resource1', 'resource2'] + color: '#C22326', + allDay: false /*, + resources: ['resource1', 'resource2'] */ }, { title: 'R1: All day', start: new Date(y, m, d, 10, 30), end: new Date(y, m, d, 11, 0), - color: 'black', + color: '#F37338', allDay: true, resources: 'resource1' }, { title: 'R2: Meeting 11.00', start: new Date(y, m, d, 11, 0), - color: 'black', + color: '#FDB632', allDay: true, resources: 'resource2' }, @@ -100,7 +100,7 @@ title: 'R1/R2: Lunch 12-14', start: new Date(y, m, d, 12, 0), end: new Date(y, m, d, 14, 0), - color: 'black', + color: '#027878', allDay: false, resources: ['resource1', 'resource2'] }, @@ -109,7 +109,7 @@ title: 'R1: Lunch', start: new Date(y, m, d, 12, 0), end: new Date(y, m, d, 14, 0), - color: 'black', + color: '#801638', allDay: false, resources: ['resource1'] }, @@ -117,7 +117,7 @@ title: 'R3: Breakfast', start: new Date(y, m, d, 8, 0), end: new Date(y, m, d, 8, 30), - color: 'black', + color: '#EBC137', allDay: false, resources: ['resource3'] }, @@ -125,7 +125,7 @@ id: 999, title: 'Repeating Event', start: new Date(y, m, d - 3, 16, 0), - color: 'black', + color: '#E38C2D', allDay: false, resources: 'resource2' }, @@ -133,7 +133,7 @@ id: 999, title: 'Repeating Event', start: new Date(y, m, d + 4, 16, 0), - color: 'black', + color: '#DB4C2C', allDay: false, resources: 'resource2' } diff --git a/src/resource/ResourceMonthEventRenderer.js b/src/resource/ResourceMonthEventRenderer.js index faed4b3bf9..93de393dec 100644 --- a/src/resource/ResourceMonthEventRenderer.js +++ b/src/resource/ResourceMonthEventRenderer.js @@ -71,15 +71,22 @@ function ResourceEventMonthRenderer() { } var startDate = moment(mStart).startOf('day'); - if (event.resources instanceof Array) { - - event.resources.forEach(function(resource) { + if(event.resources.length === 0) { + // resources property on event is not specified, assume event uses all resources! + t.calendar.options.resources.forEach(function(resource, resourceIndex) { - var resourceIndex = getResourceIndexFromId(resource, t.calendar.options.resources); //console.info("resource:" + resource +"; index:"+resourceIndex); if (resourceIndex > -1) { segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate, event.allDay)); } + + }); + + } else + if (event.resources instanceof Array) { + + event.resources.forEach(function(resource, resourceIndex) { + segments = segments.concat(createSegmentsForResource(resource, resourceIndex, numDays, mStart, mEnd, startDate, event.allDay)); }); } else { From cd3affdaf4d0ef5be93a46475947ed6525fc469d Mon Sep 17 00:00:00 2001 From: Nic Newdigate Date: Fri, 25 Sep 2015 12:50:35 +0100 Subject: [PATCH 5/5] added screenshot to readme.md --- readme.md | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/readme.md b/readme.md index e503903080..a221161ff9 100644 --- a/readme.md +++ b/readme.md @@ -1,26 +1,7 @@ -[![Build Status](https://travis-ci.org/seankenny/fullcalendar.png?branch=v2)](https://travis-ci.org/seankenny/fullcalendar) - -#FORK INFO +#Resource Month Calendar View fork This fork adds a vertical resource view to FullCalendar. Some details on usage, etc are available here: -http://www.seankenny.me/blog/2013/08/14/fullcalendar-with-a-resource-day-view/ -http://www.seankenny.me/blog/2014/07/24/resource-fullcalendar-dragging-and-clicking/ -http://www.seankenny.me/blog/2014/08/11/fullcalendar-with-a-resource-day-view-v2-0-2/ - -[plunkr demo](http://plnkr.co/KRXcK2oNd9eX2IMBM6yY). - -# FullCalendar - -A full-sized drag & drop event calendar (jQuery plugin). - -- [Project website and demos](http://arshaw.com/fullcalendar/) -- [Documentation](http://arshaw.com/fullcalendar/docs/) -- [Support](http://arshaw.com/fullcalendar/support/) -- [Changelog](changelog.md) -- [License](license.txt) -For contributors: +![screenshot](https://cloud.githubusercontent.com/assets/3748334/6966722/0996aeee-d953-11e4-9d97-0d83fcf6b25e.png) -- [Ways to contribute](http://arshaw.com/fullcalendar/wiki/Contributing/) -- [General coding guidelines](https://github.com/arshaw/fullcalendar/wiki/Contributing-Code) -- [Contributing features](https://github.com/arshaw/fullcalendar/wiki/Contributing-Features) -- [Contributing bugfixes](https://github.com/arshaw/fullcalendar/wiki/Contributing-Bugfixes) +- [Original Project website and demos](https://github.com/fullcalendar/fullcalendar) +- [This project is a fork of] (https://github.com/seankenny/fullcalendar)