0

I'm trying to enhance a jquery snippet I found here at stackoverlow (HTML markup for multi-day calendar events) but I've run into a perculiar problem that I really don't know how to overcome. Basically what I'm trying to achieve is to avoid stacking of multi-day events on top of each other in the calendar. Since I only have 1 reputation point (as the newbie I am) I can't explain myself with a screen dump, but have to (at least try to) explain myself in writing.

So, here's my problem. Index [3] in the array events[] below begins on day four, but since day four also contain indexes [0, 1] the eventCount is set to 3. This means that if a new event occurs on day four it will be given evenCount 4. So far no problem. On day five however there are three events (indexes [0, 3, 4]). Now... here's my problem. Index [3] began on day 4 on eventCount 3 and continues to day 12 whereas index [5] begin on day 5 and is given the same eventCount as index [3] simply because there are only three events that day. Here's the code snippet (kudos to ThinkingStiff for the basic code):

var events = [{ from: 3, to: 9 }, { from: 4, to: 4 }, { from: 9, to: 11 },{ from: 4, to: 12 },{ from: 5, to: 7 }];

for( var eventIndex = 0, event; event = events[eventIndex], eventIndex < events.length; eventIndex++ ) {
for( var dayIndex = event.from; dayIndex <= event.to; dayIndex++ ) {
    var dayElement = document.getElementById( 'day' + dayIndex ),
        firstDay = document.getElementsByClassName( 'event' + eventIndex ),
        top;
    if( firstDay.length ) {
        top = firstDay[0].style.top;
    } else {
        var eventCount = dayElement.getElementsByClassName( 'event' ).length;
        top = ( eventCount * 20 ) + 'px';
    };
    var html = '<div '
        + 'class="event event' + eventIndex + '" '
        + 'style="top: ' + top + ';">' 
        + eventIndex
        + '</div>';
    dayElement.insertAdjacentHTML( 'beforeEnd', html );
};
};    

What I want to achieve is a check that determines if there is a continuing event from the day before that need to be added to the eventCount BEFORE adding new eventCounts so that new events don't stack in top of existing events. How do I achieve this?

And while I'm at it - as it turns out, the array do not allow for additional parameters, such as for example "subject". Is it possible to expand the array to contain custom parameters without breaking the entire code?

Every hint is greatly appreciated.

Community
  • 1
  • 1
Kristofer Gisslén
  • 1,252
  • 1
  • 11
  • 28

1 Answers1

0

I threw out this question on Facebook too in order to expand it to a wider audience of programmers and I got this fantastic solution that I improved a little to meet my certain needs. (I've left out the css and HTML - you can find it in the link below)

Multi-day event calendar with javascript

var events = [{
    name: 'Pelle',
    color: 'red',
    from: 3,
    to: 9
}, {
    name: 'Pelle',
    color: '#c93',
    from: 9,
    to: 10
}, {
    name: 'Martin',
    color: 'blue',
    from: 4,
    to: 4
}, {
    name: 'Willy',
    color: 'green',
    from: 9,
    to: 11
}, {
    name: 'Erik',
    color: 'magenta',
    from: 4,
    to: 12
}, {
    name: 'Barbro',
    color: 'yellow',
    from: 5,
    to: 7
}, {
    name: 'Ludwig',
    color: '#39c',
    from: 11,
    to: 14
}];


var daysInMonth = 31,
    firstDayOfMonth = 1, // 0 = Monday and so on
    slotsInDay = 4, // How many slots can be in a single day? Depends on height.
    eventHeightPx = 20,
    days = {},
    dayNames = ['Måndag', 'Tisdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lördag', 'Söndag'],
    s,
    i,
    d;


// Populate our days-object.
// Each day is named after the date and contains events and slots
for (i = 0; i < daysInMonth; i++) {
    days[i + 1] = {
        events: [],
        slots: []
    };
}


// Run through all the events and push a copy of it to all days where it occurs.
// Also assign them unique IDs.
for (var eventIndex = 0; eventIndex < events.length; eventIndex++) {
    events[eventIndex].id = eventIndex;
    for (var dayIndex = events[eventIndex].from; dayIndex <= events[eventIndex].to; dayIndex++) {
        days[dayIndex].events.push(events[eventIndex]);
    }
}


// Output the HTML for the days of the calendar
// Each day is a list item in the calendar list

// Day names
for (i = 0; i < dayNames.length; i++) {
    $('#calendar').append('<li class="dayName">' + dayNames[i] + '</li>');
}

// Last month
for (i = 0; i < firstDayOfMonth; i++) {
    $('#calendar').append('<li class="dayInPreviousMonth"></li>');
}

// This month
for (var day in days) {
    $('#calendar').append('<li data-date="' + day + '" class="dayInThisMonth">' + day + '</li>');
}

// Next month (to fill out the full last week)
for (var i = 0; i < 7 - (firstDayOfMonth + daysInMonth) % 7; i++) {
    $('#calendar').append('<li class="dayInNextMonth"></li>');
}


// Run through all the days of this month
$('.dayInThisMonth').each(function () {
            var date = $(this).data('date'); // Grab date from the data-attribute

    // If this day have events
    if (days[date].events.length) {
        var s,
            useSlot;

        c('\n*** Day ' + date + ' ***\n');

        // Run through all the events for this day
        for (var e in days[date].events) {

            c('\nEvent id ' + days[date].events[e].id +
                ' name ' + days[date].events[e].name +
                ' from ' + days[date].events[e].from +
                ' to ' + days[date].events[e].to);

            useSlot = -1;

            if(date != days[date].events[e].from) {
                // If this is not the first day for the event
                // check the slots in the first day of the event to find the position
                c("Event should be previously allocated.");
                for (s = 0; s < slotsInDay; s++) {

                    c('Checking slot ' + s + ' in day '+ days[date].events[e].from);

                    if (days[days[date].events[e].from].slots[s] !== undefined &&
                        days[days[date].events[e].from].slots[s] == days[date].events[e].id) {
                        c('Found this event. Use that slot.');
                        useSlot = s;
                        break;
                    }
                }
            }
            else
            {
                // If this is the first day of the event
                // find the first open slot of current day.
                for (s = 0; s < slotsInDay; s++) {
                    c('Checking slot ' + s + ' for availabilty');
                    if (days[date].slots[s] !== undefined) {
                        c('Slot is not free. Allocated by ' + days[date].slots[s]);
                        slotIsFree = false;
                    } else {
                        c('Slot is free. Use it.');
                        useSlot = s;
                        break;
                    }
                }
            }

            c("Will use slot " + s);

            var top = s * eventHeightPx;
            $(this).append('<div class="event ' + (days[date].events[e].from == date ? 'firstEvent' : '') +
                ' ' + (days[date].events[e].to == date ? 'lastEvent' : '') +
                '" style="background-color: ' + days[date].events[e].color + ';top: ' + top + 'px">' + days[date].events[e].name + '</div>');

            c('Allocating slot '+s+' to event id '+days[date].events[e].id);
            days[date].slots[s] = days[date].events[e].id;

        }
    }
});


function c(t) {
    $('#console').append(t + "\n");
}

This neat little javascript renders it all just as I wished for. With some tweaking I've removed the HTML-render since the calendar grid is rendered with VB.NET. I'd wish to thank Niclas Lardh for his swift programming.

Kristofer Gisslén
  • 1,252
  • 1
  • 11
  • 28