2
\$\begingroup\$

I setup my code at jsfiddle. Along the review I want to know the solution of a problem I'm facing in this code.

  1. On click the Edit button I want to enable the calendar which is disabled by default. And when it will be enabled user can select dates manually. Once user is done with selecting dates and will click the save button. All the selected dates should be saved in the array getDatesBackup[].

Following is the JS code I'm using for the point 1 functionality. Might be it looks good but the placing of this code is not correct I'm sure.

 $("#editCalendar").click(function(){
        console.log("Save");
        var $this = $(this);
         $this.toggleClass("editCalendar");
        if($this.hasClass('editCalendar')){
            $this.text('Save');
            $("#myCalendar").multiDatesPicker('disable');
            console.log("Save");
        } else {
            $this.text('Edit');
            $("#myCalendar").multiDatesPicker('enable');
            console.log("Edit");
        }

    });

Usage of this little program is like this, you have to provide the start and end date and then select the occurrence and it will generate the calendar and will select dates within the given range. Please correct me where ever I'm wrong. And please don't vote me down. Help me in becoming better programmer.

Following is the complete code.

$(document).ready(function () {
    var getDatesBackup=[];
    var date = new Date();
    $("#from").datepicker({onClose:function( selectedDate ) {
        $( "#to" ).datepicker( "option", "minDate", selectedDate );
    }});
    $("#to").datepicker({onClose:function( selectedDate ) {
        $( "#from" ).datepicker( "option", "maxDate", selectedDate );
    }});

    $("#editCalendar").click(function(){
        console.log("Save");
        var $this = $(this);
         $this.toggleClass("editCalendar");
        if($this.hasClass('editCalendar')){
            $this.text('Save');
            $("#myCalendar").multiDatesPicker('disable');
            console.log("Save");
        } else {
            $this.text('Edit');
            $("#myCalendar").multiDatesPicker('enable');
            console.log("Edit");
        }
    });

    $('#occurrence').change( function() {
        //index to each month of the year.
        var monthIndex = [0,1,2,3,4,5,6,7,8,9,10,11];    
        var start = $("#from").datepicker("getDate");
        var end = $("#to").datepicker("getDate");

        var startYear = start.getFullYear();
        var endYear = end.getFullYear();
        var yearDifference = (12 * (endYear - startYear)) + end.getMonth();
        var currentDate = new Date(start),
                endDate = new Date(end),
                between = new Array();
        var selected = new Array();    
        var interval = $('select[name="occurrence"]').val();
        var delay = 0;
        var showDates = [];
        var today = new Date();
        var year = today.getFullYear();
        $("#myCalendar").multiDatesPicker({numberOfMonths:[3, 4]});

        //Save the size of the selected dates array.
        var selectedArrayLength;

        //Save the range of dates in the between array.
        while (currentDate <= endDate) {
            between.push(new Date(currentDate));
            currentDate.setDate(currentDate.getDate() + 1);
        }

        //save the size of selected array.
        selectedArrayLength = selected.length;
        console.log("Selected Array Length : "+selectedArrayLength);

        switch (true) {
            case (interval == 7):
                //Push in the selected dates in the selected array.
                for (var i = 0; i < between.length; i += 7) {
                    selected.push(between[i]);
                    //console.log(between[i]);

                }
                markDates(selected);
                break;
            case (interval == 30):
                //Push in the selected dates in the selected array.
                for (var i = 0; i < between.length; i += 28) {
                    var thisDate = new Date(between[i]);
                    var backDate = new Date(between[i - 28]);

                    if (thisDate.getMonth() !== backDate.getMonth() ) {
                        selected.push(between[i]);
                        //console.log(between[i]);
                    }
                }
                markDates(selected);
                break;
            case (interval == 15):
                //Push in the selected dates in the selected array.
                for (var i = 0; i < between.length; i += 14) {
                    var thisDate = new Date(between[i]);
                    var backDate = new Date(between[i - 28]);
                    if (thisDate.getMonth() !== backDate.getMonth()) {
                        selected.push(between[i]);
                        //console.log(between[i]);
                    }
                }
                markDates(selected);
                break;
            case (interval == 0):               
                //Push in the selected dates in the selected array.
                selected.push(between[0]);
                markDates(selected);
                break;

            default:
                return undefined;
        }

        function markDates(selectedDates) {
            //select dates on the calendar within the given range.
            for (var d = 0; d < selectedDates.length; d++) {
                var sDate = new Date(selectedDates[d]);

                (function (selectedDate) {
                    window.setTimeout(function () {
                        showDates.push(selectedDate);
                        $("#myCalendar").multiDatesPicker("addDates", showDates);
                        if (selectedDate == sDate) {
                            //getDates = $("#inlineDp").multiDatesPicker("getDates");
                        }
                    }, delay);
                })(sDate);
            }//end for var d=0;
        }

        //Reset the previously selected dates.
        $("#myCalendar").multiDatesPicker("resetDates");

    });//end getSelected

    $("#occurrence").trigger('change');

    //Take backup of the dates.
    getDatesBackup = $("#myCalendar").multiDatesPicker("getDates");
    $("#saveCalendar").click(function(){
        console.log(getDatesBackup);
    });

});
\$\endgroup\$
2
\$\begingroup\$

This looks way too complex to me. I've already provided you with a function for calculating recurring dates @stackoverflow, and had some time left to review and refactor your code using that function here.

You can find the result in this jsFiddle. I've added a number input field, a recurrence type selector (days, weeks, months), and a button to trigger display of the months with recurrency dates in it. It looks like this:

// handler for button
function showRecurrence(e) {
    var start = $("#from").datepicker("getDate");
    var end = $("#to").datepicker("getDate");
    var nOcc = +$('#recur').val();
    var occType = $('#occurrence').val() || 'Date';
    var allowWeekends = !$('#weekends').is(':checked');
    var yearspanned = 1 + end.getFullYear() - start.getFullYear();

    if (occType == 'Week') {
        occType = 'Date';
        nOcc = nOcc * 7;
    }

    // no date or recurrence value: do nothing
    if (nOcc < 1 || !start || !end) { return true; }

    // calculate recurrence
    var selectedDates = recurringDates(start, end, nOcc, occType, allowWeekends );

    $("#myCalendar").multiDatesPicker({
        numberOfMonths: [(yearspanned || 1)*12, 1]
    });

    // add calculated dates
    setTimeout(
        function () {
            $("#myCalendar").multiDatesPicker("addDates", selectedDates);
        }, 0);
    $("#myCalendar").multiDatesPicker("resetDates");

}

Where recurringDates is:

function recurringDates(startDate, endDate, interval, intervalType, noweekends) {
    intervalType = intervalType || 'Date';
    var date = startDate;
    var recurrent = [];
    var setget = {
        set: 'set' + intervalType,
        get: 'get' + intervalType
    };

    while (date < endDate) {
        recurrent.push(noweekends ? noWeekend() : new Date(date));
        date[setget.set](date[setget.get]() + interval);
    }

    // add 1 day for sunday, subtract one for saturday
    function noWeekend() {
        var add, currdate = new Date(date),
            day = date.getDay();
        if (~[6, 0].indexOf(day)) {
            currdate.setDate(currdate.getDate() + (add = day == 6 ? -1 : 1));
        }
        return new Date(currdate);
    }

    return recurrent;
}
\$\endgroup\$
  • \$\begingroup\$ Actually I was waiting for you to review it :D You promised that you will teach me and since then we are in a Student Teacher relationship :D Thanks a lot for your time. \$\endgroup\$ – Superman Jan 13 '15 at 9:42
1
\$\begingroup\$

Some simplifications are possible.

In the switch statement, since all the cases are about matching values of interval, you could rewrite in a more natural form like this:

    switch (interval) {
        case 7:
            // ...
            markDates(selected);
            break;
        case 30:
            // ...
            markDates(selected);
            break;

And since as the last step in all the case statements you have markDates(selected);, you can move that outside of the switch to reduce code duplication.

There's one more thing to reduce code duplication: in the handling of interval == 30 and interval == 15 you have almost the same code. It would be better to extract that part to a common utility function so that you don't repeat yourself.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.