material-kit/js/bootstrap-datepicker.js
[bootswatch] / material-kit / js / bootstrap-datepicker.js
1 /* =========================================================
2  * bootstrap-datepicker.js 
3  * http://www.eyecon.ro/bootstrap-datepicker
4  * =========================================================
5  * Copyright 2012 Stefan Petre
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================================================= */
19  var color = '';
20 !function( $ ) {
21         
22         // Picker object
23
24         var Datepicker = function(element, options){
25                 this.element = $(element);
26                 this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
27                 this.picker = $(DPGlobal.template)
28                                                         .appendTo('body')
29                                                         .on({
30                                                                 click: $.proxy(this.click, this)//,
31                                                                 //mousedown: $.proxy(this.mousedown, this)
32                                                         });
33                 this.isInput = this.element.is('input');
34                 this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
35                 
36                 if (this.isInput) {
37                         this.element.on({
38                                 focus: $.proxy(this.show, this),
39                                 //blur: $.proxy(this.hide, this),
40                                 keyup: $.proxy(this.update, this)
41                         });
42                 } else {
43                         if (this.component){
44                                 this.component.on('click', $.proxy(this.show, this));
45                         } else {
46                                 this.element.on('click', $.proxy(this.show, this));
47                         }
48                 }
49         
50                 this.minViewMode = options.minViewMode||this.element.data('date-minviewmode')||0;
51                 if (typeof this.minViewMode === 'string') {
52                         switch (this.minViewMode) {
53                                 case 'months':
54                                         this.minViewMode = 1;
55                                         break;
56                                 case 'years':
57                                         this.minViewMode = 2;
58                                         break;
59                                 default:
60                                         this.minViewMode = 0;
61                                         break;
62                         }
63                 }
64                 this.viewMode = options.viewMode||this.element.data('date-viewmode')||0;
65                 if (typeof this.viewMode === 'string') {
66                         switch (this.viewMode) {
67                                 case 'months':
68                                         this.viewMode = 1;
69                                         break;
70                                 case 'years':
71                                         this.viewMode = 2;
72                                         break;
73                                 default:
74                                         this.viewMode = 0;
75                                         break;
76                         }
77                 }
78                 this.color = options.color||'azure';
79                 this.startViewMode = this.viewMode;
80                 this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
81                 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
82                 this.onRender = options.onRender;
83                 this.fillDow();
84                 this.fillMonths();
85                 this.update();
86                 this.showMode();
87                 
88         };
89         
90         
91         
92         Datepicker.prototype = {
93                 constructor: Datepicker,
94                 
95                 show: function(e) {
96                     var datepicker = this.picker;
97
98                         this.picker.show();
99                         this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
100                         this.place();
101                         $(window).on('resize', $.proxy(this.place, this));
102                         if (e ) {
103                                 e.stopPropagation();
104                                 e.preventDefault();
105                         }
106                         if (!this.isInput) {
107                         }
108                         var that = this;
109                         $(document).on('mousedown', function(ev){
110                                 if ($(ev.target).closest('.datepicker').length == 0) {
111                                         that.hide();
112                                 }
113                         });
114                         this.element.trigger({
115                                 type: 'show',
116                                 date: this.date
117                         });
118                         
119                         setTimeout(function(){
120                         datepicker.addClass('open');
121                         }, 170);
122                 },
123                 
124                 hide: function(){
125                         var datepicker = this.picker;
126                         datepicker.removeClass('open');
127                         
128                         setTimeout(function(){
129                         this.picker.hide();
130                         }, 500);
131                         
132                         $(window).off('resize', this.place);
133                         this.viewMode = this.startViewMode;
134                         this.showMode();
135                         if (!this.isInput) {
136                                 $(document).off('mousedown', this.hide);
137                         }
138                         //this.set();
139                         this.element.trigger({
140                                 type: 'hide',
141                                 date: this.date
142                         });
143                         
144         },
145                 
146                 set: function() {
147                         var formated = DPGlobal.formatDate(this.date, this.format);
148                         if (!this.isInput) {
149                                 if (this.component){
150                                         this.element.find('input').prop('value', formated);
151                                 }
152                                 this.element.data('date', formated);
153                         } else {
154                                 this.element.prop('value', formated);
155                         }
156                 },
157                 
158                 setValue: function(newDate) {
159                         if (typeof newDate === 'string') {
160                                 this.date = DPGlobal.parseDate(newDate, this.format);
161                         } else {
162                                 this.date = new Date(newDate);
163                         }
164                         this.set();
165                         this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
166                         this.fill();
167                 },
168                 
169                 place: function(){
170                         var offset = this.component ? this.component.offset() : this.element.offset();
171                         this.picker.css({
172                                 top: offset.top + this.height,
173                                 left: offset.left
174                         });
175                 },
176                 
177                 update: function(newDate){
178                         this.date = DPGlobal.parseDate(
179                                 typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')),
180                                 this.format
181                         );
182                         this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
183                         this.fill();
184                 },
185                 
186                 fillDow: function(){
187                         var dowCnt = this.weekStart;
188                         var html = '<tr>';
189                         while (dowCnt < this.weekStart + 7) {
190                                 html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
191                         }
192                         html += '</tr>';
193                         this.picker.find('.datepicker-days thead').append(html);
194                 },
195                 
196                 fillMonths: function(){
197                         var html = '';
198                         var i = 0
199                         while (i < 12) {
200                                 html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
201                         }
202                         this.picker.find('.datepicker-months td').append(html);
203                 },
204                 
205                 fill: function() {
206                         var d = new Date(this.viewDate),
207                                 year = d.getFullYear(),
208                                 month = d.getMonth(),
209                                 currentDate = this.date.valueOf();
210                         this.picker.find('.datepicker-days th:eq(1)')
211                                                 .text(DPGlobal.dates.months[month]+' '+year);
212                         var prevMonth = new Date(year, month-1, 28,0,0,0,0),
213                                 day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
214                         prevMonth.setDate(day);
215                         prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
216                         var nextMonth = new Date(prevMonth);
217                         nextMonth.setDate(nextMonth.getDate() + 42);
218                         nextMonth = nextMonth.valueOf();
219                         var html = [];
220                         var clsName,
221                                 prevY,
222                                 prevM;
223                         while(prevMonth.valueOf() < nextMonth) {
224                                 if (prevMonth.getDay() === this.weekStart) {
225                                         html.push('<tr>');
226                                 }
227                                 clsName = this.onRender(prevMonth);
228                                 prevY = prevMonth.getFullYear();
229                                 prevM = prevMonth.getMonth();
230                                 if ((prevM < month &&  prevY === year) ||  prevY < year) {
231                                         clsName += ' old';
232                                 } else if ((prevM > month && prevY === year) || prevY > year) {
233                                         clsName += ' new';
234                                 }
235                                 if (prevMonth.valueOf() === currentDate) {
236                                         clsName += ' active ' + this.color;
237                                 }
238                                 html.push('<td class="day '+clsName+'"><p>'+prevMonth.getDate() + '</p></td>');
239                                 if (prevMonth.getDay() === this.weekEnd) {
240                                         html.push('</tr>');
241                                 }
242                                 prevMonth.setDate(prevMonth.getDate()+1);
243                         }
244                         this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
245                         var currentYear = this.date.getFullYear();
246                         
247                         var months = this.picker.find('.datepicker-months')
248                                                 .find('th:eq(1)')
249                                                         .text(year)
250                                                         .end()
251                                                 .find('span').removeClass('active');
252                         if (currentYear === year) {
253                                 months.eq(this.date.getMonth()).addClass('active').addClass(this.color);
254                         }
255                         
256                         html = '';
257                         year = parseInt(year/10, 10) * 10;
258                         var yearCont = this.picker.find('.datepicker-years')
259                                                                 .find('th:eq(1)')
260                                                                         .text(year + '-' + (year + 9))
261                                                                         .end()
262                                                                 .find('td');
263                         year -= 1;
264                         for (var i = -1; i < 11; i++) {
265                                 html += '<span class="year'+(i === -1 || i === 10 ? ' old' : '')+(currentYear === year ? ' active ' : '')+ this.color + '">'+year+'</span>';
266                                 year += 1;
267                         }
268                         yearCont.html(html);
269                 },
270                 
271                 click: function(e) {
272                         e.stopPropagation();
273                         e.preventDefault();
274                         var target = $(e.target).closest('span, td, th');
275                         if (target.length === 1) {
276                                 switch(target[0].nodeName.toLowerCase()) {
277                                         case 'th':
278                                                 switch(target[0].className) {
279                                                         case 'switch-datepicker':
280                                                                 this.showMode(1);
281                                                                 break;
282                                                         case 'prev':
283                                                         case 'next':
284                                                                 this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
285                                                                         this.viewDate,
286                                                                         this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) + 
287                                                                         DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1)
288                                                                 );
289                                                                 this.fill();
290                                                                 this.set();
291                                                                 break;
292                                                 }
293                                                 break;
294                                         case 'span':
295                                                 if (target.is('.month')) {
296                                                         var month = target.parent().find('span').index(target);
297                                                         this.viewDate.setMonth(month);
298                                                 } else {
299                                                         var year = parseInt(target.text(), 10)||0;
300                                                         this.viewDate.setFullYear(year);
301                                                 }
302                                                 if (this.viewMode !== 0) {
303                                                         this.date = new Date(this.viewDate);
304                                                         this.element.trigger({
305                                                                 type: 'changeDate',
306                                                                 date: this.date,
307                                                                 viewMode: DPGlobal.modes[this.viewMode].clsName
308                                                         });
309                                                 }
310                                                 this.showMode(-1);
311                                                 this.fill();
312                                                 this.set();
313                                                 break;
314                                         case 'td':
315                                                 if (target.is('.day') && !target.is('.disabled')){
316                                                         var day = parseInt(target.text(), 10)||1;
317                                                         var month = this.viewDate.getMonth();
318                                                         if (target.is('.old')) {
319                                                                 month -= 1;
320                                                         } else if (target.is('.new')) {
321                                                                 month += 1;
322                                                         }
323                                                         var year = this.viewDate.getFullYear();
324                                                         this.date = new Date(year, month, day,0,0,0,0);
325                                                         this.viewDate = new Date(year, month, Math.min(28, day),0,0,0,0);
326                                                         this.fill();
327                                                         this.set();
328                                                         this.element.trigger({
329                                                                 type: 'changeDate',
330                                                                 date: this.date,
331                                                                 viewMode: DPGlobal.modes[this.viewMode].clsName
332                                                         });
333                                                 }
334                                                 break;
335                                 }
336                         }
337                 },
338                 
339                 mousedown: function(e){
340                         e.stopPropagation();
341                         e.preventDefault();
342                 },
343                 
344                 showMode: function(dir) {
345                         if (dir) {
346                                 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
347                         }
348                         this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
349                 }
350         };
351         
352         $.fn.datepicker = function ( option, val ) {
353                 return this.each(function () {
354                         var $this = $(this),
355                                 data = $this.data('datepicker'),
356                                 options = typeof option === 'object' && option;
357                         if (!data) {
358                                 $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
359                         }
360                         if (typeof option === 'string') data[option](val);
361                 });
362         };
363
364         $.fn.datepicker.defaults = {
365                 onRender: function(date) {
366                         return '';
367                 }
368         };
369         $.fn.datepicker.Constructor = Datepicker;
370         
371         var DPGlobal = {
372                 modes: [
373                         {
374                                 clsName: 'days',
375                                 navFnc: 'Month',
376                                 navStep: 1
377                         },
378                         {
379                                 clsName: 'months',
380                                 navFnc: 'FullYear',
381                                 navStep: 1
382                         },
383                         {
384                                 clsName: 'years',
385                                 navFnc: 'FullYear',
386                                 navStep: 10
387                 }],
388                 dates:{
389                         days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
390                         daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
391                         daysMin: ["S", "M", "T", "W", "T", "F", "S", "S"],
392                         months: ["JAN.", "FEB.", "MAR.", "APR.", "MAY", "JUN.", "JUL.", "AUG.", "SEPT.", "OCT.", "NOV.", "DEC."],
393                         monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
394                 },
395                 isLeapYear: function (year) {
396                         return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
397                 },
398                 getDaysInMonth: function (year, month) {
399                         return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
400                 },
401                 parseFormat: function(format){
402                         var separator = format.match(/[.\/\-\s].*?/),
403                                 parts = format.split(/\W+/);
404                         if (!separator || !parts || parts.length === 0){
405                                 throw new Error("Invalid date format.");
406                         }
407                         return {separator: separator, parts: parts};
408                 },
409                 parseDate: function(date, format) {
410                         var parts = date.split(format.separator),
411                                 date = new Date(),
412                                 val;
413                         date.setHours(0);
414                         date.setMinutes(0);
415                         date.setSeconds(0);
416                         date.setMilliseconds(0);
417                         if (parts.length === format.parts.length) {
418                                 var year = date.getFullYear(), day = date.getDate(), month = date.getMonth();
419                                 for (var i=0, cnt = format.parts.length; i < cnt; i++) {
420                                         val = parseInt(parts[i], 10)||1;
421                                         switch(format.parts[i]) {
422                                                 case 'dd':
423                                                 case 'd':
424                                                         day = val;
425                                                         date.setDate(val);
426                                                         break;
427                                                 case 'mm':
428                                                 case 'm':
429                                                         month = val - 1;
430                                                         date.setMonth(val - 1);
431                                                         break;
432                                                 case 'yy':
433                                                         year = 2000 + val;
434                                                         date.setFullYear(2000 + val);
435                                                         break;
436                                                 case 'yyyy':
437                                                         year = val;
438                                                         date.setFullYear(val);
439                                                         break;
440                                         }
441                                 }
442                                 date = new Date(year, month, day, 0 ,0 ,0);
443                         }
444                         return date;
445                 },
446                 formatDate: function(date, format){
447                         var val = {
448                                 d: date.getDate(),
449                                 m: date.getMonth() + 1,
450                                 yy: date.getFullYear().toString().substring(2),
451                                 yyyy: date.getFullYear()
452                         };
453                         val.dd = (val.d < 10 ? '0' : '') + val.d;
454                         val.mm = (val.m < 10 ? '0' : '') + val.m;
455                         var date = [];
456                         for (var i=0, cnt = format.parts.length; i < cnt; i++) {
457                                 date.push(val[format.parts[i]]);
458                         }
459                         return date.join(format.separator);
460                 },
461                 headTemplate: '<thead>'+
462                                                         '<tr>'+
463                                                                 '<th class="prev"><p>&lsaquo;</p></th>'+
464                                                                 '<th colspan="5" class="switch-datepicker"></th>'+
465                                                                 '<th class="next"><p>&rsaquo;</p></th>'+
466                                                         '</tr>'+
467                                                 '</thead>',
468                 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
469         };
470         
471         DPGlobal.template = '<div class="datepicker dropdown-menu">'+
472                                                         '<div class="datepicker-days">'+
473                                                                 '<table class=" table-condensed">'+
474                                                                         DPGlobal.headTemplate+
475                                                                         '<tbody></tbody>'+
476                                                                 '</table>'+
477                                                         '</div>'+
478                                                         '<div class="datepicker-months">'+
479                                                                 '<table class="table-condensed">'+
480                                                                         DPGlobal.headTemplate+
481                                                                         DPGlobal.contTemplate+
482                                                                 '</table>'+
483                                                         '</div>'+
484                                                         '<div class="datepicker-years">'+
485                                                                 '<table class="table-condensed">'+
486                                                                         DPGlobal.headTemplate+
487                                                                         DPGlobal.contTemplate+
488                                                                 '</table>'+
489                                                         '</div>'+
490                                                 '</div>';
491
492 }( window.jQuery );