﻿(function($) {
    var calendar = {
        str: function(val) { return val + ''; },
        num: function(val) { return val * 1;  },
        dayCodes: ['sun','mon','tue','wed','thu','fri','sat'],
        leapYear: function(year) {
            return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
        },
        daysInMonth: function(year, month) {
            if (month == 1 || month == 3 || month == 5 || month == 7
                || month == 8 || month == 10 || month == 12) {
                return 31;
            }
            if (month == 4 || month == 6 || month == 9 || month == 11) {
                return 30;
            }
            if (month == 2) {
                return (this.leapYear(year) ? 29 : 28);
            }
            throw 'Invalid month.';
        },
        // 0 = Sunday
        // 1 = Monday
        // 2 = Tuesday
        // 3 = Wednesday
        // 4 = Thursday
        // 5 = Friday
        // 6 = Saturday
        dayOfWeek: function(year, month, day) {
            var q = this.num(day);
            var m = this.num(month);
            var Y = this.num(year);
            if (m == 1 || m == 2) {
                m += 12;
                Y--;
            }
            return (q + Math.floor(((m+1)*26)/10) + Y + Math.floor(Y/4) + (6*Math.floor(Y/100)) + Math.floor(Y/400) - 1) % 7;
        },
        weeks: function(year, month) {
            var daysInMonth = this.daysInMonth(year, month);
        
            var monthStartDOW = this.dayOfWeek(year, month, 1);
            var monthEndDOW   = this.dayOfWeek(year, month, daysInMonth);
        
            var weeks = [];
            var week = {};
            var dow = monthStartDOW;
            for (var i = 1; i <= daysInMonth; i++) {
                week[this.dayCodes[dow]] = {
                    year: year,
                    month: month,
                    day: i
                };
                dow++;
                if (dow == 7) {
                    weeks.push(week);
                    week = {};
                    dow = 0;
                }
            }
            if (monthEndDOW != 6) {
                weeks.push(week);
            }
            return weeks;
        }
    };
    var render = function() {
        var input = this;
        var today = new Date();
        var cal = $(this).next('.calendar');
        
        // year
        cal.find('.calendar-year-1').html(input.datetime.getFullYear() - 1);
        cal.find('.calendar-year-2').html(input.datetime.getFullYear());
        cal.find('.calendar-year-3').html(input.datetime.getFullYear() + 1);

        // month
        cal.find('.calendar-month').removeClass('selected');
        cal.find('.calendar-month-' + (input.datetime.getMonth() + 1)).addClass('selected');

        // day
        cal.find('.calendar-week').hide();
        cal.find('.calendar-day').html('').removeClass('selected').removeClass('today');
        var weeks = calendar.weeks(input.datetime.getFullYear(), input.datetime.getMonth() + 1);
        $(weeks).each(function(i, week) {
            cal.find('.calendar-week-' + (i + 1)).show();
            for (var dow in week) {
                if (week.hasOwnProperty(dow)) {
                    var date = week[dow];
                    var element = cal.find('.calendar-week-' + (i + 1) + '-day-' + dow);
                    element.html(date.day);
                    if (date.day == input.datetime.getDate()) {
                        element.addClass('selected');
                    }
                    if (date.year == today.getFullYear()
                        && date.month == today.getMonth() + 1
                        && date.day == today.getDate()) {
                        element.addClass('today');
                    }
                }
            }
        });

        // hour
        cal.find('.calendar-hour').removeClass('selected');
        cal.find('.calendar-hour-' + (input.datetime.getHours() % 12)).addClass('selected');

        // time
        cal.find('.calendar-minute').removeClass('selected');
        cal.find('.calendar-minute-' + input.datetime.getMinutes()).addClass('selected');

        // meridian
        cal.find('.calendar-meridian').removeClass('selected');
        cal.find('.calendar-meridian-' + (input.datetime.getHours() >= 12 ? 'pm' : 'am')).addClass('selected');

        // timezone
        cal.find('.calendar-timezone').removeClass('selected');
        cal.find('.calendar-timezone-' + input.timezone).addClass('selected');
        
        $(input).val(input.datetime.getFullYear()
            + '-' + padLeft(input.datetime.getMonth() + 1, 2, '0')
            + '-' + padLeft(input.datetime.getDate(), 2, '0')
            + (input.time ? ' ' + padLeft(input.datetime.getHours(), 2, '0') + ':' + padLeft(input.datetime.getMinutes(), 2, '0') : '')
            + (input.timezone ? ' ' + input.timezone : ''));
    };
    var padLeft = function(str, count, padding) {
        if (!padding) {
            padding = ' ';
        }
        str = calendar.str(str);
        while (str.length < count) {
            str = padding + str;
        }
        return str;
    };
    $.fn.datetime = function(options) {
        options = $.extend({
            time: true,
            timezone: true
        }, options);
        return this.each(function() {
            var input = this;
            $(input).attr('readonly', true);
            if (options.time) {
                input.time = true;
            }
            if (!$(input).val()) {
                input.datetime = new Date();
                input.datetime.setMinutes(0, 0, 0);
                if (options.timezone) {
                    var tzo = -Math.max( // account for daylight savings when determining timezone offset
                        new Date(input.datetime.getFullYear(), 0, 1).getTimezoneOffset(),
                        new Date(input.datetime.getFullYear(), 6, 1).getTimezoneOffset()
                    );
                    switch (Math.floor(Math.abs(tzo) / 60)) {
                        case 4:
                            input.timezone = 'Atlantic';
                            break;
                        case 5:
                            input.timezone = 'Eastern';
                            break;
                        case 6:
                            input.timezone = 'Central';
                            break;
                        case 7:
                            input.timezone = 'Mountain';
                            break;
                        case 8:
                            input.timezone = 'Pacific';
                            break;
                        case 9:
                            input.timezone = 'Alaskan';
                            break;
                        case 10:
                            input.timezone = 'Hawaiian';
                            break;
                    }
                }
            } else {
                var split = $(input).val().split(' ');
                var datesplit = split[0].split('-');
                if (options.time) {
                    var timesplit = split[1].split(':');
                    input.datetime = new Date(datesplit[0], datesplit[1] - 1, datesplit[2], timesplit[0], timesplit[1], 0, 0);
                } else {
                    input.datetime = new Date(datesplit[0], datesplit[1] - 1, datesplit[2], 0, 0, 0, 0);
                }
                if (options.timezone) {
                    input.timezone = split[2];
                }
            }
            $('<table class="calendar" style="display:none" cellspacing="0">\
                    <tr>\
                        <td valign="top">\
                            <table cellspacing="0">\
                                <tr>\
                                    <td class="calendar-header">Date</td>' + (options.time ? '\
                                    <td class="calendar-header" colspan="4">Time</td>' : '') + '\
                                </tr>\
                                <tr>\
                                    <td valign="top">\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-year-control calendar-year-prev">&lArr;</td>\
                                                <td class="calendar-year calendar-year-1"></td>\
                                                <td class="calendar-year calendar-year-2 selected"></td>\
                                                <td class="calendar-year calendar-year-3"></td>\
                                                <td class="calendar-year-control calendar-year-next">&rArr;</td>\
                                            </tr>\
                                        </table>\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-month calendar-month-1">Jan</td>\
                                                <td class="calendar-month calendar-month-2">Feb</td>\
                                                <td class="calendar-month calendar-month-3">Mar</td>\
                                                <td class="calendar-month calendar-month-4">Apr</td>\
                                                <td class="calendar-month calendar-month-5">May</td>\
                                                <td class="calendar-month calendar-month-6">Jun</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-month calendar-month-7">Jul</td>\
                                                <td class="calendar-month calendar-month-8">Aug</td>\
                                                <td class="calendar-month calendar-month-9">Sep</td>\
                                                <td class="calendar-month calendar-month-10">Oct</td>\
                                                <td class="calendar-month calendar-month-11">Nov</td>\
                                                <td class="calendar-month calendar-month-12">Dec</td>\
                                            </tr>\
                                        </table>\
                                        <table cellspacing="0">\
                                            <tr class="calendar-week calendar-week-1">\
                                                <td class="calendar-day calendar-week-1-day-sun"></td>\
                                                <td class="calendar-day calendar-week-1-day-mon"></td>\
                                                <td class="calendar-day calendar-week-1-day-tue"></td>\
                                                <td class="calendar-day calendar-week-1-day-wed"></td>\
                                                <td class="calendar-day calendar-week-1-day-thu"></td>\
                                                <td class="calendar-day calendar-week-1-day-fri"></td>\
                                                <td class="calendar-day calendar-week-1-day-sat"></td>\
                                            </tr>\
                                            <tr class="calendar-week calendar-week-2">\
                                                <td class="calendar-day calendar-week-2-day-sun"></td>\
                                                <td class="calendar-day calendar-week-2-day-mon"></td>\
                                                <td class="calendar-day calendar-week-2-day-tue"></td>\
                                                <td class="calendar-day calendar-week-2-day-wed"></td>\
                                                <td class="calendar-day calendar-week-2-day-thu"></td>\
                                                <td class="calendar-day calendar-week-2-day-fri"></td>\
                                                <td class="calendar-day calendar-week-2-day-sat"></td>\
                                            </tr>\
                                            <tr class="calendar-week calendar-week-3">\
                                                <td class="calendar-day calendar-week-3-day-sun"></td>\
                                                <td class="calendar-day calendar-week-3-day-mon"></td>\
                                                <td class="calendar-day calendar-week-3-day-tue"></td>\
                                                <td class="calendar-day calendar-week-3-day-wed"></td>\
                                                <td class="calendar-day calendar-week-3-day-thu"></td>\
                                                <td class="calendar-day calendar-week-3-day-fri"></td>\
                                                <td class="calendar-day calendar-week-3-day-sat"></td>\
                                            </tr>\
                                            <tr class="calendar-week calendar-week-4">\
                                                <td class="calendar-day calendar-week-4-day-sun"></td>\
                                                <td class="calendar-day calendar-week-4-day-mon"></td>\
                                                <td class="calendar-day calendar-week-4-day-tue"></td>\
                                                <td class="calendar-day calendar-week-4-day-wed"></td>\
                                                <td class="calendar-day calendar-week-4-day-thu"></td>\
                                                <td class="calendar-day calendar-week-4-day-fri"></td>\
                                                <td class="calendar-day calendar-week-4-day-sat"></td>\
                                            </tr>\
                                            <tr class="calendar-week calendar-week-5">\
                                                <td class="calendar-day calendar-week-5-day-sun"></td>\
                                                <td class="calendar-day calendar-week-5-day-mon"></td>\
                                                <td class="calendar-day calendar-week-5-day-tue"></td>\
                                                <td class="calendar-day calendar-week-5-day-wed"></td>\
                                                <td class="calendar-day calendar-week-5-day-thu"></td>\
                                                <td class="calendar-day calendar-week-5-day-fri"></td>\
                                                <td class="calendar-day calendar-week-5-day-sat"></td>\
                                            </tr>\
                                            <tr class="calendar-week calendar-week-6">\
                                                <td class="calendar-day calendar-week-6-day-sun"></td>\
                                                <td class="calendar-day calendar-week-6-day-mon"></td>\
                                                <td class="calendar-day calendar-week-6-day-tue"></td>\
                                                <td class="calendar-day calendar-week-6-day-wed"></td>\
                                                <td class="calendar-day calendar-week-6-day-thu"></td>\
                                                <td class="calendar-day calendar-week-6-day-fri"></td>\
                                                <td class="calendar-day calendar-week-6-day-sat"></td>\
                                            </tr>\
                                        </table>\
                                    </td>' + (options.time ? '\
                                    <td valign="top">\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-0">12</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-1">1</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-2">2</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-3">3</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-4">4</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-5">5</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-6">6</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-7">7</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-8">8</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-9">9</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-10">10</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-hour calendar-hour-11">11</td>\
                                            </tr>\
                                        </table>\
                                    </td>\
                                    <td valign="top">\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-0">00</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-5">05</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-10">10</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-15">15</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-20">20</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-25">25</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-30">30</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-35">35</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-40">40</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-45">45</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-50">50</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-minute calendar-minute-55">55</td>\
                                            </tr>\
                                        </table>\
                                    </td>\
                                    <td valign="top">\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-meridian calendar-meridian-am">am</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-meridian calendar-meridian-pm">pm</td>\
                                            </tr>\
                                        </table>\
                                    </td>' : '') + (options.timezone ? '\
                                    <td valign="top">\
                                        <table cellspacing="0">\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Atlantic">Atlantic</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Eastern">Eastern</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Central">Central</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Mountain">Mountain</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Pacific">Pacific</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Alaskan">Alaskan</td>\
                                            </tr>\
                                            <tr>\
                                                <td class="calendar-timezone calendar-timezone-Hawaiian">Hawaiian</td>\
                                            </tr>\
                                        </table>\
                                    </td>' : '') + '\
                                </tr>\
                            </table>\
                        </td>\
                    </tr>\
                </table>').insertAfter($(input));
            var cal = $(input).next('.calendar');
            cal.find('.calendar-year-prev').click(function() {
                var year = input.datetime.getFullYear() - 1;
                input.datetime.setYear(year);
                render.call(input);
            });
            cal.find('.calendar-year-next').click(function() {
                var year = input.datetime.getFullYear() + 1;
                input.datetime.setYear(year);
                render.call(input);
            });
            cal.find('.calendar-year').click(function() {
                var year = calendar.num($(this).html());
                input.datetime.setYear(year);
                render.call(input);
            });
            cal.find('.calendar-month').click(function() {
                var month = calendar.num(this.className.split(' ')[1].split('-')[2]);
                input.datetime.setMonth(month - 1);
                render.call(input);
            });
            cal.find('.calendar-day').click(function() {
                var date = calendar.num($(this).html());
                if (!date) { return; }
                input.datetime.setDate(date);
                render.call(input);
            });
            if (options.time) {
                cal.find('.calendar-hour').click(function() {
                    var hours = input.datetime.getHours();
                    var hour = calendar.num(this.className.split(' ')[1].split('-')[2]);
                    if (hours >= 12) {
                        hour += 12;
                    }
                    input.datetime.setHours(hour);
                    render.call(input);
                });
                cal.find('.calendar-minute').click(function() {
                    var minute = calendar.num(this.className.split(' ')[1].split('-')[2]);
                    input.datetime.setMinutes(minute);
                    render.call(input);
                });
                cal.find('.calendar-meridian').click(function() {
                    var hours = input.datetime.getHours();
                    var meridian = this.className.split(' ')[1].split('-')[2];
                    if (meridian == 'am' && hours >= 12) {
                        hours -= 12;
                    } else if (meridian == 'pm' && hours < 12) {
                        hours += 12;
                    }
                    input.datetime.setHours(hours);
                    render.call(input);
                });
            }
            if (options.timezone) {
                cal.find('.calendar-timezone').click(function() {
                    input.timezone = this.className.split(' ')[1].split('-')[2];
                    render.call(input);
                });
            }
            $(input).click(function() {
                var cal, visible, position;
                $(this).blur();
                cal = $(this).next('.calendar');
                visible = cal.is(':visible');
                $('.calendar').hide();
                position = $(this).position();
                if (!visible) {
                    position.top += $(this).outerHeight();
                    cal.css(position);
                    cal.show();
                }
            });
            $('body').click(function(e) {
                if (!$(e.target).closest('.calendar,.datetimeplus,.datetime,.date').length) {
                    $('.calendar').hide();
                }
            });
            render.call(this);
        });
    }
})(jQuery);
