1 |
|
2 | export var CalendarDate = {
|
3 |
|
4 | |
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | getDaysInMonth: function(year, month_index) {
|
11 | return new Date(year, month_index+1, 0).getDate();
|
12 | },
|
13 |
|
14 | getWeeksInMonth: function(year, month_index) {
|
15 | if (this.getDayOfWeekOfFirstMonthDay(year, month_index) === 0 && this.getDaysInMonth(year, month_index) === 28) {
|
16 | return 4;
|
17 | } else {
|
18 | return 5;
|
19 | }
|
20 | },
|
21 |
|
22 | |
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | getDayOfWeekOfFirstMonthDay(year, month_index) {
|
29 | var d = new Date(year, month_index).getDay();
|
30 | if (d === 0) {
|
31 | d = 7;
|
32 | }
|
33 | return d-1;
|
34 | },
|
35 |
|
36 | |
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | getMonthMatrix(year, month_index) {
|
45 | var first_day_of_week = this.getDayOfWeekOfFirstMonthDay(year, month_index);
|
46 | var days_in_month = this.getDaysInMonth(year, month_index);
|
47 | var weeks_in_month = this.getWeeksInMonth(year, month_index);
|
48 |
|
49 | var matrix = [];
|
50 | var day_nr = 1;
|
51 | for (var j = 0; j < weeks_in_month; j++) {
|
52 | matrix[j] = [];
|
53 | for (var k = 0; k < 7; k++) {
|
54 | var td = document.createElement('td');
|
55 | if (j == 0) {
|
56 |
|
57 | if (k >= first_day_of_week) {
|
58 | matrix[j][k] = day_nr;
|
59 | day_nr++;
|
60 | } else {
|
61 | matrix[j][k] = null;
|
62 | }
|
63 | } else if (j == weeks_in_month - 1) {
|
64 |
|
65 | if (day_nr <= days_in_month) {
|
66 | matrix[j][k] = day_nr;
|
67 | day_nr++;
|
68 | } else {
|
69 | matrix[j][k] = null;
|
70 | }
|
71 | } else {
|
72 |
|
73 | matrix[j][k] = day_nr;
|
74 | day_nr++;
|
75 | }
|
76 | }
|
77 | }
|
78 | return matrix;
|
79 | },
|
80 |
|
81 | getCurrentDate() {
|
82 | var date = new Date();
|
83 | var year = date.getUTCFullYear();
|
84 | var month_index = date.getUTCMonth();
|
85 | var day = date.getUTCDate();
|
86 | return {
|
87 | year: year,
|
88 | monthIndex: month_index,
|
89 | day: day
|
90 | }
|
91 | }
|
92 | };
|
93 |
|
94 | export var CalendarMarkup = {
|
95 |
|
96 | lang: {
|
97 | daysOfWeeks: ['Mon', 'Thu', 'Wed', 'Thr', 'Fri', 'Sat', 'Sun'],
|
98 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
99 | },
|
100 |
|
101 | createTableHead: function() {
|
102 | var thead = document.createElement('div');
|
103 | var tr = document.createElement('div');
|
104 | var th_collection = [];
|
105 | for (var k = 0; k < 7; k++) {
|
106 | var th = document.createElement('div');
|
107 | th.innerHTML = this.lang.daysOfWeeks[k];
|
108 | th_collection.push(th);
|
109 | tr.appendChild(th)
|
110 | }
|
111 | thead.appendChild(tr);
|
112 | return {
|
113 | thead: thead,
|
114 | row: tr,
|
115 | thCollection: th_collection
|
116 | }
|
117 | },
|
118 |
|
119 | createTableBody: function(year, month_index, current_day = null) {
|
120 |
|
121 | var matrix = CalendarDate.getMonthMatrix(year, month_index);
|
122 |
|
123 | var tbody = document.createElement('div');
|
124 | var inactive_td_collection = [];
|
125 | var current_td = null;
|
126 | var current_date_td = null;
|
127 | var today = CalendarDate.getCurrentDate();
|
128 | var td_collection = [];
|
129 | var row_collection = [];
|
130 |
|
131 | for (var j = 0; j < matrix.length; j++) {
|
132 | var row = document.createElement('div');
|
133 | row_collection.push(row);
|
134 | tbody.appendChild(row);
|
135 | for (var k = 0; k < matrix[j].length; k++) {
|
136 | var td = document.createElement('div');
|
137 | if (matrix[j][k] === null) {
|
138 | inactive_td_collection.push(td);
|
139 | } else {
|
140 | td.innerHTML = matrix[j][k];
|
141 | td_collection.push(td);
|
142 | if (current_day !== null && matrix[j][k] == current_day) {
|
143 | current_td = td;
|
144 | }
|
145 | if (today.year === year && today.monthIndex === month_index && today.day === matrix[j][k]) {
|
146 | current_date_td = td;
|
147 | }
|
148 | }
|
149 | row.appendChild(td);
|
150 | }
|
151 | }
|
152 |
|
153 | return {
|
154 | tbody: tbody,
|
155 | rowCollection: row_collection,
|
156 | tdCollection: td_collection,
|
157 | inactiveTdCollection: inactive_td_collection,
|
158 | currentTd: current_td,
|
159 | currentDateTd: current_date_td
|
160 | };
|
161 | },
|
162 |
|
163 | createEmptyTable: function() {
|
164 | var table = document.createElement('div');
|
165 | return table;
|
166 | },
|
167 |
|
168 | createPrevButton: function(content = '') {
|
169 | var el = document.createElement('div');
|
170 | el.innerHTML = content;
|
171 | return el;
|
172 | },
|
173 |
|
174 | createNextButton: function(content = '') {
|
175 | var el = document.createElement('div');
|
176 | el.innerHTML = content;
|
177 | return el;
|
178 | },
|
179 |
|
180 | createMonthSelect: function(selected_month_index = 0) {
|
181 | var select = document.createElement('select');
|
182 | for (var month_index = 0; month_index < this.lang.months.length; month_index++) {
|
183 | var option = document.createElement('option');
|
184 | option.setAttribute('value', month_index);
|
185 | option.innerHTML = this.lang.months[month_index];
|
186 | if (month_index == selected_month_index) {
|
187 | option.setAttribute('selected', 1);
|
188 | }
|
189 | select.appendChild(option);
|
190 | }
|
191 | return select;
|
192 | },
|
193 |
|
194 | createYearSelect: function(selected_year = 2000, min_year = 1950, max_year = 2050) {
|
195 | var select = document.createElement('select');
|
196 | for (var i = min_year; i <= max_year; i++) {
|
197 | var option = document.createElement('option');
|
198 | option.setAttribute('value', i);
|
199 | option.innerHTML = i;
|
200 | if (i == selected_year) {
|
201 | option.setAttribute('selected', true);
|
202 | }
|
203 | select.appendChild(option);
|
204 | }
|
205 | return select;
|
206 | },
|
207 |
|
208 | createEmptyHeader: function() {
|
209 | var header = document.createElement('header');
|
210 | return header;
|
211 | },
|
212 |
|
213 | createEmptyCalendar: function() {
|
214 | var calendar = document.createElement('calendar');
|
215 | return calendar;
|
216 | }
|
217 | };
|
218 |
|
219 | export var CalendarDecorator = {
|
220 | theme: {
|
221 | def: {
|
222 | classPrefix: 'bny-',
|
223 |
|
224 | calendarClass: 'cal',
|
225 | headerClass: 'cal-header',
|
226 | monthClass: 'cal-month',
|
227 | yearClass: 'cal-year',
|
228 | prevButtonClass: 'cal-prev',
|
229 | nextButtonClass: 'cal-next',
|
230 |
|
231 | tableClass: 'cal-table',
|
232 | tableHeadClass: 'cal-head',
|
233 | tableHeadDayOfWeekClass: 'cal-day-of-week',
|
234 | tableBodyClass: 'cal-body',
|
235 | tableRowClass: 'cal-row',
|
236 | weekClass: 'cal-week',
|
237 | dayClass: 'cal-day',
|
238 | dayInactiveClass: 'cal-day-inactive',
|
239 | dayCurrentClass: 'cal-day-current',
|
240 | currentDateClass: 'cal-day-today',
|
241 |
|
242 | popupClass: 'cal-popup',
|
243 | popupBodyFadeClass: 'cal-popup-body-fade'
|
244 | }
|
245 | },
|
246 |
|
247 | buildClass: function(classProperty, theme = 'def') {
|
248 | return this.theme[theme].classPrefix + this.theme[theme][classProperty + 'Class'];
|
249 | },
|
250 |
|
251 | decorateTableHead: function(thead, row, th_collection) {
|
252 | thead.classList.add(this.buildClass('tableHead'));
|
253 | row.classList.add(this.buildClass('tableRow'));
|
254 | for (var k = 0; k < th_collection.length; k++) {
|
255 | th_collection[k].classList.add(this.buildClass('tableHeadDayOfWeek'));
|
256 | }
|
257 | },
|
258 |
|
259 | decorateTableBody: function(tbody, row_collection, td_collection, inactive_td_collection, current_td, current_date_td) {
|
260 | tbody.classList.add(this.buildClass('tableBody'));
|
261 | for (var k = 0; k < row_collection.length; k++) {
|
262 | row_collection[k].classList.add(this.buildClass('tableRow'));
|
263 | row_collection[k].classList.add(this.buildClass('week'));
|
264 | }
|
265 | for (k = 0; k < td_collection.length; k++) {
|
266 | td_collection[k].classList.add(this.buildClass('day'));
|
267 | }
|
268 | for (k = 0; k < inactive_td_collection.length; k++) {
|
269 | inactive_td_collection[k].classList.add(this.buildClass('dayInactive'));
|
270 | }
|
271 | if (current_td !== null) {
|
272 | current_td.classList.add(this.buildClass('dayCurrent'));
|
273 | }
|
274 | if (current_date_td !== null) {
|
275 | current_date_td.classList.add(this.buildClass('currentDate'));
|
276 | }
|
277 | },
|
278 |
|
279 | setCurrentDay: function(td_collection, day) {
|
280 | for (var k = 0; k < td_collection.length; k++) {
|
281 | if (td_collection[k].innerHTML == day) {
|
282 | td_collection[k].classList.add(this.buildClass('dayCurrent'));
|
283 | } else if (td_collection[k].classList.contains(this.buildClass('dayCurrent'))) {
|
284 | td_collection[k].classList.remove(this.buildClass('dayCurrent'));
|
285 | }
|
286 | }
|
287 | },
|
288 |
|
289 | decorateTable: function(table) {
|
290 | table.classList.add(this.buildClass('table'));
|
291 | },
|
292 |
|
293 | decoratePrevButton: function(prev_button) {
|
294 | prev_button.classList.add(this.buildClass('prevButton'));
|
295 | },
|
296 |
|
297 | decorateNextButton: function(next_button) {
|
298 | next_button.classList.add(this.buildClass('nextButton'));
|
299 | },
|
300 |
|
301 | decorateMonthSelect: function(select) {
|
302 | select.classList.add(this.buildClass('month'));
|
303 | },
|
304 |
|
305 | decorateYearSelect: function(select) {
|
306 | select.classList.add(this.buildClass('year'));
|
307 | },
|
308 |
|
309 | decorateHeader: function(header) {
|
310 | header.classList.add(this.buildClass('header'));
|
311 | },
|
312 |
|
313 | decorateCalendar: function(calendar) {
|
314 | calendar.classList.add(this.buildClass('calendar'));
|
315 | },
|
316 |
|
317 | decorateCalendarPopup(calendar) {
|
318 | calendar.classList.add(this.buildClass('popup'));
|
319 | document.body.classList.add(this.buildClass('popupBodyFade'));
|
320 | },
|
321 |
|
322 | undecorateCalendarPopup(calendar) {
|
323 | calendar.classList.remove(this.buildClass('popup'));
|
324 | document.body.classList.remove(this.buildClass('popupBodyFade'));
|
325 | }
|
326 | };
|
327 |
|
328 | export var CalendarController = {
|
329 |
|
330 | attachDayClickEvent: function(calendar_id) {
|
331 | var td_collection = Calendar._calendars[calendar_id].tableBody.tdCollection;
|
332 | for (var k = 0; k < td_collection.length; k++) {
|
333 | td_collection[k].addEventListener('click', function() {
|
334 | Calendar.pickDay(calendar_id, Calendar._calendars[calendar_id].displayedYear, Calendar._calendars[calendar_id].displayedMonthIndex, this.innerHTML);
|
335 | });
|
336 | }
|
337 | },
|
338 |
|
339 | attachMonthSelectEvent: function(calendar_id) {
|
340 | Calendar._calendars[calendar_id].headerMonthSelect.addEventListener('change', function() {
|
341 | Calendar.changeMonth(calendar_id, Calendar._calendars[calendar_id].displayedYear, +this.value);
|
342 | });
|
343 | },
|
344 |
|
345 | attachYearSelectEvent: function(calendar_id) {
|
346 | Calendar._calendars[calendar_id].headerYearSelect.addEventListener('change', function() {
|
347 | Calendar.changeMonth(calendar_id, this.value, Calendar._calendars[calendar_id].displayedMonthIndex);
|
348 | });
|
349 | },
|
350 |
|
351 | attachPrevClickEvent: function(calendar_id) {
|
352 | Calendar._calendars[calendar_id].headerPrevButton.addEventListener('click', function() {
|
353 | if (Calendar._calendars[calendar_id].displayedMonthIndex === 0) {
|
354 | Calendar.changeMonth(calendar_id, Calendar._calendars[calendar_id].displayedYear - 1, 11);
|
355 | } else {
|
356 | Calendar.changeMonth(calendar_id, Calendar._calendars[calendar_id].displayedYear, Calendar._calendars[calendar_id].displayedMonthIndex - 1);
|
357 | }
|
358 | });
|
359 | },
|
360 |
|
361 | attachNextClickEvent: function(calendar_id) {
|
362 | Calendar._calendars[calendar_id].headerNextButton.addEventListener('click', function() {
|
363 | if (Calendar._calendars[calendar_id].displayedMonthIndex === 11) {
|
364 | Calendar.changeMonth(calendar_id, Calendar._calendars[calendar_id].displayedYear + 1, 0);
|
365 | } else {
|
366 | Calendar.changeMonth(calendar_id, Calendar._calendars[calendar_id].displayedYear, Calendar._calendars[calendar_id].displayedMonthIndex + 1);
|
367 | }
|
368 | });
|
369 | },
|
370 |
|
371 | attachCloseOnOutsideClickEvent: function(calendar_id) {
|
372 | document.addEventListener('click', function() {
|
373 | if (!Calendar.isHidden(calendar_id)) {
|
374 | Calendar.hide(calendar_id);
|
375 | }
|
376 | });
|
377 |
|
378 | Calendar.getCalendar(calendar_id).addEventListener('click', function(e) {
|
379 | e.stopPropagation();
|
380 | });
|
381 | }
|
382 |
|
383 | };
|
384 |
|
385 | export var Calendar = {
|
386 |
|
387 | _calendars: {},
|
388 |
|
389 | create: function(id = 'datepicker', min_year = 1950, max_year = 2050, year = null, month_index = null, day = null) {
|
390 |
|
391 | var current_date = CalendarDate.getCurrentDate();
|
392 |
|
393 | if (year === null) {
|
394 | year = current_date.year;
|
395 | }
|
396 | if (month_index === null) {
|
397 | month_index = current_date.monthIndex;
|
398 | }
|
399 | if (day === null) {
|
400 | day = current_date.day;
|
401 | }
|
402 |
|
403 | var calendar = CalendarMarkup.createEmptyCalendar();
|
404 | CalendarDecorator.decorateCalendar(calendar);
|
405 |
|
406 | var header = CalendarMarkup.createEmptyHeader();
|
407 | CalendarDecorator.decorateHeader(header);
|
408 |
|
409 | var header_prev_btn = CalendarMarkup.createPrevButton();
|
410 | CalendarDecorator.decoratePrevButton(header_prev_btn);
|
411 |
|
412 | var header_next_btn = CalendarMarkup.createNextButton();
|
413 | CalendarDecorator.decorateNextButton(header_next_btn);
|
414 |
|
415 | var header_month_select = CalendarMarkup.createMonthSelect(month_index);
|
416 | CalendarDecorator.decorateMonthSelect(header_month_select);
|
417 |
|
418 | var header_year_select = CalendarMarkup.createYearSelect(year, min_year, max_year);
|
419 | CalendarDecorator.decorateYearSelect(header_year_select);
|
420 |
|
421 | header.appendChild(header_prev_btn);
|
422 | header.appendChild(header_month_select);
|
423 | header.appendChild(header_year_select);
|
424 | header.appendChild(header_next_btn);
|
425 |
|
426 | var table = CalendarMarkup.createEmptyTable();
|
427 | CalendarDecorator.decorateTable(table);
|
428 |
|
429 | var table_head = CalendarMarkup.createTableHead();
|
430 | CalendarDecorator.decorateTableHead(table_head.thead, table_head.row, table_head.thCollection);
|
431 |
|
432 | var table_body = CalendarMarkup.createTableBody(year, month_index, day);
|
433 | CalendarDecorator.decorateTableBody(
|
434 | table_body.tbody,
|
435 | table_body.rowCollection,
|
436 | table_body.tdCollection,
|
437 | table_body.inactiveTdCollection,
|
438 | table_body.currentTd,
|
439 | table_body.currentDateTd
|
440 | );
|
441 |
|
442 | table.appendChild(table_head.thead);
|
443 | table.appendChild(table_body.tbody);
|
444 |
|
445 | calendar.appendChild(header);
|
446 | calendar.appendChild(table);
|
447 |
|
448 | this._calendars[id] = {
|
449 | selectedYear: year,
|
450 | selectedMonthIndex: month_index,
|
451 | selectedDay: day,
|
452 | displayedYear: year,
|
453 | displayedMonthIndex: month_index,
|
454 | minYear: min_year,
|
455 | maxYear: max_year,
|
456 | calendar: calendar,
|
457 | header: header,
|
458 | headerPrevButton: header_prev_btn,
|
459 | headerMonthSelect: header_month_select,
|
460 | headerYearSelect: header_year_select,
|
461 | headerNextButton: header_next_btn,
|
462 | table: table,
|
463 | tableHead: table_head,
|
464 | tableBody: table_body,
|
465 | onPickHandlers: []
|
466 | };
|
467 |
|
468 | CalendarController.attachPrevClickEvent(id);
|
469 | CalendarController.attachNextClickEvent(id);
|
470 |
|
471 | CalendarController.attachMonthSelectEvent(id);
|
472 | CalendarController.attachYearSelectEvent(id);
|
473 |
|
474 | CalendarController.attachDayClickEvent(id);
|
475 |
|
476 | CalendarController.attachCloseOnOutsideClickEvent(id);
|
477 |
|
478 | return calendar;
|
479 | },
|
480 |
|
481 | changeMonth(calendar_id, year, month_index) {
|
482 | var cal = this._calendars[calendar_id];
|
483 | if (year < cal.minYear || year > cal.maxYear) {
|
484 | return false;
|
485 | }
|
486 | var current_day = null;
|
487 | if (year == cal.selectedYear && month_index == cal.selectedMonthIndex) {
|
488 | current_day = cal.selectedDay;
|
489 | }
|
490 | cal.displayedYear = year;
|
491 | cal.displayedMonthIndex = month_index;
|
492 | var table_body = CalendarMarkup.createTableBody(year, month_index, current_day);
|
493 | CalendarDecorator.decorateTableBody(
|
494 | table_body.tbody,
|
495 | table_body.rowCollection,
|
496 | table_body.tdCollection,
|
497 | table_body.inactiveTdCollection,
|
498 | table_body.currentTd,
|
499 | table_body.currentDateTd
|
500 | );
|
501 |
|
502 | cal.tableBody.tbody.parentNode.removeChild(cal.tableBody.tbody);
|
503 | cal.tableBody = table_body;
|
504 | cal.table.appendChild(table_body.tbody);
|
505 |
|
506 | cal.headerYearSelect.querySelector('option[selected]').removeAttribute('selected');
|
507 | cal.headerYearSelect.querySelector('option[value="'+year+'"]').setAttribute('selected', 'selected');
|
508 | cal.headerYearSelect.value = year;
|
509 |
|
510 | cal.headerMonthSelect.querySelector('option[selected]').removeAttribute('selected');
|
511 | cal.headerMonthSelect.options[month_index].setAttribute('selected', 'selected');
|
512 | cal.headerMonthSelect.value = month_index;
|
513 |
|
514 | CalendarController.attachDayClickEvent(calendar_id);
|
515 | },
|
516 |
|
517 | pickDay: function(calendar_id, year, month_index, day) {
|
518 | this._calendars[calendar_id].selectedYear = year;
|
519 | this._calendars[calendar_id].selectedMonthIndex = month_index;
|
520 | this._calendars[calendar_id].selectedDay = day;
|
521 | CalendarDecorator.setCurrentDay(this._calendars[calendar_id].tableBody.tdCollection, day);
|
522 | for (var i = 0; i < this._calendars[calendar_id].onPickHandlers.length; i++) {
|
523 | var month = parseInt(month_index) + 1;
|
524 | var year = parseInt(year);
|
525 | var day = parseInt(day);
|
526 | this._calendars[calendar_id].onPickHandlers[i](year, month, day);
|
527 | }
|
528 | },
|
529 |
|
530 | onPick: function(calendar_id, handler) {
|
531 | this._calendars[calendar_id].onPickHandlers.push(handler);
|
532 | },
|
533 |
|
534 | getCalendar(calendar_id) {
|
535 | return this._calendars[calendar_id].calendar;
|
536 | },
|
537 |
|
538 | show: function(calendar_id, popup = false) {
|
539 | if (popup) {
|
540 | CalendarDecorator.decorateCalendarPopup(this._calendars[calendar_id].calendar)
|
541 | }
|
542 | this._calendars[calendar_id].calendar.classList.remove('hidden');
|
543 | },
|
544 |
|
545 | hide: function(calendar_id, popup = false) {
|
546 | if (popup) {
|
547 | CalendarDecorator.undecorateCalendarPopup(this._calendars[calendar_id].calendar)
|
548 | }
|
549 | this._calendars[calendar_id].calendar.classList.add('hidden');
|
550 | },
|
551 |
|
552 | isHidden: function(calendar_id) {
|
553 | return this._calendars[calendar_id].calendar.classList.contains('hidden');
|
554 | }
|
555 | };
|