calendar.js 7.54 KB
Newer Older
Administrator committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*global gettext, pgettext, get_format, quickElement, removeChildren*/
/*
calendar.js - Calendar functions by Adrian Holovaty
depends on core.js for utility functions like removeChildren or quickElement
*/

(function() {
    'use strict';
    // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
    var CalendarNamespace = {
        monthsOfYear: [
            gettext('January'),
            gettext('February'),
            gettext('March'),
            gettext('April'),
            gettext('May'),
            gettext('June'),
            gettext('July'),
            gettext('August'),
            gettext('September'),
            gettext('October'),
            gettext('November'),
            gettext('December')
        ],
        daysOfWeek: [
            pgettext('one letter Sunday', 'S'),
            pgettext('one letter Monday', 'M'),
            pgettext('one letter Tuesday', 'T'),
            pgettext('one letter Wednesday', 'W'),
            pgettext('one letter Thursday', 'T'),
            pgettext('one letter Friday', 'F'),
            pgettext('one letter Saturday', 'S')
        ],
        firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
        isLeapYear: function(year) {
36
            return (((year % 4) === 0) && ((year % 100) !== 0) || ((year % 400) === 0));
Administrator committed
37 38 39
        },
        getDaysInMonth: function(month, year) {
            var days;
40

Administrator committed
41 42
            if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) {
                days = 31;
43
            } else if (month === 4 || month === 6 || month === 9 || month === 11) {
Administrator committed
44
                days = 30;
45
            } else if (month === 2 && CalendarNamespace.isLeapYear(year)) {
Administrator committed
46
                days = 29;
47
            } else {
Administrator committed
48 49
                days = 28;
            }
50

Administrator committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
            return days;
        },
        draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
            var today = new Date();
            var todayDay = today.getDate();
            var todayMonth = today.getMonth() + 1;
            var todayYear = today.getFullYear();
            var todayClass = '';

            // Use UTC functions here because the date field does not contain time
            // and using the UTC function variants prevent the local time offset
            // from altering the date, specifically the day field.  For example:
            //
            // ```
            // var x = new Date('2013-10-02');
            // var day = x.getDate();
            // ```
            //
            // The day variable above will be 1 instead of 2 in, say, US Pacific time
            // zone.
            var isSelectedMonth = false;
72

Administrator committed
73 74 75 76 77 78 79
            if (typeof selected !== 'undefined') {
                isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month);
            }

            month = parseInt(month);
            year = parseInt(year);
            var calDiv = document.getElementById(div_id);
80

Administrator committed
81 82
            removeChildren(calDiv);
            var calTable = document.createElement('table');
83

Administrator committed
84 85 86 87 88
            quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year);
            var tableBody = quickElement('tbody', calTable);

            // Draw days-of-week header
            var tableRow = quickElement('tr', tableBody);
89

Administrator committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
            for (var i = 0; i < 7; i++) {
                quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
            }

            var startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
            var days = CalendarNamespace.getDaysInMonth(month, year);

            var nonDayCell;

            // Draw blanks before first of month
            tableRow = quickElement('tr', tableBody);
            for (i = 0; i < startingPos; i++) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            function calendarMonth(y, m) {
                function onClick(e) {
                    e.preventDefault();
                    callback(y, m, this.textContent);
                }
111

Administrator committed
112 113 114 115 116
                return onClick;
            }

            // Draw days of month
            var currentDay = 1;
117

Administrator committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
            for (i = startingPos; currentDay <= days; i++) {
                if (i % 7 === 0 && currentDay !== 1) {
                    tableRow = quickElement('tr', tableBody);
                }
                if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) {
                    todayClass = 'today';
                } else {
                    todayClass = '';
                }

                // use UTC function; see above for explanation.
                if (isSelectedMonth && currentDay === selected.getUTCDate()) {
                    if (todayClass !== '') {
                        todayClass += " ";
                    }
                    todayClass += "selected";
                }

                var cell = quickElement('td', tableRow, '', 'class', todayClass);
                var link = quickElement('a', cell, currentDay, 'href', '#');
138

Administrator committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
                link.addEventListener('click', calendarMonth(year, month));
                currentDay++;
            }

            // Draw blanks after end of month (optional, but makes for valid code)
            while (tableRow.childNodes.length < 7) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            calDiv.appendChild(calTable);
        }
    };

    // Calendar -- A calendar instance
    function Calendar(div_id, callback, selected) {
        // div_id (string) is the ID of the element in which the calendar will
        //     be displayed
        // callback (string) is the name of a JavaScript function that will be
        //     called with the parameters (year, month, day) when a day in the
        //     calendar is clicked
        this.div_id = div_id;
        this.callback = callback;
        this.today = new Date();
        this.currentMonth = this.today.getMonth() + 1;
        this.currentYear = this.today.getFullYear();
        if (typeof selected !== 'undefined') {
            this.selected = selected;
        }
    }
    Calendar.prototype = {
        drawCurrent: function() {
            CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
        },
        drawDate: function(month, year, selected) {
            this.currentMonth = month;
            this.currentYear = year;

177
            if (selected) {
Administrator committed
178 179 180 181 182 183 184 185 186
                this.selected = selected;
            }

            this.drawCurrent();
        },
        drawPreviousMonth: function() {
            if (this.currentMonth === 1) {
                this.currentMonth = 12;
                this.currentYear--;
187
            } else {
Administrator committed
188 189 190 191 192 193 194 195
                this.currentMonth--;
            }
            this.drawCurrent();
        },
        drawNextMonth: function() {
            if (this.currentMonth === 12) {
                this.currentMonth = 1;
                this.currentYear++;
196
            } else {
Administrator committed
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
                this.currentMonth++;
            }
            this.drawCurrent();
        },
        drawPreviousYear: function() {
            this.currentYear--;
            this.drawCurrent();
        },
        drawNextYear: function() {
            this.currentYear++;
            this.drawCurrent();
        }
    };
    window.Calendar = Calendar;
    window.CalendarNamespace = CalendarNamespace;
})();