PageSourceSearch

https://linux.org.au/wp-content/plugins/caldera-forms/fields/date_picker/cf-datepicker.js?ver=1.9.7

js linux.org.au collected 2026-09-24 08:24:33 UTC 65,084 bytes, 1,679 lines download raw bytes

1/* =========================================================
2 * bootstrap-cfdatepicker.js
3 * Repo: https://github.com/eternicode/bootstrap-cfdatepicker/
4 * Demo: http://eternicode.github.io/bootstrap-cfdatepicker/
5 * Docs: http://bootstrap-cfdatepicker.readthedocs.org/
6 * Forked from http://www.eyecon.ro/bootstrap-cfdatepicker
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.splice(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 Datepicker = 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('cfdatepicker-inline').appendTo(this.element);
107        }
108        else {
109            this.picker.addClass('cfdatepicker-dropdown dropdown-menu');
110        }
111
112        if (this.o.rtl){
113            this.picker.addClass('cfdatepicker-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    Datepicker.prototype = {
144        constructor: Datepicker,
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 cfdatepicker
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 cfdatepicker, 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().cfdatepicker;
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
551            var formID = jQuery( this.element  ).data( 'form-id' );
552            var maybeModal = document.getElementById( 'modal-' + formID + '-content' );
553            var zIndex;
554
555            if( null !== maybeModal ){
556                zIndex = 10000;
557            }else{
558                zIndex = parseInt(this.element.parents().filter(function(){
559                    return $(this).css('z-index') !== 'auto';
560                }).first().css('z-index'))+10;
561            }
562
563            var offset = this.component ? this.component.parent().offset() : this.element.offset();
564            var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
565            var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
566            var left = offset.left,
567                top = offset.top;
568
569            this.picker.removeClass(
570                'cfdatepicker-orient-top cfdatepicker-orient-bottom '+
571                'cfdatepicker-orient-right cfdatepicker-orient-left'
572            );
573
574            if (this.o.orientation.x !== 'auto'){
575                this.picker.addClass('cfdatepicker-orient-' + this.o.orientation.x);
576                if (this.o.orientation.x === 'right')
577                    left -= calendarWidth - width;
578            }
579            // auto x orientation is best-placement: if it crosses a window
580            // edge, fudge it sideways
581            else {
582                // Default to left
583                this.picker.addClass('cfdatepicker-orient-left');
584                if (offset.left < 0)
585                    left -= offset.left - visualPadding;
586                else if (offset.left + calendarWidth > windowWidth)
587                    left = windowWidth - calendarWidth - visualPadding;
588            }
589
590            // auto y orientation is best-situation: top or bottom, no fudging,
591            // decision based on which shows more of the calendar
592            var yorient = this.o.orientation.y,
593                top_overflow, bottom_overflow;
594            if (yorient === 'auto'){
595                top_overflow = -scrollTop + offset.top - calendarHeight;
596                bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
597                if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
598                    yorient = 'top';
599                else
600                    yorient = 'bottom';
601            }
602            this.picker.addClass('cfdatepicker-orient-' + yorient);
603            if (yorient === 'top')
604                top += height;
605            else
606                top -= calendarHeight + parseInt(this.picker.css('padding-top'));
607
608            this.picker.css({
609                top: top,
610                left: left,
611                zIndex: zIndex
612            });
613        },
614
615        _allow_update: true,
616        update: function(){
617            if (!this._allow_update)
618                return;
619
620            var oldDates = this.dates.copy(),
621                dates = [],
622                fromArgs = false;
623            if (arguments.length){
624                $.each(arguments, $.proxy(function(i, date){
625                    if (date instanceof Date)
626                        date = this._local_to_utc(date);
627                    dates.push(date);
628                }, this));
629                fromArgs = true;
630            }
631            else {
632                dates = this.isInput
633                    ? this.element.val()
634                    : this.element.data('date') || this.element.find('input').val();
635                if (dates && this.o.multidate)
636                    dates = dates.split(this.o.multidateSeparator);
637                else
638                    dates = [dates];
639                delete this.element.data().date;
640            }
641
642            dates = $.map(dates, $.proxy(function(date){
643                return DPGlobal.parseDate(date, this.o.format, this.o.language);
644            }, this));
645            dates = $.grep(dates, $.proxy(function(date){
646                return (
647                    date < this.o.startDate ||
648                    date > this.o.endDate ||
649                    !date
650                );
651            }, this), true);
652            this.dates.replace(dates);
653
654            if (this.dates.length)
655                this.viewDate = new Date(this.dates.get(-1));
656            else if (this.viewDate < this.o.startDate)
657                this.viewDate = new Date(this.o.startDate);
658            else if (this.viewDate > this.o.endDate)
659                this.viewDate = new Date(this.o.endDate);
660
661            if (fromArgs){
662                // setting date by clicking
663                this.setValue();
664            }
665            else if (dates.length){
666                // setting date by typing
667                if (String(oldDates) !== String(this.dates))
668                    this._trigger('changeDate');
669            }
670            if (!this.dates.length && oldDates.length)
671                this._trigger('clearDate');
672
673            this.fill();
674        },
675
676        fillDow: function(){
677            var dowCnt = this.o.weekStart,
678                html = '<tr>';
679            if (this.o.calendarWeeks){
680                var cell = '<th class="cw">&nbsp;</th>';
681                html += cell;
682                this.picker.find('.cfdatepicker-days thead tr:first-child').prepend(cell);
683            }
684            while (dowCnt < this.o.weekStart + 7){
685                html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
686            }
687            html += '</tr>';
688            this.picker.find('.cfdatepicker-days thead').append(html);
689        },
690
691        fillMonths: function(){
692            var html = '',
693                i = 0;
694            while (i < 12){
695                html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
696            }
697            this.picker.find('.cfdatepicker-months td').html(html);
698        },
699
700        setRange: function(range){
701            if (!range || !range.length)
702                delete this.range;
703            else
704                this.range = $.map(range, function(d){
705                    return d.valueOf();
706                });
707            this.fill();
708        },
709
710        getClassNames: function(date){
711            var cls = [],
712                year = this.viewDate.getUTCFullYear(),
713                month = this.viewDate.getUTCMonth(),
714                today = new Date();
715            if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
716                cls.push('old');
717            }
718            else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
719                cls.push('new');
720            }
721            if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
722                cls.push('focused');
723            // Compare internal UTC date with local today, not UTC today
724            if (this.o.todayHighlight &&
725                date.getUTCFullYear() === today.getFullYear() &&
726                date.getUTCMonth() === today.getMonth() &&
727                date.getUTCDate() === today.getDate()){
728                cls.push('today');
729            }
730            if (this.dates.contains(date) !== -1)
731                cls.push('active');
732            if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
733                $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
734                cls.push('disabled');
735            }
736            if (this.range){
737                if (date > this.range[0] && date < this.range[this.range.length-1]){
738                    cls.push('range');
739                }
740                if ($.inArray(date.valueOf(), this.range) !== -1){
741                    cls.push('selected');
742                }
743            }
744            return cls;
745        },
746
747        fill: function(){
748            var d = new Date(this.viewDate),
749                year = d.getUTCFullYear(),
750                month = d.getUTCMonth(),
751                startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
752                startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
753                endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
754                endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
755                todaytxt = dates[this.o.language].today || dates['en'].today || '',
756                cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
757                tooltip;
758            this.picker.find('.cfdatepicker-days thead th.cfdatepicker-switch')
759                .text(dates[this.o.language].months[month]+' '+year);
760            this.picker.find('tfoot th.today')
761                .text(todaytxt)
762                .toggle(this.o.todayBtn !== false);
763            this.picker.find('tfoot th.clear')
764                .text(cleartxt)
765                .toggle(this.o.clearBtn !== false);
766            this.updateNavArrows();
767            this.fillMonths();
768            var prevMonth = UTCDate(year, month-1, 28),
769                day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
770            prevMonth.setUTCDate(day);
771            prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
772            var nextMonth = new Date(prevMonth);
773            nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
774            nextMonth = nextMonth.valueOf();
775            var html = [];
776            var clsName;
777            while (prevMonth.valueOf() < nextMonth){
778                if (prevMonth.getUTCDay() === this.o.weekStart){
779                    html.push('<tr>');
780                    if (this.o.calendarWeeks){
781                        // ISO 8601: First week contains first thursday.
782                        // ISO also states week starts on Monday, but we can be more abstract here.
783                        var
784                            // Start of current week: based on weekstart/current date
785                            ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
786                            // Thursday of this week
787                            th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
788                            // First Thursday of year, year from thursday
789                            yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
790                            // Calendar week: ms between thursdays, div ms per day, div 7 days
791                            calWeek =  (th - yth) / 864e5 / 7 + 1;
792                        html.push('<td class="cw">'+ calWeek +'</td>');
793
794                    }
795                }
796                clsName = this.getClassNames(prevMonth);
797                clsName.push('day');
798
799                if (this.o.beforeShowDay !== $.noop){
800                    var before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
801                    if (before === undefined)
802                        before = {};
803                    else if (typeof(before) === 'boolean')
804                        before = {enabled: before};
805                    else if (typeof(before) === 'string')
806                        before = {classes: before};
807                    if (before.enabled === false)
808                        clsName.push('disabled');
809                    if (before.classes)
810                        clsName = clsName.concat(before.classes.split(/\s+/));
811                    if (before.tooltip)
812                        tooltip = before.tooltip;
813                }
814
815                clsName = $.unique(clsName);
816                html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
817                if (prevMonth.getUTCDay() === this.o.weekEnd){
818                    html.push('</tr>');
819                }
820                prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
821            }
822            this.picker.find('.cfdatepicker-days tbody').empty().append(html.join(''));
823
824            var months = this.picker.find('.cfdatepicker-months')
825                .find('th:eq(1)')
826                .text(year)
827                .end()
828                .find('span').removeClass('active');
829
830            $.each(this.dates, function(i, d){
831                if (d.getUTCFullYear() === year)
832                    months.eq(d.getUTCMonth()).addClass('active');
833            });
834
835            if (year < startYear || year > endYear){
836                months.addClass('disabled');
837            }
838            if (year === startYear){
839                months.slice(0, startMonth).addClass('disabled');
840            }
841            if (year === endYear){
842                months.slice(endMonth+1).addClass('disabled');
843            }
844
845            html = '';
846            year = parseInt(year/10, 10) * 10;
847            var yearCont = this.picker.find('.cfdatepicker-years')
848                .find('th:eq(1)')
849                .text(year + '-' + (year + 9))
850                .end()
851                .find('td');
852            year -= 1;
853            var years = $.map(this.dates, function(d){
854                    return d.getUTCFullYear();
855                }),
856                classes;
857            for (var i = -1; i < 11; i++){
858                classes = ['year'];
859                if (i === -1)
860                    classes.push('old');
861                else if (i === 10)
862                    classes.push('new');
863                if ($.inArray(year, years) !== -1)
864                    classes.push('active');
865                if (year < startYear || year > endYear)
866                    classes.push('disabled');
867                html += '<span class="' + classes.join(' ') + '">'+year+'</span>';
868                year += 1;
869            }
870            yearCont.html(html);
871        },
872
873        updateNavArrows: function(){
874            if (!this._allow_update)
875                return;
876
877            var d = new Date(this.viewDate),
878                year = d.getUTCFullYear(),
879                month = d.getUTCMonth();
880            switch (this.viewMode){
881                case 0:
882                    if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){
883                        this.picker.find('.prev').css({visibility: 'hidden'});
884                    }
885                    else {
886                        this.picker.find('.prev').css({visibility: 'visible'});
887                    }
888                    if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){
889                        this.picker.find('.next').css({visibility: 'hidden'});
890                    }
891                    else {
892                        this.picker.find('.next').css({visibility: 'visible'});
893                    }
894                    break;
895                case 1:
896                case 2:
897                    if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){
898                        this.picker.find('.prev').css({visibility: 'hidden'});
899                    }
900                    else {
901                        this.picker.find('.prev').css({visibility: 'visible'});
902                    }
903                    if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){
904                        this.picker.find('.next').css({visibility: 'hidden'});
905                    }
906                    else {
907                        this.picker.find('.next').css({visibility: 'visible'});
908                    }
909                    break;
910            }
911        },
912
913        click: function(e){
914            e.preventDefault();
915            var target = $(e.target).closest('span, td, th'),
916                year, month, day;
917            if (target.length === 1){
918                switch (target[0].nodeName.toLowerCase()){
919                    case 'th':
920                        switch (target[0].className){
921                            case 'cfdatepicker-switch':
922                                this.showMode(1);
923                                break;
924                            case 'prev':
925                            case 'next':
926                                var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1);
927                                switch (this.viewMode){
928                                    case 0:
929                                        this.viewDate = this.moveMonth(this.viewDate, dir);
930                                        this._trigger('changeMonth', this.viewDate);
931                                        break;
932                                    case 1:
933                                    case 2:
934                                        this.viewDate = this.moveYear(this.viewDate, dir);
935                                        if (this.viewMode === 1)
936                                            this._trigger('changeYear', this.viewDate);
937                                        break;
938                                }
939                                this.fill();
940                                break;
941                            case 'today':
942                                var date = new Date();
943                                date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
944
945                                this.showMode(-2);
946                                var which = this.o.todayBtn === 'linked' ? null : 'view';
947                                this._setDate(date, which);
948                                break;
949                            case 'clear':
950                                var element;
951                                if (this.isInput)
952                                    element = this.element;
953                                else if (this.component)
954                                    element = this.element.find('input');
955                                if (element)
956                                    element.val("").change();
957                                this.update();
958                                this._trigger('changeDate');
959                                if (this.o.autoclose)
960                                    this.hide();
961                                break;
962                        }
963                        break;
964                    case 'span':
965                        if (!target.is('.disabled')){
966                            this.viewDate.setUTCDate(1);
967                            if (target.is('.month')){
968                                day = 1;
969                                month = target.parent().find('span').index(target);
970                                year = this.viewDate.getUTCFullYear();
971                                this.viewDate.setUTCMonth(month);
972                                this._trigger('changeMonth', this.viewDate);
973                                if (this.o.minViewMode === 1){
974                                    this._setDate(UTCDate(year, month, day));
975                                }
976                            }
977                            else {
978                                day = 1;
979                                month = 0;
980                                year = parseInt(target.text(), 10)||0;
981                                this.viewDate.setUTCFullYear(year);
982                                this._trigger('changeYear', this.viewDate);
983                                if (this.o.minViewMode === 2){
984                                    this._setDate(UTCDate(year, month, day));
985                                }
986                            }
987                            this.showMode(-1);
988                            this.fill();
989                        }
990                        break;
991                    case 'td':
992                        if (target.is('.day') && !target.is('.disabled')){
993                            day = parseInt(target.text(), 10)||1;
994                            year = this.viewDate.getUTCFullYear();
995                            month = this.viewDate.getUTCMonth();
996                            if (target.is('.old')){
997                                if (month === 0){
998                                    month = 11;
999                                    year -= 1;
1000                                }
1001                                else {
1002                                    month -= 1;
1003                                }
1004                            }
1005                            else if (target.is('.new')){
1006                                if (month === 11){
1007                                    month = 0;
1008                                    year += 1;
1009                                }
1010                                else {
1011                                    month += 1;
1012                                }
1013                            }
1014                            this._setDate(UTCDate(year, month, day));
1015                        }
1016                        break;
1017                }
1018            }
1019            if (this.picker.is(':visible') && this._focused_from){
1020                $(this._focused_from).focus();
1021            }
1022            delete this._focused_from;
1023        },
1024
1025        _toggle_multidate: function(date){
1026            var ix = this.dates.contains(date);
1027            if (!date){
1028                this.dates.clear();
1029            }
1030            else if (ix !== -1){
1031                this.dates.remove(ix);
1032            }
1033            else {
1034                this.dates.push(date);
1035            }
1036            if (typeof this.o.multidate === 'number')
1037                while (this.dates.length > this.o.multidate)
1038                    this.dates.remove(0);
1039        },
1040
1041        _setDate: function(date, which){
1042            if (!which || which === 'date')
1043                this._toggle_multidate(date && new Date(date));
1044            if (!which || which  === 'view')
1045                this.viewDate = date && new Date(date);
1046
1047            this.fill();
1048            this.setValue();
1049            this._trigger('changeDate');
1050            var element;
1051            if (this.isInput){
1052                element = this.element;
1053            }
1054            else if (this.component){
1055                element = this.element.find('input');
1056            }
1057            if (element){
1058                element.change();
1059            }
1060            if (this.o.autoclose && (!which || which === 'date')){
1061                this.hide();
1062            }
1063        },
1064
1065        moveMonth: function(date, dir){
1066            if (!date)
1067                return undefined;
1068            if (!dir)
1069                return date;
1070            var new_date = new Date(date.valueOf()),
1071                day = new_date.getUTCDate(),
1072                month = new_date.getUTCMonth(),
1073                mag = Math.abs(dir),
1074                new_month, test;
1075            dir = dir > 0 ? 1 : -1;
1076            if (mag === 1){
1077                test = dir === -1
1078                    // If going back one month, make sure month is not current month
1079                    // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1080                    ? function(){
1081                        return new_date.getUTCMonth() === month;
1082                    }
1083                    // If going forward one month, make sure month is as expected
1084                    // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1085                    : function(){
1086                        return new_date.getUTCMonth() !== new_month;
1087                    };
1088                new_month = month + dir;
1089                new_date.setUTCMonth(new_month);
1090                // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1091                if (new_month < 0 || new_month > 11)
1092                    new_month = (new_month + 12) % 12;
1093            }
1094            else {
1095                // For magnitudes >1, move one month at a time...
1096                for (var i=0; i < mag; i++)
1097                    // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1098                    new_date = this.moveMonth(new_date, dir);
1099                // ...then reset the day, keeping it in the new month
1100                new_month = new_date.getUTCMonth();
1101                new_date.setUTCDate(day);
1102                test = function(){
1103                    return new_month !== new_date.getUTCMonth();
1104                };
1105            }
1106            // Common date-resetting loop -- if date is beyond end of month, make it
1107            // end of month
1108            while (test()){
1109                new_date.setUTCDate(--day);
1110                new_date.setUTCMonth(new_month);
1111            }
1112            return new_date;
1113        },
1114
1115        moveYear: function(date, dir){
1116            return this.moveMonth(date, dir*12);
1117        },
1118
1119        dateWithinRange: function(date){
1120            return date >= this.o.startDate && date <= this.o.endDate;
1121        },
1122
1123        keydown: function(e){
1124            if (this.picker.is(':not(:visible)')){
1125                if (e.keyCode === 27) // allow escape to hide and re-show picker
1126                    this.show();
1127                return;
1128            }
1129            var dateChanged = false,
1130                dir, newDate, newViewDate,
1131                focusDate = this.focusDate || this.viewDate;
1132            switch (e.keyCode){
1133                case 27: // escape
1134                    if (this.focusDate){
1135                        this.focusDate = null;
1136                        this.viewDate = this.dates.get(-1) || this.viewDate;
1137                        this.fill();
1138                    }
1139                    else
1140                        this.hide();
1141                    e.preventDefault();
1142                    break;
1143                case 37: // left
1144                case 39: // right
1145                    if (!this.o.keyboardNavigation)
1146                        break;
1147                    dir = e.keyCode === 37 ? -1 : 1;
1148                    if (e.ctrlKey){
1149                        newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1150                        newViewDate = this.moveYear(focusDate, dir);
1151                        this._trigger('changeYear', this.viewDate);
1152                    }
1153                    else if (e.shiftKey){
1154                        newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1155                        newViewDate = this.moveMonth(focusDate, dir);
1156                        this._trigger('changeMonth', this.viewDate);
1157                    }
1158                    else {
1159                        newDate = new Date(this.dates.get(-1) || UTCToday());
1160                        newDate.setUTCDate(newDate.getUTCDate() + dir);
1161                        newViewDate = new Date(focusDate);
1162                        newViewDate.setUTCDate(focusDate.getUTCDate() + dir);
1163                    }
1164                    if (this.dateWithinRange(newDate)){
1165                        this.focusDate = this.viewDate = newViewDate;
1166                        this.setValue();
1167                        this.fill();
1168                        e.preventDefault();
1169                    }
1170                    break;
1171                case 38: // up
1172                case 40: // down
1173                    if (!this.o.keyboardNavigation)
1174                        break;
1175                    dir = e.keyCode === 38 ? -1 : 1;
1176                    if (e.ctrlKey){
1177                        newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1178                        newViewDate = this.moveYear(focusDate, dir);
1179                        this._trigger('changeYear', this.viewDate);
1180                    }
1181                    else if (e.shiftKey){
1182                        newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1183                        newViewDate = this.moveMonth(focusDate, dir);
1184                        this._trigger('changeMonth', this.viewDate);
1185                    }
1186                    else {
1187                        newDate = new Date(this.dates.get(-1) || UTCToday());
1188                        newDate.setUTCDate(newDate.getUTCDate() + dir * 7);
1189                        newViewDate = new Date(focusDate);
1190                        newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7);
1191                    }
1192                    if (this.dateWithinRange(newDate)){
1193                        this.focusDate = this.viewDate = newViewDate;
1194                        this.setValue();
1195                        this.fill();
1196                        e.preventDefault();
1197                    }
1198                    break;
1199                case 32: // spacebar
1200                    // Spacebar is used in manually typing dates in some formats.
1201                    // As such, its behavior should not be hijacked.
1202                    break;
1203                case 13: // enter
1204                    focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
1205                    this._toggle_multidate(focusDate);
1206                    dateChanged = true;
1207                    this.focusDate = null;
1208                    this.viewDate = this.dates.get(-1) || this.viewDate;
1209                    this.setValue();
1210                    this.fill();
1211                    if (this.picker.is(':visible')){
1212                        e.preventDefault();
1213                        if (this.o.autoclose)
1214                            this.hide();
1215                    }
1216                    break;
1217                case 9: // tab
1218                    this.focusDate = null;
1219                    this.viewDate = this.dates.get(-1) || this.viewDate;
1220                    this.fill();
1221                    this.hide();
1222                    break;
1223            }
1224            if (dateChanged){
1225                if (this.dates.length)
1226                    this._trigger('changeDate');
1227                else
1228                    this._trigger('clearDate');
1229                var element;
1230                if (this.isInput){
1231                    element = this.element;
1232                }
1233                else if (this.component){
1234                    element = this.element.find('input');
1235                }
1236                if (element){
1237                    element.change();
1238                }
1239            }
1240        },
1241
1242        showMode: function(dir){
1243            if (dir){
1244                this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
1245            }
1246            this.picker
1247                .find('>div')
1248                .hide()
1249                .filter('.cfdatepicker-'+DPGlobal.modes[this.viewMode].clsName)
1250                .css('display', 'block');
1251            this.updateNavArrows();
1252        }
1253    };
1254
1255    var DateRangePicker = function(element, options){
1256        this.element = $(element);
1257        this.inputs = $.map(options.inputs, function(i){
1258            return i.jquery ? i[0] : i;
1259        });
1260        delete options.inputs;
1261
1262        $(this.inputs)
1263            .cfdatepicker(options)
1264            .bind('changeDate', $.proxy(this.dateUpdated, this));
1265
1266        this.pickers = $.map(this.inputs, function(i){
1267            return $(i).data('cfdatepicker');
1268        });
1269        this.updateDates();
1270    };
1271    DateRangePicker.prototype = {
1272        updateDates: function(){
1273            this.dates = $.map(this.pickers, function(i){
1274                return i.getUTCDate();
1275            });
1276            this.updateRanges();
1277        },
1278        updateRanges: function(){
1279            var range = $.map(this.dates, function(d){
1280                return d.valueOf();
1281            });
1282            $.each(this.pickers, function(i, p){
1283                p.setRange(range);
1284            });
1285        },
1286        dateUpdated: function(e){
1287            // `this.updating` is a workaround for preventing infinite recursion
1288            // between `changeDate` triggering and `setUTCDate` calling.  Until
1289            // there is a better mechanism.
1290            if (this.updating)
1291                return;
1292            this.updating = true;
1293
1294            var dp = $(e.target).data('cfdatepicker'),
1295                new_date = dp.getUTCDate(),
1296                i = $.inArray(e.target, this.inputs),
1297                l = this.inputs.length;
1298            if (i === -1)
1299                return;
1300
1301            $.each(this.pickers, function(i, p){
1302                if (!p.getUTCDate())
1303                    p.setUTCDate(new_date);
1304            });
1305
1306            if (new_date < this.dates[i]){
1307                // Date being moved earlier/left
1308                while (i >= 0 && new_date < this.dates[i]){
1309                    this.pickers[i--].setUTCDate(new_date);
1310                }
1311            }
1312            else if (new_date > this.dates[i]){
1313                // Date being moved later/right
1314                while (i < l && new_date > this.dates[i]){
1315                    this.pickers[i++].setUTCDate(new_date);
1316                }
1317            }
1318            this.updateDates();
1319
1320            delete this.updating;
1321        },
1322        remove: function(){
1323            $.map(this.pickers, function(p){ p.remove(); });
1324            delete this.element.data().cfdatepicker;
1325        }
1326    };
1327
1328    function opts_from_el(el, prefix){
1329        // Derive options from element data-attrs
1330        var data = $(el).data(),
1331            out = {}, inkey,
1332            replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
1333        prefix = new RegExp('^' + prefix.toLowerCase());
1334        function re_lower(_,a){
1335            return a.toLowerCase();
1336        }
1337        for (var key in data)
1338            if (prefix.test(key)){
1339                inkey = key.replace(replace, re_lower);
1340                out[inkey] = data[key];
1341            }
1342        return out;
1343    }
1344
1345    function opts_from_locale(lang){
1346        // Derive options from locale plugins
1347        var out = {};
1348        // Check if "de-DE" style date is available, if not language should
1349        // fallback to 2 letter code eg "de"
1350        if (!dates[lang]){
1351            lang = lang.split('-')[0];
1352            if (!dates[lang])
1353                return;
1354        }
1355        var d = dates[lang];
1356        $.each(locale_opts, function(i,k){
1357            if (k in d)
1358                out[k] = d[k];
1359        });
1360        return out;
1361    }
1362
1363    var old = $.fn.cfdatepicker;
1364    $.fn.cfdatepicker = function(option){
1365        var args = Array.apply(null, arguments);
1366        args.shift();
1367        var internal_return;
1368        this.each(function(){
1369            var $this = $(this),
1370                data = $this.data('cfdatepicker'),
1371                options = typeof option === 'object' && option;
1372            if (!data){
1373                var elopts = opts_from_el(this, 'date'),
1374                    // Preliminary otions
1375                    xopts = $.extend({}, defaults, elopts, options),
1376                    locopts = opts_from_locale(xopts.language),
1377                    // Options priority: js args, data-attrs, locales, defaults
1378                    opts = $.extend({}, defaults, locopts, elopts, options);
1379                if ($this.is('.input-daterange') || opts.inputs){
1380                    var ropts = {
1381                        inputs: opts.inputs || $this.find('input').toArray()
1382                    };
1383                    $this.data('cfdatepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
1384                }
1385                else {
1386                    $this.data('cfdatepicker', (data = new Datepicker(this, opts)));
1387                }
1388            }
1389            if (typeof option === 'string' && typeof data[option] === 'function'){
1390                internal_return = data[option].apply(data, args);
1391                if (internal_return !== undefined)
1392                    return false;
1393            }
1394        });
1395        if (internal_return !== undefined)
1396            return internal_return;
1397        else
1398            return this;
1399    };
1400
1401    var defaults = $.fn.cfdatepicker.defaults = {
1402        autoclose: false,
1403        beforeShowDay: $.noop,
1404        calendarWeeks: false,
1405        clearBtn: false,
1406        daysOfWeekDisabled: [],
1407        endDate: Infinity,
1408        forceParse: true,
1409        format: 'mm/dd/yyyy',
1410        keyboardNavigation: true,
1411        language: 'en',
1412        minViewMode: 0,
1413        multidate: false,
1414        multidateSeparator: ',',
1415        orientation: "auto",
1416        rtl: false,
1417        startDate: -Infinity,
1418        startView: 0,
1419        todayBtn: false,
1420        todayHighlight: false,
1421        weekStart: 0
1422    };
1423    var locale_opts = $.fn.cfdatepicker.locale_opts = [
1424        'format',
1425        'rtl',
1426        'weekStart'
1427    ];
1428    $.fn.cfdatepicker.Constructor = Datepicker;
1429    var dates = $.fn.cfdatepicker.dates = {
1430        en: {
1431            days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1432            daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1433            daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1434            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
1434November", "December"],
1435            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1436            today: "Today",
1437            clear: "Clear"
1438        }
1439    };
1440
1441    var DPGlobal = {
1442        modes: [
1443            {
1444                clsName: 'days',
1445                navFnc: 'Month',
1446                navStep: 1
1447            },
1448            {
1449                clsName: 'months',
1450                navFnc: 'FullYear',
1451                navStep: 1
1452            },
1453            {
1454                clsName: 'years',
1455                navFnc: 'FullYear',
1456                navStep: 10
1457            }],
1458        isLeapYear: function(year){
1459            return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
1460        },
1461        getDaysInMonth: function(year, month){
1462            return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
1463        },
1464        validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
1465        nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
1466        parseFormat: function(format){
1467            // IE treats \0 as a string end in inputs (truncating the value),
1468            // so it's a bad format delimiter, anyway
1469            var separators = format.replace(this.validParts, '\0').split('\0'),
1470                parts = format.match(this.validParts);
1471            if (!separators || !separators.length || !parts || parts.length === 0){
1472                throw new Error("Invalid date format.");
1473            }
1474            return {separators: separators, parts: parts};
1475        },
1476        parseDate: function(date, format, language){
1477            if (!date)
1478                return undefined;
1479            if (date instanceof Date)
1480                return date;
1481            if (typeof format === 'string')
1482                format = DPGlobal.parseFormat(format);
1483            var part_re = /([\-+]\d+)([dmwy])/,
1484                parts = date.match(/([\-+]\d+)([dmwy])/g),
1485                part, dir, i;
1486            if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){
1487                date = new Date();
1488                for (i=0; i < parts.length; i++){
1489                    part = part_re.exec(parts[i]);
1490                    dir = parseInt(part[1]);
1491                    switch (part[2]){
1492                        case 'd':
1493                            date.setUTCDate(date.getUTCDate() + dir);
1494                            break;
1495                        case 'm':
1496                            date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
1497                            break;
1498                        case 'w':
1499                            date.setUTCDate(date.getUTCDate() + dir * 7);
1500                            break;
1501                        case 'y':
1502                            date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
1503                            break;
1504                    }
1505                }
1506                return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
1507            }
1508            parts = date && date.match(this.nonpunctuation) || [];
1509            date = new Date();
1510            var parsed = {},
1511                setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
1512                setters_map = {
1513                    yyyy: function(d,v){
1514                        return d.setUTCFullYear(v);
1515                    },
1516                    yy: function(d,v){
1517                        return d.setUTCFullYear(2000+v);
1518                    },
1519                    m: function(d,v){
1520                        if (isNaN(d))
1521                            return d;
1522                        v -= 1;
1523                        while (v < 0) v += 12;
1524                        v %= 12;
1525                        d.setUTCMonth(v);
1526                        while (d.getUTCMonth() !== v)
1527                            d.setUTCDate(d.getUTCDate()-1);
1528                        return d;
1529                    },
1530                    d: function(d,v){
1531                        return d.setUTCDate(v);
1532                    }
1533                },
1534                val, filtered;
1535            setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1536            setters_map['dd'] = setters_map['d'];
1537            date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
1538            var fparts = format.parts.slice();
1539            // Remove noop parts
1540            if (parts.length !== fparts.length){
1541                fparts = $(fparts).filter(function(i,p){
1542                    return $.inArray(p, setters_order) !== -1;
1543                }).toArray();
1544            }
1545            // Process remainder
1546            function match_part(){
1547                var m = this.slice(0, parts[i].length),
1548                    p = parts[i].slice(0, m.length);
1549                return m === p;
1550            }
1551            if (parts.length === fparts.length){
1552                var cnt;
1553                for (i=0, cnt = fparts.length; i < cnt; i++){
1554                    val = parseInt(parts[i], 10);
1555                    part = fparts[i];
1556                    if (isNaN(val)){
1557                        switch (part){
1558                            case 'MM':
1559                                filtered = $(dates[language].months).filter(match_part);
1560                                val = $.inArray(filtered[0], dates[language].months) + 1;
1561                                break;
1562                            case 'M':
1563                                filtered = $(dates[language].monthsShort).filter(match_part);
1564                                val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1565                                break;
1566                        }
1567                    }
1568                    parsed[part] = val;
1569                }
1570                var _date, s;
1571                for (i=0; i < setters_order.length; i++){
1572                    s = setters_order[i];
1573                    if (s in parsed && !isNaN(parsed[s])){
1574                        _date = new Date(date);
1575                        setters_map[s](_date, parsed[s]);
1576                        if (!isNaN(_date))
1577                            date = _date;
1578                    }
1579                }
1580            }
1581            return date;
1582        },
1583        formatDate: function(date, format, language){
1584            if (!date)
1585                return '';
1586            if (typeof format === 'string')
1587                format = DPGlobal.parseFormat(format);
1588            var val = {
1589                d: date.getUTCDate(),
1590                D: dates[language].daysShort[date.getUTCDay()],
1591                DD: dates[language].days[date.getUTCDay()],
1592                m: date.getUTCMonth() + 1,
1593                M: dates[language].monthsShort[date.getUTCMonth()],
1594                MM: dates[language].months[date.getUTCMonth()],
1595                yy: date.getUTCFullYear().toString().substring(2),
1596                yyyy: date.getUTCFullYear()
1597            };
1598            val.dd = (val.d < 10 ? '0' : '') + val.d;
1599            val.mm = (val.m < 10 ? '0' : '') + val.m;
1600            date = [];
1601            var seps = $.extend([], format.separators);
1602            for (var i=0, cnt = format.parts.length; i <= cnt; i++){
1603                if (seps.length)
1604                    date.push(seps.shift());
1605                date.push(val[format.parts[i]]);
1606            }
1607            return date.join('');
1608        },
1609        headTemplate: '<thead>'+
1610        '<tr>'+
1611        '<th class="prev">&laquo;</th>'+
1612        '<th colspan="5" class="cfdatepicker-switch"></th>'+
1613        '<th class="next">&raquo;</th>'+
1614        '</tr>'+
1615        '</thead>',
1616        contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1617        footTemplate: '<tfoot>'+
1618        '<tr>'+
1619        '<th colspan="7" class="today"></th>'+
1620        '</tr>'+
1621        '<tr>'+
1622        '<th colspan="7" class="clear"></th>'+
1623        '</tr>'+
1624        '</tfoot>'
1625    };
1626    DPGlobal.template = '<div class="cfdatepicker">'+
1627        '<div class="cfdatepicker-days">'+
1628        '<table class=" table-condensed">'+
1629        DPGlobal.headTemplate+
1630        '<tbody></tbody>'+
1631        DPGlobal.footTemplate+
1632        '</table>'+
1633        '</div>'+
1634        '<div class="cfdatepicker-months">'+
1635        '<table class="table-condensed">'+
1636        DPGlobal.headTemplate+
1637        DPGlobal.contTemplate+
1638        DPGlobal.footTemplate+
1639        '</table>'+
1640        '</div>'+
1641        '<div class="cfdatepicker-years">'+
1642        '<table class="table-condensed">'+
1643        DPGlobal.headTemplate+
1644        DPGlobal.contTemplate+
1645        DPGlobal.footTemplate+
1646        '</table>'+
1647        '</div>'+
1648        '</div>';
1649
1650    $.fn.cfdatepicker.DPGlobal = DPGlobal;
1651
1652
1653    /* DATEPICKER NO CONFLICT
1654     * =================== */
1655
1656    $.fn.cfdatepicker.noConflict = function(){
1657        $.fn.cfdatepicker = old;
1658        return this;
1659    };
1660
1661    /* DATEPICKER DATA-API
1662     * ================== */
1663    $(document).on(
1664        'focus.cfdatepicker.data-api click.cfdatepicker.data-api',
1665        '[data-provide="cfdatepicker"]',
1666        function(e){
1667            var $this = $(this);
1668            if ($this.data('cfdatepicker'))
1669                return;
1670
1671            // component click requires us to explicitly show it
1672            e.preventDefault();
1673            $this.cfdatepicker('show')
1674                .on('show', function(){ $(this).trigger('blur'); })
1675                .on('hide', function(){ $(this).attr("disabled", false); })
1676        }
1677    );
1678
1679}(window.jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.