PageSourceSearch

https://secure.kolbe.com/_shared/z/datepicker/js/bootstrap-datepicker.js

js kolbe.com collected 2026-09-24 17:20:33 UTC 32,094 bytes, 1,018 lines download raw bytes

1/* =========================================================
2 * bootstrap-datepicker.js
3 * http://www.eyecon.ro/bootstrap-datepicker
4 * =========================================================
5 * Copyright 2012 Stefan Petre
6 * Improvements by Andrew Rowls
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ========================================================= */
20
21!function( $ ) {
22
23	function UTCDate(){
24		return new Date(Date.UTC.apply(Date, arguments));
25	}
26	function UTCToday(){
27		var today = new Date();
28		return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
29	}
30
31	// Picker object
32
33	var Datepicker = function(element, options) {
34		var that = this;
35
36		this.element = $(element);
37		this.language = options.language||this.element.data('date-language')||"en";
38		this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de"
39		this.language = this.language in dates ? this.language : "en";
40		this.isRTL = dates[this.language].rtl||false;
41		this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy');
42		this.isInline = false;
43		this.isInput = this.element.is('input');
44		this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
45		this.hasInput = this.component && this.element.find('input').length;
46		if(this.component && this.component.length === 0)
47			this.component = false;
48
49		this._attachEvents();
50
51		this.forceParse = true;
52		if ('forceParse' in options) {
53			this.forceParse = options.forceParse;
54		} else if ('dateForceParse' in this.element.data()) {
55			this.forceParse = this.element.data('date-force-parse');
56		}
57
58
59		this.picker = $(DPGlobal.template)
60							.appendTo(this.isInline ? this.element : 'body')
61							.on({
62								click: $.proxy(this.click, this),
63								mousedown: $.proxy(this.mousedown, this)
64							});
65
66		if(this.isInline) {
67			this.picker.addClass('datepicker-inline');
68		} else {
69			this.picker.addClass('datepicker-dropdown dropdown-menu');
70		}
71		if (this.isRTL){
72			this.picker.addClass('datepicker-rtl');
73			this.picker.find('.prev i, .next i')
74						.toggleClass('icon-arrow-left icon-arrow-right');
75		}
76		$(document).on('mousedown', function (e) {
77			// Clicked outside the datepicker, hide it
78			if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) {
79				that.hide();
80			}
81		});
82
83		this.autoclose = false;
84		if ('autoclose' in options) {
85			this.autoclose = options.autoclose;
86		} else if ('dateAutoclose' in this.element.data()) {
87			this.autoclose = this.element.data('date-autoclose');
88		}
89
90		this.keyboardNavigation = true;
91		if ('keyboardNavigation' in options) {
92			this.keyboardNavigation = options.keyboardNavigation;
93		} else if ('dateKeyboardNavigation' in this.element.data()) {
94			this.keyboardNavigation = this.element.data('date-keyboard-navigation');
95		}
96
97		this.viewMode = this.startViewMode = 0;
98		switch(options.startView || this.element.data('date-start-view')){
99			case 2:
100			case 'decade':
101				this.viewMode = this.startViewMode = 2;
102				break;
103			case 1:
104			case 'year':
105				this.viewMode = this.startViewMode = 1;
106				break;
107		}
108
109		this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0;
110		if (typeof this.minViewMode === 'string') {
111			switch (this.minViewMode) {
112				case 'months':
113					this.minViewMode = 1;
114					break;
115				case 'years':
116					this.minViewMode = 2;
117					break;
118				default:
119					this.minViewMode = 0;
120					break;
121			}
122		}
123
124		this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode);
125
126		this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
127		this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
128
129		this.calendarWeeks = false;
130		if ('calendarWeeks' in options) {
131			this.calendarWeeks = options.calendarWeeks;
132		} else if ('dateCalendarWeeks' in this.element.data()) {
133			this.calendarWeeks = this.element.data('date-calendar-weeks');
134		}
135		if (this.calendarWeeks)
136			this.picker.find('tfoot th.today')
137						.attr('colspan', function(i, val){
138							return parseInt(val) + 1;
139						});
140
141		this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
142		this.weekEnd = ((this.weekStart + 6) % 7);
143		this.startDate = -Infinity;
144		this.endDate = Infinity;
145		this.daysOfWeekDisabled = [];
146		this.setStartDate(options.startDate||this.element.data('date-startdate'));
147		this.setEndDate(options.endDate||this.element.data('date-enddate'));
148		this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled'));
149		this.fillDow();
150		this.fillMonths();
151		this.update();
152		this.showMode();
153
154		if(this.isInline) {
155			this.show();
156		}
157	};
158
159	Datepicker.prototype = {
160		constructor: Datepicker,
161
162		_events: [],
163		_attachEvents: function(){
164			this._detachEvents();
165			if (this.isInput) { // single input
166				this._events = [
167					[this.element, {
168						focus: $.proxy(this.show, this),
169						keyup: $.proxy(this.update, this),
170						keydown: $.proxy(this.keydown, this)
171					}]
172				];
173			}
174			else if (this.component && this.hasInput){ // component: input + button
175				this._events = [
176					// For components that are not readonly, allow keyboard nav
177					[this.element.find('input'), {
178						focus: $.proxy(this.show, this),
179						keyup: $.proxy(this.update, this),
180						keydown: $.proxy(this.keydown, this)
181					}],
182					[this.component, {
183						click: $.proxy(this.show, this)
184					}]
185				];
186			}
187						else if (this.element.is('div')) {  // inline datepicker
188							this.isInline = true;
189						}
190			else {
191				this._events = [
192					[this.element, {
193						click: $.proxy(this.show, this)
194					}]
195				];
196			}
197			for (var i=0, el, ev; i<this._events.length; i++){
198				el = this._events[i][0];
199				ev = this._events[i][1];
200				el.on(ev);
201			}
202		},
203		_detachEvents: function(){
204			for (var i=0, el, ev; i<this._events.length; i++){
205				el = this._events[i][0];
206				ev = this._events[i][1];
207				el.off(ev);
208			}
209			this._events = [];
210		},
211
212		show: function(e) {
213			this.picker.show();
214			this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
215			this.update();
216			this.place();
217			$(window).on('resize', $.proxy(this.place, this));
218			if (e ) {
219				e.stopPropagation();
220				e.preventDefault();
221			}
222			this.element.trigger({
223				type: 'show',
224				date: this.date
225			});
226		},
227
228		hide: function(e){
229			if(this.isInline) return;
230			if (!this.picker.is(':visible')) return;
231			this.picker.hide();
232			$(window).off('resize', this.place);
233			this.viewMode = this.startViewMode;
234			this.showMode();
235			if (!this.isInput) {
236				$(document).off('mousedown', this.hide);
237			}
238
239			if (
240				this.forceParse &&
241				(
242					this.isInput && this.element.val() ||
243					this.hasInput && this.element.find('input').val()
244				)
245			)
246				this.setValue();
247			this.element.trigger({
248				type: 'hide',
249				date: this.date
250			});
251		},
252
253		remove: function() {
254			this._detachEvents();
255			this.picker.remove();
256			delete this.element.data().datepicker;
257			if (!this.isInput) {
258				delete this.element.data().date;
259			}
260		},
261
262		getDate: function() {
263			var d = this.getUTCDate();
264			return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
265		},
266
267		getUTCDate: function() {
268			return this.date;
269		},
270
271		setDate: function(d) {
272			this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
273		},
274
275		setUTCDate: function(d) {
276			this.date = d;
277			this.setValue();
278		},
279
280		setValue: function() {
281			var formatted = this.getFormattedDate();
282			if (!this.isInput) {
283				if (this.component){
284					this.element.find('input').val(formatted);
285				}
286				this.element.data('date', formatted);
287			} else {
288				this.element.val(formatted);
289			}
290		},
291
292		getFormattedDate: function(format) {
293			if (format === undefined)
294				format = this.format;
295			return DPGlobal.formatDate(this.date, format, this.language);
296		},
297
298		setStartDate: function(startDate){
299			this.startDate = startDate||-Infinity;
300			if (this.startDate !== -Infinity) {
301				this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
302			}
303			this.update();
304			this.updateNavArrows();
305		},
306
307		setEndDate: function(endDate){
308			this.endDate = endDate||Infinity;
309			if (this.endDate !== Infinity) {
310				this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
311			}
312			this.update();
313			this.updateNavArrows();
314		},
315
316		setDaysOfWeekDisabled: function(daysOfWeekDisabled){
317			this.daysOfWeekDisabled = daysOfWeekDisabled||[];
318			if (!$.isArray(this.daysOfWeekDisabled)) {
319				this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
320			}
321			this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
322				return parseInt(d, 10);
323			});
324			this.update();
325			this.updateNavArrows();
326		},
327
328		place: function(){
329						if(this.isInline) return;
330			var zIndex = parseInt(this.element.parents().filter(function() {
331							return $(this).css('z-index') != 'auto';
332						}).first().css('z-index'))+10;
333			var offset = this.component ? this.component.parent().offset() : this.element.offset();
334			var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
335			this.picker.css({
336				top: offset.top + height,
337				left: offset.left,
338				zIndex: zIndex
339			});
340		},
341
342		update: function(){
343			var date, fromArgs = false;
344			if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
345				date = arguments[0];
346				fromArgs = true;
347			} else {
348				date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
349			}
350
351			this.date = DPGlobal.parseDate(date, this.format, this.language);
352
353			if(fromArgs) this.setValue();
354
355			if (this.date < this.startDate) {
356				this.viewDate = new Date(this.startDate);
357			} else if (this.date > this.endDate) {
358				this.viewDate = new Date(this.endDate);
359			} else {
360				this.viewDate = new Date(this.date);
361			}
362			this.fill();
363		},
364
365		fillDow: function(){
366			var dowCnt = this.weekStart,
367			html = '<tr>';
368			if(this.calendarWeeks){
369				var cell = '<th class="cw">&nbsp;</th>';
370				html += cell;
371				this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
372			}
373			while (dowCnt < this.weekStart + 7) {
374				html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
375			}
376			html += '</tr>';
377			this.picker.find('.datepicker-days thead').append(html);
378		},
379
380		fillMonths: function(){
381			var html = '',
382			i = 0;
383			while (i < 12) {
384				html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
385			}
386			this.picker.find('.datepicker-months td').html(html);
387		},
388
389		fill: function() {
390			var d = new Date(this.viewDate),
391				year = d.getUTCFullYear(),
392				month = d.getUTCMonth(),
393				startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
394				startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
395				endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
396				endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
397				currentDate = this.date && this.date.valueOf(),
398				today = new Date();
399			this.picker.find('.datepicker-days thead th.switch')
400						.text(dates[this.language].months[month]+' '+year);
401			this.picker.find('tfoot th.today')
402						.text(dates[this.language].today)
403						.toggle(this.todayBtn !== false);
404			this.updateNavArrows();
405			this.fillMonths();
406			var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
407				day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
408			prevMonth.setUTCDate(day);
409			prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
410			var nextMonth = new Date(prevMonth);
411			nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
412			nextMonth = nextMonth.valueOf();
413			var html = [];
414			var clsName;
415			while(prevMonth.valueOf() < nextMonth) {
416				if (prevMonth.getUTCDay() == this.weekStart) {
417					html.push('<tr>');
418					if(this.calendarWeeks){
419						// ISO 8601: First week contains first thursday.
420						// ISO also states week starts on Monday, but we can be more abstract here.
421						var
422							// Start of current week: based on weekstart/current date
423							ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
424							// Thursday of this week
425							th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
426							// First Thursday of year, year from thursday
427							yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
428							// Calendar week: ms between thursdays, div ms per day, div 7 days
429							calWeek =  (th - yth) / 864e5 / 7 + 1;
430						html.push('<td class="cw">'+ calWeek +'</td>');
431
432					}
433				}
434				clsName = '';
435				if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
436					clsName += ' old';
437				} else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
438					clsName += ' new';
439				}
440				// Compare internal UTC date with local today, not UTC today
441				if (this.todayHighlight &&
442					prevMonth.getUTCFullYear() == today.getFullYear() &&
443					prevMonth.getUTCMonth() == today.getMonth() &&
444					prevMonth.getUTCDate() == today.getDate()) {
445					clsName += ' today';
446				}
447				if (currentDate && prevMonth.valueOf() == currentDate) {
448					clsName += ' active';
449				}
450				if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
451					$.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
452					clsName += ' disabled';
453				}
454				html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
455				if (prevMonth.getUTCDay() == this.weekEnd) {
456					html.push('</tr>');
457				}
458				prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
459			}
460			this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
461			var currentYear = this.date && this.date.getUTCFullYear();
462
463			var months = this.picker.find('.datepicker-months')
464						.find('th:eq(1)')
465							.text(year)
466							.end()
467						.find('span').removeClass('active');
468			if (currentYear && currentYear == year) {
469				months.eq(this.date.getUTCMonth()).addClass('active');
470			}
471			if (year < startYear || year > endYear) {
472				months.addClass('disabled');
473			}
474			if (year == startYear) {
475				months.slice(0, startMonth).addClass('disabled');
476			}
477			if (year == endYear) {
478				months.slice(endMonth+1).addClass('disabled');
479			}
480
481			html = '';
482			year = parseInt(year/10, 10) * 10;
483			var yearCont = this.picker.find('.datepicker-years')
484								.find('th:eq(1)')
485									.text(year + '-' + (year + 9))
486									.end()
487								.find('td');
488			year -= 1;
489			for (var i = -1; i < 11; i++) {
490				html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
491				year += 1;
492			}
493			yearCont.html(html);
494		},
495
496		updateNavArrows: function() {
497			var d = new Date(this.viewDate),
498				year = d.getUTCFullYear(),
499				month = d.getUTCMonth();
500			switch (this.viewMode) {
501				case 0:
502					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
503						this.picker.find('.prev').css({visibility: 'hidden'});
504					} else {
505						this.picker.find('.prev').css({visibility: 'visible'});
506					}
507					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
508						this.picker.find('.next').css({visibility: 'hidden'});
509					} else {
510						this.picker.find('.next').css({visibility: 'visible'});
511					}
512					break;
513				case 1:
514				case 2:
515					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
516						this.picker.find('.prev').css({visibility: 'hidden'});
517					} else {
518						this.picker.find('.prev').css({visibility: 'visible'});
519					}
520					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
521						this.picker.find('.next').css({visibility: 'hidden'});
522					} else {
523						this.picker.find('.next').css({visibility: 'visible'});
524					}
525					break;
526			}
527		},
528
529		click: function(e) {
530			e.stopPropagation();
531			e.preventDefault();
532			var target = $(e.target).closest('span, td, th');
533			if (target.length == 1) {
534				switch(target[0].nodeName.toLowerCase()) {
535					case 'th':
536						switch(target[0].className) {
537							case 'switch':
538								this.showMode(1);
539								break;
540							case 'prev':
541							case 'next':
542								var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
543								switch(this.viewMode){
544									case 0:
545										this.viewDate = this.moveMonth(this.viewDate, dir);
546										break;
547									case 1:
548									case 2:
549										this.viewDate = this.moveYear(this.viewDate, dir);
550										break;
551								}
552								this.fill();
553								break;
554							case 'today':
555								var date = new Date();
556								date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
557
558								this.showMode(-2);
559								var which = this.todayBtn == 'linked' ? null : 'view';
560								this._setDate(date, which);
561								break;
562						}
563						break;
564					case 'span':
565						if (!target.is('.disabled')) {
566							this.viewDate.setUTCDate(1);
567							if (target.is('.month')) {
568								var day = 1;
569								var month = target.parent().find('span').index(target);
570								var year = this.viewDate.getUTCFullYear();
571								this.viewDate.setUTCMonth(month);
572								this.element.trigger({
573									type: 'changeMonth',
574									date: this.viewDate
575								});
576								if ( this.minViewMode == 1 ) {
577									this._setDate(UTCDate(year, month, day,0,0,0,0));
578								}
579							} else {
580								var year = parseInt(target.text(), 10)||0;
581								var day = 1;
582								var month = 0;
583								this.viewDate.setUTCFullYear(year);
584								this.element.trigger({
585									type: 'changeYear',
586									date: this.viewDate
587								});
588								if ( this.minViewMode == 2 ) {
589									this._setDate(UTCDate(year, month, day,0,0,0,0));
590								}
591							}
592							this.showMode(-1);
593							this.fill();
594						}
595						break;
596					case 'td':
597						if (target.is('.day') && !target.is('.disabled')){
598							var day = parseInt(target.text(), 10)||1;
599							var year = this.viewDate.getUTCFullYear(),
600								month = this.viewDate.getUTCMonth();
601							if (target.is('.old')) {
602								if (month === 0) {
603									month = 11;
604									year -= 1;
605								} else {
606									month -= 1;
607								}
608							} else if (target.is('.new')) {
609								if (month == 11) {
610									month = 0;
611									year += 1;
612								} else {
613									month += 1;
614								}
615							}
616							this._setDate(UTCDate(year, month, day,0,0,0,0));
617						}
618						break;
619				}
620			}
621		},
622
623		_setDate: function(date, which){
624			if (!which || which == 'date')
625				this.date = date;
626			if (!which || which  == 'view')
627				this.viewDate = date;
628			this.fill();
629			this.setValue();
630			this.element.trigger({
631				type: 'changeDate',
632				date: this.date
633			});
634			var element;
635			if (this.isInput) {
636				element = this.element;
637			} else if (this.component){
638				element = this.element.find('input');
639			}
640			if (element) {
641				element.change();
642				if (this.autoclose && (!which || which == 'date')) {
643					this.hide();
644				}
645			}
646		},
647
648		moveMonth: function(date, dir){
649			if (!dir) return date;
650			var new_date = new Date(date.valueOf()),
651				day = new_date.getUTCDate(),
652				month = new_date.getUTCMonth(),
653				mag = Math.abs(dir),
654				new_month, test;
655			dir = dir > 0 ? 1 : -1;
656			if (mag == 1){
657				test = dir == -1
658					// If going back one month, make sure month is not current month
659					// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
660					? function(){ return new_date.getUTCMonth() == month; }
661					// If going forward one month, make sure month is as expected
662					// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
663					: function(){ return new_date.getUTCMonth() != new_month; };
664				new_month = month + dir;
665				new_date.setUTCMonth(new_month);
666				// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
667				if (new_month < 0 || new_month > 11)
668					new_month = (new_month + 12) % 12;
669			} else {
670				// For magnitudes >1, move one month at a time...
671				for (var i=0; i<mag; i++)
672					// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
673					new_date = this.moveMonth(new_date, dir);
674				// ...then reset the day, keeping it in the new month
675				new_month = new_date.getUTCMonth();
676				new_date.setUTCDate(day);
677				test = function(){ return new_month != new_date.getUTCMonth(); };
678			}
679			// Common date-resetting loop -- if date is beyond end of month, make it
680			// end of month
681			while (test()){
682				new_date.setUTCDate(--day);
683				new_date.setUTCMonth(new_month);
684			}
685			return new_date;
686		},
687
688		moveYear: function(date, dir){
689			return this.moveMonth(date, dir*12);
690		},
691
692		dateWithinRange: function(date){
693			return date >= this.startDate && date <= this.endDate;
694		},
695
696		keydown: function(e){
697			if (this.picker.is(':not(:visible)')){
698				if (e.keyCode == 27) // allow escape to hide and re-show picker
699					this.show();
700				return;
701			}
702			var dateChanged = false,
703				dir, day, month,
704				newDate, newViewDate;
705			switch(e.keyCode){
706				case 27: // escape
707					this.hide();
708					e.preventDefault();
709					break;
710				case 37: // left
711				case 39: // right
712					if (!this.keyboardNavigation) break;
713					dir = e.keyCode == 37 ? -1 : 1;
714					if (e.ctrlKey){
715						newDate = this.moveYear(this.date, dir);
716						newViewDate = this.moveYear(this.viewDate, dir);
717					} else if (e.shiftKey){
718						newDate = this.moveMonth(this.date, dir);
719						newViewDate = this.moveMonth(this.viewDate, dir);
720					} else {
721						newDate = new Date(this.date);
722						newDate.setUTCDate(this.date.getUTCDate() + dir);
723						newViewDate = new Date(this.viewDate);
724						newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
725					}
726					if (this.dateWithinRange(newDate)){
727						this.date = newDate;
728						this.viewDate = newViewDate;
729						this.setValue();
730						this.update();
731						e.preventDefault();
732						dateChanged = true;
733					}
734					break;
735				case 38: // up
736				case 40: // down
737					if (!this.keyboardNavigation) break;
738					dir = e.keyCode == 38 ? -1 : 1;
739					if (e.ctrlKey){
740						newDate = this.moveYear(this.date, dir);
741						newViewDate = this.moveYear(this.viewDate, dir);
742					} else if (e.shiftKey){
743						newDate = this.moveMonth(this.date, dir);
744						newViewDate = this.moveMonth(this.viewDate, dir);
745					} else {
746						newDate = new Date(this.date);
747						newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
748						newViewDate = new Date(this.viewDate);
749						newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
750					}
751					if (this.dateWithinRange(newDate)){
752						this.date = newDate;
753						this.viewDate = newViewDate;
754						this.setValue();
755						this.update();
756						e.preventDefault();
757						dateChanged = true;
758					}
759					break;
760				case 13: // enter
761					this.hide();
762					e.preventDefault();
763					break;
764				case 9: // tab
765					this.hide();
766					break;
767			}
768			if (dateChanged){
769				this.element.trigger({
770					type: 'changeDate',
771					date: this.date
772				});
773				var element;
774				if (this.isInput) {
775					element = this.element;
776				} else if (this.component){
777					element = this.element.find('input');
778				}
779				if (element) {
780					element.change();
781				}
782			}
783		},
784
785		showMode: function(dir) {
786			if (dir) {
787				this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
788			}
789			/*
790				vitalets: fixing bug of very special conditions:
791				jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
792				Method show() does not set display css correctly and datepicker is not shown.
793				Changed to .css('display', 'block') solve the problem.
794				See https://github.com/vitalets/x-editable/issues/37
795
796				In jquery 1.7.2+ everything works fine.
797			*/
798			//this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
799			this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
800			this.updateNavArrows();
801		}
802	};
803
804	$.fn.datepicker = function ( option ) {
805		var args = Array.apply(null, arguments);
806		args.shift();
807		return this.each(function () {
808			var $this = $(this),
809				data = $this.data('datepicker'),
810				options = typeof option == 'object' && option;
811			if (!data) {
812				$this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
813			}
814			if (typeof option == 'string' && typeof data[option] == 'function') {
815				data[option].apply(data, args);
816			}
817		});
818	};
819
820	$.fn.datepicker.defaults = {
821	};
822	$.fn.datepicker.Constructor = Datepicker;
823	var dates = $.fn.datepicker.dates = {
824		en: {
825			days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
826			daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
827			daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
828			months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
828November", "December"],
829			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
830			today: "Today"
831		}
832	};
833
834	var DPGlobal = {
835		modes: [
836			{
837				clsName: 'days',
838				navFnc: 'Month',
839				navStep: 1
840			},
841			{
842				clsName: 'months',
843				navFnc: 'FullYear',
844				navStep: 1
845			},
846			{
847				clsName: 'years',
848				navFnc: 'FullYear',
849				navStep: 10
850		}],
851		isLeapYear: function (year) {
852			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
853		},
854		getDaysInMonth: function (year, month) {
855			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
856		},
857		validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
858		nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
859		parseFormat: function(format){
860			// IE treats \0 as a string end in inputs (truncating the value),
861			// so it's a bad format delimiter, anyway
862			var separators = format.replace(this.validParts, '\0').split('\0'),
863				parts = format.match(this.validParts);
864			if (!separators || !separators.length || !parts || parts.length === 0){
865				throw new Error("Invalid date format.");
866			}
867			return {separators: separators, parts: parts};
868		},
869		parseDate: function(date, format, language) {
870			if (date instanceof Date) return date;
871			if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
872				var part_re = /([\-+]\d+)([dmwy])/,
873					parts = date.match(/([\-+]\d+)([dmwy])/g),
874					part, dir;
875				date = new Date();
876				for (var i=0; i<parts.length; i++) {
877					part = part_re.exec(parts[i]);
878					dir = parseInt(part[1]);
879					switch(part[2]){
880						case 'd':
881							date.setUTCDate(date.getUTCDate() + dir);
882							break;
883						case 'm':
884							date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
885							break;
886						case 'w':
887							date.setUTCDate(date.getUTCDate() + dir * 7);
888							break;
889						case 'y':
890							date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
891							break;
892					}
893				}
894				return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
895			}
896			var parts = date && date.match(this.nonpunctuation) || [],
897				date = new Date(),
898				parsed = {},
899				setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
900				setters_map = {
901					yyyy: function(d,v){ return d.setUTCFullYear(v); },
902					yy: function(d,v){ return d.setUTCFullYear(2000+v); },
903					m: function(d,v){
904						v -= 1;
905						while (v<0) v += 12;
906						v %= 12;
907						d.setUTCMonth(v);
908						while (d.getUTCMonth() != v)
909							d.setUTCDate(d.getUTCDate()-1);
910						return d;
911					},
912					d: function(d,v){ return d.setUTCDate(v); }
913				},
914				val, filtered, part;
915			setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
916			setters_map['dd'] = setters_map['d'];
917			date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
918			var fparts = format.parts.slice();
919			// Remove noop parts
920			if (parts.length != fparts.length) {
921				fparts = $(fparts).filter(function(i,p){
922					return $.inArray(p, setters_order) !== -1;
923				}).toArray();
924			}
925			// Process remainder
926			if (parts.length == fparts.length) {
927				for (var i=0, cnt = fparts.length; i < cnt; i++) {
928					val = parseInt(parts[i], 10);
929					part = fparts[i];
930					if (isNaN(val)) {
931						switch(part) {
932							case 'MM':
933								filtered = $(dates[language].months).filter(function(){
934									var m = this.slice(0, parts[i].length),
935										p = parts[i].slice(0, m.length);
936									return m == p;
937								});
938								val = $.inArray(filtered[0], dates[language].months) + 1;
939								break;
940							case 'M':
941								filtered = $(dates[language].monthsShort).filter(function(){
942									var m = this.slice(0, parts[i].length),
943										p = parts[i].slice(0, m.length);
944									return m == p;
945								});
946								val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
947								break;
948						}
949					}
950					parsed[part] = val;
951				}
952				for (var i=0, s; i<setters_order.length; i++){
953					s = setters_order[i];
954					if (s in parsed && !isNaN(parsed[s]))
955						setters_map[s](date, parsed[s]);
956				}
957			}
958			return date;
959		},
960		formatDate: function(date, format, language){
961			var val = {
962				d: date.getUTCDate(),
963				D: dates[language].daysShort[date.getUTCDay()],
964				DD: dates[language].days[date.getUTCDay()],
965				m: date.getUTCMonth() + 1,
966				M: dates[language].monthsShort[date.getUTCMonth()],
967				MM: dates[language].months[date.getUTCMonth()],
968				yy: date.getUTCFullYear().toString().substring(2),
969				yyyy: date.getUTCFullYear()
970			};
971			val.dd = (val.d < 10 ? '0' : '') + val.d;
972			val.mm = (val.m < 10 ? '0' : '') + val.m;
973			var date = [],
974				seps = $.extend([], format.separators);
975			for (var i=0, cnt = format.parts.length; i < cnt; i++) {
976				if (seps.length)
977					date.push(seps.shift());
978				date.push(val[format.parts[i]]);
979			}
980			return date.join('');
981		},
982		headTemplate: '<thead>'+
983							'<tr>'+
984								'<th class="prev"><i class="icon-arrow-left"/></th>'+
985								'<th colspan="5" class="switch"></th>'+
986								'<th class="next"><i class="icon-arrow-right"/></th>'+
987							'</tr>'+
988						'</thead>',
989		contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
990		footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
991	};
992	DPGlobal.template = '<div class="datepicker">'+
993							'<div class="datepicker-days">'+
994								'<table class=" table-condensed">'+
995									DPGlobal.headTemplate+
996									'<tbody></tbody>'+
997									DPGlobal.footTemplate+
998								'</table>'+
999							'</div>'+
1000							'<div class="datepicker-months">'+
1001								'<table class="table-condensed">'+
1002									DPGlobal.headTemplate+
1003									DPGlobal.contTemplate+
1004									DPGlobal.footTemplate+
1005								'</table>'+
1006							'</div>'+
1007							'<div class="datepicker-years">'+
1008								'<table class="table-condensed">'+
1009									DPGlobal.headTemplate+
1010									DPGlobal.contTemplate+
1011									DPGlobal.footTemplate+
1012								'</table>'+
1013							'</div>'+
1014						'</div>';
1015
1016	$.fn.datepicker.DPGlobal = DPGlobal;
1017
1018}( 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.