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