/[projects]/misc/horsensspejder-web/jquery/jquery-ui-1.10.3/ui/jquery.ui.datepicker.js
ViewVC logotype

Contents of /misc/horsensspejder-web/jquery/jquery-ui-1.10.3/ui/jquery.ui.datepicker.js

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20