PluginProbe
Calendar / 1.3.18
Calendar v1.3.18
trunk 1.0 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 All 28 releases
calendar / javascript.js

javascript.js in Calendar 1.3.18, at javascript.js

609 lines 18.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * CalendarJS v1.5
3 *
4 * Copyright 2011-2012, Dimitar Ivanov (http://www.bulgaria-web-developers.com/projects/javascript/calendar/)
5 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL Version 3
6 * (http://www.opensource.org/licenses/gpl-3.0.html) license.
7 *
8 * Date: Thu Jul 26 23:15:56 2012 +0300
9 */
10 (function (window, undefined) {
11 var now = new Date(),
12 today = [now.getFullYear(), now.getMonth(), now.getDate()].join('-'),
13 midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()),
14 d = window.document;
15
16 function Calendar(options) {
17 this.version = "1.5";
18 this.isOpen = false;
19 this.focus = false;
20 this.id = null;
21 this.container = null;
22 this.element = null;
23 this.selectedDate = null;
24 this.opts = {
25 year: new Date().getFullYear(),
26 month: new Date().getMonth(),
27 dayNames: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
28 dayNamesFull: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
29 monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
30 monthNamesFull: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
31 startDay: 0,
32 weekNumbers: false,
33 selectOtherMonths: false,
34 showOtherMonths: true,
35 showNavigation: true,
36 months: 1,
37 inline: false,
38 disablePast: false,
39 dateFormat: 'Y-m-d',
40 position: 'right',
41 minDate: null,
42 onBeforeOpen: function () {},
43 onBeforeClose: function () {},
44 onOpen: function () {},
45 onClose: function () {},
46 onSelect: function () {},
47 onBeforeShowDay: function () {
48 return [true, ''];
49 }
50 };
51 for (var key in options) {
52 if (options.hasOwnProperty(key)) {
53 this.opts[key] = options[key];
54 }
55 }
56 this.init.call(this);
57 }
58 /* Static functions */
59 Calendar.Util = {
60 addClass: function (ele, cls) {
61 if (ele && !this.hasClass(ele, cls)) {
62 ele.className += ele.className.length > 0 ? " " + cls : cls;
63 }
64 },
65 hasClass: function (ele, cls) {
66 if (ele && typeof ele.className != 'undefined' && typeof ele.nodeType != 'undefined' && ele.nodeType === 1) {
67 return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
68 }
69 return false;
70 },
71 removeClass: function (ele, cls) {
72 if (this.hasClass(ele, cls)) {
73 var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
74 ele.className = ele.className.replace(reg, ' ');
75 }
76 },
77 addEvent: function (obj, type, fn) {
78 if (obj.addEventListener) {
79 obj.addEventListener(type, fn, false);
80 } else if (obj.attachEvent) {
81 obj["e" + type + fn] = fn;
82 obj[type + fn] = function () {
83 obj["e" + type + fn](window.event);
84 };
85 obj.attachEvent("on" + type, obj[type + fn]);
86 } else {
87 obj["on" + type] = obj["e" + type + fn];
88 }
89 },
90 getElementsByClass: function (searchClass, node, tag) {
91 var classElements = [];
92 if (node === null) {
93 node = d;
94 }
95 if (tag === null) {
96 tag = '*';
97 }
98 var els = node.getElementsByTagName(tag);
99 var elsLen = els.length;
100 var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
101 for (var i = 0, j = 0; i < elsLen; i++) {
102 if (pattern.test(els[i].className)) {
103 classElements[j] = els[i];
104 j++;
105 }
106 }
107 return classElements;
108 },
109 getEventTarget: function (e) {
110 var targ;
111 if (!e) {
112 e = window.event;
113 }
114 if (e.target) {
115 targ = e.target;
116 } else if (e.srcElement) {
117 targ = e.srcElement;
118 }
119 if (targ.nodeType == 3) {
120 targ = targ.parentNode;
121 }
122 return targ;
123 }
124 };
125 /* Private functions */
126 function emptyRow(weekNumbers) {
127 var i, cell, cols = weekNumbers ? 8 : 7,
128 row = d.createElement('tr');
129 for (i = 0; i < cols; i++) {
130 cell = d.createElement('td');
131 Calendar.Util.addClass(cell, 'bcal-empty');
132 row.appendChild(cell);
133 }
134 return row;
135 }
136 /**
137 * @param Object obj
138 * @return Array
139 */
140 function findPos(obj) {
141 var curleft = 0, curtop = 0;
142 if (obj.offsetParent) {
143 do {
144 curleft += obj.offsetLeft;
145 curtop += obj.offsetTop;
146 } while (obj = obj.offsetParent);
147 return [curleft, curtop];
148 }
149 }
150 /**
151 * @param Number i
152 * @param Number month
153 * @return Number
154 */
155 function getIndex(i, months) {
156 if (i > 0 && i < months - 1) {
157 return 0;
158 } else if (i > 0 && i === months - 1) {
159 return 2;
160 } else if (i === 0 && i === months - 1) {
161 return 3;
162 } else if (i === 0 && i < months - 1) {
163 return 1;
164 }
165 }
166 /**
167 * Format date
168 *
169 * @param String format
170 * @param Number date
171 * @return String
172 */
173 function _formatDate(format, date) {
174
175 function pad(input) {
176 return (input + "").length === 2 ? input : "0" + input;
177 }
178
179 var i, len, f,
180 output = [],
181 dt = new Date(date);
182 for (i = 0, len = format.length; i < len; i++) {
183 f = format.charAt(i);
184 switch (f) {
185 case 'Y':
186 output.push(dt.getFullYear());
187 break;
188 case 'y':
189 output.push((dt.getFullYear() + "").slice(-2));
190 break;
191 case 'm':
192 output.push(pad(dt.getMonth() + 1));
193 break;
194 case 'n':
195 output.push(dt.getMonth() + 1);
196 break;
197 case 'F':
198 output.push(this.opts.monthNamesFull[dt.getMonth()]);
199 break;
200 case 'M':
201 output.push(this.opts.monthNames[dt.getMonth()]);
202 break;
203 case 'd':
204 output.push(pad(dt.getDate()));
205 break;
206 case 'j':
207 output.push(dt.getDate());
208 break;
209 case 'D':
210 output.push(this.opts.dayNamesFull[dt.getDay()].slice(0, 3));
211 break;
212 case 'l':
213 output.push(this.opts.dayNamesFull[dt.getDay()]);
214 break;
215 default:
216 output.push(f);
217 }
218 }
219 return output.join("");
220 }
221
222 function is(type, obj) {
223 var clas = Object.prototype.toString.call(obj).slice(8, -1);
224 return obj !== undefined && obj !== null && clas === type;
225 }
226
227 Calendar.prototype = {
228 /**
229 * @return Instance of calendar
230 */
231 init: function () {
232 var self = this,
233 i = 0, attrname,
234 body = d.getElementsByTagName("body")[0],
235 div = d.createElement('div');
236 self.id = Math.floor(Math.random() * 9999999);
237 self.element = d.getElementById(self.opts.element);
238 if (!self.element) {
239 return;
240 }
241 if (self.element.nodeType === 1 && self.element.nodeName == "INPUT" && self.element.value.length > 0) {
242 var now = new Date(self.element.value);
243 self.selectedDate = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
244 self.opts.year = self.selectedDate.getFullYear();
245 self.opts.month = self.selectedDate.getMonth();
246 }
247 self.element.style.cursor = 'pointer';
248 div.setAttribute('id', ['bcal-container', self.id].join('-'));
249 Calendar.Util.addClass(div, 'bcal-container');
250 if (!self.opts.inline) {
251 div.style.display = 'none';
252 div.style.position = 'absolute';
253 Calendar.Util.addEvent(self.element, 'focus', function (e) {
254 if (self.isOpen) {
255 self.close();
256 } else {
257 self.open();
258 }
259 });
260 Calendar.Util.addEvent(self.element, 'blur', function (e) {
261 if (self.isOpen && !self.focus) {
262 self.close();
263 }
264 });
265 Calendar.Util.addEvent(self.element, 'keydown', function (e) {
266 var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
267 switch (key) {
268 case 9: //Tab
269 self.close();
270 break;
271 case 27: //Escape
272 self.close();
273 break;
274 }
275 });
276 Calendar.Util.addEvent(document, "mousedown", function (e) {
277 var target = Calendar.Util.getEventTarget(e);
278 if (Calendar.Util.hasClass(target, "bcal-container") ||
279 Calendar.Util.hasClass(target, "bcal-table") ||
280 Calendar.Util.hasClass(target, "bcal-date") ||
281 Calendar.Util.hasClass(target, "bcal-today") ||
282 Calendar.Util.hasClass(target, "bcal-empty") ||
283 Calendar.Util.hasClass(target, "bcal-selected") ||
284 Calendar.Util.hasClass(target, "bcal-week") ||
285 Calendar.Util.hasClass(target, "bcal-nav") ||
286 Calendar.Util.hasClass(target, "bcal-navi") ||
287 Calendar.Util.hasClass(target, "bcal-month") ||
288 Calendar.Util.hasClass(target, "bcal-wday") ||
289 Calendar.Util.hasClass(target, "bcal-wnum") ||
290 Calendar.Util.hasClass(target.parentNode, "bcal-container") ||
291 Calendar.Util.hasClass(target.parentNode, "bcal-table")) {
292
293 } else {
294 self.close();
295 }
296 });
297 body.appendChild(div);
298 } else {
299 self.element.appendChild(div);
300 }
301 self.container = div;
302 var y = self.opts.year, m = self.opts.month;
303 for (i = 0; i < self.opts.months; i++) {
304 self.draw(y, m + i, getIndex(i, self.opts.months));
305 }
306 return self;
307 },
308 /**
309 * @param String format
310 * @param Number date
311 * @return String
312 */
313 formatDate: function () {
314 return _formatDate.apply(this, arguments);
315 },
316 /**
317 * @param Number year
318 * @param Number month
319 * @param Number index (0 - without navigation, 1 - prev navigation, 2 - next navigation, 3 - prev and next navigation)
320 * @param Number id
321 */
322 draw: function (year, month, index, id) {
323 var self = this,
324 autoId = typeof id === 'undefined' ? Math.floor(Math.random() * 9999999) : id,
325 firstOfMonth = new Date(year, month, 1),
326 daysInMonth = new Date(year, month + 1, 0).getDate(),
327 daysInPrevMonth = new Date(year, month, 0).getDate(),
328 startDay = firstOfMonth.getUTCDay(),
329 first = firstOfMonth.getDay(),
330 i, day, date, rows = 0, cols = self.opts.weekNumbers ? 8 : 7,
331 table = d.createElement('table'),
332 thead = d.createElement('thead'),
333 tbody = d.createElement('tbody'),
334 row, cell, text, a, b, jsdate, current, oBsd,
335 s_arr, si, slen,
336 minDate = false;
337
338 if (self.opts.minDate !== null) {
339 minDate = true;
340 }
341
342 row = d.createElement('tr');
343 // Prev month link
344 cell = d.createElement('th');
345 if (self.opts.showNavigation && (index === 1 || index === 3)) {
346 Calendar.Util.addEvent(cell, 'click', function (e) {
347 self.container.innerHTML = '';
348 for (i = 0; i < self.opts.months; i++) {
349 self.draw(year, month - self.opts.months + i, getIndex(i, self.opts.months));
350 if (i === 0) {
351 self.opts.month = month - self.opts.months;
352 self.opts.year = year;
353 }
354 }
355 });
356 cell.style.cursor = 'pointer';
357 Calendar.Util.addClass(cell, "bcal-nav");
358 text = d.createTextNode('<');
359 cell.appendChild(text);
360 } else {
361 Calendar.Util.addClass(cell, "bcal-navi");
362 }
363 row.appendChild(cell);
364
365 // Month name, Year
366 cell = d.createElement('th');
367 cell.colSpan = (cols === 7) ? 5 : 6;
368 Calendar.Util.addClass(cell, "bcal-month");
369 cell.appendChild(d.createTextNode(self.opts.monthNamesFull[firstOfMonth.getMonth()] + ' ' + firstOfMonth.getFullYear()));
370 row.appendChild(cell);
371
372 // Next month link
373 cell = d.createElement('th');
374 if (self.opts.showNavigation && (index === 2 || index === 3)) {
375 cell.style.cursor = 'pointer';
376 Calendar.Util.addClass(cell, "bcal-nav");
377 text = d.createTextNode('>');
378 Calendar.Util.addEvent(cell, 'click', function (e) {
379 self.container.innerHTML = '';
380 for (i = 0; i < self.opts.months; i++) {
381 self.draw(year, month + i + 1, getIndex(i, self.opts.months));
382 if (i === 0) {
383 self.opts.month = month + 1;
384 self.opts.year = year;
385 }
386 }
387 });
388 cell.appendChild(text);
389 } else {
390 Calendar.Util.addClass(cell, "bcal-navi");
391 }
392 row.appendChild(cell);
393 thead.appendChild(row);
394
395 row = d.createElement('tr');
396 if (self.opts.weekNumbers) {
397 cell = d.createElement('th');
398 cell.appendChild(d.createTextNode('wk'));
399 Calendar.Util.addClass(cell, "bcal-wnum");
400 row.appendChild(cell);
401 }
402
403 for (i = 0; i < 7; i++) {
404 cell = d.createElement('th');
405 text = d.createTextNode(self.opts.dayNames[(self.opts.startDay + i) % 7]);
406 Calendar.Util.addClass(cell, "bcal-wday");
407 cell.appendChild(text);
408 row.appendChild(cell);
409 }
410 thead.appendChild(row);
411 table.appendChild(thead);
412
413 day = self.opts.startDay + 1 - first;
414 while (day > 1) {
415 day -= 7;
416 }
417 while (day <= daysInMonth) {
418 jsdate = new Date(year, month, day + startDay);
419 row = d.createElement('tr');
420 if (self.opts.weekNumbers) {
421 cell = d.createElement('td');
422 Calendar.Util.addClass(cell, 'bcal-week');
423 a = new Date(jsdate.getFullYear(), jsdate.getMonth(), jsdate.getDate() - (jsdate.getDay() || 7) + 3);
424 b = new Date(a.getFullYear(), 0, 4);
425 cell.appendChild(d.createTextNode(1 + Math.round((a - b) / 864e5 / 7)));
426 row.appendChild(cell);
427 }
428
429 for (i = 0; i < 7; i++) {
430 cell = d.createElement('td');
431 if (day > 0 && day <= daysInMonth) {
432 current = new Date(year, month, day);
433 cell.setAttribute('bcal-date', current.getTime());
434 Calendar.Util.addClass(cell, 'bcal-date');
435 if (today === [current.getFullYear(), current.getMonth(), current.getDate()].join('-')) {
436 Calendar.Util.addClass(cell, 'bcal-today');
437 }
438 text = d.createTextNode(day);
439 cell.appendChild(text);
440 oBsd = self.opts.onBeforeShowDay.apply(self, [current]);
441 if (self.opts.disablePast === true && current < midnight) {
442 Calendar.Util.addClass(cell, 'bcal-past');
443 } else if (minDate && current < self.opts.minDate) {
444 Calendar.Util.addClass(cell, 'bcal-past');
445 } else if (oBsd[0] === false) {
446 Calendar.Util.addClass(cell, oBsd[1]);
447 } else {
448 self.bind.call(self, cell);
449 }
450
451 } else {
452 if (self.opts.showOtherMonths) {
453 var _day = day > 0 ? day - daysInMonth: daysInPrevMonth + day,
454 _month = day > 0 ? month + 1 : month - 1;
455 text = d.createTextNode(_day);
456 cell.appendChild(text);
457
458 current = new Date(year, _month, _day);
459 cell.setAttribute('bcal-date', current.getTime());
460
461 if (self.opts.selectOtherMonths) {
462 self.bind.call(self, cell);
463 }
464 }
465 Calendar.Util.addClass(cell, 'bcal-empty');
466 }
467 if (self.selectedDate !== null && self.selectedDate.getTime() === current.getTime() && self.opts.month === month) {
468 Calendar.Util.addClass(cell, 'bcal-selected');
469 }
470 row.appendChild(cell);
471 tbody.appendChild(row);
472 day++;
473 }
474 rows++;
475 }
476 if (rows === 5) {
477 tbody.appendChild(emptyRow(self.opts.weekNumbers));
478 } else if (rows === 4) {
479 tbody.appendChild(emptyRow(self.opts.weekNumbers));
480 tbody.appendChild(emptyRow(self.opts.weekNumbers));
481 }
482
483 Calendar.Util.addClass(table, 'bcal-table');
484 table.setAttribute('id', ['bcal-table', autoId].join('-'));
485 table.appendChild(tbody);
486
487 Calendar.Util.addEvent(table, 'click', function (e) {
488 self.focus = true;
489 });
490
491 var tbl = d.getElementById(['bcal-table', autoId].join('-'));
492 if (tbl) {
493 self.container.removeChild(tbl);
494 }
495 self.container.appendChild(table);
496 },
497 bind: function (cell) {
498 var self = this,
499 s_arr, si, slen;
500 Calendar.Util.addEvent(cell, 'click', (function (self, cell) {
501 return function () {
502 s_arr = Calendar.Util.getElementsByClass('bcal-selected', self.container, 'td');
503 for (si = 0, slen = s_arr.length; si < slen; si++) {
504 Calendar.Util.removeClass(s_arr[si], 'bcal-selected');
505 }
506 Calendar.Util.addClass(cell, 'bcal-selected');
507 var ts = parseInt(cell.getAttribute('bcal-date'), 10);
508 self.selectedDate = new Date(ts);
509 self.opts.year = self.selectedDate.getFullYear();
510 self.opts.month = self.selectedDate.getMonth();
511 if (self.opts.element && !self.opts.inline) {
512 self.close();
513 self.element.value = self.formatDate(self.opts.dateFormat, ts);
514 }
515 self.opts.onSelect.apply(self, [self.element, self.formatDate(self.opts.dateFormat, ts), ts, cell]);
516 self.refresh.call(self);
517 };
518 })(self, cell));
519 },
520 /**
521 * @return Instance of calendar
522 */
523 open: function () {
524 var self = this,
525 pos = findPos(self.element),
526 result;
527 result = self.opts.onBeforeOpen.apply(self, []);
528 if (result === false) {
529 return self;
530 }
531 switch (self.opts.position) {
532 case 'right':
533 self.container.style.top = (pos[1]) + 'px';
534 self.container.style.left = (pos[0]+self.element.offsetWidth) + 'px';
535 break;
536 case 'bottom':
537 self.container.style.top = (pos[1] + self.element.offsetHeight) + 'px';
538 self.container.style.left = pos[0] + 'px';
539 break;
540 case 'top':
541 self.container.style.display = '';
542 self.container.style.top = (pos[1] - self.container.offsetHeight) + 'px';
543 self.container.style.left = pos[0] + 'px';
544 break;
545 }
546
547 self.container.style.display = '';
548 self.opts.onOpen.apply(self, [self.element]);
549 self.isOpen = true;
550 self.focus = true;
551 return self;
552 },
553 /**
554 * @return Instance of calendar
555 */
556 close: function () {
557 var self = this,
558 result;
559 result = self.opts.onBeforeClose.apply(self, []);
560 if (result === false) {
561 return self;
562 }
563 self.container.style.display = 'none';
564 self.opts.onClose.apply(self, []);
565 self.isOpen = false;
566 self.focus = false;
567 return self;
568 },
569 detach: function () {
570 var self = this;
571 self.element.style.cursor = 'text';
572 self.container.parentNode.removeChild(self.container);
573 return self.element;
574 },
575 option: function (optName) {
576 var self = this;
577 switch (arguments.length) {
578 case 1:
579 if (is('String', optName) && self.opts[optName]) {
580 return self.opts[optName];
581 } else if (is('Object', optName)) {
582 for (var x in optName) {
583 if (optName.hasOwnProperty(x)) {
584 self.opts[x] = optName[x];
585 }
586 }
587 }
588 break;
589 case 2:
590 if (self.opts[optName]) {
591 self.opts[optName] = arguments[1];
592 }
593 break;
594 }
595 return self;
596 },
597 refresh: function () {
598 var self = this;
599 self.container.innerHTML = '';
600 var y = self.opts.year, m = self.opts.month;
601 for (i = 0; i < self.opts.months; i++) {
602 self.draw(y, m + i, getIndex(i, self.opts.months));
603 }
604 return self;
605 }
606 };
607 return (window.Calendar = Calendar);
608 })(window);
609