Projet

Général

Profil

Paste
Télécharger (75,2 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / jquery_update / replace / ui / ui / jquery.ui.datepicker.js @ 503b3f7b

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);