|
afe42d0…
|
ragelink
|
1 |
/*global gettext, pgettext, get_format, quickElement, removeChildren*/ |
|
afe42d0…
|
ragelink
|
2 |
/* |
|
afe42d0…
|
ragelink
|
3 |
calendar.js - Calendar functions by Adrian Holovaty |
|
afe42d0…
|
ragelink
|
4 |
depends on core.js for utility functions like removeChildren or quickElement |
|
afe42d0…
|
ragelink
|
5 |
*/ |
|
afe42d0…
|
ragelink
|
6 |
'use strict'; |
|
afe42d0…
|
ragelink
|
7 |
{ |
|
afe42d0…
|
ragelink
|
8 |
// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions |
|
afe42d0…
|
ragelink
|
9 |
const CalendarNamespace = { |
|
afe42d0…
|
ragelink
|
10 |
monthsOfYear: [ |
|
afe42d0…
|
ragelink
|
11 |
gettext('January'), |
|
afe42d0…
|
ragelink
|
12 |
gettext('February'), |
|
afe42d0…
|
ragelink
|
13 |
gettext('March'), |
|
afe42d0…
|
ragelink
|
14 |
gettext('April'), |
|
afe42d0…
|
ragelink
|
15 |
gettext('May'), |
|
afe42d0…
|
ragelink
|
16 |
gettext('June'), |
|
afe42d0…
|
ragelink
|
17 |
gettext('July'), |
|
afe42d0…
|
ragelink
|
18 |
gettext('August'), |
|
afe42d0…
|
ragelink
|
19 |
gettext('September'), |
|
afe42d0…
|
ragelink
|
20 |
gettext('October'), |
|
afe42d0…
|
ragelink
|
21 |
gettext('November'), |
|
afe42d0…
|
ragelink
|
22 |
gettext('December') |
|
afe42d0…
|
ragelink
|
23 |
], |
|
afe42d0…
|
ragelink
|
24 |
monthsOfYearAbbrev: [ |
|
afe42d0…
|
ragelink
|
25 |
pgettext('abbrev. month January', 'Jan'), |
|
afe42d0…
|
ragelink
|
26 |
pgettext('abbrev. month February', 'Feb'), |
|
afe42d0…
|
ragelink
|
27 |
pgettext('abbrev. month March', 'Mar'), |
|
afe42d0…
|
ragelink
|
28 |
pgettext('abbrev. month April', 'Apr'), |
|
afe42d0…
|
ragelink
|
29 |
pgettext('abbrev. month May', 'May'), |
|
afe42d0…
|
ragelink
|
30 |
pgettext('abbrev. month June', 'Jun'), |
|
afe42d0…
|
ragelink
|
31 |
pgettext('abbrev. month July', 'Jul'), |
|
afe42d0…
|
ragelink
|
32 |
pgettext('abbrev. month August', 'Aug'), |
|
afe42d0…
|
ragelink
|
33 |
pgettext('abbrev. month September', 'Sep'), |
|
afe42d0…
|
ragelink
|
34 |
pgettext('abbrev. month October', 'Oct'), |
|
afe42d0…
|
ragelink
|
35 |
pgettext('abbrev. month November', 'Nov'), |
|
afe42d0…
|
ragelink
|
36 |
pgettext('abbrev. month December', 'Dec') |
|
afe42d0…
|
ragelink
|
37 |
], |
|
afe42d0…
|
ragelink
|
38 |
daysOfWeek: [ |
|
afe42d0…
|
ragelink
|
39 |
gettext('Sunday'), |
|
afe42d0…
|
ragelink
|
40 |
gettext('Monday'), |
|
afe42d0…
|
ragelink
|
41 |
gettext('Tuesday'), |
|
afe42d0…
|
ragelink
|
42 |
gettext('Wednesday'), |
|
afe42d0…
|
ragelink
|
43 |
gettext('Thursday'), |
|
afe42d0…
|
ragelink
|
44 |
gettext('Friday'), |
|
afe42d0…
|
ragelink
|
45 |
gettext('Saturday') |
|
afe42d0…
|
ragelink
|
46 |
], |
|
afe42d0…
|
ragelink
|
47 |
daysOfWeekAbbrev: [ |
|
afe42d0…
|
ragelink
|
48 |
pgettext('abbrev. day Sunday', 'Sun'), |
|
afe42d0…
|
ragelink
|
49 |
pgettext('abbrev. day Monday', 'Mon'), |
|
afe42d0…
|
ragelink
|
50 |
pgettext('abbrev. day Tuesday', 'Tue'), |
|
afe42d0…
|
ragelink
|
51 |
pgettext('abbrev. day Wednesday', 'Wed'), |
|
afe42d0…
|
ragelink
|
52 |
pgettext('abbrev. day Thursday', 'Thur'), |
|
afe42d0…
|
ragelink
|
53 |
pgettext('abbrev. day Friday', 'Fri'), |
|
afe42d0…
|
ragelink
|
54 |
pgettext('abbrev. day Saturday', 'Sat') |
|
afe42d0…
|
ragelink
|
55 |
], |
|
afe42d0…
|
ragelink
|
56 |
daysOfWeekInitial: [ |
|
afe42d0…
|
ragelink
|
57 |
pgettext('one letter Sunday', 'S'), |
|
afe42d0…
|
ragelink
|
58 |
pgettext('one letter Monday', 'M'), |
|
afe42d0…
|
ragelink
|
59 |
pgettext('one letter Tuesday', 'T'), |
|
afe42d0…
|
ragelink
|
60 |
pgettext('one letter Wednesday', 'W'), |
|
afe42d0…
|
ragelink
|
61 |
pgettext('one letter Thursday', 'T'), |
|
afe42d0…
|
ragelink
|
62 |
pgettext('one letter Friday', 'F'), |
|
afe42d0…
|
ragelink
|
63 |
pgettext('one letter Saturday', 'S') |
|
afe42d0…
|
ragelink
|
64 |
], |
|
afe42d0…
|
ragelink
|
65 |
firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), |
|
afe42d0…
|
ragelink
|
66 |
isLeapYear: function(year) { |
|
afe42d0…
|
ragelink
|
67 |
return (((year % 4) === 0) && ((year % 100) !== 0 ) || ((year % 400) === 0)); |
|
afe42d0…
|
ragelink
|
68 |
}, |
|
afe42d0…
|
ragelink
|
69 |
getDaysInMonth: function(month, year) { |
|
afe42d0…
|
ragelink
|
70 |
let days; |
|
afe42d0…
|
ragelink
|
71 |
if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) { |
|
afe42d0…
|
ragelink
|
72 |
days = 31; |
|
afe42d0…
|
ragelink
|
73 |
} |
|
afe42d0…
|
ragelink
|
74 |
else if (month === 4 || month === 6 || month === 9 || month === 11) { |
|
afe42d0…
|
ragelink
|
75 |
days = 30; |
|
afe42d0…
|
ragelink
|
76 |
} |
|
afe42d0…
|
ragelink
|
77 |
else if (month === 2 && CalendarNamespace.isLeapYear(year)) { |
|
afe42d0…
|
ragelink
|
78 |
days = 29; |
|
afe42d0…
|
ragelink
|
79 |
} |
|
afe42d0…
|
ragelink
|
80 |
else { |
|
afe42d0…
|
ragelink
|
81 |
days = 28; |
|
afe42d0…
|
ragelink
|
82 |
} |
|
afe42d0…
|
ragelink
|
83 |
return days; |
|
afe42d0…
|
ragelink
|
84 |
}, |
|
afe42d0…
|
ragelink
|
85 |
draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999 |
|
afe42d0…
|
ragelink
|
86 |
const today = new Date(); |
|
afe42d0…
|
ragelink
|
87 |
const todayDay = today.getDate(); |
|
afe42d0…
|
ragelink
|
88 |
const todayMonth = today.getMonth() + 1; |
|
afe42d0…
|
ragelink
|
89 |
const todayYear = today.getFullYear(); |
|
afe42d0…
|
ragelink
|
90 |
let todayClass = ''; |
|
afe42d0…
|
ragelink
|
91 |
|
|
afe42d0…
|
ragelink
|
92 |
// Use UTC functions here because the date field does not contain time |
|
afe42d0…
|
ragelink
|
93 |
// and using the UTC function variants prevent the local time offset |
|
afe42d0…
|
ragelink
|
94 |
// from altering the date, specifically the day field. For example: |
|
afe42d0…
|
ragelink
|
95 |
// |
|
afe42d0…
|
ragelink
|
96 |
// ``` |
|
afe42d0…
|
ragelink
|
97 |
// var x = new Date('2013-10-02'); |
|
afe42d0…
|
ragelink
|
98 |
// var day = x.getDate(); |
|
afe42d0…
|
ragelink
|
99 |
// ``` |
|
afe42d0…
|
ragelink
|
100 |
// |
|
afe42d0…
|
ragelink
|
101 |
// The day variable above will be 1 instead of 2 in, say, US Pacific time |
|
afe42d0…
|
ragelink
|
102 |
// zone. |
|
afe42d0…
|
ragelink
|
103 |
let isSelectedMonth = false; |
|
afe42d0…
|
ragelink
|
104 |
if (typeof selected !== 'undefined') { |
|
afe42d0…
|
ragelink
|
105 |
isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month); |
|
afe42d0…
|
ragelink
|
106 |
} |
|
afe42d0…
|
ragelink
|
107 |
|
|
afe42d0…
|
ragelink
|
108 |
month = parseInt(month); |
|
afe42d0…
|
ragelink
|
109 |
year = parseInt(year); |
|
afe42d0…
|
ragelink
|
110 |
const calDiv = document.getElementById(div_id); |
|
afe42d0…
|
ragelink
|
111 |
removeChildren(calDiv); |
|
afe42d0…
|
ragelink
|
112 |
const calTable = document.createElement('table'); |
|
afe42d0…
|
ragelink
|
113 |
quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year); |
|
afe42d0…
|
ragelink
|
114 |
const tableBody = quickElement('tbody', calTable); |
|
afe42d0…
|
ragelink
|
115 |
|
|
afe42d0…
|
ragelink
|
116 |
// Draw days-of-week header |
|
afe42d0…
|
ragelink
|
117 |
let tableRow = quickElement('tr', tableBody); |
|
afe42d0…
|
ragelink
|
118 |
for (let i = 0; i < 7; i++) { |
|
afe42d0…
|
ragelink
|
119 |
quickElement('th', tableRow, CalendarNamespace.daysOfWeekInitial[(i + CalendarNamespace.firstDayOfWeek) % 7]); |
|
afe42d0…
|
ragelink
|
120 |
} |
|
afe42d0…
|
ragelink
|
121 |
|
|
afe42d0…
|
ragelink
|
122 |
const startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); |
|
afe42d0…
|
ragelink
|
123 |
const days = CalendarNamespace.getDaysInMonth(month, year); |
|
afe42d0…
|
ragelink
|
124 |
|
|
afe42d0…
|
ragelink
|
125 |
let nonDayCell; |
|
afe42d0…
|
ragelink
|
126 |
|
|
afe42d0…
|
ragelink
|
127 |
// Draw blanks before first of month |
|
afe42d0…
|
ragelink
|
128 |
tableRow = quickElement('tr', tableBody); |
|
afe42d0…
|
ragelink
|
129 |
for (let i = 0; i < startingPos; i++) { |
|
afe42d0…
|
ragelink
|
130 |
nonDayCell = quickElement('td', tableRow, ' '); |
|
afe42d0…
|
ragelink
|
131 |
nonDayCell.className = "nonday"; |
|
afe42d0…
|
ragelink
|
132 |
} |
|
afe42d0…
|
ragelink
|
133 |
|
|
afe42d0…
|
ragelink
|
134 |
function calendarMonth(y, m) { |
|
afe42d0…
|
ragelink
|
135 |
function onClick(e) { |
|
afe42d0…
|
ragelink
|
136 |
e.preventDefault(); |
|
afe42d0…
|
ragelink
|
137 |
callback(y, m, this.textContent); |
|
afe42d0…
|
ragelink
|
138 |
} |
|
afe42d0…
|
ragelink
|
139 |
return onClick; |
|
afe42d0…
|
ragelink
|
140 |
} |
|
afe42d0…
|
ragelink
|
141 |
|
|
afe42d0…
|
ragelink
|
142 |
// Draw days of month |
|
afe42d0…
|
ragelink
|
143 |
let currentDay = 1; |
|
afe42d0…
|
ragelink
|
144 |
for (let i = startingPos; currentDay <= days; i++) { |
|
afe42d0…
|
ragelink
|
145 |
if (i % 7 === 0 && currentDay !== 1) { |
|
afe42d0…
|
ragelink
|
146 |
tableRow = quickElement('tr', tableBody); |
|
afe42d0…
|
ragelink
|
147 |
} |
|
afe42d0…
|
ragelink
|
148 |
if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) { |
|
afe42d0…
|
ragelink
|
149 |
todayClass = 'today'; |
|
afe42d0…
|
ragelink
|
150 |
} else { |
|
afe42d0…
|
ragelink
|
151 |
todayClass = ''; |
|
afe42d0…
|
ragelink
|
152 |
} |
|
afe42d0…
|
ragelink
|
153 |
|
|
afe42d0…
|
ragelink
|
154 |
// use UTC function; see above for explanation. |
|
afe42d0…
|
ragelink
|
155 |
if (isSelectedMonth && currentDay === selected.getUTCDate()) { |
|
afe42d0…
|
ragelink
|
156 |
if (todayClass !== '') { |
|
afe42d0…
|
ragelink
|
157 |
todayClass += " "; |
|
afe42d0…
|
ragelink
|
158 |
} |
|
afe42d0…
|
ragelink
|
159 |
todayClass += "selected"; |
|
afe42d0…
|
ragelink
|
160 |
} |
|
afe42d0…
|
ragelink
|
161 |
|
|
afe42d0…
|
ragelink
|
162 |
const cell = quickElement('td', tableRow, '', 'class', todayClass); |
|
afe42d0…
|
ragelink
|
163 |
const link = quickElement('a', cell, currentDay, 'href', '#'); |
|
afe42d0…
|
ragelink
|
164 |
link.addEventListener('click', calendarMonth(year, month)); |
|
afe42d0…
|
ragelink
|
165 |
currentDay++; |
|
afe42d0…
|
ragelink
|
166 |
} |
|
afe42d0…
|
ragelink
|
167 |
|
|
afe42d0…
|
ragelink
|
168 |
// Draw blanks after end of month (optional, but makes for valid code) |
|
afe42d0…
|
ragelink
|
169 |
while (tableRow.childNodes.length < 7) { |
|
afe42d0…
|
ragelink
|
170 |
nonDayCell = quickElement('td', tableRow, ' '); |
|
afe42d0…
|
ragelink
|
171 |
nonDayCell.className = "nonday"; |
|
afe42d0…
|
ragelink
|
172 |
} |
|
afe42d0…
|
ragelink
|
173 |
|
|
afe42d0…
|
ragelink
|
174 |
calDiv.appendChild(calTable); |
|
afe42d0…
|
ragelink
|
175 |
} |
|
afe42d0…
|
ragelink
|
176 |
}; |
|
afe42d0…
|
ragelink
|
177 |
|
|
afe42d0…
|
ragelink
|
178 |
// Calendar -- A calendar instance |
|
afe42d0…
|
ragelink
|
179 |
function Calendar(div_id, callback, selected) { |
|
afe42d0…
|
ragelink
|
180 |
// div_id (string) is the ID of the element in which the calendar will |
|
afe42d0…
|
ragelink
|
181 |
// be displayed |
|
afe42d0…
|
ragelink
|
182 |
// callback (string) is the name of a JavaScript function that will be |
|
afe42d0…
|
ragelink
|
183 |
// called with the parameters (year, month, day) when a day in the |
|
afe42d0…
|
ragelink
|
184 |
// calendar is clicked |
|
afe42d0…
|
ragelink
|
185 |
this.div_id = div_id; |
|
afe42d0…
|
ragelink
|
186 |
this.callback = callback; |
|
afe42d0…
|
ragelink
|
187 |
this.today = new Date(); |
|
afe42d0…
|
ragelink
|
188 |
this.currentMonth = this.today.getMonth() + 1; |
|
afe42d0…
|
ragelink
|
189 |
this.currentYear = this.today.getFullYear(); |
|
afe42d0…
|
ragelink
|
190 |
if (typeof selected !== 'undefined') { |
|
afe42d0…
|
ragelink
|
191 |
this.selected = selected; |
|
afe42d0…
|
ragelink
|
192 |
} |
|
afe42d0…
|
ragelink
|
193 |
} |
|
afe42d0…
|
ragelink
|
194 |
Calendar.prototype = { |
|
afe42d0…
|
ragelink
|
195 |
drawCurrent: function() { |
|
afe42d0…
|
ragelink
|
196 |
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected); |
|
afe42d0…
|
ragelink
|
197 |
}, |
|
afe42d0…
|
ragelink
|
198 |
drawDate: function(month, year, selected) { |
|
afe42d0…
|
ragelink
|
199 |
this.currentMonth = month; |
|
afe42d0…
|
ragelink
|
200 |
this.currentYear = year; |
|
afe42d0…
|
ragelink
|
201 |
|
|
afe42d0…
|
ragelink
|
202 |
if(selected) { |
|
afe42d0…
|
ragelink
|
203 |
this.selected = selected; |
|
afe42d0…
|
ragelink
|
204 |
} |
|
afe42d0…
|
ragelink
|
205 |
|
|
afe42d0…
|
ragelink
|
206 |
this.drawCurrent(); |
|
afe42d0…
|
ragelink
|
207 |
}, |
|
afe42d0…
|
ragelink
|
208 |
drawPreviousMonth: function() { |
|
afe42d0…
|
ragelink
|
209 |
if (this.currentMonth === 1) { |
|
afe42d0…
|
ragelink
|
210 |
this.currentMonth = 12; |
|
afe42d0…
|
ragelink
|
211 |
this.currentYear--; |
|
afe42d0…
|
ragelink
|
212 |
} |
|
afe42d0…
|
ragelink
|
213 |
else { |
|
afe42d0…
|
ragelink
|
214 |
this.currentMonth--; |
|
afe42d0…
|
ragelink
|
215 |
} |
|
afe42d0…
|
ragelink
|
216 |
this.drawCurrent(); |
|
afe42d0…
|
ragelink
|
217 |
}, |
|
afe42d0…
|
ragelink
|
218 |
drawNextMonth: function() { |
|
afe42d0…
|
ragelink
|
219 |
if (this.currentMonth === 12) { |
|
afe42d0…
|
ragelink
|
220 |
this.currentMonth = 1; |
|
afe42d0…
|
ragelink
|
221 |
this.currentYear++; |
|
afe42d0…
|
ragelink
|
222 |
} |
|
afe42d0…
|
ragelink
|
223 |
else { |
|
afe42d0…
|
ragelink
|
224 |
this.currentMonth++; |
|
afe42d0…
|
ragelink
|
225 |
} |
|
afe42d0…
|
ragelink
|
226 |
this.drawCurrent(); |
|
afe42d0…
|
ragelink
|
227 |
}, |
|
afe42d0…
|
ragelink
|
228 |
drawPreviousYear: function() { |
|
afe42d0…
|
ragelink
|
229 |
this.currentYear--; |
|
afe42d0…
|
ragelink
|
230 |
this.drawCurrent(); |
|
afe42d0…
|
ragelink
|
231 |
}, |
|
afe42d0…
|
ragelink
|
232 |
drawNextYear: function() { |
|
afe42d0…
|
ragelink
|
233 |
this.currentYear++; |
|
afe42d0…
|
ragelink
|
234 |
this.drawCurrent(); |
|
afe42d0…
|
ragelink
|
235 |
} |
|
afe42d0…
|
ragelink
|
236 |
}; |
|
afe42d0…
|
ragelink
|
237 |
window.Calendar = Calendar; |
|
afe42d0…
|
ragelink
|
238 |
window.CalendarNamespace = CalendarNamespace; |
|
afe42d0…
|
ragelink
|
239 |
} |