2 datepickr - pick your date not your nose
3 Copyright (c) 2010 josh.salverda - 2012 bohwaz Apache License 2.0
4 https://code.google.com/p/datepickr/
5 http://dev.kd2.org/garradin/
8 function datepickr(targetElement
, userConfig
) {
11 fullCurrentMonth
: true,
12 dateFormat
: 'F jS, Y',
14 weekdays
: ['Sun', 'Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur'],
15 months
: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
16 suffix
: { 1: 'st', 2: 'nd', 3: 'rd', 21: 'st', 22: 'nd', 23: 'rd', 31: 'st' },
19 currentDate
= new Date(),
20 currentPosition
= new Array(0,0),
22 // shortcuts to get date info
26 return currentDate
.getFullYear();
30 return currentDate
.getMonth();
32 string: function(full
) {
33 var date
= currentDate
.getMonth();
34 return monthToStr(date
, full
);
38 return currentDate
.getDate();
43 return currentMonthView
;
45 string: function(full
) {
46 var date
= currentMonthView
;
47 return monthToStr(date
, full
);
50 // checks to see if february is a leap year otherwise return the respective # of days
51 return (get.month
.integer() == 1 && !(currentYearView
& 3) && (currentYearView
% 1e2
|| !(currentYearView
% 4e2
))) ? 29 : daysInMonth
[get.month
.integer()];
55 // variables used throughout the class
56 daysInMonth
= [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
57 element
, container
, body
, month
, prevMonth
, nextMonth
,
58 currentYearView
= get.current
.year(),
59 currentMonthView
= get.current
.month
.integer(),
60 i
, x
, buildCache
= [];
62 function build(nodeName
, attributes
, content
) {
65 if(!(nodeName
in buildCache
)) {
66 buildCache
[nodeName
] = document
.createElement(nodeName
);
69 element
= buildCache
[nodeName
].cloneNode(false);
71 if(attributes
!= null) {
72 for(var attribute
in attributes
) {
73 element
[attribute
] = attributes
[attribute
];
78 if(typeof(content
) == 'object') {
79 element
.appendChild(content
);
81 element
.innerHTML
= content
;
88 function monthToStr(date
, full
) {
89 return ((full
== true) ? config
.months
[date
] : ((config
.months
[date
].length
> 3) ? config
.months
[date
].substring(0, 3) : config
.months
[date
]));
92 function formatDate(milliseconds
) {
93 var formattedDate
= '',
94 dateObj
= new Date(milliseconds
),
98 return (day
< 10) ? '0' + day
: day
;
101 return config
.weekdays
[format
.w()].substring(0, 3);
104 return dateObj
.getDate();
107 return config
.weekdays
[format
.w()] + 'day';
110 return config
.suffix
[format
.j()] || config
.defaultSuffix
;
113 return dateObj
.getDay();
116 return monthToStr(format
.n(), true);
119 var month
= format
.n() + 1;
120 return (month
< 10) ? '0' + month
: month
;
123 return monthToStr(format
.n(), false);
126 return dateObj
.getMonth();
129 return dateObj
.getFullYear();
132 return format
.Y().substring(2, 4);
135 formatPieces
= config
.dateFormat
.split('');
137 for(i
= 0, x
= formatPieces
.length
; i
< x
; i
++) {
138 formattedDate
+= format
[formatPieces
[i
]] ? format
[formatPieces
[i
]]() : formatPieces
[i
];
141 return formattedDate
;
144 function handleMonthClick() {
145 // if we go too far into the past
146 if(currentMonthView
< 0) {
149 // start our month count at 11 (11 = december)
150 currentMonthView
= 11;
153 // if we go too far into the future
154 if(currentMonthView
> 11) {
157 // restart our month count (0 = january)
158 currentMonthView
= 0;
161 month
.innerHTML
= get.month
.string(config
.fullCurrentMonth
) + ' ' + currentYearView
;
163 // rebuild the calendar
164 while(body
.hasChildNodes()){
165 body
.removeChild(body
.lastChild
);
167 body
.appendChild(buildCalendar());
173 function bindMonthLinks() {
174 prevMonth
.onclick = function() {
176 return handleMonthClick();
179 nextMonth
.onclick = function() {
181 return handleMonthClick();
185 // our link binding function
186 function bindDayLinks() {
187 var days
= body
.getElementsByTagName('a');
189 for(i
= 0, x
= days
.length
; i
< x
; i
++) {
190 days
[i
].onclick = function() {
191 currentDate
= new Date(currentYearView
, currentMonthView
, this.innerHTML
);
192 element
.value
= formatDate(currentDate
.getTime());
193 element
.onchange(element
);
200 function buildWeekdays() {
201 var html
= document
.createDocumentFragment();
202 // write out the names of each week day
203 for(i
= 0, x
= config
.weekdays
.length
; i
< x
; i
++) {
204 html
.appendChild(build('th', {}, config
.weekdays
[i
].substring(0, 2)));
209 function buildCalendar() {
210 // get the first day of the month we are currently viewing
211 var firstOfMonth
= new Date(currentYearView
, currentMonthView
, config
.firstDayOfWeek
).getDay(),
212 // get the total number of days in the month we are currently viewing
213 numDays
= get.month
.numDays(),
214 // declare our day counter
217 html
= document
.createDocumentFragment(),
220 // print out previous month's "days"
221 for(i
= 1; i
<= firstOfMonth
; i
++) {
222 row
.appendChild(build('td', {}, ''));
226 for(i
= 1; i
<= numDays
; i
++) {
227 // if we have reached the end of a week, wrap to the next line
229 html
.appendChild(row
);
235 // output the text that goes inside each td
236 // if the day is the current day, add a class of "today"
237 var today
= (i
== get.current
.day() && currentMonthView
== get.current
.month
.integer() && currentYearView
== get.current
.year());
240 currentPosition
= [weekCount
+1, dayCount
];
242 row
.appendChild(build('td', { className
: today
? 'today' : '' }, build('a', { href
: 'javascript:void(0)' }, i
)));
246 // if we haven't finished at the end of the week, start writing out the "days" for the next month
247 for(i
= 1; i
<= (7 - dayCount
); i
++) {
248 row
.appendChild(build('td', {}, ''));
251 html
.appendChild(row
);
253 currentMaxRows
= weekCount
+1;
259 document
.onmousedown = function(e
) {
260 e
= e
|| window
.event
;
261 var target
= e
.target
|| e
.srcElement
;
263 var parentNode
= target
.parentNode
;
264 if(target
!= element
&& parentNode
!= container
) {
265 while(parentNode
!= container
) {
266 parentNode
= parentNode
.parentNode
;
267 if(parentNode
== null) {
274 if (target
== element
)
282 document
.onkeyup = function(e
) {
283 var k
= e
.keyCode
|| e
.which
;
293 document
.onkeypress = function(e
) {
294 var k
= e
.keyCode
|| e
.which
;
300 return handleMonthClick();
302 else if (k
== 34) // PgDn
306 return handleMonthClick();
308 else if (k
>= 37 && k
<= 40) // Arrows
311 var pos
= currentPosition
.slice();
312 if (k
== 37) { // left
313 if (pos
[1] == 0) return;
316 else if (k
== 38) { // up
317 if (pos
[0] <= 1) return;
320 else if (k
== 39) { // right
321 if (pos
[1] == 6) return;
325 if (pos
[0] == currentMaxRows
) return;
329 var table
= container
.getElementsByTagName('table')[0];
330 var row
= table
.getElementsByTagName('td')[pos
[0]*7+pos
[1]-7];
332 if (row
.innerHTML
== "") return;
334 table
.getElementsByTagName('td')[currentPosition
[0]*7+currentPosition
[1]-7].className
= '';
335 row
.className
= 'today';
337 currentPosition
= pos
;
338 currentDate
= new Date(currentYearView
, currentMonthView
, row
.firstChild
.innerHTML
);
340 else if (k
== 13 || k
== 32)
342 element
.value
= formatDate(currentDate
.getTime());
343 element
.onchange(element
);
351 container
.style
.display
= 'block';
355 document
.onmousedown
= null;
356 document
.onkeypress
= null;
357 container
.style
.display
= 'none';
360 function initialise(userConfig
) {
362 for(var key
in userConfig
) {
363 if(config
.hasOwnProperty(key
)) {
364 config
[key
] = userConfig
[key
];
371 var d
= element
.value
.split('/').reverse();
372 currentDate
= new Date(parseInt(d
[0], 10), parseInt(d
[1], 10) - 1, parseInt(d
[2], 10), 0, 0, 0, 0);
373 currentYearView
= get.current
.year();
374 currentMonthView
= get.current
.month
.integer();
376 container
= build('div', { className
: 'calendar' });
377 container
.style
.cssText
= 'display: none; position: absolute; z-index: 9999;';
379 var months
= build('div', { className
: 'months' });
380 prevMonth
= build('span', { className
: 'prev-month' }, build('a', { href
: '#' }, '<'));
381 nextMonth
= build('span', { className
: 'next-month' }, build('a', { href
: '#' }, '>'));
382 month
= build('span', { className
: 'current-month' }, get.month
.string(config
.fullCurrentMonth
) + ' ' + currentYearView
);
384 months
.appendChild(prevMonth
);
385 months
.appendChild(nextMonth
);
386 months
.appendChild(month
);
388 var calendar
= build('table', {}, build('thead', {}, build('tr', { className
: 'weekdays' }, buildWeekdays())));
389 body
= build('tbody', {}, buildCalendar());
391 calendar
.appendChild(body
);
393 container
.appendChild(months
);
394 container
.appendChild(calendar
);
396 element
.parentNode
.style
.position
= 'relative';
397 element
.parentNode
.appendChild(container
);
401 element
.onfocus
= open
;
402 element
.onblur
= close
;
406 element
= typeof(targetElement
) == 'string' ? document
.getElementById(targetElement
) : targetElement
;
407 initialise(userConfig
);
411 // Add-on for HTML5 input type="date" fallback
415 fullCurrentMonth
: true,
418 weekdays
: ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'],
419 months
: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
424 function dateInputFallback()
426 var inputs
= document
.getElementsByTagName('input');
427 var length
= inputs
.length
;
430 for (i
= 0; i
< inputs
.length
; i
++)
432 if (inputs
[i
].getAttribute('type') == 'date')
434 var new_input
= inputs
[i
].cloneNode(true);
435 inputs
[i
].type
= 'hidden';
436 inputs
[i
].removeAttribute('pattern');
437 inputs
[i
].removeAttribute('id');
438 inputs
[i
].removeAttribute('required');
440 new_input
.removeAttribute('name');
441 new_input
.setAttribute('type', 'text');
442 new_input
.className
+= ' date';
444 new_input
.maxlength
= 10;
445 new_input
.value
= inputs
[i
].value
.split('-').reverse().join('/');
446 new_input
.setAttribute('pattern', '([012][0-9]|3[01])/(0[0-9]|1[0-2])/[12][0-9]{3}');
448 new_input
.onchange = function ()
450 if (this.value
.match(/\d{2}\/\d{2}\/\d{4}/))
451 this.nextSibling
.value
= this.value
.split('/').reverse().join('-');
453 this.nextSibling
.value
= this.value
;
456 inputs
[i
].parentNode
.insertBefore(new_input
, inputs
[i
]);
457 new datepickr(new_input
, config_fr
);