PluginProbe
ShiftController Employee Shift Scheduling / 4.9.95
ShiftController Employee Shift Scheduling v4.9.95
4.9.97 4.9.96 4.9.95 4.9.74 4.9.75 4.9.76 4.9.77 4.9.78 4.9.84 4.9.85 4.9.87 4.9.91 4.9.92 trunk 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 3.2.4 All 38 releases
shiftcontroller / hc3 / ui / element / input / datepicker / assets / input.js

input.js in ShiftController Employee Shift Scheduling 4.9.95, at hc3/ui/element/input/datepicker/assets/input.js

1,710 lines 47.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* =========================================================
2 * bootstrap-datepicker.js
3 * Repo: https://github.com/eternicode/bootstrap-datepicker/
4 * Demo: http://eternicode.github.io/bootstrap-datepicker/
5 * Docs: http://bootstrap-datepicker.readthedocs.org/
6 * Forked from http://www.eyecon.ro/bootstrap-datepicker
7 * =========================================================
8 * Started by Stefan Petre; improvements by Andrew Rowls + contributors
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 * ========================================================= */
22
23 (function($, undefined){
24
25 var $window = $(window);
26
27 function UTCDate(){
28 return new Date(Date.UTC.apply(Date, arguments));
29 }
30 function UTCToday(){
31 var today = new Date();
32 return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
33 }
34 function alias(method){
35 return function(){
36 return this[method].apply(this, arguments);
37 };
38 }
39
40 var DateArray = (function(){
41 var extras = {
42 get: function(i){
43 return this.slice(i)[0];
44 },
45 contains: function(d){
46 // Array.indexOf is not cross-browser;
47 // $.inArray doesn't work with Dates
48 var val = d && d.valueOf();
49 for (var i=0, l=this.length; i < l; i++)
50 if (this[i].valueOf() === val)
51 return i;
52 return -1;
53 },
54 remove: function(i){
55 this.splice(i,1);
56 },
57 replace: function(new_array){
58 if (!new_array)
59 return;
60 if (!$.isArray(new_array))
61 new_array = [new_array];
62 this.clear();
63 this.push.apply(this, new_array);
64 },
65 clear: function(){
66 this.length = 0;
67 },
68 copy: function(){
69 var a = new DateArray();
70 a.replace(this);
71 return a;
72 }
73 };
74
75 return function(){
76 var a = [];
77 a.push.apply(a, arguments);
78 $.extend(a, extras);
79 return a;
80 };
81 })();
82
83
84 // Picker object
85
86 var hc_Datepicker2 = function(element, options){
87 this.dates = new DateArray();
88 this.viewDate = UTCToday();
89 this.focusDate = null;
90
91 this._process_options(options);
92
93 this.element = $(element);
94 this.isInline = false;
95 this.isInput = this.element.is('input');
96 this.component = this.element.is('.date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
97 this.hasInput = this.component && this.element.find('input').length;
98 if (this.component && this.component.length === 0)
99 this.component = false;
100
101 this.picker = $(DPGlobal.template);
102 this._buildEvents();
103 this._attachEvents();
104
105 if (this.isInline){
106 this.picker.addClass('hc-datepicker2-inline').appendTo(this.element);
107 }
108 else {
109 this.picker.addClass('hc-datepicker2-dropdown hc-dropdown-menu');
110 }
111
112 if (this.o.rtl){
113 this.picker.addClass('hc-datepicker2-rtl');
114 }
115
116 this.viewMode = this.o.startView;
117
118 if (this.o.calendarWeeks)
119 this.picker.find('tfoot th.today')
120 .attr('colspan', function(i, val){
121 return parseInt(val) + 1;
122 });
123
124 this._allow_update = false;
125
126 this.setStartDate(this._o.startDate);
127 this.setEndDate(this._o.endDate);
128 this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
129
130 this.fillDow();
131 this.fillMonths();
132
133 this._allow_update = true;
134
135 this.update();
136 this.showMode();
137
138 if (this.isInline){
139 this.show();
140 }
141 };
142
143 hc_Datepicker2.prototype = {
144 constructor: hc_Datepicker2,
145
146 _process_options: function(opts){
147 // Store raw options for reference
148 this._o = $.extend({}, this._o, opts);
149 // Processed options
150 var o = this.o = $.extend({}, this._o);
151
152 // Check if "de-DE" style date is available, if not language should
153 // fallback to 2 letter code eg "de"
154 var lang = o.language;
155 if (!dates[lang]){
156 lang = lang.split('-')[0];
157 if (!dates[lang])
158 lang = defaults.language;
159 }
160 o.language = lang;
161
162 switch (o.startView){
163 case 2:
164 case 'decade':
165 o.startView = 2;
166 break;
167 case 1:
168 case 'year':
169 o.startView = 1;
170 break;
171 default:
172 o.startView = 0;
173 }
174
175 switch (o.minViewMode){
176 case 1:
177 case 'months':
178 o.minViewMode = 1;
179 break;
180 case 2:
181 case 'years':
182 o.minViewMode = 2;
183 break;
184 default:
185 o.minViewMode = 0;
186 }
187
188 o.startView = Math.max(o.startView, o.minViewMode);
189
190 // true, false, or Number > 0
191 if (o.multidate !== true){
192 o.multidate = Number(o.multidate) || false;
193 if (o.multidate !== false)
194 o.multidate = Math.max(0, o.multidate);
195 else
196 o.multidate = 1;
197 }
198 o.multidateSeparator = String(o.multidateSeparator);
199
200 o.weekStart %= 7;
201 o.weekEnd = ((o.weekStart + 6) % 7);
202
203 var format = DPGlobal.parseFormat(o.format);
204 if (o.startDate !== -Infinity){
205 if (!!o.startDate){
206 if (o.startDate instanceof Date)
207 o.startDate = this._local_to_utc(this._zero_time(o.startDate));
208 else
209 o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
210 }
211 else {
212 o.startDate = -Infinity;
213 }
214 }
215 if (o.endDate !== Infinity){
216 if (!!o.endDate){
217 if (o.endDate instanceof Date)
218 o.endDate = this._local_to_utc(this._zero_time(o.endDate));
219 else
220 o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
221 }
222 else {
223 o.endDate = Infinity;
224 }
225 }
226
227 o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
228 if (!$.isArray(o.daysOfWeekDisabled))
229 o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
230 o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){
231 return parseInt(d, 10);
232 });
233
234 var plc = String(o.orientation).toLowerCase().split(/\s+/g),
235 _plc = o.orientation.toLowerCase();
236 plc = $.grep(plc, function(word){
237 return (/^auto|left|right|top|bottom$/).test(word);
238 });
239 o.orientation = {x: 'auto', y: 'auto'};
240 if (!_plc || _plc === 'auto')
241 ; // no action
242 else if (plc.length === 1){
243 switch (plc[0]){
244 case 'top':
245 case 'bottom':
246 o.orientation.y = plc[0];
247 break;
248 case 'left':
249 case 'right':
250 o.orientation.x = plc[0];
251 break;
252 }
253 }
254 else {
255 _plc = $.grep(plc, function(word){
256 return (/^left|right$/).test(word);
257 });
258 o.orientation.x = _plc[0] || 'auto';
259
260 _plc = $.grep(plc, function(word){
261 return (/^top|bottom$/).test(word);
262 });
263 o.orientation.y = _plc[0] || 'auto';
264 }
265 },
266 _events: [],
267 _secondaryEvents: [],
268 _applyEvents: function(evs){
269 for (var i=0, el, ch, ev; i < evs.length; i++){
270 el = evs[i][0];
271 if (evs[i].length === 2){
272 ch = undefined;
273 ev = evs[i][1];
274 }
275 else if (evs[i].length === 3){
276 ch = evs[i][1];
277 ev = evs[i][2];
278 }
279 el.on(ev, ch);
280 }
281 },
282 _unapplyEvents: function(evs){
283 for (var i=0, el, ev, ch; i < evs.length; i++){
284 el = evs[i][0];
285 if (evs[i].length === 2){
286 ch = undefined;
287 ev = evs[i][1];
288 }
289 else if (evs[i].length === 3){
290 ch = evs[i][1];
291 ev = evs[i][2];
292 }
293 el.off(ev, ch);
294 }
295 },
296 _buildEvents: function(){
297 if (this.isInput){ // single input
298 this._events = [
299 [this.element, {
300 focus: $.proxy(this.show, this),
301 keyup: $.proxy(function(e){
302 if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
303 this.update();
304 }, this),
305 keydown: $.proxy(this.keydown, this)
306 }]
307 ];
308 }
309 else if (this.component && this.hasInput){ // component: input + button
310 this._events = [
311 // For components that are not readonly, allow keyboard nav
312 [this.element.find('input'), {
313 focus: $.proxy(this.show, this),
314 keyup: $.proxy(function(e){
315 if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
316 this.update();
317 }, this),
318 keydown: $.proxy(this.keydown, this)
319 }],
320 [this.component, {
321 click: $.proxy(this.show, this)
322 }]
323 ];
324 }
325 else if (this.element.is('div')){ // inline datepicker
326 this.isInline = true;
327 }
328 else {
329 this._events = [
330 [this.element, {
331 click: $.proxy(this.show, this)
332 }]
333 ];
334 }
335 this._events.push(
336 // Component: listen for blur on element descendants
337 [this.element, '*', {
338 blur: $.proxy(function(e){
339 this._focused_from = e.target;
340 }, this)
341 }],
342 // Input: listen for blur on element
343 [this.element, {
344 blur: $.proxy(function(e){
345 this._focused_from = e.target;
346 }, this)
347 }]
348 );
349
350 this._secondaryEvents = [
351 [this.picker, {
352 click: $.proxy(this.click, this)
353 }],
354 [$(window), {
355 resize: $.proxy(this.place, this)
356 }],
357 [$(document), {
358 'mousedown touchstart': $.proxy(function(e){
359 // Clicked outside the datepicker, hide it
360 if (!(
361 this.element.is(e.target) ||
362 this.element.find(e.target).length ||
363 this.picker.is(e.target) ||
364 this.picker.find(e.target).length
365 )){
366 this.hide();
367 }
368 }, this)
369 }]
370 ];
371 },
372 _attachEvents: function(){
373 this._detachEvents();
374 this._applyEvents(this._events);
375 },
376 _detachEvents: function(){
377 this._unapplyEvents(this._events);
378 },
379 _attachSecondaryEvents: function(){
380 this._detachSecondaryEvents();
381 this._applyEvents(this._secondaryEvents);
382 },
383 _detachSecondaryEvents: function(){
384 this._unapplyEvents(this._secondaryEvents);
385 },
386 _trigger: function(event, altdate){
387 var date = altdate || this.dates.get(-1),
388 local_date = this._utc_to_local(date);
389
390 this.element.trigger({
391 type: event,
392 date: local_date,
393 dates: $.map(this.dates, this._utc_to_local),
394 format: $.proxy(function(ix, format){
395 if (arguments.length === 0){
396 ix = this.dates.length - 1;
397 format = this.o.format;
398 }
399 else if (typeof ix === 'string'){
400 format = ix;
401 ix = this.dates.length - 1;
402 }
403 format = format || this.o.format;
404 var date = this.dates.get(ix);
405 return DPGlobal.formatDate(date, format, this.o.language);
406 }, this)
407 });
408 },
409
410 show: function(){
411 if (!this.isInline)
412 this.picker.appendTo('body');
413 this.picker.show();
414 this.place();
415 this._attachSecondaryEvents();
416 this._trigger('show');
417 },
418
419 hide: function(){
420 if (this.isInline)
421 return;
422 if (!this.picker.is(':visible'))
423 return;
424 this.focusDate = null;
425 this.picker.hide().detach();
426 this._detachSecondaryEvents();
427 this.viewMode = this.o.startView;
428 this.showMode();
429
430 if (
431 this.o.forceParse &&
432 (
433 this.isInput && this.element.val() ||
434 this.hasInput && this.element.find('input').val()
435 )
436 )
437 this.setValue();
438 this._trigger('hide');
439 },
440
441 remove: function(){
442 this.hide();
443 this._detachEvents();
444 this._detachSecondaryEvents();
445 this.picker.remove();
446 delete this.element.data().hc_datepicker2;
447 if (!this.isInput){
448 delete this.element.data().date;
449 }
450 },
451
452 _utc_to_local: function(utc){
453 return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000));
454 },
455 _local_to_utc: function(local){
456 return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000));
457 },
458 _zero_time: function(local){
459 return local && new Date(local.getFullYear(), local.getMonth(), local.getDate());
460 },
461 _zero_utc_time: function(utc){
462 return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate()));
463 },
464
465 getDates: function(){
466 return $.map(this.dates, this._utc_to_local);
467 },
468
469 getUTCDates: function(){
470 return $.map(this.dates, function(d){
471 return new Date(d);
472 });
473 },
474
475 getDate: function(){
476 return this._utc_to_local(this.getUTCDate());
477 },
478
479 getUTCDate: function(){
480 return new Date(this.dates.get(-1));
481 },
482
483 setDates: function(){
484 var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
485 this.update.apply(this, args);
486 this._trigger('changeDate');
487 this.setValue();
488 },
489
490 setUTCDates: function(){
491 var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
492 this.update.apply(this, $.map(args, this._utc_to_local));
493 this._trigger('changeDate');
494 this.setValue();
495 },
496
497 setDate: alias('setDates'),
498 setUTCDate: alias('setUTCDates'),
499
500 setValue: function(){
501 var formatted = this.getFormattedDate();
502 if (!this.isInput){
503 if (this.component){
504 this.element.find('input').val(formatted).change();
505 }
506 }
507 else {
508 this.element.val(formatted).change();
509 }
510 },
511
512 getFormattedDate: function(format){
513 if (format === undefined)
514 format = this.o.format;
515
516 var lang = this.o.language;
517 return $.map(this.dates, function(d){
518 return DPGlobal.formatDate(d, format, lang);
519 }).join(this.o.multidateSeparator);
520 },
521
522 setStartDate: function(startDate){
523 this._process_options({startDate: startDate});
524 this.update();
525 this.updateNavArrows();
526 },
527
528 setEndDate: function(endDate){
529 this._process_options({endDate: endDate});
530 this.update();
531 this.updateNavArrows();
532 },
533
534 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
535 this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
536 this.update();
537 this.updateNavArrows();
538 },
539
540 place: function(){
541 if (this.isInline)
542 return;
543 var calendarWidth = this.picker.outerWidth(),
544 calendarHeight = this.picker.outerHeight(),
545 visualPadding = 10,
546 windowWidth = $window.width(),
547 windowHeight = $window.height(),
548 scrollTop = $window.scrollTop();
549
550 var zIndex = parseInt(this.element.parents().filter(function(){
551 return $(this).css('z-index') !== 'auto';
552 }).first().css('z-index'))+10;
553 var offset = this.component ? this.component.parent().offset() : this.element.offset();
554 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
555 var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
556 var left = offset.left,
557 top = offset.top;
558
559 this.picker.removeClass(
560 'hc-datepicker2-orient-top hc-datepicker2-orient-bottom '+
561 'hc-datepicker2-orient-right hc-datepicker2-orient-left'
562 );
563
564 if (this.o.orientation.x !== 'auto'){
565 this.picker.addClass('hc-datepicker2-orient-' + this.o.orientation.x);
566 if (this.o.orientation.x === 'right')
567 left -= calendarWidth - width;
568 }
569 // auto x orientation is best-placement: if it crosses a window
570 // edge, fudge it sideways
571 else {
572 // Default to left
573 this.picker.addClass('hc-datepicker2-orient-left');
574 if (offset.left < 0)
575 left -= offset.left - visualPadding;
576 else if (offset.left + calendarWidth > windowWidth)
577 left = windowWidth - calendarWidth - visualPadding;
578 }
579
580 // auto y orientation is best-situation: top or bottom, no fudging,
581 // decision based on which shows more of the calendar
582 var yorient = this.o.orientation.y,
583 top_overflow, bottom_overflow;
584 if (yorient === 'auto'){
585 top_overflow = -scrollTop + offset.top - calendarHeight;
586 bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
587 if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
588 yorient = 'top';
589 else
590 yorient = 'bottom';
591 }
592 this.picker.addClass('hc-datepicker2-orient-' + yorient);
593 if (yorient === 'top')
594 top += height;
595 else
596 top -= calendarHeight + parseInt(this.picker.css('padding-top'));
597
598 this.picker.css({
599 top: top,
600 left: left,
601 zIndex: zIndex
602 });
603 },
604
605 _allow_update: true,
606 update: function(){
607 if (!this._allow_update)
608 return;
609
610 var oldDates = this.dates.copy(),
611 dates = [],
612 fromArgs = false;
613 if (arguments.length){
614 $.each(arguments, $.proxy(function(i, date){
615 if (date instanceof Date)
616 date = this._local_to_utc(date);
617 dates.push(date);
618 }, this));
619 fromArgs = true;
620 }
621 else {
622 dates = this.isInput
623 ? this.element.val()
624 : this.element.data('date') || this.element.find('input').val();
625 if (dates && this.o.multidate)
626 dates = dates.split(this.o.multidateSeparator);
627 else
628 dates = [dates];
629 delete this.element.data().date;
630 }
631
632 dates = $.map(dates, $.proxy(function(date){
633 return DPGlobal.parseDate(date, this.o.format, this.o.language);
634 }, this));
635 dates = $.grep(dates, $.proxy(function(date){
636 return (
637 date < this.o.startDate ||
638 date > this.o.endDate ||
639 !date
640 );
641 }, this), true);
642 this.dates.replace(dates);
643
644 if (this.dates.length)
645 this.viewDate = new Date(this.dates.get(-1));
646 else if (this.viewDate < this.o.startDate)
647 this.viewDate = new Date(this.o.startDate);
648 else if (this.viewDate > this.o.endDate)
649 this.viewDate = new Date(this.o.endDate);
650
651 if (fromArgs){
652 // setting date by clicking
653 this.setValue();
654 }
655 else if (dates.length){
656 // setting date by typing
657 if (String(oldDates) !== String(this.dates))
658 this._trigger('changeDate');
659 }
660 if (!this.dates.length && oldDates.length)
661 this._trigger('clearDate');
662
663 this.fill();
664 },
665
666 fillDow: function(){
667 var dowCnt = this.o.weekStart,
668 html = '<tr>';
669 if (this.o.calendarWeeks){
670 var cell = '<th class="cw">&nbsp;</th>';
671 html += cell;
672 this.picker.find('.hc-datepicker2-days thead tr:first-child').prepend(cell);
673 }
674 while (dowCnt < this.o.weekStart + 7){
675 html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
676 }
677 html += '</tr>';
678 this.picker.find('.hc-datepicker2-days thead').append(html);
679 },
680
681 fillMonths: function(){
682 var html = '',
683 i = 0;
684 while (i < 12){
685 html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
686 }
687 this.picker.find('.hc-datepicker2-months td').html(html);
688 },
689
690 setRange: function(range){
691 if (!range || !range.length)
692 delete this.range;
693 else
694 this.range = $.map(range, function(d){
695 return d.valueOf();
696 });
697 this.fill();
698 },
699
700 getClassNames: function(date){
701 var cls = [],
702 year = this.viewDate.getUTCFullYear(),
703 month = this.viewDate.getUTCMonth(),
704 today = new Date();
705 if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
706 cls.push('old');
707 }
708 else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
709 cls.push('new');
710 }
711 if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
712 cls.push('focused');
713 // Compare internal UTC date with local today, not UTC today
714 if (this.o.todayHighlight &&
715 date.getUTCFullYear() === today.getFullYear() &&
716 date.getUTCMonth() === today.getMonth() &&
717 date.getUTCDate() === today.getDate()){
718 cls.push('today');
719 }
720 if (this.dates.contains(date) !== -1)
721 cls.push('active');
722 if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
723 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
724 cls.push('disabled');
725 }
726 if (this.range){
727 if (date > this.range[0] && date < this.range[this.range.length-1]){
728 cls.push('range');
729 }
730 if ($.inArray(date.valueOf(), this.range) !== -1){
731 cls.push('selected');
732 }
733 }
734 return cls;
735 },
736
737 fill: function(){
738 var d = new Date(this.viewDate),
739 year = d.getUTCFullYear(),
740 month = d.getUTCMonth(),
741 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
742 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
743 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
744 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
745 todaytxt = dates[this.o.language].today || dates['en'].today || '',
746 cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
747 tooltip;
748 this.picker.find('.hc-datepicker2-days thead th.hc-datepicker2-switch')
749 .text(dates[this.o.language].months[month]+' '+year);
750 this.picker.find('tfoot th.today')
751 .text(todaytxt)
752 .toggle(this.o.todayBtn !== false);
753 this.picker.find('tfoot th.clear')
754 .text(cleartxt)
755 .toggle(this.o.clearBtn !== false);
756 this.updateNavArrows();
757 this.fillMonths();
758 var prevMonth = UTCDate(year, month-1, 28),
759 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
760 prevMonth.setUTCDate(day);
761 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
762 var nextMonth = new Date(prevMonth);
763 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
764 nextMonth = nextMonth.valueOf();
765 var html = [];
766 var clsName;
767 while (prevMonth.valueOf() < nextMonth){
768 if (prevMonth.getUTCDay() === this.o.weekStart){
769 html.push('<tr>');
770 if (this.o.calendarWeeks){
771 // ISO 8601: First week contains first thursday.
772 // ISO also states week starts on Monday, but we can be more abstract here.
773 var
774 // Start of current week: based on weekstart/current date
775 ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
776 // Thursday of this week
777 th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
778 // First Thursday of year, year from thursday
779 yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
780 // Calendar week: ms between thursdays, div ms per day, div 7 days
781 calWeek = (th - yth) / 864e5 / 7 + 1;
782 html.push('<td class="cw">'+ calWeek +'</td>');
783
784 }
785 }
786 clsName = this.getClassNames(prevMonth);
787 clsName.push('day');
788
789 if (this.o.beforeShowDay !== $.noop){
790 var before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
791 if (before === undefined)
792 before = {};
793 else if (typeof(before) === 'boolean')
794 before = {enabled: before};
795 else if (typeof(before) === 'string')
796 before = {classes: before};
797 if (before.enabled === false)
798 clsName.push('disabled');
799 if (before.classes)
800 clsName = clsName.concat(before.classes.split(/\s+/));
801 if (before.tooltip)
802 tooltip = before.tooltip;
803 }
804
805 clsName = $.unique(clsName);
806 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
807 if (prevMonth.getUTCDay() === this.o.weekEnd){
808 html.push('</tr>');
809 }
810 prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
811 }
812 this.picker.find('.hc-datepicker2-days tbody').empty().append(html.join(''));
813
814 var months = this.picker.find('.hc-datepicker2-months')
815 .find('th:eq(1)')
816 .text(year)
817 .end()
818 .find('span').removeClass('active');
819
820 $.each(this.dates, function(i, d){
821 if (d.getUTCFullYear() === year)
822 months.eq(d.getUTCMonth()).addClass('active');
823 });
824
825 if (year < startYear || year > endYear){
826 months.addClass('disabled');
827 }
828 if (year === startYear){
829 months.slice(0, startMonth).addClass('disabled');
830 }
831 if (year === endYear){
832 months.slice(endMonth+1).addClass('disabled');
833 }
834
835 html = '';
836 year = parseInt(year/10, 10) * 10;
837 var yearCont = this.picker.find('.hc-datepicker2-years')
838 .find('th:eq(1)')
839 .text(year + '-' + (year + 9))
840 .end()
841 .find('td');
842 year -= 1;
843 var years = $.map(this.dates, function(d){
844 return d.getUTCFullYear();
845 }),
846 classes;
847 for (var i = -1; i < 11; i++){
848 classes = ['year'];
849 if (i === -1)
850 classes.push('old');
851 else if (i === 10)
852 classes.push('new');
853 if ($.inArray(year, years) !== -1)
854 classes.push('active');
855 if (year < startYear || year > endYear)
856 classes.push('disabled');
857 html += '<span class="' + classes.join(' ') + '">'+year+'</span>';
858 year += 1;
859 }
860 yearCont.html(html);
861 },
862
863 updateNavArrows: function(){
864 if (!this._allow_update)
865 return;
866
867 var d = new Date(this.viewDate),
868 year = d.getUTCFullYear(),
869 month = d.getUTCMonth();
870 switch (this.viewMode){
871 case 0:
872 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){
873 this.picker.find('.prev').css({visibility: 'hidden'});
874 }
875 else {
876 this.picker.find('.prev').css({visibility: 'visible'});
877 }
878 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){
879 this.picker.find('.next').css({visibility: 'hidden'});
880 }
881 else {
882 this.picker.find('.next').css({visibility: 'visible'});
883 }
884 break;
885 case 1:
886 case 2:
887 if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){
888 this.picker.find('.prev').css({visibility: 'hidden'});
889 }
890 else {
891 this.picker.find('.prev').css({visibility: 'visible'});
892 }
893 if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){
894 this.picker.find('.next').css({visibility: 'hidden'});
895 }
896 else {
897 this.picker.find('.next').css({visibility: 'visible'});
898 }
899 break;
900 }
901 },
902
903 click: function(e){
904 e.preventDefault();
905 var target = $(e.target).closest('span, td, th'),
906 year, month, day;
907 if (target.length === 1){
908 switch (target[0].nodeName.toLowerCase()){
909 case 'th':
910 switch (target[0].className){
911 case 'hc-datepicker2-switch':
912 this.showMode(1);
913 break;
914 case 'prev':
915 case 'next':
916 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1);
917 switch (this.viewMode){
918 case 0:
919 this.viewDate = this.moveMonth(this.viewDate, dir);
920 this._trigger('changeMonth', this.viewDate);
921 break;
922 case 1:
923 case 2:
924 this.viewDate = this.moveYear(this.viewDate, dir);
925 if (this.viewMode === 1)
926 this._trigger('changeYear', this.viewDate);
927 break;
928 }
929 this.fill();
930 break;
931 case 'today':
932 var date = new Date();
933 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
934
935 this.showMode(-2);
936 var which = this.o.todayBtn === 'linked' ? null : 'view';
937 this._setDate(date, which);
938 break;
939 case 'clear':
940 var element;
941 if (this.isInput)
942 element = this.element;
943 else if (this.component)
944 element = this.element.find('input');
945 if (element)
946 element.val("").change();
947 this.update();
948 this._trigger('changeDate');
949 if (this.o.autoclose)
950 this.hide();
951 break;
952 }
953 break;
954 case 'span':
955 if (!target.is('.disabled')){
956 this.viewDate.setUTCDate(1);
957 if (target.is('.month')){
958 day = 1;
959 month = target.parent().find('span').index(target);
960 year = this.viewDate.getUTCFullYear();
961 this.viewDate.setUTCMonth(month);
962 this._trigger('changeMonth', this.viewDate);
963 if (this.o.minViewMode === 1){
964 this._setDate(UTCDate(year, month, day));
965 }
966 }
967 else {
968 day = 1;
969 month = 0;
970 year = parseInt(target.text(), 10)||0;
971 this.viewDate.setUTCFullYear(year);
972 this._trigger('changeYear', this.viewDate);
973 if (this.o.minViewMode === 2){
974 this._setDate(UTCDate(year, month, day));
975 }
976 }
977 this.showMode(-1);
978 this.fill();
979 }
980 break;
981 case 'td':
982 if (target.is('.day') && !target.is('.disabled')){
983 day = parseInt(target.text(), 10)||1;
984 year = this.viewDate.getUTCFullYear();
985 month = this.viewDate.getUTCMonth();
986 if (target.is('.old')){
987 if (month === 0){
988 month = 11;
989 year -= 1;
990 }
991 else {
992 month -= 1;
993 }
994 }
995 else if (target.is('.new')){
996 if (month === 11){
997 month = 0;
998 year += 1;
999 }
1000 else {
1001 month += 1;
1002 }
1003 }
1004 this._setDate(UTCDate(year, month, day));
1005 }
1006 break;
1007 }
1008 }
1009 if (this.picker.is(':visible') && this._focused_from){
1010 $(this._focused_from).focus();
1011 }
1012 delete this._focused_from;
1013 },
1014
1015 _toggle_multidate: function(date){
1016 var ix = this.dates.contains(date);
1017 if (!date){
1018 this.dates.clear();
1019 }
1020 else if (ix !== -1){
1021 this.dates.remove(ix);
1022 }
1023 else {
1024 this.dates.push(date);
1025 }
1026 if (typeof this.o.multidate === 'number')
1027 while (this.dates.length > this.o.multidate)
1028 this.dates.remove(0);
1029 },
1030
1031 _setDate: function(date, which){
1032 if (!which || which === 'date')
1033 this._toggle_multidate(date && new Date(date));
1034 if (!which || which === 'view')
1035 this.viewDate = date && new Date(date);
1036
1037 this.fill();
1038 this.setValue();
1039 this._trigger('changeDate');
1040 var element;
1041 if (this.isInput){
1042 element = this.element;
1043 }
1044 else if (this.component){
1045 element = this.element.find('input');
1046 }
1047 if (element){
1048 element.change();
1049 }
1050 if (this.o.autoclose && (!which || which === 'date')){
1051 this.hide();
1052 }
1053 },
1054
1055 moveMonth: function(date, dir){
1056 if (!date)
1057 return undefined;
1058 if (!dir)
1059 return date;
1060 var new_date = new Date(date.valueOf()),
1061 day = new_date.getUTCDate(),
1062 month = new_date.getUTCMonth(),
1063 mag = Math.abs(dir),
1064 new_month, test;
1065 dir = dir > 0 ? 1 : -1;
1066 if (mag === 1){
1067 test = dir === -1
1068 // If going back one month, make sure month is not current month
1069 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1070 ? function(){
1071 return new_date.getUTCMonth() === month;
1072 }
1073 // If going forward one month, make sure month is as expected
1074 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1075 : function(){
1076 return new_date.getUTCMonth() !== new_month;
1077 };
1078 new_month = month + dir;
1079 new_date.setUTCMonth(new_month);
1080 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1081 if (new_month < 0 || new_month > 11)
1082 new_month = (new_month + 12) % 12;
1083 }
1084 else {
1085 // For magnitudes >1, move one month at a time...
1086 for (var i=0; i < mag; i++)
1087 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1088 new_date = this.moveMonth(new_date, dir);
1089 // ...then reset the day, keeping it in the new month
1090 new_month = new_date.getUTCMonth();
1091 new_date.setUTCDate(day);
1092 test = function(){
1093 return new_month !== new_date.getUTCMonth();
1094 };
1095 }
1096 // Common date-resetting loop -- if date is beyond end of month, make it
1097 // end of month
1098 while (test()){
1099 new_date.setUTCDate(--day);
1100 new_date.setUTCMonth(new_month);
1101 }
1102 return new_date;
1103 },
1104
1105 moveYear: function(date, dir){
1106 return this.moveMonth(date, dir*12);
1107 },
1108
1109 dateWithinRange: function(date){
1110 return date >= this.o.startDate && date <= this.o.endDate;
1111 },
1112
1113 keydown: function(e){
1114 if (this.picker.is(':not(:visible)')){
1115 if (e.keyCode === 27) // allow escape to hide and re-show picker
1116 this.show();
1117 return;
1118 }
1119 var dateChanged = false,
1120 dir, newDate, newViewDate,
1121 focusDate = this.focusDate || this.viewDate;
1122 switch (e.keyCode){
1123 case 27: // escape
1124 if (this.focusDate){
1125 this.focusDate = null;
1126 this.viewDate = this.dates.get(-1) || this.viewDate;
1127 this.fill();
1128 }
1129 else
1130 this.hide();
1131 e.preventDefault();
1132 break;
1133 case 37: // left
1134 case 39: // right
1135 if (!this.o.keyboardNavigation)
1136 break;
1137 dir = e.keyCode === 37 ? -1 : 1;
1138 if (e.ctrlKey){
1139 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1140 newViewDate = this.moveYear(focusDate, dir);
1141 this._trigger('changeYear', this.viewDate);
1142 }
1143 else if (e.shiftKey){
1144 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1145 newViewDate = this.moveMonth(focusDate, dir);
1146 this._trigger('changeMonth', this.viewDate);
1147 }
1148 else {
1149 newDate = new Date(this.dates.get(-1) || UTCToday());
1150 newDate.setUTCDate(newDate.getUTCDate() + dir);
1151 newViewDate = new Date(focusDate);
1152 newViewDate.setUTCDate(focusDate.getUTCDate() + dir);
1153 }
1154 if (this.dateWithinRange(newDate)){
1155 this.focusDate = this.viewDate = newViewDate;
1156 this.setValue();
1157 this.fill();
1158 e.preventDefault();
1159 }
1160 break;
1161 case 38: // up
1162 case 40: // down
1163 if (!this.o.keyboardNavigation)
1164 break;
1165 dir = e.keyCode === 38 ? -1 : 1;
1166 if (e.ctrlKey){
1167 newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1168 newViewDate = this.moveYear(focusDate, dir);
1169 this._trigger('changeYear', this.viewDate);
1170 }
1171 else if (e.shiftKey){
1172 newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1173 newViewDate = this.moveMonth(focusDate, dir);
1174 this._trigger('changeMonth', this.viewDate);
1175 }
1176 else {
1177 newDate = new Date(this.dates.get(-1) || UTCToday());
1178 newDate.setUTCDate(newDate.getUTCDate() + dir * 7);
1179 newViewDate = new Date(focusDate);
1180 newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7);
1181 }
1182 if (this.dateWithinRange(newDate)){
1183 this.focusDate = this.viewDate = newViewDate;
1184 this.setValue();
1185 this.fill();
1186 e.preventDefault();
1187 }
1188 break;
1189 case 32: // spacebar
1190 // Spacebar is used in manually typing dates in some formats.
1191 // As such, its behavior should not be hijacked.
1192 break;
1193 case 13: // enter
1194 focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
1195 this._toggle_multidate(focusDate);
1196 dateChanged = true;
1197 this.focusDate = null;
1198 this.viewDate = this.dates.get(-1) || this.viewDate;
1199 this.setValue();
1200 this.fill();
1201 if (this.picker.is(':visible')){
1202 e.preventDefault();
1203 if (this.o.autoclose)
1204 this.hide();
1205 }
1206 break;
1207 case 9: // tab
1208 this.focusDate = null;
1209 this.viewDate = this.dates.get(-1) || this.viewDate;
1210 this.fill();
1211 this.hide();
1212 break;
1213 }
1214 if (dateChanged){
1215 if (this.dates.length)
1216 this._trigger('changeDate');
1217 else
1218 this._trigger('clearDate');
1219 var element;
1220 if (this.isInput){
1221 element = this.element;
1222 }
1223 else if (this.component){
1224 element = this.element.find('input');
1225 }
1226 if (element){
1227 element.change();
1228 }
1229 }
1230 },
1231
1232 showMode: function(dir){
1233 if (dir){
1234 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
1235 }
1236 this.picker
1237 .find('>div')
1238 .hide()
1239 .filter('.hc-datepicker2-'+DPGlobal.modes[this.viewMode].clsName)
1240 .css('display', 'block');
1241 this.updateNavArrows();
1242 }
1243 };
1244
1245 var DateRangePicker = function(element, options){
1246 this.element = $(element);
1247 this.inputs = $.map(options.inputs, function(i){
1248 return i.jquery ? i[0] : i;
1249 });
1250 delete options.inputs;
1251
1252 $(this.inputs)
1253 .hc_datepicker2(options)
1254 .bind('changeDate', $.proxy(this.dateUpdated, this));
1255
1256 this.pickers = $.map(this.inputs, function(i){
1257 return $(i).data('hc_datepicker2');
1258 });
1259 this.updateDates();
1260 };
1261 DateRangePicker.prototype = {
1262 updateDates: function(){
1263 this.dates = $.map(this.pickers, function(i){
1264 return i.getUTCDate();
1265 });
1266 this.updateRanges();
1267 },
1268 updateRanges: function(){
1269 var range = $.map(this.dates, function(d){
1270 return d.valueOf();
1271 });
1272 $.each(this.pickers, function(i, p){
1273 p.setRange(range);
1274 });
1275 },
1276 dateUpdated: function(e){
1277 // `this.updating` is a workaround for preventing infinite recursion
1278 // between `changeDate` triggering and `setUTCDate` calling. Until
1279 // there is a better mechanism.
1280 if (this.updating)
1281 return;
1282 this.updating = true;
1283
1284 var dp = $(e.target).data('hc_datepicker2'),
1285 new_date = dp.getUTCDate(),
1286 i = $.inArray(e.target, this.inputs),
1287 l = this.inputs.length;
1288 if (i === -1)
1289 return;
1290
1291 $.each(this.pickers, function(i, p){
1292 if (!p.getUTCDate())
1293 p.setUTCDate(new_date);
1294 });
1295
1296 if (new_date < this.dates[i]){
1297 // Date being moved earlier/left
1298 while (i >= 0 && new_date < this.dates[i]){
1299 this.pickers[i--].setUTCDate(new_date);
1300 }
1301 }
1302 else if (new_date > this.dates[i]){
1303 // Date being moved later/right
1304 while (i < l && new_date > this.dates[i]){
1305 this.pickers[i++].setUTCDate(new_date);
1306 }
1307 }
1308 this.updateDates();
1309
1310 delete this.updating;
1311 },
1312 remove: function(){
1313 $.map(this.pickers, function(p){ p.remove(); });
1314 delete this.element.data().hc_datepicker2;
1315 }
1316 };
1317
1318 function opts_from_el(el, prefix){
1319 // Derive options from element data-attrs
1320 var data = $(el).data(),
1321 out = {}, inkey,
1322 replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
1323 prefix = new RegExp('^' + prefix.toLowerCase());
1324 function re_lower(_,a){
1325 return a.toLowerCase();
1326 }
1327 for (var key in data)
1328 if (prefix.test(key)){
1329 inkey = key.replace(replace, re_lower);
1330 out[inkey] = data[key];
1331 }
1332 return out;
1333 }
1334
1335 function opts_from_locale(lang){
1336 // Derive options from locale plugins
1337 var out = {};
1338 // Check if "de-DE" style date is available, if not language should
1339 // fallback to 2 letter code eg "de"
1340 if (!dates[lang]){
1341 lang = lang.split('-')[0];
1342 if (!dates[lang])
1343 return;
1344 }
1345 var d = dates[lang];
1346 $.each(locale_opts, function(i,k){
1347 if (k in d)
1348 out[k] = d[k];
1349 });
1350 return out;
1351 }
1352
1353 var old = $.fn.hc_datepicker2;
1354 $.fn.hc_datepicker2 = function(option){
1355 var args = Array.apply(null, arguments);
1356 args.shift();
1357 var internal_return;
1358 this.each(function(){
1359 var $this = $(this),
1360 data = $this.data('hc_datepicker2'),
1361 options = typeof option === 'object' && option;
1362 if (!data){
1363 var elopts = opts_from_el(this, 'date'),
1364 // Preliminary otions
1365 xopts = $.extend({}, defaults, elopts, options),
1366 locopts = opts_from_locale(xopts.language),
1367 // Options priority: js args, data-attrs, locales, defaults
1368 opts = $.extend({}, defaults, locopts, elopts, options);
1369 if ($this.is('.input-daterange') || opts.inputs){
1370 var ropts = {
1371 inputs: opts.inputs || $this.find('input').toArray()
1372 };
1373 $this.data('hc_datepicker2', (data = new DateRangePicker(this, $.extend(opts, ropts))));
1374 }
1375 else {
1376 $this.data('hc_datepicker2', (data = new hc_Datepicker2(this, opts)));
1377 }
1378 }
1379 if (typeof option === 'string' && typeof data[option] === 'function'){
1380 internal_return = data[option].apply(data, args);
1381 if (internal_return !== undefined)
1382 return false;
1383 }
1384 });
1385 if (internal_return !== undefined)
1386 return internal_return;
1387 else
1388 return this;
1389 };
1390
1391 var defaults = $.fn.hc_datepicker2.defaults = {
1392 autoclose: true,
1393 beforeShowDay: $.noop,
1394 calendarWeeks: false,
1395 clearBtn: false,
1396 daysOfWeekDisabled: [],
1397 endDate: Infinity,
1398 forceParse: true,
1399 format: 'mm/dd/yyyy',
1400 keyboardNavigation: true,
1401 language: 'en',
1402 minViewMode: 0,
1403 multidate: false,
1404 multidateSeparator: ',',
1405 orientation: "auto",
1406 rtl: false,
1407 startDate: -Infinity,
1408 startView: 0,
1409 todayBtn: false,
1410 todayHighlight: false,
1411 weekStart: 0
1412 };
1413 var locale_opts = $.fn.hc_datepicker2.locale_opts = [
1414 'format',
1415 'rtl',
1416 'weekStart'
1417 ];
1418 $.fn.hc_datepicker2.Constructor = hc_Datepicker2;
1419 var dates = $.fn.hc_datepicker2.dates = {
1420 en: {
1421 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1422 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1423 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1424 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1425 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1426 today: "Today",
1427 clear: "Clear"
1428 }
1429 };
1430
1431 var DPGlobal = {
1432 modes: [
1433 {
1434 clsName: 'days',
1435 navFnc: 'Month',
1436 navStep: 1
1437 },
1438 {
1439 clsName: 'months',
1440 navFnc: 'FullYear',
1441 navStep: 1
1442 },
1443 {
1444 clsName: 'years',
1445 navFnc: 'FullYear',
1446 navStep: 10
1447 }],
1448 isLeapYear: function(year){
1449 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
1450 },
1451 getDaysInMonth: function(year, month){
1452 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
1453 },
1454 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
1455 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
1456 parseFormat: function(format){
1457 // IE treats \0 as a string end in inputs (truncating the value),
1458 // so it's a bad format delimiter, anyway
1459 var separators = format.replace(this.validParts, '\0').split('\0'),
1460 parts = format.match(this.validParts);
1461 if (!separators || !separators.length || !parts || parts.length === 0){
1462 throw new Error("Invalid date format.");
1463 }
1464 return {separators: separators, parts: parts};
1465 },
1466 parseDate: function(date, format, language){
1467 if (!date)
1468 return undefined;
1469 if (date instanceof Date)
1470 return date;
1471 if (typeof format === 'string')
1472 format = DPGlobal.parseFormat(format);
1473 var part_re = /([\-+]\d+)([dmwy])/,
1474 parts = date.match(/([\-+]\d+)([dmwy])/g),
1475 part, dir, i;
1476 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){
1477 date = new Date();
1478 for (i=0; i < parts.length; i++){
1479 part = part_re.exec(parts[i]);
1480 dir = parseInt(part[1]);
1481 switch (part[2]){
1482 case 'd':
1483 date.setUTCDate(date.getUTCDate() + dir);
1484 break;
1485 case 'm':
1486 date = hc_Datepicker2.prototype.moveMonth.call(hc_Datepicker2.prototype, date, dir);
1487 break;
1488 case 'w':
1489 date.setUTCDate(date.getUTCDate() + dir * 7);
1490 break;
1491 case 'y':
1492 date = hc_Datepicker2.prototype.moveYear.call(hc_Datepicker2.prototype, date, dir);
1493 break;
1494 }
1495 }
1496 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
1497 }
1498 parts = date && date.match(this.nonpunctuation) || [];
1499 date = new Date();
1500 var parsed = {},
1501 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
1502 setters_map = {
1503 yyyy: function(d,v){
1504 return d.setUTCFullYear(v);
1505 },
1506 yy: function(d,v){
1507 return d.setUTCFullYear(2000+v);
1508 },
1509 m: function(d,v){
1510 if (isNaN(d))
1511 return d;
1512 v -= 1;
1513 while (v < 0) v += 12;
1514 v %= 12;
1515 d.setUTCMonth(v);
1516 while (d.getUTCMonth() !== v)
1517 d.setUTCDate(d.getUTCDate()-1);
1518 return d;
1519 },
1520 d: function(d,v){
1521 return d.setUTCDate(v);
1522 }
1523 },
1524 val, filtered;
1525 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1526 setters_map['dd'] = setters_map['d'];
1527 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
1528 var fparts = format.parts.slice();
1529 // Remove noop parts
1530 if (parts.length !== fparts.length){
1531 fparts = $(fparts).filter(function(i,p){
1532 return $.inArray(p, setters_order) !== -1;
1533 }).toArray();
1534 }
1535 // Process remainder
1536 function match_part(){
1537 var m = this.slice(0, parts[i].length),
1538 p = parts[i].slice(0, m.length);
1539 return m === p;
1540 }
1541 if (parts.length === fparts.length){
1542 var cnt;
1543 for (i=0, cnt = fparts.length; i < cnt; i++){
1544 val = parseInt(parts[i], 10);
1545 part = fparts[i];
1546 if (isNaN(val)){
1547 switch (part){
1548 case 'MM':
1549 filtered = $(dates[language].months).filter(match_part);
1550 val = $.inArray(filtered[0], dates[language].months) + 1;
1551 break;
1552 case 'M':
1553 filtered = $(dates[language].monthsShort).filter(match_part);
1554 val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1555 break;
1556 }
1557 }
1558 parsed[part] = val;
1559 }
1560 var _date, s;
1561 for (i=0; i < setters_order.length; i++){
1562 s = setters_order[i];
1563 if (s in parsed && !isNaN(parsed[s])){
1564 _date = new Date(date);
1565 setters_map[s](_date, parsed[s]);
1566 if (!isNaN(_date))
1567 date = _date;
1568 }
1569 }
1570 }
1571 return date;
1572 },
1573 formatDate: function(date, format, language){
1574 if (!date)
1575 return '';
1576 if (typeof format === 'string')
1577 format = DPGlobal.parseFormat(format);
1578 var val = {
1579 d: date.getUTCDate(),
1580 D: dates[language].daysShort[date.getUTCDay()],
1581 DD: dates[language].days[date.getUTCDay()],
1582 m: date.getUTCMonth() + 1,
1583 M: dates[language].monthsShort[date.getUTCMonth()],
1584 MM: dates[language].months[date.getUTCMonth()],
1585 yy: date.getUTCFullYear().toString().substring(2),
1586 yyyy: date.getUTCFullYear()
1587 };
1588 val.dd = (val.d < 10 ? '0' : '') + val.d;
1589 val.mm = (val.m < 10 ? '0' : '') + val.m;
1590 date = [];
1591 var seps = $.extend([], format.separators);
1592 for (var i=0, cnt = format.parts.length; i <= cnt; i++){
1593 if (seps.length)
1594 date.push(seps.shift());
1595 date.push(val[format.parts[i]]);
1596 }
1597 return date.join('');
1598 },
1599 headTemplate: '<thead>'+
1600 '<tr>'+
1601 '<th class="prev">&laquo;</th>'+
1602 '<th colspan="5" class="hc-datepicker2-switch"></th>'+
1603 '<th class="next">&raquo;</th>'+
1604 '</tr>'+
1605 '</thead>',
1606 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1607 footTemplate: '<tfoot>'+
1608 '<tr>'+
1609 '<th colspan="7" class="today"></th>'+
1610 '</tr>'+
1611 '<tr>'+
1612 '<th colspan="7" class="clear"></th>'+
1613 '</tr>'+
1614 '</tfoot>'
1615 };
1616 DPGlobal.template = '<div class="hc-datepicker2">'+
1617 '<div class="hc-datepicker2-days">'+
1618 '<table class=" table-condensed">'+
1619 DPGlobal.headTemplate+
1620 '<tbody></tbody>'+
1621 DPGlobal.footTemplate+
1622 '</table>'+
1623 '</div>'+
1624 '<div class="hc-datepicker2-months">'+
1625 '<table class="table-condensed">'+
1626 DPGlobal.headTemplate+
1627 DPGlobal.contTemplate+
1628 DPGlobal.footTemplate+
1629 '</table>'+
1630 '</div>'+
1631 '<div class="hc-datepicker2-years">'+
1632 '<table class="table-condensed">'+
1633 DPGlobal.headTemplate+
1634 DPGlobal.contTemplate+
1635 DPGlobal.footTemplate+
1636 '</table>'+
1637 '</div>'+
1638 '</div>';
1639
1640 $.fn.hc_datepicker2.DPGlobal = DPGlobal;
1641
1642
1643 /* DATEPICKER NO CONFLICT
1644 * =================== */
1645
1646 $.fn.hc_datepicker2.noConflict = function(){
1647 $.fn.hc_datepicker2 = old;
1648 return this;
1649 };
1650
1651
1652 /* DATEPICKER DATA-API
1653 * ================== */
1654
1655 $(document).on(
1656 'focus.datepicker.data-api click.datepicker.data-api',
1657 '[data-provide="datepicker"]',
1658 function(e){
1659 var $this = $(this);
1660 if ($this.data('hc_datepicker2'))
1661 return;
1662 e.preventDefault();
1663 // component click requires us to explicitly show it
1664 $this.hc_datepicker2('show');
1665 }
1666 );
1667 $(function(){
1668 $('[data-provide="datepicker2-inline"]').hc_datepicker2();
1669 });
1670
1671 document.addEventListener('DOMContentLoaded', function()
1672 {
1673 var inputs = jQuery( '.hc-datepicker2' );
1674 inputs.hc_datepicker2({
1675 })
1676 .on('changeDate', function(ev){
1677 var dbDate =
1678 ev.date.getFullYear()
1679 + "" +
1680 ("00" + (ev.date.getMonth()+1) ).substr(-2)
1681 + "" +
1682 ("00" + ev.date.getDate()).substr(-2);
1683
1684 // remove '_display' from end
1685 var display_name = jQuery(this).attr('name');
1686 var display_suffix = '_display';
1687 var value_name = display_name.substr(0, (display_name.length - display_suffix.length) );
1688
1689 jQuery(this).closest('form').find('[name="' + value_name + '"]')
1690 .val( dbDate )
1691 .trigger('change')
1692 ;
1693 });
1694
1695 // jQuery.each( inputs, function(el){
1696 // var dbDate = jQuery(this).val();
1697 // var display_name = jQuery(this).attr('name');
1698 // var display_suffix = '_display';
1699 // var value_name = display_name.substr(0, (display_name.length - display_suffix.length) );
1700 // jQuery(this).closest('form').find('[name="' + value_name + '"]')
1701 // .val( dbDate )
1702 // .trigger('change')
1703 // ;
1704 // alert( jQuery(this).val() );
1705 // });
1706 });
1707
1708 }(window.jQuery));
1709
1710