PageSourceSearch

https://www.gerold-klarinetten.at/assets/scripts/datepick.js?1765310991

js gerold-klarinetten.at collected 2026-09-25 10:32:39 UTC 103,804 bytes, 2,573 lines download raw bytes

1/* Simple JavaScript Inheritance
2 * By John Resig http://ejohn.org/
3 * MIT Licensed.
4 */
5// Inspired by base2 and Prototype
6(function(){
7	var initializing = false;
8
9	// The base JQClass implementation (does nothing)
10	window.JQClass = function(){};
11
12	// Collection of derived classes
13	JQClass.classes = {};
14 
15	// Create a new JQClass that inherits from this class
16	JQClass.extend = function extender(prop) {
17		var base = this.prototype;
18
19		// Instantiate a base class (but only create the instance,
20		// don't run the init constructor)
21		initializing = true;
22		var prototype = new this();
23		initializing = false;
24
25		// Copy the properties over onto the new prototype
26		for (var name in prop) {
27			// Check if we're overwriting an existing function
28			prototype[name] = typeof prop[name] == 'function' &&
29				typeof base[name] == 'function' ?
30				(function(name, fn){
31					return function() {
32						var __super = this._super;
33
34						// Add a new ._super() method that is the same method
35						// but on the super-class
36						this._super = function(args) {
37							return base[name].apply(this, args || []);
38						};
39
40						var ret = fn.apply(this, arguments);				
41
42						// The method only need to be bound temporarily, so we
43						// remove it when we're done executing
44						this._super = __super;
45
46						return ret;
47					};
48				})(name, prop[name]) :
49				prop[name];
50		}
51
52		// The dummy class constructor
53		function JQClass() {
54			// All construction is actually done in the init method
55			if (!initializing && this._init) {
56				this._init.apply(this, arguments);
57			}
58		}
59
60		// Populate our constructed prototype object
61		JQClass.prototype = prototype;
62
63		// Enforce the constructor to be what we expect
64		JQClass.prototype.constructor = JQClass;
65
66		// And make this class extendable
67		JQClass.extend = extender;
68
69		return JQClass;
70	};
71})();
72
73(function($) { // Ensure $, encapsulate
74
75	/** Abstract base class for collection plugins v1.0.1.
76		Written by Keith Wood (kbwood{at}iinet.com.au) December 2013.
77		Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license.
78		@module $.JQPlugin
79		@abstract */
80	JQClass.classes.JQPlugin = JQClass.extend({
81
82		/** Name to identify this plugin.
83			@example name: 'tabs' */
84		name: 'plugin',
85
86		/** Default options for instances of this plugin (default: {}).
87			@example defaultOptions: {
88 	selectedClass: 'selected',
89 	triggers: 'click'
90 } */
91		defaultOptions: {},
92		
93		/** Options dependent on the locale.
94			Indexed by language and (optional) country code, with '' denoting the default language (English/US).
95			@example regionalOptions: {
96	'': {
97		greeting: 'Hi'
98	}
99 } */
100		regionalOptions: {},
101		
102		/** Names of getter methods - those that can't be chained (default: []).
103			@example _getters: ['activeTab'] */
104		_getters: [],
105
106		/** Retrieve a marker class for affected elements.
107			@private
108			@return {string} The marker class. */
109		_getMarker: function() {
110			return 'is-' + this.name;
111		},
112		
113		/** Initialise the plugin.
114			Create the jQuery bridge - plugin name <code>xyz</code>
115			produces <code>$.xyz</code> and <code>$.fn.xyz</code>. */
116		_init: function() {
117			// Apply default localisations
118			$.extend(this.defaultOptions, (this.regionalOptions && this.regionalOptions['']) || {});
119			// Camel-case the name
120			var jqName = camelCase(this.name);
121			// Expose jQuery singleton manager
122			$[jqName] = this;
123			// Expose jQuery collection plugin
124			$.fn[jqName] = function(options) {
125				var otherArgs = Array.prototype.slice.call(arguments, 1);
126				if ($[jqName]._isNotChained(options, otherArgs)) {
127					return $[jqName][options].apply($[jqName], [this[0]].concat(otherArgs));
128				}
129				return this.each(function() {
130					if (typeof options === 'string') {
131						if (options[0] === '_' || !$[jqName][options]) {
132							throw 'Unknown method: ' + options;
133						}
134						$[jqName][options].apply($[jqName], [this].concat(otherArgs));
135					}
136					else {
137						$[jqName]._attach(this, options);
138					}
139				});
140			};
141		},
142
143		/** Set default values for all subsequent instances.
144			@param options {object} The new default options.
145			@example $.plugin.setDefauls({name: value}) */
146		setDefaults: function(options) {
147			$.extend(this.defaultOptions, options || {});
148		},
149		
150		/** Determine whether a method is a getter and doesn't permit chaining.
151			@private
152			@param name {string}
152 The method name.
153			@param otherArgs {any[]} Any other arguments for the method.
154			@return {boolean} True if this method is a getter, false otherwise. */
155		_isNotChained: function(name, otherArgs) {
156			if (name === 'option' && (otherArgs.length === 0 ||
157					(otherArgs.length === 1 && typeof otherArgs[0] === 'string'))) {
158				return true;
159			}
160			return $.inArray(name, this._getters) > -1;
161		},
162		
163		/** Initialise an element. Called internally only.
164			Adds an instance object as data named for the plugin.
165			@param elem {Element} The element to enhance.
166			@param options {object} Overriding settings. */
167		_attach: function(elem, options) {
168			elem = $(elem);
169			if (elem.hasClass(this._getMarker())) {
170				return;
171			}
172			elem.addClass(this._getMarker());
173			options = $.extend({}, this.defaultOptions, this._getMetadata(elem), options || {});
174			var inst = $.extend({name: this.name, elem: elem, options: options},
175				this._instSettings(elem, options));
176			elem.data(this.name, inst); // Save instance against element
177			this._postAttach(elem, inst);
178			this.option(elem, options);
179		},
180
181		/** Retrieve additional instance settings.
182			Override this in a sub-class to provide extra settings.
183			@param elem {jQuery} The current jQuery element.
184			@param options {object} The instance options.
185			@return {object} Any extra instance values.
186			@example _instSettings: function(elem, options) {
187 	return {nav: elem.find(options.navSelector)};
188 } */
189		_instSettings: function(elem, options) {
190			return {};
191		},
192
193		/** Plugin specific post initialisation.
194			Override this in a sub-class to perform extra activities.
195			@param elem {jQuery} The current jQuery element.
196			@param inst {object} The instance settings.
197			@example _postAttach: function(elem, inst) {
198 	elem.on('click.' + this.name, function() {
199 		...
200 	});
201 } */
202		_postAttach: function(elem, inst) {
203		},
204
205		/** Retrieve metadata configuration from the element.
206			Metadata is specified as an attribute:
207			<code>data-&lt;plugin name>="&lt;setting name>: '&lt;value>', ..."</code>.
208			Dates should be specified as strings in this format: 'new Date(y, m-1, d)'.
209			@private
210			@param elem {jQuery} The source element.
211			@return {object} The inline configuration or {}. */
212		_getMetadata: function(elem) {
213			try {
214				var data = elem.data(this.name.toLowerCase()) || '';
215				data = data.replace(/'/g, '"');
216				data = data.replace(/([a-zA-Z0-9]+):/g, function(match, group, i) { 
217					var count = data.substring(0, i).match(/"/g); // Handle embedded ':'
218					return (!count || count.length % 2 === 0 ? '"' + group + '":' : group + ':');
219				});
220				data = $.parseJSON('{' + data + '}');
221				for (var name in data) { // Convert dates
222					var value = data[name];
223					if (typeof value === 'string' && value.match(/^new Date\((.*)\)$/)) {
224						data[name] = eval(value);
225					}
226				}
227				return data;
228			}
229			catch (e) {
230				return {};
231			}
232		},
233
234		/** Retrieve the instance data for element.
235			@param elem {Element} The source element.
236			@return {object} The instance data or {}. */
237		_getInst: function(elem) {
238			return $(elem).data(this.name) || {};
239		},
240		
241		/** Retrieve or reconfigure the settings for a plugin.
242			@param elem {Element} The source element.
243			@param name {object|string} The collection of new option values or the name of a single option.
244			@param [value] {any} The value for a single named option.
245			@return {any|object} If retrieving a single value or all options.
246			@example $(selector).plugin('option', 'name', value)
247 $(selector).plugin('option', {name: value, ...})
248 var value = $(selector).plugin('option', 'name')
249 var options = $(selector).plugin('option') */
250		option: function(elem, name, value) {
251			elem = $(elem);
252			var inst = elem.data(this.name);
253			if  (!name || (typeof name === 'string' && value == null)) {
254				var options = (inst || {}).options;
255				return (options && name ? options[name] : options);
256			}
257			if (!elem.hasClass(this._getMarker())) {
258				return;
259			}
260			var options = name || {};
261			if (typeof name === 'string') {
262				options = {};
263				options[name] = value;
264			}
265			this._optionsChanged(elem, inst, options);
266			$.extend(inst.options, options);
267		},
268		
269		/** Plugin specific options processing.
270			Old value available in <code>inst.options[name]</code>, new value in <code>options[name]</code>.
271			Override this in a sub-class to perform extra activities.
272			@param elem {jQuery} The current jQuery element.
273			@param inst {object} The instance settings.
274			@param options {object} The new options.
275			@example _optionsChanged: function(elem, inst, options) {
276 	if (options.name != inst.options.name) {
277 		elem.removeClass(inst.options.name).addClass(options.name);
278 	}
279 } */
280		_optionsChanged: function(elem, inst, options) {
281		},
282		
283		/** Remove all trace of the plugin.
284			Override <code>_preDestroy</code> for plugin-specific processing.
285			@param elem {Element} The source element.
286			@example $(selector).plugin('destroy') */
287		destroy: function(elem) {
288			elem = $(elem);
289			if (!elem.hasClass(this._getMarker())) {
290				return;
291			}
292			this._preDestroy(elem, this._getInst(elem));
293			elem.removeData(this.name).removeClass(this._getMarker());
294		},
295
296		/** Plugin specific pre destruction.
297			Override this in a sub-class to perform extra activities and undo everything that was
298			done in the <code>_postAttach</code> or <code>_optionsChanged</code> functions.
299			@param elem {jQuery} The current jQuery element.
300			@param inst {object} The instance settings.
301			@example _preDestroy: function(elem, inst) {
302 	elem.off('.' + this.name);
303 } */
304		_preDestroy: function(elem, inst) {
305		}
306	});
307	
308	/** Convert names from hyphenated to camel-case.
309		@private
310		@param value {string} The original hyphenated name.
311		@return {string} The camel-case version. */
312	function camelCase(name) {
313		return name.replace(/-([a-z])/g, function(match, group) {
314			return group.toUpperCase();
315		});
316	}
317	
318	/** Expose the plugin base.
319		@namespace "$.JQPlugin" */
320	$.JQPlugin = {
321	
322		/** Create a new collection plugin.
323			@memberof "$.JQPlugin"
324			@param [superClass='JQPlugin'] {string} The name of the parent class to inherit from.
325			@param overrides {object} The property/function overrides for the new class.
326			@example $.JQPlugin.createPlugin({
327 	name: 'tabs',
328 	defaultOptions: {selectedClass: 'selected'},
329 	_initSettings: function(elem, options) { return {...}; },
330 	_postAttach: function(elem, inst) { ... }
331 }); */
332		createPlugin: function(superClass, overrides) {
333			if (typeof superClass === 'object') {
334				overrides = superClass;
335				superClass = 'JQPlugin';
336			}
337			superClass = camelCase(superClass);
338			var className = camelCase(overrides.name);
339			JQClass.classes[className] = JQClass.classes[superClass].extend(overrides);
340			new JQClass.classes[className]();
341		}
342	};
343
344})(jQuery);
345
346
347/* http://keith-wood.name/datepick.html
348   Date picker for jQuery v5.0.0.
349   Written by Keith Wood (kbwood{at}iinet.com.au) February 2010.
350   Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) licence. 
351   Please attribute the author if you use it. */
352
353(function($) { // Hide scope, no $ conflict
354
355	var pluginName = 'datepick';
356
357
358	/** Create the datepicker plugin.
359		<p>Sets an input field to popup a calendar for date entry,
360			or a <code>div</code> or <code>span</code> to show an inline calendar.</p>
361		<p>Expects HTML like:</p>
362		<pre>&lt;input type="text"> or &lt;div>&lt;/div></pre>
363		<p>Provide inline configuration like:</p>
364		<pre>&lt;input type="text" data-datepick="name: 'value'"/></pre>
365	 	@module Datepick
366		@augments JQPlugin
367		@example $(selector).datepick()
368 $(selector).datepick({minDate: 0, maxDate: '+1m +1w'}) */
369	$.JQPlugin.createPlugin({
370	
371		/** The name of the plugin. */
372		name: pluginName,
373		
374		/** Default template for generating a datepicker.
375			Insert anywhere: '{l10n:name}' to insert localised value for name,
376			'{link:name}' to insert a link trigger for command name,
377			'{button:name}' to insert a button trigger for command name,
378			'{popup:start}...{popup:end}' to mark a section for inclusion in a popup datepicker only,
379			'{inline:start}...{inline:end}' to mark a section for inclusion in an inline datepicker only.
380			@property picker {string} Overall structure: '{months}' to insert calendar months.
381			@property monthRow {string} One row of months: '{months}' to insert calendar months.
382			@property month {string} A single month: '{monthHeader<em>:dateFormat</em>}' to insert the month header -
383						<em>dateFormat</em> is optional and defaults to 'MM yyyy',
384						'{weekHeader}' to insert a week header, '{weeks}' to insert the month's weeks.
385			@property weekHeader {string} A week header: '{days}' to insert individual day names.
386			@property dayHeader {string} Individual day header: '{day}' to insert day name.
387			@property week {string} One week of the month: '{days}' to insert the week's days,
388						'{weekOfYear}' to insert week of year.
389			@property day {string} An individual day: '{day}' to insert day value.
390			@property monthSelector {string} jQuery selector, relative to picker, for a single month.
391			@property daySelector {string} jQuery selector, relative to picker, for individual days.
392			@property rtlClass {string} Class for right-to-left (RTL) languages.
393			@property multiClass {string} Class for multi-month datepickers.
394			@property defaultClass {string} Class for selectable dates.
395			@property selectedClass {string} Class for currently selected dates.
396			@property highlightedClass {string} Class for highlighted dates.
397			@property todayClass {string} Class for today.
398			@property otherMonthClass {string} Class for days from other months.
399			@property weekendClass {string} Class for days on weekends.
400			@property commandClass {string} Class prefix for commands.
401			@property commandButtonClass {string} Extra class(es) for commands that are buttons.
402			@property commandLinkClass {string} Extra class(es) for commands that are links.
403			@property disabledClass {string} Class for disabled commands. */
404		defaultRenderer: {
405			picker: '<div class="datepick">' +
406			'<div class="datepick-nav">{link:prev}{link:today}{link:next}</div>{months}' +
407			'{popup:start}<div class="datepick-ctrl">{link:clear}{link:close}</div>{popup:end}' +
408			'<div class="datepick-clear-fix"></div></div>',
409			monthRow: '<div class="datepick-month-row">{months}</div>',
410			month: '<div class="datepick-month"><div class="datepick-month-header">{monthHeader}</div>' +
411			'<table><thead>{weekHeader}</thead><tbody>{weeks}</tbody></table></div>',
412			weekHeader: '<tr>{days}</tr>',
413			dayHeader: '<th>{day}</th>',
414			week: '<tr>{days}</tr>',
415			day: '<td>{day}</td>',
416			monthSelector: '.datepick-month',
417			daySelector: 'td',
418			rtlClass: 'datepick-rtl',
419			multiClass: 'datepick-multi',
420			defaultClass: '',
421			selectedClass: 'datepick-selected',
422			highlightedClass: 'datepick-highlight',
423			todayClass: 'datepick-today',
424			otherMonthClass: 'datepick-other-month',
425			weekendClass: 'datepick-weekend',
426			commandClass: 'datepick-cmd',
427			commandButtonClass: '',
428			commandLinkClass: '',
429			disabledClass: 'datepick-disabled'
430		},
431	
432		/** Command actions that may be added to a layout by name.
433			<ul>
434			<li>prev - Show the previous month (based on <code>monthsToStep</code> option) - <em>PageUp</em></li>
435			<li>prevJump - Show the previous year (based on <code>monthsToJump</code> option) - <em>Ctrl+PageUp</em></li>
436			<li>next - Show the next month (based on <code>monthsToStep</code> option) - <em>PageDown</em></li>
437			<li>nextJump - Show the next year (based on <code>monthsToJump</code> option) - <em>Ctrl+PageDown</em></li>
438			<li>current - Show the currently selected month or today's if none selected - <em>Ctrl+Home</em></li>
439			<li>today - Show today's month - <em>Ctrl+Home</em></li>
440			<li>clear - Erase the date and close the datepicker popup - <em>Ctrl+End</em></li>
441			<li>close - Close the datepicker popup - <em>Esc</em></li>
442			<li>prevWeek - Move the cursor to the previous week - <em>Ctrl+Up</em></li>
443			<li>prevDay - Move the cursor to the previous day - <em>Ctrl+Left</em></li>
444			<li>nextDay - Move the cursor to the next day - <em>Ctrl+Right</em></li>
445			<li>nextWeek - Move the cursor to the next week - <em>Ctrl+Down</em></li>
446			</ul>
447			The command name is the key name and is used to add the command to a layout
448			with '{button:name}' or '{link:name}'. Each has the following attributes.
449			@property text {string} The field in the regional settings for the displayed text.
450			@property status {string} The field in the regional settings for the status text.
451			@property keystroke {object} The keystroke to trigger the action, with attributes:
452				<code>keyCode</code> {number} the code for the keystroke,
453				<code>ctrlKey</code> {boolean} <code>true</code> if <em>Ctrl</em> is required,
454				<code>altKey</code> {boolean} <code>true</code> if <em>Alt</em> is required,
455				<code>shiftKey</code> {boolean} <code>true</code> if <em>Shift</em> is required.
456			@property enabled {DatepickCommandEnabled} The function that indicates the command is enabled.
457			@property date {DatepickCommandDate} The function to get the date associated with this action.
458			@property action {DatepickCommandAction} The function that implements the action. */
459		commands: {
460			prev: {text: 'prevText', status: 'prevStatus', // Previous month
461				keystroke: {keyCode: 33}, // Page up
462				enabled: function(inst) {
463					var minDate = inst.curMinDate();
464					return (!minDate || plugin.add(plugin.day(
465						plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate),
466						1 - inst.options.monthsToStep, 'm'), inst), 1), -1, 'd').
467						getTime() >= minDate.getTime()); },
468				date: function(inst) {
469					return plugin.day(plugin._applyMonthsOffset(plugin.add(
470						plugin.newDate(inst.drawDate), -inst.options.monthsToStep, 'm'), inst), 1); },
471				action: function(inst) {
472					plugin.changeMonth(this, -inst.options.monthsToStep); }
473			},
474			prevJump: {text: 'prevJumpText', status: 'prevJumpStatus', // Previous year
475				keystroke: {keyCode: 33, ctrlKey: true}, // Ctrl + Page up
476				enabled: function(inst) {
477					var minDate = inst.curMinDate();
478					return (!minDate || plugin.add(plugin.day(
479						plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate),
480						1 - inst.options.monthsToJump, 'm'), inst), 1), -1, 'd').
481						getTime() >= minDate.getTime()); },
482				date: function(inst) {
483					return plugin.day(plugin._applyMonthsOffset(plugin.add(
484						plugin.newDate(inst.drawDate), -inst.options.monthsToJump, 'm'), inst), 1); },
485				action: function(inst) {
486					plugin.changeMonth(this, -inst.options.monthsToJump); }
487			},
488			next: {text: 'nextText', status: 'nextStatus', // Next month
489				keystroke: {keyCode: 34}, // Page down
490				enabled: function(inst) {
491					var maxDate = inst.get('maxDate');
492					return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add(
493						plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1).
494						getTime() <= maxDate.getTime()); },
495				date: function(inst) {
496					return plugin.day(plugin._applyMonthsOffset(plugin.add(
497						plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1); },
498				action: function(inst) {
499					plugin.changeMonth(this, inst.options.monthsToStep); }
500			},
501			nextJump: {text: 'nextJumpText', status: 'nextJumpStatus', // Next year
502				keystroke: {keyCode: 34, ctrlKey: true}, // Ctrl + Page down
503				enabled: function(inst) {
504					var maxDate = inst.get('maxDate');
505					return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add(
506						plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1).
507						getTime() <= maxDate.getTime()); },
508				date: function(inst) {
509					return plugin.day(plugin._applyMonthsOffset(plugin.add(
510						plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1); },
511				action: function(inst) {
512					plugin.changeMonth(this, inst.options.monthsToJump); }
513			},
514			current: {text: 'currentText', status: 'currentStatus', // Current month
515				keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home
516				enabled: function(inst) {
517					var minDate = inst.curMinDate();
518					var maxDate = inst.get('maxDate');
519					var curDate = inst.selectedDates[0] || plugin.today();
520					return (!minDate || curDate.getTime() >= minDate.getTime()) &&
521						(!maxDate || curDate.getTime() <= maxDate.getTime()); },
522				date: function(inst) {
523					return inst.selectedDates[0] || plugin.today(); },
524				action: function(inst) {
525					var curDate = inst.selectedDates[0] || plugin.today();
526					plugin.showMonth(this, curDate.getFullYear(), curDate.getMonth() + 1); }
527			},
528			today: {text: 'todayText', status: 'todayStatus', // Today's month
529				keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home
530				enabled: function(inst) {
531					var minDate = inst.curMinDate();
532					var maxDate = inst.get('maxDate');
533					return (!minDate || plugin.today().getTime() >= minDate.getTime()) &&
534						(!maxDate || plugin.today().getTime() <= maxDate.getTime()); },
535				date: function(inst) { return plugin.today(); },
536				action: function(inst) { plugin.showMonth(this); }
537			},
538			clear: {text: 'clearText', status: 'clearStatus', // Clear the datepicker
539				keystroke: {keyCode: 35, ctrlKey: true}, // Ctrl + End
540				enabled: function(inst) { return true; },
541				date: function(inst) { return null; },
542				action: function(inst) { plugin.clear(this); }
543			},
544			close: {text: 'closeText', status: 'closeStatus', // Close the datepicker
545				keystroke: {keyCode: 27}, // Escape
546				enabled: function(inst) { return true; },
547				date: function(inst) { return null; },
548				action: function(inst) { plugin.hide(this); }
549			},
550			prevWeek: {text: 'prevWeekText', status: 'prevWeekStatus', // Previous week
551				keystroke: {keyCode: 38, ctrlKey: true}, // Ctrl + Up
552				enabled: function(inst) {
553					var minDate = inst.curMinDate();
554					return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -7, 'd').
555						getTime() >= minDate.getTime()); },
556				date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -7, 'd'); },
557				action: function(inst) { plugin.changeDay(this, -7); }
558			},
559			prevDay: {text: 'prevDayText', status: 'prevDayStatus', // Previous day
560				keystroke: {keyCode: 37, ctrlKey: true}, // Ctrl + Left
561				enabled: function(inst) {
562					var minDate = inst.curMinDate();
563					return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -1, 'd').
564						getTime() >= minDate.getTime()); },
565				date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -1, 'd'); },
566				action: function(inst) { plugin.changeDay(this, -1); }
567			},
568			nextDay: {text: 'nextDayText', status: 'nextDayStatus', // Next day
569				keystroke: {keyCode: 39, ctrlKey: true}, // Ctrl + Right
570				enabled: function(inst) {
571					var maxDate = inst.get('maxDate');
572					return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 1, 'd').
573						getTime() <= maxDate.getTime()); },
574				date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 1, 'd'); },
575				action: function(inst) { plugin.changeDay(this, 1); }
576			},
577			nextWeek: {text: 'nextWeekText', status: 'nextWeekStatus', // Next week
578				keystroke: {keyCode: 40, ctrlKey: true}, // Ctrl + Down
579				enabled: function(inst) {
580					var maxDate = inst.get('maxDate');
581					return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 7, 'd').
582						getTime() <= maxDate.getTime()); },
583				date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 7, 'd'); },
584				action: function(inst) { plugin.changeDay(this, 7); }
585			}
586		},
587
588		/** Determine whether a command is enabled.
589			@callback DatepickCommandEnabled
590			@param inst {object} The current instance settings.
591			@return {boolean} <code>true</code> if this command is enabled, <code>false</code> if not.
592			@example enabled: function(inst) {
593	return !!inst.curMinDate();
594 } */
595
596		/** Calculate the representative date for a command.
597			@callback DatepickCommandDate
598			@param inst {object} The current instance settings.
599			@return {Date} A date appropriate for this command.
600			@example date: function(inst) {
601	return inst.curMinDate();
602 } */
603
604		/** Perform the action for a command.
605			@callback DatepickCommandAction
606			@param inst {object} The current instance settings.
607			@example date: function(inst) {
608	$.datepick.setDate(inst.elem, inst.curMinDate());
609 } */
610
611		/** Calculate the week of the year for a date.
612			@callback DatepickCalculateWeek
613			@param date {Date} The date to evaluate.
614			@return {number} The week of the year.
615			@example calculateWeek: function(date) {
616	return Math.floor(($.datepick.dayOfYear(date) - 1) / 7) + 1;
617 } */
618
619		/** Provide information about an individual date shown in the calendar.
620			@callback DatepickOnDate
621			@param date {Date} The date to evaluate.
622			@return {object} Information about that date, with the properties above.
623			@property selectable {boolean} <code>true</code> if this date can be selected.
624			@property dateClass {string} Class(es) to be applied to the date.
625			@property content {string} The date cell content.
626			@property tooltip {string} A popup tooltip for the date.
627			@example onDate: function(date) {
628	return {selectable: date.getDay() > 0 && date.getDay() &lt; 5,
629		dateClass: date.getDay() == 4 ? 'last-day' : ''};
630 } */
631
632		/** Update the datepicker display.
633			@callback DatepickOnShow
634			@param picker {jQuery} The datepicker <code>div</code> to be shown.
635			@param inst {object} The current instance settings.
636			@example onShow: function(picker, inst) {
637	picker.append('&lt;button type="button">Hi&lt;/button>').
638		find('button:last').click(function() {
639			alert('Hi!');
640		});
641 } */
642
643		/** React to navigating through the months/years.
644			@callback DatepickOnChangeMonthYear
645			@param year {number} The new year.
646			@param month {number} The new month (1 to 12).
647			@example onChangeMonthYear: function(year, month) {
648	alert('Now in ' + month + '/' + year);
649 } */
650			
651		/** Datepicker on select callback.
652			Triggered when a date is selected.
653			@callback DatepickOnSelect
654			@param dates {Date[]} The selected date(s).
655			@example onSelect: function(dates) {
656 	alert('Selected ' + dates);
657 } */
658			
659		/** Datepicker on close callback.
660			Triggered when a popup calendar is closed.
661			@callback DatepickOnClose
662			@param dates {Date[]} The selected date(s).
663			@example onClose: function(dates) {
664 	alert('Selected ' + dates);
665 } */
666			
667		/** Default settings for the plugin.
668			@property [pickerClass=''] {string} CSS class to add to this instance of the datepicker.
669			@property [showOnFocus=true] {boolean} <code>true</code> for popup on focus, <code>false</code> for not.
670			@property [showTrigger=null] {string|Element|jQuery} Element to be cloned for a trigger, <code>null</code> for none.
671			@property [showAnim='show'] {string} Name of jQuery animation for popup, '' for no animation.
672			@property [showOptions=null] {object} Options for enhanced animations.
673			@property [showSpeed='normal'] {string} Duration of display/closure.
674			@property [popupContainer=null] {string|Element|jQuery} The element to which a popup calendar is added, <code>null</code> for body.
675			@property [alignment='bottom'] {string} Alignment of popup - with nominated corner of input:
676						'top' or 'bottom' aligns depending on language direction,
677						'topLeft', 'topRight', 'bottomLeft', 'bottomRight'.
678			@property [fixedWeeks=false] {boolean} <code>true</code> to always show 6 weeks, <code>false</code> to only show as many as are needed.
679			@property [firstDay=0] {number} First day of the week, 0 = Sunday, 1 = Monday, etc.
680			@property [calculateWeek=this.iso8601Week] {DatepickCalculateWeek} Calculate week of the year from a date, <code>null</code> for ISO8601.
681			@property [monthsToShow=1] {number|number[]} How many months to show, cols or [rows, cols].
682			@property [monthsOffset=0] {number} How many months to offset the primary month by;
683						may be a function that takes the date and returns the offset.
684			@property [monthsToStep=1] {number} How many months to move when prev/next clicked.
685			@property [monthsToJump=12] {number} How many months to move when large prev/next clicked.
686			@property [useMouseWheel=true] {boolean} <code>true</code> to use mousewheel if available, <code>false</code> to never use it.
687			@property [changeMonth=true] {boolean} <code>true</code> to change month/year via drop-down, <code>false</code> for navigation only.
688			@property [yearRange='c-10:c+10'] {string} Range of years to show in drop-down: 'any' for direct text entry
689						or 'start:end', where start/end are '+-nn' for relative to today
690						or 'c+-nn' for relative to the currently selected date
691						or 'nnnn' for an absolute year.
692			@property [shortYearCutoff='+10'] {string} Cutoff for two-digit year in the current century.
693			@property [showOtherMonths=false] {boolean} <code>true</code> to show dates from other months, <code>false</code> to not show them.
694			@property [selectOtherMonths=false] {boolean} <code>true</code> to allow selection of dates from other months too.
695			@property [defaultDate=null] {string|number|Date} Date to show if no other selected.
696			@property [selectDefaultDate=false] {boolean} <code>true</code> to pre-select the default date if no other is chosen.
697			@property [minDate=null] {string|number|Date} The minimum selectable date.
698			@property [maxDate=null] {string|number|Date} The maximum selectable date.
699			@property [dateFormat='mm/dd/yyyy'] {string} Format for dates.
700			@property [autoSize=false] {boolean} <code>true</code> to size the input field according to the date format.
701			@property [rangeSelect=false] {boolean} Allows for selecting a date range on one date picker.
702			@property [rangeSeparator=' - '] {string} Text between two dates in a range.
703			@property [multiSelect=0] {number} Maximum number of selectable dates, zero for single select.
704			@property [multiSeparator=','] {string} Text between multiple dates.
705			@property [onDate=null] {DatepickOnDate} Callback as a date is added to the datepicker.
706			@property [onShow=null] {DatepickOnShow} Callback just before a datepicker is shown.
707			@property [onChangeMonthYear=null] {DatepickOnChangeMonthYear} Callback when a new month/year is selected.
708			@property [onSelect=null] {DatepickOnSelect} Callback when a date is selected.
709			@property [onClose=null] {DatepickOnClose} Callback when a datepicker is closed.
710			@property [altField=null] {string|Element|jQuery} Alternate field to update in synch with the datepicker.
711			@property [altFormat=null] {string} Date format for alternate field, defaults to <code>dateFormat</code>.
712			@property [constrainInput=true] {boolean} <code>true</code> to constrain typed input to <code>dateFormat</code> allowed characters.
713			@property [commandsAsDateFormat=false] {boolean} <code>true</code> to apply
714						<code><a href="#formatDate">formatDate</a></code> to the command texts.
715			@property [commands=this.commands] {object} Command actions that may be added to a layout by name. */
716		defaultOptions: {
717			pickerClass: '',
718			showOnFocus: true,
719			showTrigger: null,
720			showAnim: 'show',
721			showOptions: {},
722			showSpeed: 'normal',
723			popupContainer: null,
724			alignment: 'bottom',
725			fixedWeeks: false,
726			firstDay: 0,
727			calculateWeek: null, // this.iso8601Week,
728			monthsToShow: 1,
729			monthsOffset: 0,
730			monthsToStep: 1,
731			monthsToJump: 12,
732			useMouseWheel: true,
733			changeMonth: true,
734			yearRange: 'c-10:c+10',
735			shortYearCutoff: '+10',
736			showOtherMonths: false,
737			selectOtherMonths: false,
738			defaultDate: null,
739			selectDefaultDate: false,
740			minDate: null,
741			maxDate: null,
742			dateFormat: 'mm/dd/yyyy',
743			autoSize: false,
744			rangeSelect: false,
745			rangeSeparator: ' - ',
746			multiSelect: 0,
747			multiSeparator: ',',
748			onDate: null,
749			onShow: null,
750			onChangeMonthYear: null,
751			onSelect: null,
752			onClose: null,
753			altField: null,
754			altFormat: null,
755			constrainInput: true,
756			commandsAsDateFormat: false,
757			commands: {} // this.commands
758		},
759
760		/** Localisations for the plugin.
761			Entries are objects indexed by the language code ('' being the default US/English).
762			Each object has the following attributes.
763			@property [monthNames=['January','February','March','April','May','June','July','August','September','October','November','December']]
764						The long names of the months.
765			@property [monthNamesShort=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']]
766						The short names of the months.
767			@property [dayNames=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']]
768						The long names of the days of the week.
769			@property [dayNamesShort=['Sun','Mon','Tue','Wed','Thu','Fri','Sat']] The short names of the days of the week.
770			@property [dayNamesMin=['Su','Mo','Tu','We','Th','Fr','Sa']] The minimal names of the days of the week.
771			@property [dateFormat='mm/dd/yyyy'] {string} See options on <code><a href="#formatDate">formatDate</a></code>.
772			@property [firstDay=0] {number} The first day of the week, Sun = 0, Mon = 1, etc.
773			@property [renderer=this.defaultRenderer] {string} The rendering templates.
774			@property [prevText='&lt;Prev'] {string} Text for the previous month command.
775			@property [prevStatus='Show the previous month'] {string} Status text for the previous month command.
776			@property [prevJumpText='&lt;&lt;'] {string} Text for the previous year command.
777			@property [prevJumpStatus='Show the previous year'] {string} Status text for the previous year command.
778			@property [nextText='Next&gt;'] {string} Text for the next month command.
779			@property [nextStatus='Show the next month'] {string} Status text for the next month command.
780			@property [nextJumpText='&gt;&gt;'] {string} Text for the next year command.
781			@property [nextJumpStatus='Show the next year'] {string} Status text for the next year command.
782			@property [currentText='Current'] {string} Text for the current month command.
783			@property [currentStatus='Show the current month'] {string} Status text for the current month command.
784			@property [todayText='Today'] {string} Text for the today's month command.
785			@property [todayStatus='Show today\'s month'] {string} Status text for the today's month command.
786			@property [clearText='Clear'] {string} Text for the clear command.
787			@property [clearStatus='Clear all the dates'] {string} Status text for the clear command.
788			@property [closeText='Close'] {string} Text for the close command.
789			@property [closeStatus='Close the datepicker'] {string} Status text for the close command.
790			@property [yearStatus='Change the year'] {string} Status text for year selection.
791			@property [monthStatus='Change the month'] {string} Status text for month selection.
792			@property [weekText='Wk'] {string} Text for week of the year column header.
793			@property [weekStatus='Week of the year'] {string} Status text for week of the year column header.
794			@property [dayStatus='Select DD,&nbsp;M&nbsp;d,&nbsp;yyyy'] {string} Status text for selectable days.
795			@property [defaultStatus='Select a date'] {string} Status text shown by default.
796			@property [isRTL=false] {boolean} <code>true</code> if language is right-to-left. */
797		regionalOptions: { // Available regional settings, indexed by language/country code
798			'': { // Default regional settings - English/US
799				monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
800				'July', 'August', 'September', 'October', 'November', 'December'],
801				monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
802				dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
803				dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
804				dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
805				dateFormat: 'mm/dd/yyyy',
806				firstDay: 0,
807				renderer: {}, // this.defaultRenderer
808				prevText: '&lt;Prev',
809				prevStatus: 'Show the previous month',
810				prevJumpText: '&lt;&lt;',
811				prevJumpStatus: 'Show the previous year',
812				nextText: 'Next&gt;',
813				nextStatus: 'Show the next month',
814				nextJumpText: '&gt;&gt;',
815				nextJumpStatus: 'Show the next year',
816				currentText: 'Current',
817				currentStatus: 'Show the current month',
818				todayText: 'Today',
819				todayStatus: 'Show today\'s month',
820				clearText: 'Clear',
821				clearStatus: 'Clear all the dates',
822				closeText: 'Close',
823				closeStatus: 'Close the datepicker',
824				yearStatus: 'Change the year',
825				monthStatus: 'Change the month',
826				weekText: 'Wk',
827				weekStatus: 'Week of the year',
828				dayStatus: 'Free',
829				defaultStatus: 'Select a date',
830				isRTL: false
831			}
832		},
833		
834		/** Names of getter methods - those that can't be chained. */
835		_getters: ['getDate', 'isDisabled', 'isSelectable', 'retrieveDate'],
836
837		_disabled: [],
838		
839		_popupClass: pluginName + '-popup', // Marker for popup division
840		_triggerClass: pluginName + '-trigger', // Marker for trigger element
841		_disableClass: pluginName + '-disable', // Marker for disabled element
842		_monthYearClass: pluginName + '-month-year', // Marker for month/year inputs
843		_curMonthClass: pluginName + '-month-', // Marker for current month/year
844		_anyYearClass: pluginName + '-any-year', // Marker for year direct input
845		_curDoWClass: pluginName + '-dow-', // Marker for day of week
846
847		_ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
848			Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
849		_msPerDay: 24 * 60 * 60 * 1000,
850
851		/** The date format for use with Atom (RFC 3339/ISO 8601). */
852		ATOM: 'yyyy-mm-dd',
853		/** The date format for use with cookies. */
854		COOKIE: 'D, dd M yyyy',
855		/** The date format for full display. */
856		FULL: 'DD, MM d, yyyy',
857		/** The date format for use with ISO 8601. */
858		ISO_8601: 'yyyy-mm-dd',
859		/** The date format for Julian dates. */
860		JULIAN: 'J',
861		/** The date format for use with RFC 822. */
862		RFC_822: 'D, d M yy',
863		/** The date format for use with RFC 850. */
864		RFC_850: 'DD, dd-M-yy',
865		/** The date format for use with RFC 1036. */
866		RFC_1036: 'D, d M yy',
867		/** The date format for use with RFC 1123. */
868		RFC_1123: 'D, d M yyyy',
869		/** The date format for use with RFC 2822. */
870		RFC_2822: 'D, d M yyyy',
871		/** The date format for use with RSS (RFC 822). */
872		RSS: 'D, d M yy',
873		/** The date format for Windows ticks. */
874		TICKS: '!',
875		/** The date format for Unix timestamp. */
876		TIMESTAMP: '@',
877		/** The date format for use with W3C (ISO 8601). */
878		W3C: 'yyyy-mm-dd',
879
880		/** Format a date object into a string value.
881			The format can be combinations of the following:
882			<ul>
883			<li>d  - day of month (no leading zero)</li>
884			<li>dd - day of month (two digit)</li>
885			<li>o  - day of year (no leading zeros)</li>
886			<li>oo - day of year (three digit)</li>
887			<li>D  - day name short</li>
888			<li>DD - day name long</li>
889			<li>w  - week of year (no leading zero)</li>
890			<li>ww - week of year (two digit)</li>
891			<li>m  - month of year (no leading zero)</li>
892			<li>mm - month of year (two digit)</li>
893			<li>M  - month name short</li>
894			<li>MM - month name long</li>
895			<li>yy - year (two digit)</li>
896			<li>yyyy - year (four digit)</li>
897			<li>@  - Unix timestamp (s since 01/01/1970)</li>
898			<li>!  - Windows ticks (100ns since 01/01/0001)</li>
899			<li>'...' - literal text</li>
900			<li>'' - single quote</li>
901			</ul>
902			@param [format=dateFormat] {string} The desired format of the date.
903			@param date {Date} The date value to format.
904			@param [settings] {object} With the properties shown below.
905			@property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday.
906			@property [dayNames] {string[]} Names of the days from Sunday.
907			@property [monthNamesShort] {string[]} Abbreviated names of the months.
908			@property [monthNames] {string[]} Names of the months.
909			@property [calculateWeek] {DatepickCalculateWeek} Function that determines week of the year.
910			@return {string} The date in the above format.
911			@example var display = $.datepick.formatDate('yyyy-mm-dd', new Date(2014, 12-1, 25)) */
912		formatDate: function(format, date, settings) {
913			if (typeof format !== 'string') {
914				settings = date;
915				date = format;
916				format = '';
917			}
918			if (!date) {
919				return '';
920			}
921			format = format || this.defaultOptions.dateFormat;
922			settings = settings || {};
923			var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort;
924			var dayNames = settings.dayNames || this.defaultOptions.dayNames;
925			var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort;
926			var monthNames = settings.monthNames || this.defaultOptions.monthNames;
927			var calculateWeek = settings.calculateWeek || this.defaultOptions.calculateWeek;
928			// Check whether a format character is doubled
929			var doubled = function(match, step) {
930				var matches = 1;
931				while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) {
932					matches++;
933				}
934				iFormat += matches - 1;
935				return Math.floor(matches / (step || 1)) > 1;
936			};
937			// Format a number, with leading zeroes if necessary
938			var formatNumber = function(match, value, len, step) {
939				var num = '' + value;
940				if (doubled(match, step)) {
941					while (num.length < len) {
942						num = '0' + num;
943					}
944				}
945				return num;
946			};
947			// Format a name, short or long as requested
948			var formatName = function(match, value, shortNames, longNames) {
949				return (doubled(match) ? longNames[value] : shortNames[value]);
950			};
951			var output = '';
952			var literal = false;
953			for (var iFormat = 0; iFormat < format.length; iFormat++) {
954				if (literal) {
955					if (format.charAt(iFormat) === "'" && !doubled("'")) {
956						literal = false;
957					}
958					else {
959						output += format.charAt(iFormat);
960					}
961				}
962				else {
963					switch (format.charAt(iFormat)) {
964						case 'd': output += formatNumber('d', date.getDate(), 2); break;
965						case 'D': output += formatName('D', date.getDay(),
966							dayNamesShort, dayNames); break;
967						case 'o': output += formatNumber('o', this.dayOfYear(date), 3); break;
968						case 'w': output += formatNumber('w', calculateWeek(date), 2); break;
969						case 'm': output += formatNumber('m', date.getMonth() + 1, 2); break;
970						case 'M': output += formatName('M', date.getMonth(),
971							monthNamesShort, monthNames); break;
972						case 'y':
973							output += (doubled('y', 2) ? date.getFullYear() :
974								(date.getFullYear() % 100 < 10 ? '0' : '') + date.getFullYear() % 100);
975							break;
976						case '@': output += Math.floor(date.getTime() / 1000); break;
977						case '!': output += date.getTime() * 10000 + this._ticksTo1970; break;
978						case "'":
979							if (doubled("'")) {
980								output += "'";
981							}
982							else {
983								literal = true;
984							}
985							break;
986						default:
987							output += format.charAt(iFormat);
988					}
989				}
990			}
991			return output;
992		},
993
994		/** Parse a string value into a date object.
995			See <code><a href="#formatDate">formatDate</a></code> for the possible formats, plus:
996			<ul>
997			<li>* - ignore rest of string</li>
998			</ul>
999			@param format {string} The expected format of the date ('' for default datepicker format).
1000			@param value {string} The date in the above format.
1001			@param [settings] {object} With the properties shown above.
1002			@property [shortYearCutoff] {number} the cutoff year for determining the century.
1003			@property [dayNamesShort] {string[]} abbreviated names of the days from Sunday.
1004			@property [dayNames] {string[]} names of the days from Sunday.
1005			@property [monthNamesShort] {string[]} abbreviated names of the months.
1006			@property [monthNames] {string[]} names of the months.
1007			@return {Date} The extracted date value or <code>null</code> if value is blank.
1008			@throws Errors if the format and/or value are missing, if the value doesn't match the format,
1009					or if the date is invalid.
1010			@example var date = $.datepick.parseDate('dd/mm/yyyy', '25/12/2014') */
1011		parseDate: function(format, value, settings) {
1012			if (value == null) {
1013				throw 'Invalid arguments';
1014			}
1015			value = (typeof value === 'object' ? value.toString() : value + '');
1016			if (value === '') {
1017				return null;
1018			}
1019			format = format || this.defaultOptions.dateFormat;
1020			settings = settings || {};
1021			var shortYearCutoff = settings.shortYearCutoff || this.defaultOptions.shortYearCutoff;
1022			shortYearCutoff = (typeof shortYearCutoff !== 'string' ? shortYearCutoff :
1023				this.today().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1024			var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort;
1025			var dayNames = settings.dayNames || this.defaultOptions.dayNames;
1026			var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort;
1027			var monthNames = settings.monthNames || this.defaultOptions.monthNames;
1028			var year = -1;
1029			var month = -1;
1030			var day = -1;
1031			var doy = -1;
1032			var shortYear = false;
1033			var literal = false;
1034			// Check whether a format character is doubled
1035			var doubled = function(match, step) {
1036				var matches = 1;
1037				while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) {
1038					matches++;
1039				}
1040				iFormat += matches - 1;
1041				return Math.floor(matches / (step || 1)) > 1;
1042			};
1043			// Extract a number from the string value
1044			var getNumber = function(match, step) {
1045				var isDoubled = doubled(match, step);
1046				var size = [2, 3, isDoubled ? 4 : 2, 11, 20]['oy@!'.indexOf(match) + 1];
1047				var digits = new RegExp('^-?\\d{1,' + size + '}');
1048				var num = value.substring(iValue).match(digits);
1049				if (!num) {
1050					throw 'Missing number at position {0}'.replace(/\{0\}/, iValue);
1051				}
1052				iValue += num[0].length;
1053				return parseInt(num[0], 10);
1054			};
1055			// Extract a name from the string value and convert to an index
1056			var getName = function(match, shortNames, longNames, step) {
1057				var names = (doubled(match, step) ? longNames : shortNames);
1058				for (var i = 0; i < names.length; i++) {
1059					if (value.substr(iValue, names[i].length).toLowerCase() === names[i].toLowerCase()) {
1060						iValue += names[i].length;
1061						return i + 1;
1062					}
1063				}
1064				throw 'Unknown name at position {0}'.replace(/\{0\}/, iValue);
1065			};
1066			// Confirm that a literal character matches the string value
1067			var checkLiteral = function() {
1068				if (value.charAt(iValue) !== format.charAt(iFormat)) {
1069					throw 'Unexpected literal at position {0}'.replace(/\{0\}/, iValue);
1070				}
1071				iValue++;
1072			};
1073			var iValue = 0;
1074			for (var iFormat = 0; iFormat < format.length; iFormat++) {
1075				if (literal) {
1076					if (format.charAt(iFormat) === "'" && !doubled("'")) {
1077						literal = false;
1078					}
1079					else {
1080						checkLiteral();
1081					}
1082				}
1083				else {
1084					switch (format.charAt(iFormat)) {
1085						case 'd': day = getNumber('d'); break;
1086						case 'D': getName('D', dayNamesShort, dayNames); break;
1087						case 'o': doy = getNumber('o'); break;
1088						case 'w': getNumber('w'); break;
1089						case 'm': month = getNumber('m'); break;
1090						case 'M': month = getName('M', monthNamesShort, monthNames); break;
1091						case 'y':
1092							var iSave = iFormat;
1093							shortYear = !doubled('y', 2);
1094							iFormat = iSave;
1095							year = getNumber('y', 2);
1096							break;
1097						case '@':
1098							var date = this._normaliseDate(new Date(getNumber('@') * 1000));
1099							year = date.getFullYear();
1100							month = date.getMonth() + 1;
1101							day = date.getDate();
1102							break;
1103						case '!':
1104							var date = this._normaliseDate(
1105								new Date((getNumber('!') - this._ticksTo1970) / 10000));
1106							year = date.getFullYear();
1107							month = date.getMonth() + 1;
1108							day = date.getDate();
1109							break;
1110						case '*': iValue = value.length; break;
1111						case "'":
1112							if (doubled("'")) {
1113								checkLiteral();
1114							}
1115							else {
1116								literal = true;
1117							}
1118							break;
1119						default: checkLiteral();
1120					}
1121				}
1122			}
1123			if (iValue < value.length) {
1124				throw 'Additional text found at end';
1125			}
1126			if (year === -1) {
1127				year = this.today().getFullYear();
1128			}
1129			else if (year < 100 && shortYear) {
1130				year += (shortYearCutoff === -1 ? 1900 : this.today().getFullYear() -
1131					this.today().getFullYear() % 100 - (year <= shortYearCutoff ? 0 : 100));
1132			}
1133			if (doy > -1) {
1134				month = 1;
1135				day = doy;
1136				for (var dim = this.daysInMonth(year, month); day > dim;
1137						dim = this.daysInMonth(year, month)) {
1138					month++;
1139					day -= dim;
1140				}
1141			}
1142			var date = this.newDate(year, month, day);
1143			if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
1144				throw 'Invalid date';
1145			}
1146			return date;
1147		},
1148
1149		/** A date may be specified as an exact value or a relative one.
1150			@param dateSpec {Date|number|string} The date as an object or string
1151					in the given format or an offset - numeric days from today,
1152					or string amounts and periods, e.g. '+1m +2w'.
1153			@param defaultDate {Date} The date to use if no other supplied, may be <code>null</code>.
1154			@param [currentDate] {Date} The current date as a possible basis for relative dates,
1155					if <code>null</code> today is used.
1156			@param dateFormat {string} The expected date format - see <code><a href="#formatDate">formatDate</a></code>.
1157			@param settings {object} With the properties shown above.
1158			@property [shortYearCutoff] {number} The cutoff year for determining the century.
1159			@property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday.
1160			@property [dayNames] {string[]} Names of the days from Sunday.
1161			@property [monthNamesShort] {string[]} Abbreviated names of the months.
1162			@property [monthNames] {string[]} Names of the months.
1163			@return {Date} The decoded date.
1164			@example $.datepick.determineDate('+1m +2w', new Date()) */
1165		determineDate: function(dateSpec, defaultDate, currentDate, dateFormat, settings) {
1166			if (currentDate && typeof currentDate !== 'object') {
1167				settings = dateFormat;
1168				dateFormat = currentDate;
1169				currentDate = null;
1170			}
1171			if (typeof dateFormat !== 'string') {
1172				settings = dateFormat;
1173				dateFormat = '';
1174			}
1175			var offsetString = function(offset) {
1176				try {
1177					return plugin.parseDate(dateFormat, offset, settings);
1178				}
1179				catch (e) {
1180					// Ignore
1181				}
1182				offset = offset.toLowerCase();
1183				var date = (offset.match(/^c/) && currentDate ? plugin.newDate(currentDate) : null) ||
1184					plugin.today();
1185				var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g;
1186				var matches = null;
1187				while (matches = pattern.exec(offset)) {
1188					date = plugin.add(date, parseInt(matches[1], 10), matches[2] || 'd');
1189				}
1190				return date;
1191			};
1192			defaultDate = (defaultDate ? plugin.newDate(defaultDate) : null);
1193			dateSpec = (dateSpec == null ? defaultDate :
1194				(typeof dateSpec === 'string' ? offsetString(dateSpec) : (typeof dateSpec === 'number' ?
1195				(isNaN(dateSpec) || dateSpec === Infinity || dateSpec === -Infinity ? defaultDate :
1196				plugin.add(plugin.today(), dateSpec, 'd')) : plugin.newDate(dateSpec))));
1197			return dateSpec;
1198		},
1199
1200		/** Find the number of days in a given month.
1201			@param year {Date|number} The date to get days for or the full year.
1202			@param month {number} The month (1 to 12).
1203			@return {number} The number of days in this month.
1204			@example var days = $.datepick.daysInMonth(2014, 12) */
1205		daysInMonth: function(year, month) {
1206			month = (year.getFullYear ? year.getMonth() + 1 : month);
1207			year = (year.getFullYear ? year.getFullYear() : year);
1208			return this.newDate(year, month + 1, 0).getDate();
1209		},
1210
1211		/** Calculate the day of the year for a date.
1212			@param year {Date|number} The date to get the day-of-year for or the full year.
1213			@param month {number} The month (1-12).
1214			@param day {number} The day.
1215			@return {number} The day of the year.
1216			@example var doy = $.datepick.dayOfYear(2014, 12, 25) */
1217		dayOfYear: function(year, month, day) {
1218			var date = (year.getFullYear ? year : plugin.newDate(year, month, day));
1219			var newYear = plugin.newDate(date.getFullYear(), 1, 1);
1220			return Math.floor((date.getTime() - newYear.getTime()) / plugin._msPerDay) + 1;
1221		},
1222
1223		/** Set as <code>calculateWeek</code> to determine the week of the year based on the ISO 8601 definition.
1224			@param year {Date|number} The date to get the week for or the full year.
1225			@param month {number} The month (1-12).
1226			@param day {number} The day.
1227			@return {number} The number of the week within the year that contains this date.
1228			@example var week = $.datepick.iso8601Week(2014, 12, 25) */
1229		iso8601Week: function(year, month, day) {
1230			var checkDate = (year.getFullYear ?
1231				new Date(year.getTime()) : plugin.newDate(year, month, day));
1232			// Find Thursday of this week starting on Monday
1233			checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
1234			var time = checkDate.getTime();
1235			checkDate.setMonth(0, 1); // Compare with Jan 1
1236			return Math.floor(Math.round((time - checkDate) / plugin._msPerDay) / 7) + 1;
1237		},
1238
1239		/** Return today's date.
1240			@return {Date} Today.
1241			@example $.datepick.today() */
1242		today: function() {
1243			return this._normaliseDate(new Date());
1244		},
1245
1246		/** Return a new date.
1247			@param year {Date|number} The date to clone or the year.
1248			@param month {number} The month (1-12).
1249			@param day {number} The day.
1250			@return {Date} The date.
1251			@example $.datepick.newDate(oldDate)
1252 $.datepick.newDate(2014, 12, 25) */
1253		newDate: function(year, month, day) {
1254			return (!year ? null : (year.getFullYear ? this._normaliseDate(new Date(year.getTime())) :
1255				new Date(year, month - 1, day, 12)));
1256		},
1257
1258		/** Standardise a date into a common format - time portion is 12 noon.
1259			@private
1260			@param date {Date} The date to standardise.
1261			@return {Date} The normalised date. */
1262		_normaliseDate: function(date) {
1263			if (date) {
1264				date.setHours(12, 0, 0, 0);
1265			}
1266			return date;
1267		},
1268
1269		/** Set the year for a date.
1270			@param date {Date} The original date.
1271			@param year {number} The new year.
1272			@return {Date} The updated date.
1273			@example $.datepick.year(date, 2014) */
1274		year: function(date, year) {
1275			date.setFullYear(year);
1276			return this._normaliseDate(date);
1277		},
1278
1279		/** Set the month for a date.
1280			@param date  {Date} The original date.
1281			@param month {number} The new month (1-12).
1282			@return {Date} The updated date.
1283			@example $.datepick.month(date, 12) */
1284		month: function(date, month) {
1285			date.setMonth(month - 1);
1286			return this._normaliseDate(date);
1287		},
1288
1289		/** Set the day for a date.
1290			@param date {Date} The original date.
1291			@param day {number} The new day of the month.
1292			@return {Date} The updated date.
1293			@example $.datepick.day(date, 25) */
1294		day: function(date, day) {
1295			date.setDate(day);
1296			return this._normaliseDate(date);
1297		},
1298
1299		/** Add a number of periods to a date.
1300			@param date {Date} The original date.
1301			@param amount {number} The number of periods.
1302			@param period {string} The type of period d/w/m/y.
1303			@return {Date} The updated date.
1304			@example $.datepick.add(date, 10, 'd') */
1305		add: function(date, amount, period) {
1306			if (period === 'd' || period === 'w') {
1307				this._normaliseDate(date);
1308				date.setDate(date.getDate() + amount * (period === 'w' ? 7 : 1));
1309			}
1310			else {
1311				var year = date.getFullYear() + (period === 'y' ? amount : 0);
1312				var month = date.getMonth() + (period === 'm' ? amount : 0);
1313				date.setTime(plugin.newDate(year, month + 1,
1314					Math.min(date.getDate(), this.daysInMonth(year, month + 1))).getTime());
1315			}
1316			return date;
1317		},
1318
1319		/** Apply the months offset value to a date.
1320			@private
1321			@param date {Date} The original date.
1322			@param inst {object} The current instance settings.
1323			@return {Date} The updated date. */
1324		_applyMonthsOffset: function(date, inst) {
1325			var monthsOffset = inst.options.monthsOffset;
1326			if ($.isFunction(monthsOffset)) {
1327				monthsOffset = monthsOffset.apply(inst.elem[0], [date]);
1328			}
1329			return plugin.add(date, -monthsOffset, 'm');
1330		},
1331
1332		_init: function() {
1333			this.defaultOptions.commands = this.commands;
1334			this.defaultOptions.calculateWeek = this.iso8601Week;
1335			this.regionalOptions[''].renderer = this.defaultRenderer;
1336			this._super();
1337		},
1338
1339		_instSettings: function(elem, options) {
1340			return {selectedDates: [], drawDate: null, pickingRange: false,
1341				inline: ($.inArray(elem[0].nodeName.toLowerCase(), ['div', 'span']) > -1),
1342				get: function(name) { // Get a setting value, computing if necessary
1343					if ($.inArray(name, ['defaultDate', 'minDate', 'maxDate']) > -1) { // Decode date settings
1344						return plugin.determineDate(this.options[name], null,
1345							this.selectedDates[0], this.options.dateFormat, this.getConfig());
1346					}
1347					return this.options[name];
1348				},
1349				curMinDate: function() {
1350					return (this.pickingRange ? this.selectedDates[0] : this.get('minDate'));
1351				},
1352				getConfig: function() {
1353					return {dayNamesShort: this.options.dayNamesShort, dayNames: this.options.dayNames,
1354						monthNamesShort: this.options.monthNamesShort, monthNames: this.options.monthNames,
1355						calculateWeek: this.options.calculateWeek,
1356						shortYearCutoff: this.options.shortYearCutoff};
1357				}
1358			};
1359		},
1360
1361		_postAttach: function(elem, inst) {
1362			if (inst.inline) {
1363				inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] ||
1364					inst.get('defaultDate') || plugin.today()), inst);
1365				inst.prevDate = plugin.newDate(inst.drawDate);
1366				this._update(elem[0]);
1367				if ($.fn.mousewheel) {
1368					elem.mousewheel(this._doMouseWheel);
1369				}
1370			}
1371			else {
1372				this._attachments(elem, inst);
1373				elem.on('keydown.' + inst.name, this._keyDown).on('keypress.' + inst.name, this._keyPress).
1374					on('keyup.' + inst.name, this._keyUp);
1375				if (elem.attr('disabled')) {
1376					this.disable(elem[0]);
1377				}
1378			}
1379		},
1380
1381		_optionsChanged: function(elem, inst, options) {
1382			if (options.calendar && options.calendar !== inst.options.calendar) {
1383				var discardDate = function(name) {
1384					return (typeof inst.options[name] === 'object' ? null : inst.options[name]);
1385				};
1386				options = $.extend({defaultDate: discardDate('defaultDate'),
1387					minDate: discardDate('minDate'), maxDate: discardDate('maxDate')}, options);
1388				inst.selectedDates = [];
1389				inst.drawDate = null;
1390			}
1391			var dates = inst.selectedDates;
1392			$.extend(inst.options, options);
1393			this.setDate(elem[0], dates, null, false, true);
1394			inst.pickingRange = false;
1395			inst.drawDate = plugin.newDate(this._checkMinMax(
1396				(inst.options.defaultDate ? inst.get('defaultDate') : inst.drawDate) ||
1397				inst.get('defaultDate') || plugin.today(), inst));
1398			if (!inst.inline) {
1399				this._attachments(elem, inst);
1400			}
1401			if (inst.inline || inst.div) {
1402				this._update(elem[0]);
1403			}
1404		},
1405
1406		/** Attach events and trigger, if necessary.
1407			@private
1408			@param elem {jQuery} The control to affect.
1409			@param inst {object} The current instance settings. */
1410		_attachments: function(elem, inst) {
1411			elem.off('focus.' + inst.name);
1412			if (inst.options.showOnFocus) {
1413				elem.on('focus.' + inst.name, this.show);
1414			}
1415			if (inst.trigger) {
1416				inst.trigger.remove();
1417			}
1418			var trigger = inst.options.showTrigger;
1419			inst.trigger = (!trigger ? $([]) :
1420				$(trigger).clone().removeAttr('id').addClass(this._triggerClass)
1421					[inst.options.isRTL ? 'insertBefore' : 'insertAfter'](elem).
1422					click(function() {
1423						if (!plugin.isDisabled(elem[0])) {
1424							plugin[plugin.curInst === inst ? 'hide' : 'show'](elem[0]);
1425						}
1426					}));
1427			this._autoSize(elem, inst);
1428			var dates = this._extractDates(inst, elem.val());
1429			if (dates) {
1430				this.setDate(elem[0], dates, null, true);
1431			}
1432			var defaultDate = inst.get('defaultDate');
1433			if (inst.options.selectDefaultDate && defaultDate && inst.selectedDates.length === 0) {
1434				this.setDate(elem[0], plugin.newDate(defaultDate || plugin.today()));
1435			}
1436		},
1437
1438		/** Apply the maximum length for the date format.
1439			@private
1440			@param elem {jQuery} The control to affect.
1441			@param inst {object} The current instance settings. */
1442		_autoSize: function(elem, inst) {
1443			if (inst.options.autoSize && !inst.inline) {
1444				var date = plugin.newDate(2009, 10, 20); // Ensure double digits
1445				var dateFormat = inst.options.dateFormat;
1446				if (dateFormat.match(/[DM]/)) {
1447					var findMax = function(names) {
1448						var max = 0;
1449						var maxI = 0;
1450						for (var i = 0; i < names.length; i++) {
1451							if (names[i].length > max) {
1452								max = names[i].length;
1453								maxI = i;
1454							}
1455						}
1456						return maxI;
1457					};
1458					date.setMonth(findMax(inst.options[dateFormat.match(/MM/) ? // Longest month
1459						'monthNames' : 'monthNamesShort']));
1460					date.setDate(findMax(inst.options[dateFormat.match(/DD/) ? // Longest day
1461						'dayNames' : 'dayNamesShort']) + 20 - date.getDay());
1462				}
1463				inst.elem.attr('size', plugin.formatDate(dateFormat, date, inst.getConfig()).length);
1464			}
1465		},
1466
1467		_preDestroy: function(elem, inst) {
1468			if (inst.trigger) {
1469				inst.trigger.remove();
1470			}
1471			elem.empty().off('.' + inst.name);
1472			if (inst.inline && $.fn.mousewheel) {
1473				elem.unmousewheel();
1474			}
1475			if (!inst.inline && inst.options.autoSize) {
1476				elem.removeAttr('size');
1477			}
1478		},
1479
1480		/** Apply multiple event functions.
1481			@param fns {function} The functions to apply.
1482			@example onShow: multipleEvents(fn1, fn2, ...) */
1483		multipleEvents: function(fns) {
1484			var funcs = arguments;
1485			return function(args) {
1486				for (var i = 0; i < funcs.length; i++) {
1487					funcs[i].apply(this, arguments);
1488				}
1489			};
1490		},
1491
1492		/** Enable the control.
1493			@param elem {Element} The control to affect.
1494			@example $(selector).datepick('enable') */
1495		enable: function(elem) {
1496			elem = $(elem);
1497			if (!elem.hasClass(this._getMarker())) {
1498				return;
1499			}
1500			var inst = this._getInst(elem);
1501			if (inst.inline) {
1502				elem.children('.' + this._disableClass).remove().end().
1503					find('button,select').prop('disabled', false).end().
1504					find('a').attr('href', 'javascript:void(0)');
1505			}
1506			else {
1507				elem.prop('disabled', false);
1508				inst.trigger.filter('button.' + this._triggerClass).prop('disabled', false).end().
1509					filter('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''});
1510			}
1511			this._disabled = $.map(this._disabled,
1512				function(value) { return (value === elem[0] ? null : value); }); // Delete entry
1513		},
1514
1515		/** Disable the control.
1516			@param elem {Element} The control to affect.
1517			@example $(selector).datepick('disable') */
1518		disable: function(elem) {
1519			elem = $(elem);
1520			if (!elem.hasClass(this._getMarker())) {
1521				return;
1522			}
1523			var inst = this._getInst(elem);
1524			if (inst.inline) {
1525				var inline = elem.children(':last');
1526				var offset = inline.offset();
1527				var relOffset = {left: 0, top: 0};
1528				inline.parents().each(function() {
1529					if ($(this).css('position') === 'relative') {
1530						relOffset = $(this).offset();
1531						return false;
1532					}
1533				});
1534				var zIndex = elem.css('zIndex');
1535				zIndex = (zIndex === 'auto' ? 0 : parseInt(zIndex, 10)) + 1;
1536				elem.prepend('<div class="' + this._disableClass + '" style="' +
1537					'width: ' + inline.outerWidth() + 'px; height: ' + inline.outerHeight() +
1538					'px; left: ' + (offset.left - relOffset.left) + 'px; top: ' +
1539					(offset.top - relOffset.top) + 'px; z-index: ' + zIndex + '"></div>').
1540					find('button,select').prop('disabled', true).end().
1541					find('a').removeAttr('href');
1542			}
1543			else {
1544				elem.prop('disabled', true);
1545				inst.trigger.filter('button.' + this._triggerClass).prop('disabled', true).end().
1546					filter('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'});
1547			}
1548			this._disabled = $.map(this._disabled,
1549				function(value) { return (value === elem[0] ? null : value); }); // Delete entry
1550			this._disabled.push(elem[0]);
1551		},
1552
1553		/** Is the first field in a jQuery collection disabled as a datepicker?
1554			@param elem {Element} The control to examine.
1555			@return {boolean} <code>true</code> if disabled, <code>false</code> if enabled.
1556			@example if ($(selector).datepick('isDisabled')) {...} */
1557		isDisabled: function(elem) {
1558			return (elem && $.inArray(elem, this._disabled) > -1);
1559		},
1560
1561		/** Show a popup datepicker.
1562			@param elem {Event|Element} a focus event or the control to use.
1563			@example $(selector).datepick('show') */
1564		show: function(elem) {
1565			elem = $(elem.target || elem);
1566			var inst = plugin._getInst(elem);
1567			if (plugin.curInst === inst) {
1568				return;
1569			}
1570			if (plugin.curInst) {
1571				plugin.hide(plugin.curInst, true);
1572			}
1573			if (!$.isEmptyObject(inst)) {
1574				// Retrieve existing date(s)
1575				inst.lastVal = null;
1576				inst.selectedDates = plugin._extractDates(inst, elem.val());
1577				inst.pickingRange = false;
1578				inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] ||
1579					inst.get('defaultDate') || plugin.today()), inst);
1580				inst.prevDate = plugin.newDate(inst.drawDate);
1581				plugin.curInst = inst;
1582				// Generate content
1583				plugin._update(elem[0], true);
1584				// Adjust position before showing
1585				var offset = plugin._checkOffset(inst);
1586				inst.div.css({left: offset.left, top: offset.top});
1587				// And display
1588				var showAnim = inst.options.showAnim;
1589				var showSpeed = inst.options.showSpeed;
1590				showSpeed = (showSpeed === 'normal' && $.ui &&
1591					parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed);
1592				if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) {
1593					var data = inst.div.data(); // Update old effects data
1594					for (var key in data) {
1595						if (key.match(/^ec\.storage\./)) {
1596							data[key] = inst._mainDiv.css(key.replace(/ec\.storage\./, ''));
1597						}
1598					}
1599					inst.div.data(data).show(showAnim, inst.options.showOptions, showSpeed);
1600				}
1601				else {
1602					inst.div[showAnim || 'show'](showAnim ? showSpeed : 0);
1603				}
1604			}
1605		},
1606
1607		/** Extract possible dates from a string.
1608			@private
1609			@param inst {object} The current instance settings.
1610			@param text {string} The text to extract from.
1611			@return {Date[]} The extracted dates. */
1612		_extractDates: function(inst, datesText) {
1613			if (datesText === inst.lastVal) {
1614				return;
1615			}
1616			inst.lastVal = datesText;
1617			datesText = datesText.split(inst.options.multiSelect ? inst.options.multiSeparator :
1618				(inst.options.rangeSelect ? inst.options.rangeSeparator : '\x00'));
1619			var dates = [];
1620			for (var i = 0; i < datesText.length; i++) {
1621				try {
1622					var date = plugin.parseDate(inst.options.dateFormat, datesText[i], inst.getConfig());
1623					if (date) {
1624						var found = false;
1625						for (var j = 0; j < dates.length; j++) {
1626							if (dates[j].getTime() === date.getTime()) {
1627								found = true;
1628								break;
1629							}
1630						}
1631						if (!found) {
1632							dates.push(date);
1633						}
1634					}
1635				}
1636				catch (e) {
1637					// Ignore
1638				}
1639			}
1640			dates.splice(inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1), dates.length);
1641			if (inst.options.rangeSelect && dates.length === 1) {
1642				dates[1] = dates[0];
1643			}
1644			return dates;
1645		},
1646
1647		/** Update the datepicker display.
1648			@private
1649			@param elem {Event|Element} a focus event or the control to use.
1650			@param hidden {boolean} <code>true</code> to initially hide the datepicker. */
1651		_update: function(elem, hidden) {
1652			elem = $(elem.target || elem);
1653			var inst = plugin._getInst(elem);
1654			if (!$.isEmptyObject(inst)) {
1655				if (inst.inline || plugin.curInst === inst) {
1656					if ($.isFunction(inst.options.onChangeMonthYear) && (!inst.prevDate ||
1657							inst.prevDate.getFullYear() !== inst.drawDate.getFullYear() ||
1658							inst.prevDate.getMonth() !== inst.drawDate.getMonth())) {
1659						inst.options.onChangeMonthYear.apply(elem[0],
1660							[inst.drawDate.getFullYear(), inst.drawDate.getMonth() + 1]);
1661					}
1662				}
1663				if (inst.inline) {
1664					elem.html(this._generateContent(elem[0], inst));
1665				}
1666				else if (plugin.curInst === inst) {
1667					if (!inst.div) {
1668						inst.div = $('<div></div>').addClass(this._popupClass).
1669							css({display: (hidden ? 'none' : 'static'), position: 'absolute',
1670								left: elem.offset().left, top: elem.offset().top + elem.outerHeight()}).
1671							appendTo($(inst.options.popupContainer || 'body'));
1672						if ($.fn.mousewheel) {
1673							inst.div.mousewheel(this._doMouseWheel);
1674						}
1675					}
1676					inst.div.html(this._generateContent(elem[0], inst));
1677					elem.focus();
1678				}
1679			}
1680		},
1681
1682		/** Update the input field and any alternate field with the current dates.
1683			@private
1684			@param elem {Element} The control to use.
1685			@param keyUp {boolean} <code>true</code> if coming from <code>keyUp</code> processing (internal). */
1686		_updateInput: function(elem, keyUp) {
1687			var inst = this._getInst(elem);
1688			if (!$.isEmptyObject(inst)) {
1689				var value = '';
1690				var altValue = '';
1691				var sep = (inst.options.multiSelect ? inst.options.multiSeparator :
1692					inst.options.rangeSeparator);
1693				var altFormat = inst.options.altFormat || inst.options.dateFormat;
1694				for (var i = 0; i < inst.selectedDates.length; i++) {
1695					value += (keyUp ? '' : (i > 0 ? sep : '') + plugin.formatDate(
1696						inst.options.dateFormat, inst.selectedDates[i], inst.getConfig()));
1697					altValue += (i > 0 ? sep : '') + plugin.formatDate(
1698						altFormat, inst.selectedDates[i], inst.getConfig());
1699				}
1700				if (!inst.inline && !keyUp) {
1701					$(elem).val(value);
1702				}
1703				$(inst.options.altField).val(altValue);
1704				if ($.isFunction(inst.options.onSelect) && !keyUp && !inst.inSelect) {
1705					inst.inSelect = true; // Prevent endless loops
1706					inst.options.onSelect.apply(elem, [inst.selectedDates]);
1707					inst.inSelect = false;
1708				}
1709			}
1710		},
1711
1712		/** Retrieve the size of left and top borders for an element.
1713			@private
1714			@param elem {jQuery} The element of interest.
1715			@return {number[]} The left and top borders. */
1716		_getBorders: function(elem) {
1717			var convert = function(value) {
1718				return {thin: 1, medium: 3, thick: 5}[value] || value;
1719			};
1720			return [parseFloat(convert(elem.css('border-left-width'))),
1721				parseFloat(convert(elem.css('border-top-width')))];
1722		},
1723
1724		/** Check positioning to remain on the screen.
1725			@private
1726			@param inst {object} The current instance settings.
1727			@return {object} The updated offset for the datepicker. */
1728		_checkOffset: function(inst) {
1729			var base = (inst.elem.is(':hidden') && inst.trigger ? inst.trigger : inst.elem);
1730			var offset = base.offset();
1731			var browserWidth = $(window).width();
1732			var browserHeight = $(window).height();
1733			if (browserWidth === 0) {
1734				return offset;
1735			}
1736			var isFixed = false;
1737			$(inst.elem).parents().each(function() {
1738				isFixed |= $(this).css('position') === 'fixed';
1739				return !isFixed;
1740			});
1741			var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
1742			var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
1743			var above = offset.top - (isFixed ? scrollY : 0) - inst.div.outerHeight();
1744			var below = offset.top - (isFixed ? scrollY : 0) + base.outerHeight();
1745			var alignL = offset.left - (isFixed ? scrollX : 0);
1746			var alignR = offset.left - (isFixed ? scrollX : 0) + base.outerWidth() - inst.div.outerWidth();
1747			var tooWide = (offset.left - scrollX + inst.div.outerWidth()) > browserWidth;
1748			var tooHigh = (offset.top - scrollY + inst.elem.outerHeight() +
1749				inst.div.outerHeight()) > browserHeight;
1750			inst.div.css('position', isFixed ? 'fixed' : 'absolute');
1751			var alignment = inst.options.alignment;
1752			if (alignment === 'topLeft') {
1753				offset = {left: alignL, top: above};
1754			}
1755			else if (alignment === 'topRight') {
1756				offset = {left: alignR, top: above};
1757			}
1758			else if (alignment === 'bottomLeft') {
1759				offset = {left: alignL, top: below};
1760			}
1761			else if (alignment === 'bottomRight') {
1762				offset = {left: alignR, top: below};
1763			}
1764			else if (alignment === 'top') {
1765				offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL), top: above};
1766			}
1767			else { // bottom
1768				offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL),
1769					top: (tooHigh ? above : below)};
1770			}
1771			offset.left = Math.max((isFixed ? 0 : scrollX), offset.left);
1772			offset.top = Math.max((isFixed ? 0 : scrollY), offset.top);
1773			return offset;
1774		},
1775
1776		/** Close date picker if clicked elsewhere.
1777			@private
1778			@param event {MouseEvent} The mouse click to check. */
1779		_checkExternalClick: function(event) {
1780			if (!plugin.curInst) {
1781				return;
1782			}
1783			var elem = $(event.target);
1784			if (elem.closest('.' + plugin._popupClass + ',.' + plugin._triggerClass).length === 0 &&
1785					!elem.hasClass(plugin._getMarker())) {
1786				plugin.hide(plugin.curInst);
1787			}
1788		},
1789
1790		/** Hide a popup datepicker.
1791			@param elem {Element|object} The control to use or the current instance settings.
1792			@param immediate {boolean} <code>true</code> to close immediately without animation (internal).
1793			@example $(selector).datepick('hide') */
1794		hide: function(elem, immediate) {
1795			if (!elem) {
1796				return;
1797			}
1798			var inst = this._getInst(elem);
1799			if ($.isEmptyObject(inst)) {
1800				inst = elem;
1801			}
1802			if (inst && inst === plugin.curInst) {
1803				var showAnim = (immediate ? '' : inst.options.showAnim);
1804				var showSpeed = inst.options.showSpeed;
1805				showSpeed = (showSpeed === 'normal' && $.ui &&
1806					parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed);
1807				var postProcess = function() {
1808					if (!inst.div) {
1809						return;
1810					}
1811					inst.div.remove();
1812					inst.div = null;
1813					plugin.curInst = null;
1814					if ($.isFunction(inst.options.onClose)) {
1815						inst.options.onClose.apply(elem, [inst.selectedDates]);
1816					}
1817				};
1818				inst.div.stop();
1819				if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) {
1820					inst.div.hide(showAnim, inst.options.showOptions, showSpeed, postProcess);
1821				}
1822				else {
1823					var hideAnim = (showAnim === 'slideDown' ? 'slideUp' :
1824						(showAnim === 'fadeIn' ? 'fadeOut' : 'hide'));
1825					inst.div[hideAnim]((showAnim ? showSpeed : ''), postProcess);
1826				}
1827				if (!showAnim) {
1828					postProcess();
1829				}
1830			}
1831		},
1832
1833		/** Handle keystrokes in the datepicker.
1834			@private
1835			@param event {KeyEvent} The keystroke.
1836			@return {boolean} <code>true</code> if not handled, <code>false</code> if handled. */
1837		_keyDown: function(event) {
1838			var elem = event.target;
1839			var inst = plugin._getInst(elem);
1840			var handled = false;
1841			if (inst.div) {
1842				if (event.keyCode === 9) { // Tab - close
1843					plugin.hide(elem);
1844				}
1845				else if (event.keyCode === 13) { // Enter - select
1846					plugin.selectDate(elem,
1847						$('a.' + inst.options.renderer.highlightedClass, inst.div)[0]);
1848					handled = true;
1849				}
1850				else { // Command keystrokes
1851					var commands = inst.options.commands;
1852					for (var name in commands) {
1853						var command = commands[name];
1854						if (command.keystroke.keyCode === event.keyCode &&
1855								!!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) &&
1856								!!command.keystroke.altKey === event.altKey &&
1857								!!command.keystroke.shiftKey === event.shiftKey) {
1858							plugin.performAction(elem, name);
1859							handled = true;
1860							break;
1861						}
1862					}
1863				}
1864			}
1865			else { // Show on 'current' keystroke
1866				var command = inst.options.commands.current;
1867				if (command.keystroke.keyCode === event.keyCode &&
1868						!!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) &&
1869						!!command.keystroke.altKey === event.altKey &&
1870						!!command.keystroke.shiftKey === event.shiftKey) {
1871					plugin.show(elem);
1872					handled = true;
1873				}
1874			}
1875			inst.ctrlKey = ((event.keyCode < 48 && event.keyCode !== 32) || event.ctrlKey || event.metaKey);
1876			if (handled) {
1877				event.preventDefault();
1878				event.stopPropagation();
1879			}
1880			return !handled;
1881		},
1882
1883		/** Filter keystrokes in the datepicker.
1884			@private
1885			@param event {KeyEvent} The keystroke.
1886			@return {boolean} <code>true</code> if allowed, <code>false</code> if not allowed. */
1887		_keyPress: function(event) {
1888			var inst = plugin._getInst(event.target);
1889			if (!$.isEmptyObject(inst) && inst.options.constrainInput) {
1890				var ch = String.fromCharCode(event.keyCode || event.charCode);
1891				var allowedChars = plugin._allowedChars(inst);
1892				return (event.metaKey || inst.ctrlKey || ch < ' ' ||
1893					!allowedChars || allowedChars.indexOf(ch) > -1);
1894			}
1895			return true;
1896		},
1897
1898		/** Determine the set of characters allowed by the date format.
1899			@private
1900			@param inst {object} The current instance settings.
1901			@return {string} The set of allowed characters, or <code>null</code> if anything allowed. */
1902		_allowedChars: function(inst) {
1903			var allowedChars = (inst.options.multiSelect ? inst.options.multiSeparator :
1904				(inst.options.rangeSelect ? inst.options.rangeSeparator : ''));
1905			var literal = false;
1906			var hasNum = false;
1907			var dateFormat = inst.options.dateFormat;
1908			for (var i = 0; i < dateFormat.length; i++) {
1909				var ch = dateFormat.charAt(i);
1910				if (literal) {
1911					if (ch === "'" && dateFormat.charAt(i + 1) !== "'") {
1912						literal = false;
1913					}
1914					else {
1915						allowedChars += ch;
1916					}
1917				}
1918				else {
1919					switch (ch) {
1920						case 'd': case 'm': case 'o': case 'w':
1921							allowedChars += (hasNum ? '' : '0123456789'); hasNum = true; break;
1922						case 'y': case '@': case '!':
1923							allowedChars += (hasNum ? '' : '0123456789') + '-'; hasNum = true; break;
1924						case 'J':
1925							allowedChars += (hasNum ? '' : '0123456789') + '-.'; hasNum = true; break;
1926						case 'D': case 'M': case 'Y':
1927							return null; // Accept anything
1928						case "'":
1929							if (dateFormat.charAt(i + 1) === "'") {
1930								allowedChars += "'";
1931							}
1932							else {
1933								literal = true;
1934							}
1935							break;
1936						default:
1937							allowedChars += ch;
1938					}
1939				}
1940			}
1941			return allowedChars;
1942		},
1943
1944		/** Synchronise datepicker with the field.
1945			@private
1946			@param event {KeyEvent} The keystroke.
1947			@return {boolean} <code>true</code> if allowed, <code>false</code> if not allowed. */
1948		_keyUp: function(event) {
1949			var elem = event.target;
1950			var inst = plugin._getInst(elem);
1951			if (!$.isEmptyObject(inst) && !inst.ctrlKey && inst.lastVal !== inst.elem.val()) {
1952				try {
1953					var dates = plugin._extractDates(inst, inst.elem.val());
1954					if (dates.length > 0) {
1955						plugin.setDate(elem, dates, null, true);
1956					}
1957				}
1958				catch (event) {
1959					// Ignore
1960				}
1961			}
1962			return true;
1963		},
1964
1965		/** Increment/decrement month/year on mouse wheel activity.
1966			@private
1967			@param event {event} The mouse wheel event.
1968			@param delta {number} The amount of change. */
1969		_doMouseWheel: function(event, delta) {
1970			var elem = (plugin.curInst && plugin.curInst.elem[0]) ||
1971				$(event.target).closest('.' + plugin._getMarker())[0];
1972			if (plugin.isDisabled(elem)) {
1973				return;
1974			}
1975			var inst = plugin._getInst(elem);
1976			if (inst.options.useMouseWheel) {
1977				delta = (delta < 0 ? -1 : +1);
1978				plugin.changeMonth(elem, -inst.options[event.ctrlKey ? 'monthsToJump' : 'monthsToStep'] * delta);
1979			}
1980			event.preventDefault();
1981		},
1982
1983		/** Clear an input and close a popup datepicker.
1984			@param elem {Element} The control to use.
1985			@example $(selector).datepick('clear') */
1986		clear: function(elem) {
1987			var inst = this._getInst(elem);
1988			if (!$.isEmptyObject(inst)) {
1989				inst.selectedDates = [];
1990				this.hide(elem);
1991				var defaultDate = inst.get('defaultDate');
1992				if (inst.options.selectDefaultDate && defaultDate) {
1993					this.setDate(elem, plugin.newDate(defaultDate || plugin.today()));
1994				}
1995				else {
1996					this._updateInput(elem);
1997				}
1998			}
1999		},
2000
2001		/** Retrieve the selected date(s) for a datepicker.
2002			@param elem {Element} The control to examine.
2003			@return {Date[]} The selected date(s).
2004			@example var dates = $(selector).datepick('getDate') */
2005		getDate: function(elem) {
2006			var inst = this._getInst(elem);
2007			return (!$.isEmptyObject(inst) ? inst.selectedDates : []);
2008		},
2009
2010		/** Set the selected date(s) for a datepicker.
2011			@param elem {Element} the control to examine.
2012			@param dates {Date|number|string|array} the selected date(s).
2013			@param [endDate] {Date|number|string} the ending date for a range.
2014			@param keyUp {boolean} <code>true</code> if coming from <code>keyUp</code> processing (internal).
2015			@param setOpt {boolean} <code>true</code> if coming from option processing (internal).
2016			@example $(selector).datepick('setDate', new Date(2014, 12-1, 25))
2017 $(selector).datepick('setDate', '12/25/2014', '01/01/2015')
2018 $(selector).datepick('setDate', [date1, date2, date3]) */
2019		setDate: function(elem, dates, endDate, keyUp, setOpt) {
2020			var inst = this._getInst(elem);
2021			if (!$.isEmptyObject(inst)) {
2022				if (!$.isArray(dates)) {
2023					dates = [dates];
2024					if (endDate) {
2025						dates.push(endDate);
2026					}
2027				}
2028				var minDate = inst.get('minDate');
2029				var maxDate = inst.get('maxDate');
2030				var curDate = inst.selectedDates[0];
2031				inst.selectedDates = [];
2032				for (var i = 0; i < dates.length; i++) {
2033					var date = plugin.determineDate(
2034						dates[i], null, curDate, inst.options.dateFormat, inst.getConfig());
2035					if (date) {
2036						if ((!minDate || date.getTime() >= minDate.getTime()) &&
2037								(!maxDate || date.getTime() <= maxDate.getTime())) {
2038							var found = false;
2039							for (var j = 0; j < inst.selectedDates.length; j++) {
2040								if (inst.selectedDates[j].getTime() === date.getTime()) {
2041									found = true;
2042									break;
2043								}
2044							}
2045							if (!found) {
2046								inst.selectedDates.push(date);
2047							}
2048						}
2049					}
2050				}
2051				inst.selectedDates.splice(inst.options.multiSelect ||
2052					(inst.options.rangeSelect ? 2 : 1), inst.selectedDates.length);
2053				if (inst.options.rangeSelect) {
2054					switch (inst.selectedDates.length) {
2055						case 1: inst.selectedDates[1] = inst.selectedDates[0]; break;
2056						case 2: inst.selectedDates[1] =
2057							(inst.selectedDates[0].getTime() > inst.selectedDates[1].getTime() ?
2058							inst.selectedDates[0] : inst.selectedDates[1]); break;
2059					}
2060					inst.pickingRange = false;
2061				}
2062				inst.prevDate = (inst.drawDate ? plugin.newDate(inst.drawDate) : null);
2063				inst.drawDate = this._checkMinMax(plugin.newDate(inst.selectedDates[0] ||
2064					inst.get('defaultDate') || plugin.today()), inst);
2065				if (!setOpt) {
2066					this._update(elem);
2067					this._updateInput(elem, keyUp);
2068				}
2069			}
2070		},
2071
2072		/** Determine whether a date is selectable for this datepicker.
2073			@private
2074			@param elem {Element} The control to check.
2075			@param date {Date|string|number} The date to check.
2076			@return {boolean} <code>true</code> if selectable, <code>false</code> if not.
2077			@example var selectable = $(selector).datepick('isSelectable', date) */
2078		isSelectable: function(elem, date) {
2079			var inst = this._getInst(elem);
2080			if ($.isEmptyObject(inst)) {
2081				return false;
2082			}
2083			date = plugin.determineDate(date, inst.selectedDates[0] || this.today(), null,
2084				inst.options.dateFormat, inst.getConfig());
2085			return this._isSelectable(elem, date, inst.options.onDate,
2086				inst.get('minDate'), inst.get('maxDate'));
2087		},
2088
2089		/** Internally determine whether a date is selectable for this datepicker.
2090			@private
2091			@param elem {Element} the control to check.
2092			@param date {Date} The date to check.
2093			@param onDate {function|boolean} Any onDate callback or callback.selectable.
2094			@param minDate {Date} The minimum allowed date.
2095			@param maxDate {Date} The maximum allowed date.
2096			@return {boolean} <code>true</code> if selectable, <code>false</code> if not. */
2097		_isSelectable: function(elem, date, onDate, minDate, maxDate) {
2098			var dateInfo = (typeof onDate === 'boolean' ? {selectable: onDate} :
2099				(!$.isFunction(onDate) ? {}
2099 : onDate.apply(elem, [date, true])));
2100			return (dateInfo.selectable !== false) &&
2101				(!minDate || date.getTime() >= minDate.getTime()) &&
2102				(!maxDate || date.getTime() <= maxDate.getTime());
2103		},
2104
2105		/** Perform a named action for a datepicker.
2106			@param elem {element} The control to affect.
2107			@param action {string} The name of the action. */
2108		performAction: function(elem, action) {
2109			var inst = this._getInst(elem);
2110			if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) {
2111				var commands = inst.options.commands;
2112				if (commands[action] && commands[action].enabled.apply(elem, [inst])) {
2113					commands[action].action.apply(elem, [inst]);
2114				}
2115			}
2116		},
2117
2118		/** Set the currently shown month, defaulting to today's.
2119			@param elem {Element} The control to affect.
2120			@param [year] {number} The year to show.
2121			@param [month] {number} The month to show (1-12).
2122			@param [day] {number} The day to show.
2123			@example $(selector).datepick('showMonth', 2014, 12, 25) */
2124		showMonth: function(elem, year, month, day) {
2125			var inst = this._getInst(elem);
2126			if (!$.isEmptyObject(inst) && (day != null ||
2127					(inst.drawDate.getFullYear() !== year || inst.drawDate.getMonth() + 1 !== month))) {
2128				inst.prevDate = plugin.newDate(inst.drawDate);
2129				var show = this._checkMinMax((year != null ?
2130					plugin.newDate(year, month, 1) : plugin.today()), inst);
2131				inst.drawDate = plugin.newDate(show.getFullYear(), show.getMonth() + 1, 
2132					(day != null ? day : Math.min(inst.drawDate.getDate(),
2133					plugin.daysInMonth(show.getFullYear(), show.getMonth() + 1))));
2134				this._update(elem);
2135			}
2136		},
2137
2138		/** Adjust the currently shown month.
2139			@param elem {Element} The control to affect.
2140			@param offset {number} The number of months to change by.
2141			@example $(selector).datepick('changeMonth', 2)*/
2142		changeMonth: function(elem, offset) {
2143			var inst = this._getInst(elem);
2144			if (!$.isEmptyObject(inst)) {
2145				var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'm');
2146				this.showMonth(elem, date.getFullYear(), date.getMonth() + 1);
2147			}
2148		},
2149
2150		/** Adjust the currently shown day.
2151			@param elem {Element} The control to affect.
2152			@param offset {number} The number of days to change by.
2153			@example $(selector).datepick('changeDay', 7)*/
2154		changeDay: function(elem, offset) {
2155			var inst = this._getInst(elem);
2156			if (!$.isEmptyObject(inst)) {
2157				var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'd');
2158				this.showMonth(elem, date.getFullYear(), date.getMonth() + 1, date.getDate());
2159			}
2160		},
2161
2162		/** Restrict a date to the minimum/maximum specified.
2163			@private
2164			@param date {Date} The date to check.
2165			@param inst {object} The current instance settings. */
2166		_checkMinMax: function(date, inst) {
2167			var minDate = inst.get('minDate');
2168			var maxDate = inst.get('maxDate');
2169			date = (minDate && date.getTime() < minDate.getTime() ? plugin.newDate(minDate) : date);
2170			date = (maxDate && date.getTime() > maxDate.getTime() ? plugin.newDate(maxDate) : date);
2171			return date;
2172		},
2173
2174		/** Retrieve the date associated with an entry in the datepicker.
2175			@param elem {Element} The control to examine.
2176			@param target {Element} The selected datepicker element.
2177			@return {Date} The corresponding date, or <code>null</code>.			
2178			@example var date = $(selector).datepick('retrieveDate', $('div.datepick-popup a:contains(10)')[0]) */
2179		retrieveDate: function(elem, target) {
2180			var inst = this._getInst(elem);
2181			return ($.isEmptyObject(inst) ? null : this._normaliseDate(
2182				new Date(parseInt(target.className.replace(/^.*dp(-?\d+).*$/, '$1'), 10))));
2183		},
2184
2185		/** Select a date for this datepicker.
2186			@param elem {Element} The control to examine.
2187			@param target {Element} The selected datepicker element.
2188			@example $(selector).datepick('selectDate', $('div.datepick-popup a:contains(10)')[0]) */
2189		selectDate: function(elem, target) {
2190			var inst = this._getInst(elem);
2191			if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) {
2192				var date = this.retrieveDate(elem, target);
2193				if (inst.options.multiSelect) {
2194					var found = false;
2195					for (var i = 0; i < inst.selectedDates.length; i++) {
2196						if (date.getTime() === inst.selectedDates[i].getTime()) {
2197							inst.selectedDates.splice(i, 1);
2198							found = true;
2199							break;
2200						}
2201					}
2202					if (!found && inst.selectedDates.length < inst.options.multiSelect) {
2203						inst.selectedDates.push(date);
2204					}
2205				}
2206				else if (inst.options.rangeSelect) {
2207					if (inst.pickingRange) {
2208						inst.selectedDates[1] = date;
2209					}
2210					else {
2211						inst.selectedDates = [date, date];
2212					}
2213					inst.pickingRange = !inst.pickingRange;
2214				}
2215				else {
2216					inst.selectedDates = [date];
2217				}
2218				inst.prevDate = plugin.newDate(date);
2219				this._updateInput(elem);
2220				if (inst.inline || inst.pickingRange || inst.selectedDates.length <
2221						(inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1))) {
2222					this._update(elem);
2223				}
2224				else {
2225					this.hide(elem);
2226				}
2227			}
2228		},
2229
2230		/** Generate the datepicker content for this control.
2231			@private
2232			@param elem {Element} The control to affect.
2233			@param inst {object} The current instance settings.
2234			@return {jQuery} The datepicker content */
2235		_generateContent: function(elem, inst) {
2236			var monthsToShow = inst.options.monthsToShow;
2237			monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]);
2238			inst.drawDate = this._checkMinMax(
2239				inst.drawDate || inst.get('defaultDate') || plugin.today(), inst);
2240			var drawDate = plugin._applyMonthsOffset(plugin.newDate(inst.drawDate), inst);
2241			// Generate months
2242			var monthRows = '';
2243			for (var row = 0; row < monthsToShow[0]; row++) {
2244				var months = '';
2245				for (var col = 0; col < monthsToShow[1]; col++) {
2246					months += this._generateMonth(elem, inst, drawDate.getFullYear(),
2247						drawDate.getMonth() + 1, inst.options.renderer, (row === 0 && col === 0));
2248					plugin.add(drawDate, 1, 'm');
2249				}
2250				monthRows += this._prepare(inst.options.renderer.monthRow, inst).replace(/\{months\}/, months);
2251			}
2252			var picker = this._prepare(inst.options.renderer.picker, inst).replace(/\{months\}/, monthRows).
2253				replace(/\{weekHeader\}/g, this._generateDayHeaders(inst, inst.options.renderer));
2254			// Add commands
2255			var addCommand = function(type, open, close, name, classes) {
2256				if (picker.indexOf('{' + type + ':' + name + '}') === -1) {
2257					return;
2258				}
2259				var command = inst.options.commands[name];
2260				var date = (inst.options.commandsAsDateFormat ? command.date.apply(elem, [inst]) : null);
2261				picker = picker.replace(new RegExp('\\{' + type + ':' + name + '\\}', 'g'),
2262					'<' + open + (command.status ? ' title="' + inst.options[command.status] + '"' : '') +
2263					' class="' + inst.options.renderer.commandClass + ' ' +
2264					inst.options.renderer.commandClass + '-' + name + ' ' + classes +
2265					(command.enabled(inst) ? '' : ' ' + inst.options.renderer.disabledClass) + '">' +
2266					(date ? plugin.formatDate(inst.options[command.text], date, inst.getConfig()) :
2267					inst.options[command.text]) + '</' + close + '>');
2268			};
2269			for (var name in inst.options.commands) {
2270				addCommand('button', 'button type="button"', 'button', name,
2271					inst.options.renderer.commandButtonClass);
2272				addCommand('link', 'a href="javascript:void(0)"', 'a', name,
2273					inst.options.renderer.commandLinkClass);
2274			}
2275			picker = $(picker);
2276			if (monthsToShow[1] > 1) {
2277				var count = 0;
2278				$(inst.options.renderer.monthSelector, picker).each(function() {
2279					var nth = ++count % monthsToShow[1];
2280					$(this).addClass(nth === 1 ? 'first' : (nth === 0 ? 'last' : ''));
2281				});
2282			}
2283			// Add datepicker behaviour
2284			var self = this;
2285			picker.find(inst.options.renderer.daySelector + ' a').hover(
2286					function() { $(this).addClass(inst.options.renderer.highlightedClass); },
2287					function() {
2288						(inst.inline ? $(this).closest('.' + self._getMarker()) : inst.div).
2289							find(inst.options.renderer.daySelector + ' a').
2290							removeClass(inst.options.renderer.highlightedClass);
2291					}).
2292				click(function() {
2293					self.selectDate(elem, this);
2294				}).end().
2295				find('select.' + this._monthYearClass + ':not(.' + this._anyYearClass + ')').
2296				change(function() {
2297					var monthYear = $(this).val().split('/');
2298					self.showMonth(elem, parseInt(monthYear[1], 10), parseInt(monthYear[0], 10));
2299				}).end().
2300				find('select.' + this._anyYearClass).click(function() {
2301					$(this).css('visibility', 'hidden').
2302						next('input').css({left: this.offsetLeft, top: this.offsetTop,
2303						width: this.offsetWidth, height: this.offsetHeight}).show().focus();
2304				}).end().
2305				find('input.' + self._monthYearClass).change(function() {
2306					try {
2307						var year = parseInt($(this).val(), 10);
2308						year = (isNaN(year) ? inst.drawDate.getFullYear() : year);
2309						self.showMonth(elem, year, inst.drawDate.getMonth() + 1, inst.drawDate.getDate());
2310					}
2311					catch (e) {
2312						alert(e);
2313					}
2314				}).keydown(function(event) {
2315					if (event.keyCode === 13) { // Enter
2316						$(event.elem).change();
2317					}
2318					else if (event.keyCode === 27) { // Escape
2319						$(event.elem).hide().prev('select').css('visibility', 'visible');
2320						inst.elem.focus();
2321					}
2322				});
2323			// Add command behaviour
2324			picker.find('.' + inst.options.renderer.commandClass).click(function() {
2325					if (!$(this).hasClass(inst.options.renderer.disabledClass)) {
2326						var action = this.className.replace(
2327							new RegExp('^.*' + inst.options.renderer.commandClass + '-([^ ]+).*$'), '$1');
2328						plugin.performAction(elem, action);
2329					}
2330				});
2331			// Add classes
2332			if (inst.options.isRTL) {
2333				picker.addClass(inst.options.renderer.rtlClass);
2334			}
2335			if (monthsToShow[0] * monthsToShow[1] > 1) {
2336				picker.addClass(inst.options.renderer.multiClass);
2337			}
2338			if (inst.options.pickerClass) {
2339				picker.addClass(inst.options.pickerClass);
2340			}
2341			// Resize
2342			$('body').append(picker);
2343			var width = 0;
2344			picker.find(inst.options.renderer.monthSelector).each(function() {
2345				width += $(this).outerWidth();
2346			});
2347			picker.width(width / monthsToShow[0]);
2348			// Pre-show customisation
2349			if ($.isFunction(inst.options.onShow)) {
2350				inst.options.onShow.apply(elem, [picker, inst]);
2351			}
2352			return picker;
2353		},
2354
2355		/** Generate the content for a single month.
2356			@private
2357			@param elem {Element} The control to affect.
2358			@param inst {object} The current instance settings.
2359			@param year {number} The year to generate.
2360			@param month {number} The month to generate.
2361			@param renderer {object} The rendering templates.
2362			@param first {boolean} <code>true</code> if first of multiple months.
2363			@return {string} The month content. */
2364		_generateMonth: function(elem, inst, year, month, renderer, first) {
2365			var daysInMonth = plugin.daysInMonth(year, month);
2366			var monthsToShow = inst.options.monthsToShow;
2367			monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]);
2368			var fixedWeeks = inst.options.fixedWeeks || (monthsToShow[0] * monthsToShow[1] > 1);
2369			var firstDay = inst.options.firstDay;
2370			var leadDays = (plugin.newDate(year, month, 1).getDay() - firstDay + 7) % 7;
2371			var numWeeks = (fixedWeeks ? 6 : Math.ceil((leadDays + daysInMonth) / 7));
2372			var selectOtherMonths = inst.options.selectOtherMonths && inst.options.showOtherMonths;
2373			var minDate = (inst.pickingRange ? inst.selectedDates[0] : inst.get('minDate'));
2374			var maxDate = inst.get('maxDate');
2375			var showWeeks = renderer.week.indexOf('{weekOfYear}') > -1;
2376			var today = plugin.today();
2377			var drawDate = plugin.newDate(year, month, 1);
2378			plugin.add(drawDate, -leadDays - (fixedWeeks && (drawDate.getDay() === firstDay) ? 7 : 0), 'd');
2379			var ts = drawDate.getTime();
2380			// Generate weeks
2381			var weeks = '';
2382			for (var week = 0; week < numWeeks; week++) {
2383				var weekOfYear = (!showWeeks ? '' : '<span class="dp' + ts + '">' +
2384					($.isFunction(inst.options.calculateWeek) ? inst.options.calculateWeek(drawDate) : 0) + '</span>');
2385				var days = '';
2386				for (var day = 0; day < 7; day++) {
2387					var selected = false;
2388					if (inst.options.rangeSelect && inst.selectedDates.length > 0) {
2389						selected = (drawDate.getTime() >= inst.selectedDates[0] &&
2390							drawDate.getTime() <= inst.selectedDates[1]);
2391					}
2392					else {
2393						for (var i = 0; i < inst.selectedDates.length; i++) {
2394							if (inst.selectedDates[i].getTime() === drawDate.getTime()) {
2395								selected = true;
2396								break;
2397							}
2398						}
2399					}
2400					var dateInfo = (!$.isFunction(inst.options.onDate) ? {} :
2401						inst.options.onDate.apply(elem, [drawDate, drawDate.getMonth() + 1 === month]));
2402					var selectable = (selectOtherMonths || drawDate.getMonth() + 1 === month) &&
2403						this._isSelectable(elem, drawDate, dateInfo.selectable, minDate, maxDate);
2404					days += this._prepare(renderer.day, inst).replace(/\{day\}/g,
2405						(selectable ? '<a href="javascript:void(0)"' : '<span') +
2406						' class="dp' + ts + ' ' + (dateInfo.dateClass || '') +
2407						(selected && (selectOtherMonths || drawDate.getMonth() + 1 === month) ?
2408						' ' + renderer.selectedClass : '') +
2409						(selectable ? ' ' + renderer.defaultClass : '') +
2410						((drawDate.getDay() || 7) < 6 ? '' : ' ' + renderer.weekendClass) +
2411						(drawDate.getMonth() + 1 === month ? '' : ' ' + renderer.otherMonthClass) +
2412						(drawDate.getTime() === today.getTime() && (drawDate.getMonth() + 1) === month ?
2413						' ' + renderer.todayClass : '') +
2414						(drawDate.getTime() === inst.drawDate.getTime() && (drawDate.getMonth() + 1) === month ?
2415						' ' + renderer.highlightedClass : '') + '"' +
2416						(dateInfo.title || (inst.options.dayStatus && selectable) ? ' title="' +
2417						(dateInfo.title || plugin.formatDate(
2418						inst.options.dayStatus, drawDate, inst.getConfig())) + '"' : '') + '>' +
2419						(inst.options.showOtherMonths || (drawDate.getMonth() + 1) === month ?
2420						dateInfo.content || drawDate.getDate() : '&nbsp;') +
2421						(selectable ? '</a>' : '</span>'));
2422					plugin.add(drawDate, 1, 'd');
2423					ts = drawDate.getTime();
2424				}
2425				weeks += this._prepare(renderer.week, inst).replace(/\{days\}/g, days).
2426					replace(/\{weekOfYear\}/g, weekOfYear);
2427			}
2428			var monthHeader = this._prepare(renderer.month, inst).match(/\{monthHeader(:[^\}]+)?\}/);
2429			monthHeader = (monthHeader[0].length <= 13 ? 'MM yyyy' :
2430				monthHeader[0].substring(13, monthHeader[0].length - 1));
2431			monthHeader = (first ? this._generateMonthSelection(
2432				inst, year, month, minDate, maxDate, monthHeader, renderer) :
2433				plugin.formatDate(monthHeader, plugin.newDate(year, month, 1), inst.getConfig()));
2434			var weekHeader = this._prepare(renderer.weekHeader, inst).
2435				replace(/\{days\}/g, this._generateDayHeaders(inst, renderer));
2436			return this._prepare(renderer.month, inst).replace(/\{monthHeader(:[^\}]+)?\}/g, monthHeader).
2437				replace(/\{weekHeader\}/g, weekHeader).replace(/\{weeks\}/g, weeks);
2438		},
2439
2440		/** Generate the HTML for the day headers.
2441			@private
2442			@param inst {object} The current instance settings.
2443			@param renderer {object} The rendering templates.
2444			@return {string} A week's worth of day headers. */
2445		_generateDayHeaders: function(inst, renderer) {
2446			var header = '';
2447			for (var day = 0; day < 7; day++) {
2448				var dow = (day + inst.options.firstDay) % 7;
2449				header += this._prepare(renderer.dayHeader, inst).replace(/\{day\}/g,
2450					'<span class="' + this._curDoWClass + dow + '" title="' +
2451					inst.options.dayNames[dow] + '">' + inst.options.dayNamesMin[dow] + '</span>');
2452			}
2453			return header;
2454		},
2455
2456		/** Generate selection controls for month.
2457			@private
2458			@param inst {object} The current instance settings.
2459			@param year {number} The year to generate.
2460			@param month {number} The month to generate.
2461			@param minDate {Date} The minimum date allowed.
2462			@param maxDate {Date} The maximum date allowed.
2463			@param monthHeader {string} The month/year format.
2464			@return {string} The month selection content. */
2465		_generateMonthSelection: function(inst, year, month, minDate, maxDate, monthHeader) {
2466			if (!inst.options.changeMonth) {
2467				return plugin.formatDate(
2468					monthHeader, plugin.newDate(year, month, 1), inst.getConfig());
2469			}
2470			// Months
2471			var monthNames = inst.options['monthNames' + (monthHeader.match(/mm/i) ? '' : 'Short')];
2472			var html = monthHeader.replace(/m+/i, '\\x2E').replace(/y+/i, '\\x2F');
2473			var selector = '<select class="' + this._monthYearClass +
2474				'" title="' + inst.options.monthStatus + '">';
2475			for (var m = 1; m <= 12; m++) {
2476				if ((!minDate || plugin.newDate(year, m, plugin.daysInMonth(year, m)).
2477						getTime() >= minDate.getTime()) &&
2478						(!maxDate || plugin.newDate(year, m, 1).getTime() <= maxDate.getTime())) {
2479					selector += '<option value="' + m + '/' + year + '"' +
2480						(month === m ? ' selected="selected"' : '') + '>' +
2481						monthNames[m - 1] + '</option>';
2482				}
2483			}
2484			selector += '</select>';
2485			html = html.replace(/\\x2E/, selector);
2486			// Years
2487			var yearRange = inst.options.yearRange;
2488			if (yearRange === 'any') {
2489				selector = '<select class="' + this._monthYearClass + ' ' + this._anyYearClass +
2490					'" title="' + inst.options.yearStatus + '">' +
2491					'<option>' + year + '</option></select>' +
2492					'<input class="' + this._monthYearClass + ' ' + this._curMonthClass +
2493					month + '" value="' + year + '">';
2494			}
2495			else {
2496				yearRange = yearRange.split(':');
2497				var todayYear = plugin.today().getFullYear();
2498				var start = (yearRange[0].match('c[+-].*') ? year + parseInt(yearRange[0].substring(1), 10) :
2499					((yearRange[0].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[0], 10)));
2500				var end = (yearRange[1].match('c[+-].*') ? year + parseInt(yearRange[1].substring(1), 10) :
2501					((yearRange[1].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[1], 10)));
2502				selector = '<select class="' + this._monthYearClass +
2503					'" title="' + inst.options.yearStatus + '">';
2504				start = plugin.add(plugin.newDate(start + 1, 1, 1), -1, 'd');
2505				end = plugin.newDate(end, 1, 1);
2506				var addYear = function(y) {
2507					if (y !== 0) {
2508						selector += '<option value="' + month + '/' + y + '"' +
2509							(year === y ? ' selected="selected"' : '') + '>' + y + '</option>';
2510					}
2511				};
2512				if (start.getTime() < end.getTime()) {
2513					start = (minDate && minDate.getTime() > start.getTime() ? minDate : start).getFullYear();
2514					end = (maxDate && maxDate.getTime() < end.getTime() ? maxDate : end).getFullYear();
2515					for (var y = start; y <= end; y++) {
2516						addYear(y);
2517					}
2518				}
2519				else {
2520					start = (maxDate && maxDate.getTime() < start.getTime() ? maxDate : start).getFullYear();
2521					end = (minDate && minDate.getTime() > end.getTime() ? minDate : end).getFullYear();
2522					for (var y = start; y >= end; y--) {
2523						addYear(y);
2524					}
2525				}
2526				selector += '</select>';
2527			}
2528			html = html.replace(/\\x2F/, selector);
2529			return html;
2530		},
2531
2532		/** Prepare a render template for use.
2533			Exclude popup/inline sections that are not applicable.
2534			Localise text of the form: {l10n:name}.
2535			@private
2536			@param text {string} The text to localise.
2537			@param inst {object} The current instance settings.
2538			@return {string} The localised text. */
2539		_prepare: function(text, inst) {
2540			var replaceSection = function(type, retain) {
2541				while (true) {
2542					var start = text.indexOf('{' + type + ':start}');
2543					if (start === -1) {
2544						return;
2545					}
2546					var end = text.substring(start).indexOf('{' + type + ':end}');
2547					if (end > -1) {
2548						text = text.substring(0, start) +
2549							(retain ? text.substr(start + type.length + 8, end - type.length - 8) : '') +
2550							text.substring(start + end + type.length + 6);
2551					}
2552				}
2553			};
2554			replaceSection('inline', inst.inline);
2555			replaceSection('popup', !inst.inline);
2556			var pattern = /\{l10n:([^\}]+)\}/;
2557			var matches = null;
2558			while (matches = pattern.exec(text)) {
2559				text = text.replace(matches[0], inst.options[matches[1]]);
2560			}
2561			return text;
2562		}
2563	});
2564
2565	var plugin = $.datepick; // Singleton instance
2566
2567	$(function() {
2568		$(document).on('mousedown.' + pluginName, plugin._checkExternalClick).
2569			on('resize.' + pluginName, function() { plugin.hide(plugin.curInst); });
2570	});
2571
2572})(jQuery);
2573

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.