PluginProbe
Mailchimp List Subscribe Form / 1.5
Mailchimp List Subscribe Form v1.5
1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 All 55 releases
mailchimp / js / datepicker.js

datepicker.js in Mailchimp List Subscribe Form 1.5, at js/datepicker.js

1,816 lines 74.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * jQuery UI Datepicker 1.8.14
3 *
4 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * http://docs.jquery.com/UI/Datepicker
9 *
10 * Depends:
11 * jquery.ui.core.js
12 */
13 (function( $, undefined ) {
14
15 $.extend($.ui, { datepicker: { version: "1.8.14" } });
16
17 var PROP_NAME = 'datepicker';
18 var dpuuid = new Date().getTime();
19 var instActive;
20
21 /* Date picker manager.
22 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
23 Settings for (groups of) date pickers are maintained in an instance object,
24 allowing multiple different settings on the same page. */
25
26 function Datepicker() {
27 this.debug = false; // Change this to true to start debugging
28 this._curInst = null; // The current instance in use
29 this._keyEvent = false; // If the last event was a key event
30 this._disabledInputs = []; // List of date picker inputs that have been disabled
31 this._datepickerShowing = false; // True if the popup picker is showing , false if not
32 this._inDialog = false; // True if showing within a "dialog", false if not
33 this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
34 this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
35 this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
36 this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
37 this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
38 this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
39 this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
40 this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
41 this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
42 this.regional = []; // Available regional settings, indexed by language code
43 this.regional[''] = { // Default regional settings
44 closeText: 'Done', // Display text for close link
45 prevText: 'Prev', // Display text for previous month link
46 nextText: 'Next', // Display text for next month link
47 currentText: 'Today', // Display text for current month link
48 monthNames: ['January','February','March','April','May','June',
49 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
50 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
51 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
52 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
53 dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
54 weekHeader: 'Wk', // Column header for week of the year
55 dateFormat: 'mm/dd/yy', // See format options on parseDate
56 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
57 isRTL: false, // True if right-to-left language, false if left-to-right
58 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
59 yearSuffix: '' // Additional text to append to the year in the month headers
60 };
61 this._defaults = { // Global defaults for all the date picker instances
62 showOn: 'focus', // 'focus' for popup on focus,
63 // 'button' for trigger button, or 'both' for either
64 showAnim: 'fadeIn', // Name of jQuery animation for popup
65 showOptions: {}, // Options for enhanced animations
66 defaultDate: null, // Used when field is blank: actual date,
67 // +/-number for offset from today, null for today
68 appendText: '', // Display text following the input box, e.g. showing the format
69 buttonText: '...', // Text for trigger button
70 buttonImage: '', // URL for trigger button image
71 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
72 hideIfNoPrevNext: false, // True to hide next/previous month links
73 // if not applicable, false to just disable them
74 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
75 gotoCurrent: false, // True if today link goes back to current selection instead
76 changeMonth: false, // True if month can be selected directly, false if only prev/next
77 changeYear: false, // True if year can be selected directly, false if only prev/next
78 yearRange: 'c-10:c+10', // Range of years to display in drop-down,
79 // either relative to today's year (-nn:+nn), relative to currently displayed year
80 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
81 showOtherMonths: false, // True to show dates in other months, false to leave blank
82 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
83 showWeek: false, // True to show week of the year, false to not show it
84 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
85 // takes a Date and returns the number of the week for it
86 shortYearCutoff: '+10', // Short year values < this are in the current century,
87 // > this are in the previous century,
88 // string value starting with '+' for current year + value
89 minDate: null, // The earliest selectable date, or null for no limit
90 maxDate: null, // The latest selectable date, or null for no limit
91 duration: 'fast', // Duration of display/closure
92 beforeShowDay: null, // Function that takes a date and returns an array with
93 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
94 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
95 beforeShow: null, // Function that takes an input field and
96 // returns a set of custom settings for the date picker
97 onSelect: null, // Define a callback function when a date is selected
98 onChangeMonthYear: null, // Define a callback function when the month or year is changed
99 onClose: null, // Define a callback function when the datepicker is closed
100 numberOfMonths: 1, // Number of months to show at a time
101 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
102 stepMonths: 1, // Number of months to step back/forward
103 stepBigMonths: 12, // Number of months to step back/forward for the big links
104 altField: '', // Selector for an alternate field to store selected dates into
105 altFormat: '', // The date format to use for the alternate field
106 constrainInput: true, // The input is constrained by the current date format
107 showButtonPanel: false, // True to show button panel, false to not show it
108 autoSize: false // True to size the input for the date format, false to leave as is
109 };
110 $.extend(this._defaults, this.regional['']);
111 this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
112 }
113
114 $.extend(Datepicker.prototype, {
115 /* Class name added to elements to indicate already configured with a date picker. */
116 markerClassName: 'hasDatepicker',
117
118 //Keep track of the maximum number of rows displayed (see #7043)
119 maxRows: 4,
120
121 /* Debug logging (if enabled). */
122 log: function () {
123 if (this.debug)
124 console.log.apply('', arguments);
125 },
126
127 // TODO rename to "widget" when switching to widget factory
128 _widgetDatepicker: function() {
129 return this.dpDiv;
130 },
131
132 /* Override the default settings for all instances of the date picker.
133 @param settings object - the new settings to use as defaults (anonymous object)
134 @return the manager object */
135 setDefaults: function(settings) {
136 extendRemove(this._defaults, settings || {});
137 return this;
138 },
139
140 /* Attach the date picker to a jQuery selection.
141 @param target element - the target input field or division or span
142 @param settings object - the new settings to use for this date picker instance (anonymous) */
143 _attachDatepicker: function(target, settings) {
144 // check for settings on the control itself - in namespace 'date:'
145 var inlineSettings = null;
146 for (var attrName in this._defaults) {
147 var attrValue = target.getAttribute('date:' + attrName);
148 if (attrValue) {
149 inlineSettings = inlineSettings || {};
150 try {
151 inlineSettings[attrName] = eval(attrValue);
152 } catch (err) {
153 inlineSettings[attrName] = attrValue;
154 }
155 }
156 }
157 var nodeName = target.nodeName.toLowerCase();
158 var inline = (nodeName == 'div' || nodeName == 'span');
159 if (!target.id) {
160 this.uuid += 1;
161 target.id = 'dp' + this.uuid;
162 }
163 var inst = this._newInst($(target), inline);
164 inst.settings = $.extend({}, settings || {}, inlineSettings || {});
165 if (nodeName == 'input') {
166 this._connectDatepicker(target, inst);
167 } else if (inline) {
168 this._inlineDatepicker(target, inst);
169 }
170 },
171
172 /* Create a new instance object. */
173 _newInst: function(target, inline) {
174 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
175 return {id: id, input: target, // associated target
176 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
177 drawMonth: 0, drawYear: 0, // month being drawn
178 inline: inline, // is datepicker inline or not
179 dpDiv: (!inline ? this.dpDiv : // presentation div
180 bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
181 },
182
183 /* Attach the date picker to an input field. */
184 _connectDatepicker: function(target, inst) {
185 var input = $(target);
186 inst.append = $([]);
187 inst.trigger = $([]);
188 if (input.hasClass(this.markerClassName))
189 return;
190 this._attachments(input, inst);
191 input.addClass(this.markerClassName).keydown(this._doKeyDown).
192 keypress(this._doKeyPress).keyup(this._doKeyUp).
193 bind("setData.datepicker", function(event, key, value) {
194 inst.settings[key] = value;
195 }).bind("getData.datepicker", function(event, key) {
196 return this._get(inst, key);
197 });
198 this._autoSize(inst);
199 $.data(target, PROP_NAME, inst);
200 },
201
202 /* Make attachments based on settings. */
203 _attachments: function(input, inst) {
204 var appendText = this._get(inst, 'appendText');
205 var isRTL = this._get(inst, 'isRTL');
206 if (inst.append)
207 inst.append.remove();
208 if (appendText) {
209 inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
210 input[isRTL ? 'before' : 'after'](inst.append);
211 }
212 input.unbind('focus', this._showDatepicker);
213 if (inst.trigger)
214 inst.trigger.remove();
215 var showOn = this._get(inst, 'showOn');
216 if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
217 input.focus(this._showDatepicker);
218 if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
219 var buttonText = this._get(inst, 'buttonText');
220 var buttonImage = this._get(inst, 'buttonImage');
221 inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
222 $('<img/>').addClass(this._triggerClass).
223 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
224 $('<button type="button"></button>').addClass(this._triggerClass).
225 html(buttonImage == '' ? buttonText : $('<img/>').attr(
226 { src:buttonImage, alt:buttonText, title:buttonText })));
227 input[isRTL ? 'before' : 'after'](inst.trigger);
228 inst.trigger.click(function() {
229 if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
230 $.datepicker._hideDatepicker();
231 else
232 $.datepicker._showDatepicker(input[0]);
233 return false;
234 });
235 }
236 },
237
238 /* Apply the maximum length for the date format. */
239 _autoSize: function(inst) {
240 if (this._get(inst, 'autoSize') && !inst.inline) {
241 var date = new Date(2009, 12 - 1, 20); // Ensure double digits
242 var dateFormat = this._get(inst, 'dateFormat');
243 if (dateFormat.match(/[DM]/)) {
244 var findMax = function(names) {
245 var max = 0;
246 var maxI = 0;
247 for (var i = 0; i < names.length; i++) {
248 if (names[i].length > max) {
249 max = names[i].length;
250 maxI = i;
251 }
252 }
253 return maxI;
254 };
255 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
256 'monthNames' : 'monthNamesShort'))));
257 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
258 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
259 }
260 inst.input.attr('size', this._formatDate(inst, date).length);
261 }
262 },
263
264 /* Attach an inline date picker to a div. */
265 _inlineDatepicker: function(target, inst) {
266 var divSpan = $(target);
267 if (divSpan.hasClass(this.markerClassName))
268 return;
269 divSpan.addClass(this.markerClassName).append(inst.dpDiv).
270 bind("setData.datepicker", function(event, key, value){
271 inst.settings[key] = value;
272 }).bind("getData.datepicker", function(event, key){
273 return this._get(inst, key);
274 });
275 $.data(target, PROP_NAME, inst);
276 this._setDate(inst, this._getDefaultDate(inst), true);
277 this._updateDatepicker(inst);
278 this._updateAlternate(inst);
279 inst.dpDiv.show();
280 },
281
282 /* Pop-up the date picker in a "dialog" box.
283 @param input element - ignored
284 @param date string or Date - the initial date to display
285 @param onSelect function - the function to call when a date is selected
286 @param settings object - update the dialog date picker instance's settings (anonymous object)
287 @param pos int[2] - coordinates for the dialog's position within the screen or
288 event - with x/y coordinates or
289 leave empty for default (screen centre)
290 @return the manager object */
291 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
292 var inst = this._dialogInst; // internal instance
293 if (!inst) {
294 this.uuid += 1;
295 var id = 'dp' + this.uuid;
296 this._dialogInput = $('<input type="text" id="' + id +
297 '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
298 this._dialogInput.keydown(this._doKeyDown);
299 $('body').append(this._dialogInput);
300 inst = this._dialogInst = this._newInst(this._dialogInput, false);
301 inst.settings = {};
302 $.data(this._dialogInput[0], PROP_NAME, inst);
303 }
304 extendRemove(inst.settings, settings || {});
305 date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
306 this._dialogInput.val(date);
307
308 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
309 if (!this._pos) {
310 var browserWidth = document.documentElement.clientWidth;
311 var browserHeight = document.documentElement.clientHeight;
312 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
313 var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
314 this._pos = // should use actual width/height below
315 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
316 }
317
318 // move input on screen for focus, but hidden behind dialog
319 this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
320 inst.settings.onSelect = onSelect;
321 this._inDialog = true;
322 this.dpDiv.addClass(this._dialogClass);
323 this._showDatepicker(this._dialogInput[0]);
324 if ($.blockUI)
325 $.blockUI(this.dpDiv);
326 $.data(this._dialogInput[0], PROP_NAME, inst);
327 return this;
328 },
329
330 /* Detach a datepicker from its control.
331 @param target element - the target input field or division or span */
332 _destroyDatepicker: function(target) {
333 var $target = $(target);
334 var inst = $.data(target, PROP_NAME);
335 if (!$target.hasClass(this.markerClassName)) {
336 return;
337 }
338 var nodeName = target.nodeName.toLowerCase();
339 $.removeData(target, PROP_NAME);
340 if (nodeName == 'input') {
341 inst.append.remove();
342 inst.trigger.remove();
343 $target.removeClass(this.markerClassName).
344 unbind('focus', this._showDatepicker).
345 unbind('keydown', this._doKeyDown).
346 unbind('keypress', this._doKeyPress).
347 unbind('keyup', this._doKeyUp);
348 } else if (nodeName == 'div' || nodeName == 'span')
349 $target.removeClass(this.markerClassName).empty();
350 },
351
352 /* Enable the date picker to a jQuery selection.
353 @param target element - the target input field or division or span */
354 _enableDatepicker: function(target) {
355 var $target = $(target);
356 var inst = $.data(target, PROP_NAME);
357 if (!$target.hasClass(this.markerClassName)) {
358 return;
359 }
360 var nodeName = target.nodeName.toLowerCase();
361 if (nodeName == 'input') {
362 target.disabled = false;
363 inst.trigger.filter('button').
364 each(function() { this.disabled = false; }).end().
365 filter('img').css({opacity: '1.0', cursor: ''});
366 }
367 else if (nodeName == 'div' || nodeName == 'span') {
368 var inline = $target.children('.' + this._inlineClass);
369 inline.children().removeClass('ui-state-disabled');
370 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
371 removeAttr("disabled");
372 }
373 this._disabledInputs = $.map(this._disabledInputs,
374 function(value) { return (value == target ? null : value); }); // delete entry
375 },
376
377 /* Disable the date picker to a jQuery selection.
378 @param target element - the target input field or division or span */
379 _disableDatepicker: function(target) {
380 var $target = $(target);
381 var inst = $.data(target, PROP_NAME);
382 if (!$target.hasClass(this.markerClassName)) {
383 return;
384 }
385 var nodeName = target.nodeName.toLowerCase();
386 if (nodeName == 'input') {
387 target.disabled = true;
388 inst.trigger.filter('button').
389 each(function() { this.disabled = true; }).end().
390 filter('img').css({opacity: '0.5', cursor: 'default'});
391 }
392 else if (nodeName == 'div' || nodeName == 'span') {
393 var inline = $target.children('.' + this._inlineClass);
394 inline.children().addClass('ui-state-disabled');
395 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
396 attr("disabled", "disabled");
397 }
398 this._disabledInputs = $.map(this._disabledInputs,
399 function(value) { return (value == target ? null : value); }); // delete entry
400 this._disabledInputs[this._disabledInputs.length] = target;
401 },
402
403 /* Is the first field in a jQuery collection disabled as a datepicker?
404 @param target element - the target input field or division or span
405 @return boolean - true if disabled, false if enabled */
406 _isDisabledDatepicker: function(target) {
407 if (!target) {
408 return false;
409 }
410 for (var i = 0; i < this._disabledInputs.length; i++) {
411 if (this._disabledInputs[i] == target)
412 return true;
413 }
414 return false;
415 },
416
417 /* Retrieve the instance data for the target control.
418 @param target element - the target input field or division or span
419 @return object - the associated instance data
420 @throws error if a jQuery problem getting data */
421 _getInst: function(target) {
422 try {
423 return $.data(target, PROP_NAME);
424 }
425 catch (err) {
426 throw 'Missing instance data for this datepicker';
427 }
428 },
429
430 /* Update or retrieve the settings for a date picker attached to an input field or division.
431 @param target element - the target input field or division or span
432 @param name object - the new settings to update or
433 string - the name of the setting to change or retrieve,
434 when retrieving also 'all' for all instance settings or
435 'defaults' for all global defaults
436 @param value any - the new value for the setting
437 (omit if above is an object or to retrieve a value) */
438 _optionDatepicker: function(target, name, value) {
439 var inst = this._getInst(target);
440 if (arguments.length == 2 && typeof name == 'string') {
441 return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
442 (inst ? (name == 'all' ? $.extend({}, inst.settings) :
443 this._get(inst, name)) : null));
444 }
445 var settings = name || {};
446 if (typeof name == 'string') {
447 settings = {};
448 settings[name] = value;
449 }
450 if (inst) {
451 if (this._curInst == inst) {
452 this._hideDatepicker();
453 }
454 var date = this._getDateDatepicker(target, true);
455 var minDate = this._getMinMaxDate(inst, 'min');
456 var maxDate = this._getMinMaxDate(inst, 'max');
457 extendRemove(inst.settings, settings);
458 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
459 if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
460 inst.settings.minDate = this._formatDate(inst, minDate);
461 if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
462 inst.settings.maxDate = this._formatDate(inst, maxDate);
463 this._attachments($(target), inst);
464 this._autoSize(inst);
465 this._setDate(inst, date);
466 this._updateAlternate(inst);
467 this._updateDatepicker(inst);
468 }
469 },
470
471 // change method deprecated
472 _changeDatepicker: function(target, name, value) {
473 this._optionDatepicker(target, name, value);
474 },
475
476 /* Redraw the date picker attached to an input field or division.
477 @param target element - the target input field or division or span */
478 _refreshDatepicker: function(target) {
479 var inst = this._getInst(target);
480 if (inst) {
481 this._updateDatepicker(inst);
482 }
483 },
484
485 /* Set the dates for a jQuery selection.
486 @param target element - the target input field or division or span
487 @param date Date - the new date */
488 _setDateDatepicker: function(target, date) {
489 var inst = this._getInst(target);
490 if (inst) {
491 this._setDate(inst, date);
492 this._updateDatepicker(inst);
493 this._updateAlternate(inst);
494 }
495 },
496
497 /* Get the date(s) for the first entry in a jQuery selection.
498 @param target element - the target input field or division or span
499 @param noDefault boolean - true if no default date is to be used
500 @return Date - the current date */
501 _getDateDatepicker: function(target, noDefault) {
502 var inst = this._getInst(target);
503 if (inst && !inst.inline)
504 this._setDateFromField(inst, noDefault);
505 return (inst ? this._getDate(inst) : null);
506 },
507
508 /* Handle keystrokes. */
509 _doKeyDown: function(event) {
510 var inst = $.datepicker._getInst(event.target);
511 var handled = true;
512 var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
513 inst._keyEvent = true;
514 if ($.datepicker._datepickerShowing)
515 switch (event.keyCode) {
516 case 9: $.datepicker._hideDatepicker();
517 handled = false;
518 break; // hide on tab out
519 case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
520 $.datepicker._currentClass + ')', inst.dpDiv);
521 if (sel[0])
522 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
523 else
524 $.datepicker._hideDatepicker();
525 return false; // don't submit the form
526 break; // select the value on enter
527 case 27: $.datepicker._hideDatepicker();
528 break; // hide on escape
529 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
530 -$.datepicker._get(inst, 'stepBigMonths') :
531 -$.datepicker._get(inst, 'stepMonths')), 'M');
532 break; // previous month/year on page up/+ ctrl
533 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
534 +$.datepicker._get(inst, 'stepBigMonths') :
535 +$.datepicker._get(inst, 'stepMonths')), 'M');
536 break; // next month/year on page down/+ ctrl
537 case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
538 handled = event.ctrlKey || event.metaKey;
539 break; // clear on ctrl or command +end
540 case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
541 handled = event.ctrlKey || event.metaKey;
542 break; // current on ctrl or command +home
543 case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
544 handled = event.ctrlKey || event.metaKey;
545 // -1 day on ctrl or command +left
546 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
547 -$.datepicker._get(inst, 'stepBigMonths') :
548 -$.datepicker._get(inst, 'stepMonths')), 'M');
549 // next month/year on alt +left on Mac
550 break;
551 case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
552 handled = event.ctrlKey || event.metaKey;
553 break; // -1 week on ctrl or command +up
554 case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
555 handled = event.ctrlKey || event.metaKey;
556 // +1 day on ctrl or command +right
557 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
558 +$.datepicker._get(inst, 'stepBigMonths') :
559 +$.datepicker._get(inst, 'stepMonths')), 'M');
560 // next month/year on alt +right
561 break;
562 case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
563 handled = event.ctrlKey || event.metaKey;
564 break; // +1 week on ctrl or command +down
565 default: handled = false;
566 }
567 else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
568 $.datepicker._showDatepicker(this);
569 else {
570 handled = false;
571 }
572 if (handled) {
573 event.preventDefault();
574 event.stopPropagation();
575 }
576 },
577
578 /* Filter entered characters - based on date format. */
579 _doKeyPress: function(event) {
580 var inst = $.datepicker._getInst(event.target);
581 if ($.datepicker._get(inst, 'constrainInput')) {
582 var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
583 var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
584 return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
585 }
586 },
587
588 /* Synchronise manual entry and field/alternate field. */
589 _doKeyUp: function(event) {
590 var inst = $.datepicker._getInst(event.target);
591 if (inst.input.val() != inst.lastVal) {
592 try {
593 var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
594 (inst.input ? inst.input.val() : null),
595 $.datepicker._getFormatConfig(inst));
596 if (date) { // only if valid
597 $.datepicker._setDateFromField(inst);
598 $.datepicker._updateAlternate(inst);
599 $.datepicker._updateDatepicker(inst);
600 }
601 }
602 catch (event) {
603 $.datepicker.log(event);
604 }
605 }
606 return true;
607 },
608
609 /* Pop-up the date picker for a given input field.
610 @param input element - the input field attached to the date picker or
611 event - if triggered by focus */
612 _showDatepicker: function(input) {
613 input = input.target || input;
614 if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
615 input = $('input', input.parentNode)[0];
616 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
617 return;
618 var inst = $.datepicker._getInst(input);
619 if ($.datepicker._curInst && $.datepicker._curInst != inst) {
620 if ( $.datepicker._datepickerShowing ) {
621 $.datepicker._triggerOnClose($.datepicker._curInst);
622 }
623 $.datepicker._curInst.dpDiv.stop(true, true);
624 }
625 var beforeShow = $.datepicker._get(inst, 'beforeShow');
626 extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
627 inst.lastVal = null;
628 $.datepicker._lastInput = input;
629 $.datepicker._setDateFromField(inst);
630 if ($.datepicker._inDialog) // hide cursor
631 input.value = '';
632 if (!$.datepicker._pos) { // position below input
633 $.datepicker._pos = $.datepicker._findPos(input);
634 $.datepicker._pos[1] += input.offsetHeight; // add the height
635 }
636 var isFixed = false;
637 $(input).parents().each(function() {
638 isFixed |= $(this).css('position') == 'fixed';
639 return !isFixed;
640 });
641 if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
642 $.datepicker._pos[0] -= document.documentElement.scrollLeft;
643 $.datepicker._pos[1] -= document.documentElement.scrollTop;
644 }
645 var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
646 $.datepicker._pos = null;
647 //to avoid flashes on Firefox
648 inst.dpDiv.empty();
649 // determine sizing offscreen
650 inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
651 $.datepicker._updateDatepicker(inst);
652 // fix width for dynamic number of date pickers
653 // and adjust position before showing
654 offset = $.datepicker._checkOffset(inst, offset, isFixed);
655 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
656 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
657 left: offset.left + 'px', top: offset.top + 'px'});
658 if (!inst.inline) {
659 var showAnim = $.datepicker._get(inst, 'showAnim');
660 var duration = $.datepicker._get(inst, 'duration');
661 var postProcess = function() {
662 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
663 if( !! cover.length ){
664 var borders = $.datepicker._getBorders(inst.dpDiv);
665 cover.css({left: -borders[0], top: -borders[1],
666 width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
667 }
668 };
669 inst.dpDiv.zIndex($(input).zIndex()+1);
670 $.datepicker._datepickerShowing = true;
671 if ($.effects && $.effects[showAnim])
672 inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
673 else
674 inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
675 if (!showAnim || !duration)
676 postProcess();
677 if (inst.input.is(':visible') && !inst.input.is(':disabled'))
678 inst.input.focus();
679 $.datepicker._curInst = inst;
680 }
681 },
682
683 /* Generate the date picker content. */
684 _updateDatepicker: function(inst) {
685 var self = this;
686 self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
687 var borders = $.datepicker._getBorders(inst.dpDiv);
688 instActive = inst; // for delegate hover events
689 inst.dpDiv.empty().append(this._generateHTML(inst));
690 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
691 if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
692 cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
693 }
694 inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
695 var numMonths = this._getNumberOfMonths(inst);
696 var cols = numMonths[1];
697 var width = 17;
698 inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
699 if (cols > 1)
700 inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
701 inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
702 'Class']('ui-datepicker-multi');
703 inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
704 'Class']('ui-datepicker-rtl');
705 if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
706 // #6694 - don't focus the input if it's already focused
707 // this breaks the change event in IE
708 inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
709 inst.input.focus();
710 // deffered render of the years select (to avoid flashes on Firefox)
711 if( inst.yearshtml ){
712 var origyearshtml = inst.yearshtml;
713 setTimeout(function(){
714 //assure that inst.yearshtml didn't change.
715 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
716 inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
717 }
718 origyearshtml = inst.yearshtml = null;
719 }, 0);
720 }
721 },
722
723 /* Retrieve the size of left and top borders for an element.
724 @param elem (jQuery object) the element of interest
725 @return (number[2]) the left and top borders */
726 _getBorders: function(elem) {
727 var convert = function(value) {
728 return {thin: 1, medium: 2, thick: 3}[value] || value;
729 };
730 return [parseFloat(convert(elem.css('border-left-width'))),
731 parseFloat(convert(elem.css('border-top-width')))];
732 },
733
734 /* Check positioning to remain on screen. */
735 _checkOffset: function(inst, offset, isFixed) {
736 var dpWidth = inst.dpDiv.outerWidth();
737 var dpHeight = inst.dpDiv.outerHeight();
738 var inputWidth = inst.input ? inst.input.outerWidth() : 0;
739 var inputHeight = inst.input ? inst.input.outerHeight() : 0;
740 var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
741 var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
742
743 offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
744 offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
745 offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
746
747 // now check if datepicker is showing outside window viewport - move to a better place if so.
748 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
749 Math.abs(offset.left + dpWidth - viewWidth) : 0);
750 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
751 Math.abs(dpHeight + inputHeight) : 0);
752
753 return offset;
754 },
755
756 /* Find an object's position on the screen. */
757 _findPos: function(obj) {
758 var inst = this._getInst(obj);
759 var isRTL = this._get(inst, 'isRTL');
760 while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
761 obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
762 }
763 var position = $(obj).offset();
764 return [position.left, position.top];
765 },
766
767 /* Trigger custom callback of onClose. */
768 _triggerOnClose: function(inst) {
769 var onClose = this._get(inst, 'onClose');
770 if (onClose)
771 onClose.apply((inst.input ? inst.input[0] : null),
772 [(inst.input ? inst.input.val() : ''), inst]);
773 },
774
775 /* Hide the date picker from view.
776 @param input element - the input field attached to the date picker */
777 _hideDatepicker: function(input) {
778 var inst = this._curInst;
779 if (!inst || (input && inst != $.data(input, PROP_NAME)))
780 return;
781 if (this._datepickerShowing) {
782 var showAnim = this._get(inst, 'showAnim');
783 var duration = this._get(inst, 'duration');
784 var postProcess = function() {
785 $.datepicker._tidyDialog(inst);
786 this._curInst = null;
787 };
788 if ($.effects && $.effects[showAnim])
789 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
790 else
791 inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
792 (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
793 if (!showAnim)
794 postProcess();
795 $.datepicker._triggerOnClose(inst);
796 this._datepickerShowing = false;
797 this._lastInput = null;
798 if (this._inDialog) {
799 this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
800 if ($.blockUI) {
801 $.unblockUI();
802 $('body').append(this.dpDiv);
803 }
804 }
805 this._inDialog = false;
806 }
807 },
808
809 /* Tidy up after a dialog display. */
810 _tidyDialog: function(inst) {
811 inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
812 },
813
814 /* Close date picker if clicked elsewhere. */
815 _checkExternalClick: function(event) {
816 if (!$.datepicker._curInst)
817 return;
818 var $target = $(event.target);
819 if ($target[0].id != $.datepicker._mainDivId &&
820 $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
821 !$target.hasClass($.datepicker.markerClassName) &&
822 !$target.hasClass($.datepicker._triggerClass) &&
823 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
824 $.datepicker._hideDatepicker();
825 },
826
827 /* Adjust one of the date sub-fields. */
828 _adjustDate: function(id, offset, period) {
829 var target = $(id);
830 var inst = this._getInst(target[0]);
831 if (this._isDisabledDatepicker(target[0])) {
832 return;
833 }
834 this._adjustInstDate(inst, offset +
835 (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
836 period);
837 this._updateDatepicker(inst);
838 },
839
840 /* Action for current link. */
841 _gotoToday: function(id) {
842 var target = $(id);
843 var inst = this._getInst(target[0]);
844 if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
845 inst.selectedDay = inst.currentDay;
846 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
847 inst.drawYear = inst.selectedYear = inst.currentYear;
848 }
849 else {
850 var date = new Date();
851 inst.selectedDay = date.getDate();
852 inst.drawMonth = inst.selectedMonth = date.getMonth();
853 inst.drawYear = inst.selectedYear = date.getFullYear();
854 }
855 this._notifyChange(inst);
856 this._adjustDate(target);
857 },
858
859 /* Action for selecting a new month/year. */
860 _selectMonthYear: function(id, select, period) {
861 var target = $(id);
862 var inst = this._getInst(target[0]);
863 inst._selectingMonthYear = false;
864 inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
865 inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
866 parseInt(select.options[select.selectedIndex].value,10);
867 this._notifyChange(inst);
868 this._adjustDate(target);
869 },
870
871 /* Restore input focus after not changing month/year. */
872 _clickMonthYear: function(id) {
873 var target = $(id);
874 var inst = this._getInst(target[0]);
875 if (inst.input && inst._selectingMonthYear) {
876 setTimeout(function() {
877 inst.input.focus();
878 }, 0);
879 }
880 inst._selectingMonthYear = !inst._selectingMonthYear;
881 },
882
883 /* Action for selecting a day. */
884 _selectDay: function(id, month, year, td) {
885 var target = $(id);
886 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
887 return;
888 }
889 var inst = this._getInst(target[0]);
890 inst.selectedDay = inst.currentDay = $('a', td).html();
891 inst.selectedMonth = inst.currentMonth = month;
892 inst.selectedYear = inst.currentYear = year;
893 this._selectDate(id, this._formatDate(inst,
894 inst.currentDay, inst.currentMonth, inst.currentYear));
895 },
896
897 /* Erase the input field and hide the date picker. */
898 _clearDate: function(id) {
899 var target = $(id);
900 var inst = this._getInst(target[0]);
901 this._selectDate(target, '');
902 },
903
904 /* Update the input field with the selected date. */
905 _selectDate: function(id, dateStr) {
906 var target = $(id);
907 var inst = this._getInst(target[0]);
908 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
909 if (inst.input)
910 inst.input.val(dateStr);
911 this._updateAlternate(inst);
912 var onSelect = this._get(inst, 'onSelect');
913 if (onSelect)
914 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
915 else if (inst.input)
916 inst.input.trigger('change'); // fire the change event
917 if (inst.inline)
918 this._updateDatepicker(inst);
919 else {
920 this._hideDatepicker();
921 this._lastInput = inst.input[0];
922 if (typeof(inst.input[0]) != 'object')
923 inst.input.focus(); // restore focus
924 this._lastInput = null;
925 }
926 },
927
928 /* Update any alternate field to synchronise with the main field. */
929 _updateAlternate: function(inst) {
930 var altField = this._get(inst, 'altField');
931 if (altField) { // update alternate field too
932 var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
933 var date = this._getDate(inst);
934 var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
935 $(altField).each(function() { $(this).val(dateStr); });
936 }
937 },
938
939 /* Set as beforeShowDay function to prevent selection of weekends.
940 @param date Date - the date to customise
941 @return [boolean, string] - is this date selectable?, what is its CSS class? */
942 noWeekends: function(date) {
943 var day = date.getDay();
944 return [(day > 0 && day < 6), ''];
945 },
946
947 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
948 @param date Date - the date to get the week for
949 @return number - the number of the week within the year that contains this date */
950 iso8601Week: function(date) {
951 var checkDate = new Date(date.getTime());
952 // Find Thursday of this week starting on Monday
953 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
954 var time = checkDate.getTime();
955 checkDate.setMonth(0); // Compare with Jan 1
956 checkDate.setDate(1);
957 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
958 },
959
960 /* Parse a string value into a date object.
961 See formatDate below for the possible formats.
962
963 @param format string - the expected format of the date
964 @param value string - the date in the above format
965 @param settings Object - attributes include:
966 shortYearCutoff number - the cutoff year for determining the century (optional)
967 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
968 dayNames string[7] - names of the days from Sunday (optional)
969 monthNamesShort string[12] - abbreviated names of the months (optional)
970 monthNames string[12] - names of the months (optional)
971 @return Date - the extracted date value or null if value is blank */
972 parseDate: function (format, value, settings) {
973 if (format == null || value == null)
974 throw 'Invalid arguments';
975 value = (typeof value == 'object' ? value.toString() : value + '');
976 if (value == '')
977 return null;
978 var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
979 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
980 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
981 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
982 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
983 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
984 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
985 var year = -1;
986 var month = -1;
987 var day = -1;
988 var doy = -1;
989 var literal = false;
990 // Check whether a format character is doubled
991 var lookAhead = function(match) {
992 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
993 if (matches)
994 iFormat++;
995 return matches;
996 };
997 // Extract a number from the string value
998 var getNumber = function(match) {
999 var isDoubled = lookAhead(match);
1000 var size = (match == '@' ? 14 : (match == '!' ? 20 :
1001 (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
1002 var digits = new RegExp('^\\d{1,' + size + '}');
1003 var num = value.substring(iValue).match(digits);
1004 if (!num)
1005 throw 'Missing number at position ' + iValue;
1006 iValue += num[0].length;
1007 return parseInt(num[0], 10);
1008 };
1009 // Extract a name from the string value and convert to an index
1010 var getName = function(match, shortNames, longNames) {
1011 var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1012 return [ [k, v] ];
1013 }).sort(function (a, b) {
1014 return -(a[1].length - b[1].length);
1015 });
1016 var index = -1;
1017 $.each(names, function (i, pair) {
1018 var name = pair[1];
1019 if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
1020 index = pair[0];
1021 iValue += name.length;
1022 return false;
1023 }
1024 });
1025 if (index != -1)
1026 return index + 1;
1027 else
1028 throw 'Unknown name at position ' + iValue;
1029 };
1030 // Confirm that a literal character matches the string value
1031 var checkLiteral = function() {
1032 if (value.charAt(iValue) != format.charAt(iFormat))
1033 throw 'Unexpected literal at position ' + iValue;
1034 iValue++;
1035 };
1036 var iValue = 0;
1037 for (var iFormat = 0; iFormat < format.length; iFormat++) {
1038 if (literal)
1039 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1040 literal = false;
1041 else
1042 checkLiteral();
1043 else
1044 switch (format.charAt(iFormat)) {
1045 case 'd':
1046 day = getNumber('d');
1047 break;
1048 case 'D':
1049 getName('D', dayNamesShort, dayNames);
1050 break;
1051 case 'o':
1052 doy = getNumber('o');
1053 break;
1054 case 'm':
1055 month = getNumber('m');
1056 break;
1057 case 'M':
1058 month = getName('M', monthNamesShort, monthNames);
1059 break;
1060 case 'y':
1061 year = getNumber('y');
1062 break;
1063 case '@':
1064 var date = new Date(getNumber('@'));
1065 year = date.getFullYear();
1066 month = date.getMonth() + 1;
1067 day = date.getDate();
1068 break;
1069 case '!':
1070 var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
1071 year = date.getFullYear();
1072 month = date.getMonth() + 1;
1073 day = date.getDate();
1074 break;
1075 case "'":
1076 if (lookAhead("'"))
1077 checkLiteral();
1078 else
1079 literal = true;
1080 break;
1081 default:
1082 checkLiteral();
1083 }
1084 }
1085 if (iValue < value.length){
1086 throw "Extra/unparsed characters found in date: " + value.substring(iValue);
1087 }
1088 if (year == -1)
1089 year = new Date().getFullYear();
1090 else if (year < 100)
1091 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1092 (year <= shortYearCutoff ? 0 : -100);
1093 if (doy > -1) {
1094 month = 1;
1095 day = doy;
1096 do {
1097 var dim = this._getDaysInMonth(year, month - 1);
1098 if (day <= dim)
1099 break;
1100 month++;
1101 day -= dim;
1102 } while (true);
1103 }
1104 var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1105 if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
1106 throw 'Invalid date'; // E.g. 31/02/00
1107 return date;
1108 },
1109
1110 /* Standard date formats. */
1111 ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
1112 COOKIE: 'D, dd M yy',
1113 ISO_8601: 'yy-mm-dd',
1114 RFC_822: 'D, d M y',
1115 RFC_850: 'DD, dd-M-y',
1116 RFC_1036: 'D, d M y',
1117 RFC_1123: 'D, d M yy',
1118 RFC_2822: 'D, d M yy',
1119 RSS: 'D, d M y', // RFC 822
1120 TICKS: '!',
1121 TIMESTAMP: '@',
1122 W3C: 'yy-mm-dd', // ISO 8601
1123
1124 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1125 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1126
1127 /* Format a date object into a string value.
1128 The format can be combinations of the following:
1129 d - day of month (no leading zero)
1130 dd - day of month (two digit)
1131 o - day of year (no leading zeros)
1132 oo - day of year (three digit)
1133 D - day name short
1134 DD - day name long
1135 m - month of year (no leading zero)
1136 mm - month of year (two digit)
1137 M - month name short
1138 MM - month name long
1139 y - year (two digit)
1140 yy - year (four digit)
1141 @ - Unix timestamp (ms since 01/01/1970)
1142 ! - Windows ticks (100ns since 01/01/0001)
1143 '...' - literal text
1144 '' - single quote
1145
1146 @param format string - the desired format of the date
1147 @param date Date - the date value to format
1148 @param settings Object - attributes include:
1149 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1150 dayNames string[7] - names of the days from Sunday (optional)
1151 monthNamesShort string[12] - abbreviated names of the months (optional)
1152 monthNames string[12] - names of the months (optional)
1153 @return string - the date in the above format */
1154 formatDate: function (format, date, settings) {
1155 if (!date)
1156 return '';
1157 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
1158 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
1159 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1160 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1161 // Check whether a format character is doubled
1162 var lookAhead = function(match) {
1163 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1164 if (matches)
1165 iFormat++;
1166 return matches;
1167 };
1168 // Format a number, with leading zero if necessary
1169 var formatNumber = function(match, value, len) {
1170 var num = '' + value;
1171 if (lookAhead(match))
1172 while (num.length < len)
1173 num = '0' + num;
1174 return num;
1175 };
1176 // Format a name, short or long as requested
1177 var formatName = function(match, value, shortNames, longNames) {
1178 return (lookAhead(match) ? longNames[value] : shortNames[value]);
1179 };
1180 var output = '';
1181 var literal = false;
1182 if (date)
1183 for (var iFormat = 0; iFormat < format.length; iFormat++) {
1184 if (literal)
1185 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1186 literal = false;
1187 else
1188 output += format.charAt(iFormat);
1189 else
1190 switch (format.charAt(iFormat)) {
1191 case 'd':
1192 output += formatNumber('d', date.getDate(), 2);
1193 break;
1194 case 'D':
1195 output += formatName('D', date.getDay(), dayNamesShort, dayNames);
1196 break;
1197 case 'o':
1198 output += formatNumber('o',
1199 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1200 break;
1201 case 'm':
1202 output += formatNumber('m', date.getMonth() + 1, 2);
1203 break;
1204 case 'M':
1205 output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
1206 break;
1207 case 'y':
1208 output += (lookAhead('y') ? date.getFullYear() :
1209 (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
1210 break;
1211 case '@':
1212 output += date.getTime();
1213 break;
1214 case '!':
1215 output += date.getTime() * 10000 + this._ticksTo1970;
1216 break;
1217 case "'":
1218 if (lookAhead("'"))
1219 output += "'";
1220 else
1221 literal = true;
1222 break;
1223 default:
1224 output += format.charAt(iFormat);
1225 }
1226 }
1227 return output;
1228 },
1229
1230 /* Extract all possible characters from the date format. */
1231 _possibleChars: function (format) {
1232 var chars = '';
1233 var literal = false;
1234 // Check whether a format character is doubled
1235 var lookAhead = function(match) {
1236 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1237 if (matches)
1238 iFormat++;
1239 return matches;
1240 };
1241 for (var iFormat = 0; iFormat < format.length; iFormat++)
1242 if (literal)
1243 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1244 literal = false;
1245 else
1246 chars += format.charAt(iFormat);
1247 else
1248 switch (format.charAt(iFormat)) {
1249 case 'd': case 'm': case 'y': case '@':
1250 chars += '0123456789';
1251 break;
1252 case 'D': case 'M':
1253 return null; // Accept anything
1254 case "'":
1255 if (lookAhead("'"))
1256 chars += "'";
1257 else
1258 literal = true;
1259 break;
1260 default:
1261 chars += format.charAt(iFormat);
1262 }
1263 return chars;
1264 },
1265
1266 /* Get a setting value, defaulting if necessary. */
1267 _get: function(inst, name) {
1268 return inst.settings[name] !== undefined ?
1269 inst.settings[name] : this._defaults[name];
1270 },
1271
1272 /* Parse existing date and initialise date picker. */
1273 _setDateFromField: function(inst, noDefault) {
1274 if (inst.input.val() == inst.lastVal) {
1275 return;
1276 }
1277 var dateFormat = this._get(inst, 'dateFormat');
1278 var dates = inst.lastVal = inst.input ? inst.input.val() : null;
1279 var date, defaultDate;
1280 date = defaultDate = this._getDefaultDate(inst);
1281 var settings = this._getFormatConfig(inst);
1282 try {
1283 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1284 } catch (event) {
1285 this.log(event);
1286 dates = (noDefault ? '' : dates);
1287 }
1288 inst.selectedDay = date.getDate();
1289 inst.drawMonth = inst.selectedMonth = date.getMonth();
1290 inst.drawYear = inst.selectedYear = date.getFullYear();
1291 inst.currentDay = (dates ? date.getDate() : 0);
1292 inst.currentMonth = (dates ? date.getMonth() : 0);
1293 inst.currentYear = (dates ? date.getFullYear() : 0);
1294 this._adjustInstDate(inst);
1295 },
1296
1297 /* Retrieve the default date shown on opening. */
1298 _getDefaultDate: function(inst) {
1299 return this._restrictMinMax(inst,
1300 this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
1301 },
1302
1303 /* A date may be specified as an exact value or a relative one. */
1304 _determineDate: function(inst, date, defaultDate) {
1305 var offsetNumeric = function(offset) {
1306 var date = new Date();
1307 date.setDate(date.getDate() + offset);
1308 return date;
1309 };
1310 var offsetString = function(offset) {
1311 try {
1312 return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
1313 offset, $.datepicker._getFormatConfig(inst));
1314 }
1315 catch (e) {
1316 // Ignore
1317 }
1318 var date = (offset.toLowerCase().match(/^c/) ?
1319 $.datepicker._getDate(inst) : null) || new Date();
1320 var year = date.getFullYear();
1321 var month = date.getMonth();
1322 var day = date.getDate();
1323 var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
1324 var matches = pattern.exec(offset);
1325 while (matches) {
1326 switch (matches[2] || 'd') {
1327 case 'd' : case 'D' :
1328 day += parseInt(matches[1],10); break;
1329 case 'w' : case 'W' :
1330 day += parseInt(matches[1],10) * 7; break;
1331 case 'm' : case 'M' :
1332 month += parseInt(matches[1],10);
1333 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1334 break;
1335 case 'y': case 'Y' :
1336 year += parseInt(matches[1],10);
1337 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1338 break;
1339 }
1340 matches = pattern.exec(offset);
1341 }
1342 return new Date(year, month, day);
1343 };
1344 var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
1345 (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1346 newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
1347 if (newDate) {
1348 newDate.setHours(0);
1349 newDate.setMinutes(0);
1350 newDate.setSeconds(0);
1351 newDate.setMilliseconds(0);
1352 }
1353 return this._daylightSavingAdjust(newDate);
1354 },
1355
1356 /* Handle switch to/from daylight saving.
1357 Hours may be non-zero on daylight saving cut-over:
1358 > 12 when midnight changeover, but then cannot generate
1359 midnight datetime, so jump to 1AM, otherwise reset.
1360 @param date (Date) the date to check
1361 @return (Date) the corrected date */
1362 _daylightSavingAdjust: function(date) {
1363 if (!date) return null;
1364 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1365 return date;
1366 },
1367
1368 /* Set the date(s) directly. */
1369 _setDate: function(inst, date, noChange) {
1370 var clear = !date;
1371 var origMonth = inst.selectedMonth;
1372 var origYear = inst.selectedYear;
1373 var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1374 inst.selectedDay = inst.currentDay = newDate.getDate();
1375 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1376 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1377 if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
1378 this._notifyChange(inst);
1379 this._adjustInstDate(inst);
1380 if (inst.input) {
1381 inst.input.val(clear ? '' : this._formatDate(inst));
1382 }
1383 },
1384
1385 /* Retrieve the date(s) directly. */
1386 _getDate: function(inst) {
1387 var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
1388 this._daylightSavingAdjust(new Date(
1389 inst.currentYear, inst.currentMonth, inst.currentDay)));
1390 return startDate;
1391 },
1392
1393 /* Generate the HTML for the current state of the date picker. */
1394 _generateHTML: function(inst) {
1395 var today = new Date();
1396 today = this._daylightSavingAdjust(
1397 new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
1398 var isRTL = this._get(inst, 'isRTL');
1399 var showButtonPanel = this._get(inst, 'showButtonPanel');
1400 var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
1401 var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
1402 var numMonths = this._getNumberOfMonths(inst);
1403 var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
1404 var stepMonths = this._get(inst, 'stepMonths');
1405 var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
1406 var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1407 new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1408 var minDate = this._getMinMaxDate(inst, 'min');
1409 var maxDate = this._getMinMaxDate(inst, 'max');
1410 var drawMonth = inst.drawMonth - showCurrentAtPos;
1411 var drawYear = inst.drawYear;
1412 if (drawMonth < 0) {
1413 drawMonth += 12;
1414 drawYear--;
1415 }
1416 if (maxDate) {
1417 var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1418 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1419 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1420 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1421 drawMonth--;
1422 if (drawMonth < 0) {
1423 drawMonth = 11;
1424 drawYear--;
1425 }
1426 }
1427 }
1428 inst.drawMonth = drawMonth;
1429 inst.drawYear = drawYear;
1430 var prevText = this._get(inst, 'prevText');
1431 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1432 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1433 this._getFormatConfig(inst)));
1434 var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1435 '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1436 '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
1437 ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
1438 (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
1439 var nextText = this._get(inst, 'nextText');
1440 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1441 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1442 this._getFormatConfig(inst)));
1443 var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1444 '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1445 '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
1446 ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
1447 (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
1448 var currentText = this._get(inst, 'currentText');
1449 var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
1450 currentText = (!navigationAsDateFormat ? currentText :
1451 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1452 var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1453 '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
1454 var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
1455 (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
1456 '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
1457 '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
1458 var firstDay = parseInt(this._get(inst, 'firstDay'),10);
1459 firstDay = (isNaN(firstDay) ? 0 : firstDay);
1460 var showWeek = this._get(inst, 'showWeek');
1461 var dayNames = this._get(inst, 'dayNames');
1462 var dayNamesShort = this._get(inst, 'dayNamesShort');
1463 var dayNamesMin = this._get(inst, 'dayNamesMin');
1464 var monthNames = this._get(inst, 'monthNames');
1465 var monthNamesShort = this._get(inst, 'monthNamesShort');
1466 var beforeShowDay = this._get(inst, 'beforeShowDay');
1467 var showOtherMonths = this._get(inst, 'showOtherMonths');
1468 var selectOtherMonths = this._get(inst, 'selectOtherMonths');
1469 var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
1470 var defaultDate = this._getDefaultDate(inst);
1471 var html = '';
1472 for (var row = 0; row < numMonths[0]; row++) {
1473 var group = '';
1474 this.maxRows = 4;
1475 for (var col = 0; col < numMonths[1]; col++) {
1476 var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1477 var cornerClass = ' ui-corner-all';
1478 var calender = '';
1479 if (isMultiMonth) {
1480 calender += '<div class="ui-datepicker-group';
1481 if (numMonths[1] > 1)
1482 switch (col) {
1483 case 0: calender += ' ui-datepicker-group-first';
1484 cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
1485 case numMonths[1]-1: calender += ' ui-datepicker-group-last';
1486 cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
1487 default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
1488 }
1489 calender += '">';
1490 }
1491 calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
1492 (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
1493 (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
1494 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1495 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1496 '</div><table class="ui-datepicker-calendar"><thead>' +
1497 '<tr>';
1498 var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
1499 for (var dow = 0; dow < 7; dow++) { // days of the week
1500 var day = (dow + firstDay) % 7;
1501 thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
1502 '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
1503 }
1504 calender += thead + '</tr></thead><tbody>';
1505 var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1506 if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
1507 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1508 var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1509 var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1510 var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1511 this.maxRows = numRows;
1512 var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1513 for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1514 calender += '<tr>';
1515 var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
1516 this._get(inst, 'calculateWeek')(printDate) + '</td>');
1517 for (var dow = 0; dow < 7; dow++) { // create date picker days
1518 var daySettings = (beforeShowDay ?
1519 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
1520 var otherMonth = (printDate.getMonth() != drawMonth);
1521 var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1522 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1523 tbody += '<td class="' +
1524 ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
1525 (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
1526 ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
1527 (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
1528 // or defaultDate is current printedDate and defaultDate is selectedDate
1529 ' ' + this._dayOverClass : '') + // highlight selected day
1530 (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
1531 (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
1532 (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
1533 (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
1534 ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
1535 (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
1536 inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
1537 (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
1538 (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
1539 (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
1540 (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
1541 (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
1542 '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
1543 printDate.setDate(printDate.getDate() + 1);
1544 printDate = this._daylightSavingAdjust(printDate);
1545 }
1546 calender += tbody + '</tr>';
1547 }
1548 drawMonth++;
1549 if (drawMonth > 11) {
1550 drawMonth = 0;
1551 drawYear++;
1552 }
1553 calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
1554 ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
1555 group += calender;
1556 }
1557 html += group;
1558 }
1559 html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
1560 '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
1561 inst._keyEvent = false;
1562 return html;
1563 },
1564
1565 /* Generate the month and year header. */
1566 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1567 secondary, monthNames, monthNamesShort) {
1568 var changeMonth = this._get(inst, 'changeMonth');
1569 var changeYear = this._get(inst, 'changeYear');
1570 var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
1571 var html = '<div class="ui-datepicker-title">';
1572 var monthHtml = '';
1573 // month selection
1574 if (secondary || !changeMonth)
1575 monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
1576 else {
1577 var inMinYear = (minDate && minDate.getFullYear() == drawYear);
1578 var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
1579 monthHtml += '<select class="ui-datepicker-month" ' +
1580 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
1581 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
1582 '>';
1583 for (var month = 0; month < 12; month++) {
1584 if ((!inMinYear || month >= minDate.getMonth()) &&
1585 (!inMaxYear || month <= maxDate.getMonth()))
1586 monthHtml += '<option value="' + month + '"' +
1587 (month == drawMonth ? ' selected="selected"' : '') +
1588 '>' + monthNamesShort[month] + '</option>';
1589 }
1590 monthHtml += '</select>';
1591 }
1592 if (!showMonthAfterYear)
1593 html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
1594 // year selection
1595 if ( !inst.yearshtml ) {
1596 inst.yearshtml = '';
1597 if (secondary || !changeYear)
1598 html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
1599 else {
1600 // determine range of years to display
1601 var years = this._get(inst, 'yearRange').split(':');
1602 var thisYear = new Date().getFullYear();
1603 var determineYear = function(value) {
1604 var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
1605 (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
1606 parseInt(value, 10)));
1607 return (isNaN(year) ? thisYear : year);
1608 };
1609 var year = determineYear(years[0]);
1610 var endYear = Math.max(year, determineYear(years[1] || ''));
1611 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1612 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1613 inst.yearshtml += '<select class="ui-datepicker-year" ' +
1614 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
1615 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
1616 '>';
1617 for (; year <= endYear; year++) {
1618 inst.yearshtml += '<option value="' + year + '"' +
1619 (year == drawYear ? ' selected="selected"' : '') +
1620 '>' + year + '</option>';
1621 }
1622 inst.yearshtml += '</select>';
1623
1624 html += inst.yearshtml;
1625 inst.yearshtml = null;
1626 }
1627 }
1628 html += this._get(inst, 'yearSuffix');
1629 if (showMonthAfterYear)
1630 html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
1631 html += '</div>'; // Close datepicker_header
1632 return html;
1633 },
1634
1635 /* Adjust one of the date sub-fields. */
1636 _adjustInstDate: function(inst, offset, period) {
1637 var year = inst.drawYear + (period == 'Y' ? offset : 0);
1638 var month = inst.drawMonth + (period == 'M' ? offset : 0);
1639 var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
1640 (period == 'D' ? offset : 0);
1641 var date = this._restrictMinMax(inst,
1642 this._daylightSavingAdjust(new Date(year, month, day)));
1643 inst.selectedDay = date.getDate();
1644 inst.drawMonth = inst.selectedMonth = date.getMonth();
1645 inst.drawYear = inst.selectedYear = date.getFullYear();
1646 if (period == 'M' || period == 'Y')
1647 this._notifyChange(inst);
1648 },
1649
1650 /* Ensure a date is within any min/max bounds. */
1651 _restrictMinMax: function(inst, date) {
1652 var minDate = this._getMinMaxDate(inst, 'min');
1653 var maxDate = this._getMinMaxDate(inst, 'max');
1654 var newDate = (minDate && date < minDate ? minDate : date);
1655 newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
1656 return newDate;
1657 },
1658
1659 /* Notify change of month/year. */
1660 _notifyChange: function(inst) {
1661 var onChange = this._get(inst, 'onChangeMonthYear');
1662 if (onChange)
1663 onChange.apply((inst.input ? inst.input[0] : null),
1664 [inst.selectedYear, inst.selectedMonth + 1, inst]);
1665 },
1666
1667 /* Determine the number of months to show. */
1668 _getNumberOfMonths: function(inst) {
1669 var numMonths = this._get(inst, 'numberOfMonths');
1670 return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
1671 },
1672
1673 /* Determine the current maximum date - ensure no time components are set. */
1674 _getMinMaxDate: function(inst, minMax) {
1675 return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
1676 },
1677
1678 /* Find the number of days in a given month. */
1679 _getDaysInMonth: function(year, month) {
1680 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
1681 },
1682
1683 /* Find the day of the week of the first of a month. */
1684 _getFirstDayOfMonth: function(year, month) {
1685 return new Date(year, month, 1).getDay();
1686 },
1687
1688 /* Determines if we should allow a "next/prev" month display change. */
1689 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
1690 var numMonths = this._getNumberOfMonths(inst);
1691 var date = this._daylightSavingAdjust(new Date(curYear,
1692 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
1693 if (offset < 0)
1694 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1695 return this._isInRange(inst, date);
1696 },
1697
1698 /* Is the given date in the accepted range? */
1699 _isInRange: function(inst, date) {
1700 var minDate = this._getMinMaxDate(inst, 'min');
1701 var maxDate = this._getMinMaxDate(inst, 'max');
1702 return ((!minDate || date.getTime() >= minDate.getTime()) &&
1703 (!maxDate || date.getTime() <= maxDate.getTime()));
1704 },
1705
1706 /* Provide the configuration settings for formatting/parsing. */
1707 _getFormatConfig: function(inst) {
1708 var shortYearCutoff = this._get(inst, 'shortYearCutoff');
1709 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
1710 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1711 return {shortYearCutoff: shortYearCutoff,
1712 dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
1713 monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
1714 },
1715
1716 /* Format the given date for display. */
1717 _formatDate: function(inst, day, month, year) {
1718 if (!day) {
1719 inst.currentDay = inst.selectedDay;
1720 inst.currentMonth = inst.selectedMonth;
1721 inst.currentYear = inst.selectedYear;
1722 }
1723 var date = (day ? (typeof day == 'object' ? day :
1724 this._daylightSavingAdjust(new Date(year, month, day))) :
1725 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1726 return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
1727 }
1728 });
1729
1730 /*
1731 * Bind hover events for datepicker elements.
1732 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
1733 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
1734 */
1735 function bindHover(dpDiv) {
1736 var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
1737 return dpDiv.bind('mouseout', function(event) {
1738 var elem = $( event.target ).closest( selector );
1739 if ( !elem.length ) {
1740 return;
1741 }
1742 elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" );
1743 })
1744 .bind('mouseover', function(event) {
1745 var elem = $( event.target ).closest( selector );
1746 if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) ||
1747 !elem.length ) {
1748 return;
1749 }
1750 elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
1751 elem.addClass('ui-state-hover');
1752 if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover');
1753 if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover');
1754 });
1755 }
1756
1757 /* jQuery extend now ignores nulls! */
1758 function extendRemove(target, props) {
1759 $.extend(target, props);
1760 for (var name in props)
1761 if (props[name] == null || props[name] == undefined)
1762 target[name] = props[name];
1763 return target;
1764 };
1765
1766 /* Determine whether an object is an array. */
1767 function isArray(a) {
1768 return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
1769 (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
1770 };
1771
1772 /* Invoke the datepicker functionality.
1773 @param options string - a command, optionally followed by additional parameters or
1774 Object - settings for attaching new datepicker functionality
1775 @return jQuery object */
1776 $.fn.datepicker = function(options){
1777
1778 /* Verify an empty collection wasn't passed - Fixes #6976 */
1779 if ( !this.length ) {
1780 return this;
1781 }
1782
1783 /* Initialise the date picker. */
1784 if (!$.datepicker.initialized) {
1785 $(document).mousedown($.datepicker._checkExternalClick).
1786 find('body').append($.datepicker.dpDiv);
1787 $.datepicker.initialized = true;
1788 }
1789
1790 var otherArgs = Array.prototype.slice.call(arguments, 1);
1791 if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
1792 return $.datepicker['_' + options + 'Datepicker'].
1793 apply($.datepicker, [this[0]].concat(otherArgs));
1794 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1795 return $.datepicker['_' + options + 'Datepicker'].
1796 apply($.datepicker, [this[0]].concat(otherArgs));
1797 return this.each(function() {
1798 typeof options == 'string' ?
1799 $.datepicker['_' + options + 'Datepicker'].
1800 apply($.datepicker, [this].concat(otherArgs)) :
1801 $.datepicker._attachDatepicker(this, options);
1802 });
1803 };
1804
1805 $.datepicker = new Datepicker(); // singleton instance
1806 $.datepicker.initialized = false;
1807 $.datepicker.uuid = new Date().getTime();
1808 $.datepicker.version = "1.8.14";
1809
1810 // Workaround for #4055
1811 // Add another global to avoid noConflict issues with inline event handlers
1812 window['DP_jQuery_' + dpuuid] = $;
1813
1814 $('.date-pick').datepicker({autoFocusNextInput: true});
1815 })(jQuery);
1816