5.12.08 Multiday Calendar Datepicker JQuery Plugin

Project Page: http://plugins.jquery.com/project/jCal

releases

core assets

changelog

requirements

jCal core jQuery plugin code (<9KB, not jsmin’d, no comments)

/*
 * jCal calendar multi-day and multi-month datepicker plugin for jQuery
 *  version 0.2.1
 * Author: Jim Palmer
 * Released under MIT license.
 */

(function($) {
    $.fn.jCal = function (opt) {
        $.jCal(this, opt);
    }
    $.jCal = function (target, opt) {
        opt = $.extend({
            day:            new Date(),                                 // date to drive first cal
            days:           1,                                          // default number of days user can select
            showMonths:     1,                                          // how many side-by-side months to show
            dCheck:         function (day) { return true; },            // handler for checking if single date is valid or not
            callback:       function (day, days) { return true; },      // callback function for click on date
            selectedBG:     'rgb(0, 143, 214)',                         // default bgcolor for selected date cell
            defaultBG:      'rgb(255, 255, 255)',                       // default bgcolor for unselected date cell
            dayOffset:      0,                                          // 0=week start with sunday, 1=week starts with monday
            dow:            ['S', 'M', 'T', 'W', 'T', 'F', 'S'],        // days of week - change this to reflect your dayOffset
            ml:             ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            ms:             ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            _target:        target                                      // target DOM element - no need to set extend this variable
        }, opt);
        opt.day.setDate(1);
        $(target).stop().empty();
        for (var sm=0; sm < opt.showMonths; sm++)
            $(target).append('<div class="jCalMo"></div>');
        opt.cID = 'c' + $('.jCalMo').length;
        $('.jCalMo', target).each(
            function (ind) {
                drawCalControl($(this), $.extend( {}, opt, { 'ind':ind,
                        'day':new Date( new Date( opt.day.getTime() ).setMonth( new Date( opt.day.getTime() ).getMonth() + ind ) ) }
                    ));
                drawCal($(this), $.extend( {}, opt, { 'ind':ind,
                        'day':new Date( new Date( opt.day.getTime() ).setMonth( new Date( opt.day.getTime() ).getMonth() + ind ) ) }
                    ));
            });
        $('.jCal', target).each(
            function () {
                $('.month', this).css('width', $(this).parent().width() - ( $('.left', this).width() || 0 ) - ( $('.right', this).width() || 0 ) + 'px')
            });
    }

    function drawCalControl (target, opt) {
   
        $(target).append(
            '<div class="jCal">' +
                    ( (opt.ind == 0) ? '<div class="left"><img src="_left.gif"></div>' : '' ) +
                    '<div class="month">' + opt.ml[opt.day.getMonth()] + ' ' + opt.day.getFullYear() + '</div>' +
                    ( (opt.ind == ( opt.showMonths - 1 )) ? '<div class="right"><img src="_right.gif"></div>' : '' ) +
            '</div>');
           
        $(target).find('.jCal .left').bind("click",
            function (e) {
                if ($('.jCalMask', opt._target).length > 0) return false;
                var mD = { w:0, h:0 };
                $('.jCalMo', opt._target).each( function () {
                        mD.w += $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right'));
                        var cH = $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom'));
                        mD.h = ((cH > mD.h) ? cH : mD.h);
                    } );
                $(opt._target).prepend('<div class="jCalMo"></div>');

                opt.day = new Date( $('.day[id^=' + opt.cID + 'd_]:first', opt._target).attr('id').replace(opt.cID + 'd_', '').replace(/_/g, '/') );
                opt.day.setDate(1);
                opt.day.setMonth( opt.day.getMonth() - 1 );
                drawCalControl($('.jCalMo:first', opt._target), opt);
                drawCal($('.jCalMo:first', opt._target), opt);

                if (opt.showMonths > 1) {
                    $('.right', opt._target).clone(true).appendTo( $('.jCalMo:eq(1) .jCal', opt._target) );
                    $('.left:last, .right:last', opt._target).remove();
                }

                $(opt._target).append('<div class="jCalSpace" style="width:'+mD.w+'px; height:'+mD.h+'px;"></div>');

                $('.jCalMo', opt._target).wrapAll(
                    '<div class="jCalMask" style="clip:rect(0px '+mD.w+'px '+mD.h+'px 0px); width:'+ ( mD.w + ( mD.w / opt.showMonths ) ) +'px; height:'+mD.h+'px;">' +
                        '<div class="jCalMove"></div>' +
                    '</div>');

                $('.jCalMove', opt._target).css('margin-left', ( ( mD.w / opt.showMonths ) * -1 ) + 'px').css('opacity', 0.5).animate({ marginLeft:'0px' }, 'fast',
                    function () {
                        $(this).children('.jCalMo:not(:last)').clone(true).appendTo( $(opt._target) );
                        $('.jCalSpace, .jCalMask', opt._target).empty().remove();
                    });
            });

        $(target).find('.jCal .right').bind("click",
            function (e) {
                if ($('.jCalMask', opt._target).length > 0) return false;
                var mD = { w:0, h:0 };
                $('.jCalMo', opt._target).each( function () {
                        mD.w += $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right'));
                        var cH = $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom'));
                        mD.h = ((cH > mD.h) ? cH : mD.h);
                    } );
                $(opt._target).append('<div class="jCalMo"></div>');

                opt.day = new Date( $('.day[id^=' + opt.cID + 'd_]:last', opt._target).attr('id').replace(opt.cID + 'd_', '').replace(/_/g, '/') );
                opt.day.setDate(1);
                opt.day.setMonth( opt.day.getMonth() + 1 );
                drawCalControl($('.jCalMo:last', opt._target), opt);
                drawCal($('.jCalMo:last', opt._target), opt);

                if (opt.showMonths > 1) {
                    $('.left', opt._target).clone(true).prependTo( $('.jCalMo:eq(1) .jCal', opt._target) );
                    $('.left:first, .right:first', opt._target).remove();
                }

                $(opt._target).append('<div class="jCalSpace" style="width:'+mD.w+'px; height:'+mD.h+'px;"></div>');

                $('.jCalMo', opt._target).wrapAll(
                    '<div class="jCalMask" style="clip:rect(0px '+mD.w+'px '+mD.h+'px 0px); width:'+ ( mD.w + ( mD.w / opt.showMonths ) ) +'px; height:'+mD.h+'px;">' +
                        '<div class="jCalMove"></div>' +
                    '</div>');

                $('.jCalMove', opt._target).css('opacity', 0.5).animate({ marginLeft:( ( mD.w / opt.showMonths ) * -1 ) + 'px' }, 'fast',
                    function () {
                        $(this).children('.jCalMo:not(:first)').clone(true).appendTo( $(opt._target) );
                        $('.jCalSpace, .jCalMask', opt._target).empty().remove();
                    });
            });
           
    }

    function drawCal (target, opt) {
        for (var ds in opt.dow)
            $(target).append('<div class="dow">' + opt.dow[ds] + '</div>');
        var fd = new Date( new Date( opt.day.getTime() ).setDate(1) );
        var ldlm = new Date( new Date( fd.getTime() ).setDate(0) );
        var ld = new Date( new Date( new Date( fd.getTime() ).setMonth( fd.getMonth() + 1 ) ).setDate(0) );
        var offsetDayStart = ( ( fd.getDay() < opt.dayOffset ) ? ( opt.dayOffset - 7 ) : 1 );
        var offsetDayEnd = ( ( ld.getDay() < opt.dayOffset ) ? ( 7 - ld.getDay() ) : ld.getDay() );
        for ( var d = offsetDayStart; d < ( fd.getDay() + ld.getDate() + ( 7 - offsetDayEnd ) ); d++)
            $(target).append(
                (( d <= ( fd.getDay() - opt.dayOffset ) ) ?
                    '<div id="' + opt.cID + 'd' + d + '" class="pday">' + ( ldlm.getDate() - ( ( fd.getDay() - opt.dayOffset ) - d ) ) + '</div>'
                    : ( ( d > ( ( fd.getDay() - opt.dayOffset ) + ld.getDate() ) ) ?
                        '<div id="' + opt.cID + 'd' + d + '" class="aday">' + ( d - ( ( fd.getDay() - opt.dayOffset ) + ld.getDate() ) ) + '</div>'
                        : '<div id="' + opt.cID + 'd_' + (fd.getMonth() + 1) + '_' + ( d - ( fd.getDay() - opt.dayOffset ) ) + '_' + fd.getFullYear() + '" class="' +
                            ( ( opt.dCheck( new Date( (new Date( fd.getTime() )).setDate( d - ( fd.getDay() - opt.dayOffset ) ) ) ) ) ? 'day' : 'invday' ) +
                            '">' + ( d - ( fd.getDay() - opt.dayOffset ) )  + '</div>'
                    )
                )
            );
        $(target).find('div[id^=' + opt.cID + 'd]:first, div[id^=' + opt.cID + 'd]:nth-child(7n+2)').before( '<br style="clear:both; font-size:0.1em;" />' );
        $(target).find('div[id^=' + opt.cID + 'd_]:not(.invday)').bind("mouseover mouseout click", function(e){
            if ($('.jCalMask', opt._target).length > 0) return false;
            var osDate = new Date ( $(this).attr('id').replace(/c[0-9]{1,}d_([0-9]{1,2})_([0-9]{1,2})_([0-9]{4})/, '$1/$2/$3') );
            var sDate = new Date ( osDate.getTime() );
            if (e.type == 'click')
                $('div[id^=' + opt.cID + 'd_].selectedDay', $(opt._target).parent()).removeClass('selectedDay').animate(
                    { backgroundColor:opt.defaultBG }, 'fast', function () {
                        $(this).css('backgroundColor', '');
                    });
            for (var di=0; di < opt.days; di++) {
                var currDay = $(opt._target).find('#' + opt.cID + 'd_' + ( sDate.getMonth() + 1 ) + '_' + sDate.getDate() + '_' + sDate.getFullYear());
                if ( currDay.length == 0 || $(currDay).hasClass('invday') ) break;
                $(currDay).toggleClass( ( (e.type == 'click') ? 'selectedDay' : 'overDay' ) );
                if (e.type == 'click') $(currDay).stop().animate({ backgroundColor:opt.selectedBG }, 'fast', function () {
                    $(this).css('backgroundColor', opt.selectedBG);
                });
                else $(currDay).css('backgroundColor', '').stop();
                sDate.setDate( sDate.getDate() + 1 );
            }
            if (e.type == 'click') opt.callback( osDate, di );
        });
    }
})(jQuery);

Download this code: jCal/jCal.js

jCal CSS styles

/* default dimensions and justification for each month including title */
.jCal {
    /* each day width + border-left + border-right */
    width:224px;
    height:21px;
    text-align:center;
    vertical-align:top;
}
/* default dimensions and justification for each month not including title */
.jCalMo {
    /* each day width + border-left + border-right */
    width:224px;
    float:left;
    overflow:visible;
    height:100%;
    padding-right:2px;
    padding-left:2px;
    white-space:nowrap;
}
/* top month display block - i.e. January */
.jCal .month {
    height:16px;
    /* jCalMo - left/right button width */
    width:208px;
    text-align:center;
    vertical-align:bottom;
    font-family:Tahoma;
    font-size:8pt;
    color:#000000;
    float:left;
}
/* day block dimensions and style - for all day blocks */
.jCalMo .dow, .jCalMo .day, .jCalMo .pday, .jCalMo .aday, .jCalMo .overDay, .jCalMo .invday, .jCalMo .selectedDay {
    width:30px;
    font-family:Tahoma;
    font-size:8pt;
    color:#000000;
    border-right:1px solid #CCCCCC;
    border-bottom:1px solid #CCCCCC;
    border-left:1px solid #EEEEEE;
    text-align:center;
    cursor:default;
    float:left;
}
/* day of week header specific style */
.jCalMo .dow {
    background:#EEEEEE url(eeGrad.gif) bottom repeat-x;
    border-bottom:0px;
}
/* actual calendar day default style */
.jCalMo .day, .jCalMo .invday {
    height:30px;
    text-align:center;
}
/* selectable calendar day specific style */
.jCalMo .day {
    cursor:pointer;
    background:#ffffff;
}
/* blacked-out calendar day specific style */
.jCalMo .invday {
    color:#808080;
    background:#eeeeee;
    text-decoration:line-through;
}
/* previous and subsequent months calendar day specific style */
.jCalMo .pday, .jCalMo .aday {
    height:30px;
    background:#e3e3e3;
    color:#CCCCCC;
}
/* selected day */
.jCalMo .selectedDay {
    color:#ffffff;
    /* must use rgb() syntax for jquery.color.js compliance */
    background:rgb(0, 143, 214);
}
/* mouseover hilighted selectable day */
.jCalMo .overDay {
    color:#ffffff;
    /* must use rgb() syntax for jquery.color.js compliance */
    background:rgb(0, 102, 153);
}
/* left month navigation button - no need to change */
.jCal .left {
    width:16px;
    height:16px;
    vertical-align:middle;
    cursor:pointer;
    float:left;
}
/* right month navigation button - no need to change */
.jCal .right {
    width:16px;
    height:16px;
    vertical-align:middle;
    cursor:pointer;
    float:right;
}
/* no need to change - this is for carousel opacity */
.jCalMask, .jCalMove {
    position:absolute;
    overflow:hidden;
}

Download this code: jCal/jCal.css

example plugin use code

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>

    <script language="JavaScript" type="text/javascript" src="jquery.js"></script>
    <script language="JavaScript" type="text/javascript" src="jquery.color.js"></script>
    <script language="JavaScript" type="text/javascript" src="jCal.js"></script>

    <link rel="stylesheet" type="text/css" href="jCal.css">

    <script>
        $(document).ready(function () {
            $('#calOne').jCal({
                day:            new Date(),
                days:           4,
                showMonths:     2,
                dCheck:         function (day) {
                        return (day.getDate() != 3);
                    },
                callback:       function (day, days) {
                        $(this._target).find('.dInfo').remove();
                        var dCursor = new Date( day.getTime() );
                        for (var di=0; di < days; di++) {
                            var currDay = document.getElementById(this.cID + 'd_' +
                                ( dCursor.getMonth() + 1 ) + '_' + dCursor.getDate() + '_' + dCursor.getFullYear());
                            $(currDay, this._target).append('<div class="dInfo"><span style="color:#ccc">$</span>1231</div>');
                            dCursor.setDate( dCursor.getDate() + 1 );
                        }
                        $('#calOneResult').append('<div style="clear:both; font-size:7pt;">' + days + ' days starting ' +
                            ( day.getMonth() + 1 ) + '/' + day.getDate() + '/' + day.getFullYear() + '</div>');
                        return true;
                    }
                });
            $('#calTwo').jCal({
                day:            new Date( (new Date()).setMonth( (new Date()).getMonth() + 2 ) ),
                days:           2,
                showMonths:     1,
                sDate:          new Date(),
                dCheck:         function (day) {
                        return (day.getDay() != 6);
                    },
                callback:       function (day, days) {
                        $('#calTwoResult').append('<div style="clear:both; font-size:7pt;">' + days + ' days starting ' +
                            ( day.getMonth() + 1 ) + '/' + day.getDate() + '/' + day.getFullYear() + '</div>');
                        return true;
                    }
                });
        });
    </script>

    <style>
        .dInfo {
            font-family:tahoma;
            font-size:7pt;
            color:#fff;
            padding-top:1px;
            padding-bottom:1px;
            background:rgb(0, 102, 153);
        }
    </style>

</head>
<body marginheight=0 marginwidth=0 topmargin=0 rightmargin=0 leftmargin=0>

    <table width="100%">
        <tr>
            <td align=left id="calOne" valign=top style="padding:10px; background:#E3E3E3;">
                loading calendar one
            </td>
            <td align=left id="calTwo" valign=top style="padding:10px; background:#E3E3E3;">
                loading calendar two
            </td>
        <tr>
            <tr>
                <td align=left id="calOneResult" valign=top style="padding:10px; background:#E3E3E3;"></td>
                <td align=left id="calTwoResult" valign=top style="padding:10px; background:#E3E3E3;"></td>
            <tr>
    </table>

    <div id="ttt"></div>

</body>
</html>

Download this code: jCal/jCal.html

Another example with:


Here is the html that instantiates it: jCal.small.html
And the css that controls the style: jCal.small.css


4 Comments

Nikolaj on 5.12.08 at 2pm

ternaries triumph
tangled punctuation grows
obfuscated code

Max on 5.16.08 at 8am

THANX

Oscar on 5.31.08 at 11am

hello. Great pluging but how can I change the day to start the week in Monday?

Jim Palmer on 6.26.08 at 2pm

Oscar
Just updated this and the plugs.jquery.com with version 0.2.1 which allows the start of week to be Monday. At the bottom of the content in this post there’s an example of how to use the new Monday start of week!
Any other suggestions?




1.537s