diff --git a/ui/ui.datepicker.js b/ui/ui.datepicker.js index 3f8cb216d..a14327ec4 100644 --- a/ui/ui.datepicker.js +++ b/ui/ui.datepicker.js @@ -15,22 +15,21 @@ (function($) { // hide the namespace +var PROP_NAME = 'datepicker'; + /* Date picker manager. Use the singleton instance of this class, $.datepicker, to interact with the date picker. - Settings for (groups of) date pickers are maintained in an instance object - (DatepickerInstance), allowing multiple different settings on the same page. */ + Settings for (groups of) date pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ function Datepicker() { this.debug = false; // Change this to true to start debugging - this._nextId = 0; // Next ID for a date picker instance - this._inst = []; // List of instances indexed by ID this._curInst = null; // The current instance in use this._disabledInputs = []; // List of date picker inputs that have been disabled this._datepickerShowing = false; // True if the popup picker is showing , false if not this._inDialog = false; // True if showing within a "dialog", false if not this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division this._appendClass = 'ui-datepicker-append'; // The name of the append marker class - this._wrapClass = 'ui-datepicker-wrap'; // The name of the wrapper marker class this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class this._promptClass = 'ui-datepicker-prompt'; // The name of the dialog prompt marker class @@ -116,7 +115,7 @@ function Datepicker() { altFormat: '' // The date format to use for the alternate field }; $.extend(this._defaults, this.regional['']); - this._datepickerDiv = $('
'); + this.dpDiv = $(''); } $.extend(Datepicker.prototype, { @@ -129,18 +128,6 @@ $.extend(Datepicker.prototype, { console.log.apply('', arguments); }, - /* Register a new date picker instance - with custom settings. */ - _register: function(inst) { - var id = this._nextId++; - this._inst[id] = inst; - return id; - }, - - /* Retrieve a particular date picker instance based on its ID. */ - _getInst: function(id) { - return this._inst[id] || id; - }, - /* Override the default settings for all instances of the date picker. @param settings object - the new settings to use as defaults (anonymous object) @return the manager object */ @@ -167,49 +154,158 @@ $.extend(Datepicker.prototype, { } } var nodeName = target.nodeName.toLowerCase(); - var instSettings = (inlineSettings ? - $.extend(settings || {}, inlineSettings) : settings); + var inline = (nodeName == 'div' || nodeName == 'span'); + if (!target.id) + target.id = 'dp' + new Date().getTime(); + var inst = this._newInst($(target), inline); + inst.settings = $.extend({}, settings || {}, inlineSettings || {}); if (nodeName == 'input') { - var inst = (inst && !inlineSettings ? inst : - new DatepickerInstance(instSettings, false)); this._connectDatepicker(target, inst); - } else if (nodeName == 'div' || nodeName == 'span') { - var inst = new DatepickerInstance(instSettings, true); + } else if (inline) { this._inlineDatepicker(target, inst); } }, + /* Create a new instance object. */ + _newInst: function(target, inline) { + return {id: target[0].id, input: target, // associated target + selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection + drawMonth: 0, drawYear: 0, // month being drawn + inline: inline, // is datepicker inline or not + dpDiv: (!inline ? this.dpDiv : // presentation div + $(''))}; + }, + + /* Attach the date picker to an input field. */ + _connectDatepicker: function(target, inst) { + var input = $(target); + if (input.hasClass(this.markerClassName)) + return; + var appendText = this._get(inst, 'appendText'); + var isRTL = this._get(inst, 'isRTL'); + if (appendText) + input[isRTL ? 'before' : 'after']('' + appendText + ''); + var showOn = this._get(inst, 'showOn'); + if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field + input.focus(this._showDatepicker); + if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked + var buttonText = this._get(inst, 'buttonText'); + var buttonImage = this._get(inst, 'buttonImage'); + var trigger = $(this._get(inst, 'buttonImageOnly') ? + $('').addClass(this._triggerClass). + attr({ src: buttonImage, alt: buttonText, title: buttonText }) : + $('').addClass(this._triggerClass). + html(buttonImage == '' ? buttonText : $('').attr( + { src:buttonImage, alt:buttonText, title:buttonText }))); + input[isRTL ? 'before' : 'after'](trigger); + trigger.click(function() { + if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target) + $.datepicker._hideDatepicker(); + else + $.datepicker._showDatepicker(target); + return false; + }); + } + input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress). + bind("setData.datepicker", function(event, key, value) { + inst.settings[key] = value; + }).bind("getData.datepicker", function(event, key) { + return this._get(inst, key); + }); + $.data(target, PROP_NAME, inst); + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function(target, inst) { + var input = $(target); + if (input.hasClass(this.markerClassName)) + return; + input.addClass(this.markerClassName).append(inst.dpDiv). + bind("setData.datepicker", function(event, key, value){ + inst.settings[key] = value; + }).bind("getData.datepicker", function(event, key){ + return this._get(inst, key); + }); + $.data(target, PROP_NAME, inst); + this._setDate(inst, this._getDefaultDate(inst)); + this._updateDatepicker(inst); + }, + + /* Tidy up after displaying the date picker. */ + _inlineShow: function(inst) { + var numMonths = this._getNumberOfMonths(inst); // fix width for dynamic number of date pickers + inst.dpDiv.width(numMonths[1] * $('.ui-datepicker', inst.dpDiv[0]).width()); + }, + + /* Pop-up the date picker in a "dialog" box. + @param input element - ignored + @param dateText string - the initial date to display (in the current format) + @param onSelect function - the function(dateText) to call when a date is selected + @param settings object - update the dialog date picker instance's settings (anonymous object) + @param pos int[2] - coordinates for the dialog's position within the screen or + event - with x/y coordinates or + leave empty for default (screen centre) + @return the manager object */ + _dialogDatepicker: function(input, dateText, onSelect, settings, pos) { + var inst = this._dialogInst; // internal instance + if (!inst) { + var id = 'dp' + new Date().getTime(); + this._dialogInput = $(''); + this._dialogInput.keydown(this._doKeyDown); + $('body').append(this._dialogInput); + inst = this._dialogInst = this._newInst(this._dialogInput, false); + inst.settings = {}; + $.data(this._dialogInput[0], PROP_NAME, inst); + } + extendRemove(inst.settings, settings || {}); + this._dialogInput.val(dateText); + + this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); + if (!this._pos) { + var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; + } + + // move input on screen for focus, but hidden behind dialog + this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px'); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass(this._dialogClass); + this._showDatepicker(this._dialogInput[0]); + if ($.blockUI) + $.blockUI(this.dpDiv); + $.data(this._dialogInput[0], PROP_NAME, inst); + return this; + }, + /* Detach a datepicker from its control. @param target element - the target input field or division or span */ _destroyDatepicker: function(target) { var nodeName = target.nodeName.toLowerCase(); - var calId = target._calId; var $target = $(target); - $target.removeAttr('_calId'); + $.removeData(target, PROP_NAME); if (nodeName == 'input') { - $target.siblings('.' + this._appendClass).replaceWith('').end() - .siblings('.' + this._triggerClass).replaceWith('').end() - .removeClass(this.markerClassName) - .unbind('focus', this._showDatepicker) - .unbind('keydown', this._doKeyDown) - .unbind('keypress', this._doKeyPress); - var wrapper = $target.parents('.' + this._wrapClass); - if (wrapper) - wrapper.siblings('.' + this._appendClass).replaceWith('').end() - .replaceWith(wrapper.html()); + $target.siblings('.' + this._appendClass).remove().end(). + siblings('.' + this._triggerClass).remove().end(). + removeClass(this.markerClassName). + unbind('focus', this._showDatepicker). + unbind('keydown', this._doKeyDown). + unbind('keypress', this._doKeyPress); } else if (nodeName == 'div' || nodeName == 'span') $target.removeClass(this.markerClassName).empty(); - if ($('input[_calId=' + calId + ']').length == 0) - // clean up if last for this ID - this._inst[calId] = null; }, /* Enable the date picker to a jQuery selection. @param target element - the target input field or division or span */ _enableDatepicker: function(target) { target.disabled = false; - $(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = false; }).end() - .siblings('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''}); + $(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = false; }).end(). + siblings('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''}); this._disabledInputs = $.map(this._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry }, @@ -218,11 +314,11 @@ $.extend(Datepicker.prototype, { @param target element - the target input field or division or span */ _disableDatepicker: function(target) { target.disabled = true; - $(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = true; }).end() - .siblings('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'}); - this._disabledInputs = $.map($.datepicker._disabledInputs, + $(target).siblings('button.' + this._triggerClass).each(function() { this.disabled = true; }).end(). + siblings('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'}); + this._disabledInputs = $.map(this._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry - this._disabledInputs[$.datepicker._disabledInputs.length] = target; + this._disabledInputs[this._disabledInputs.length] = target; }, /* Is the first field in a jQuery collection disabled as a datepicker? @@ -240,8 +336,8 @@ $.extend(Datepicker.prototype, { /* Update the settings for a date picker attached to an input field or division. @param target element - the target input field or division or span - @param name string - the name of the setting to change or - object - the new settings to update + @param name object - the new settings to update or + string - the name of the setting to change or @param value any - the new value for the setting (omit if above is an object) */ _changeDatepicker: function(target, name, value) { var settings = name || {}; @@ -249,8 +345,8 @@ $.extend(Datepicker.prototype, { settings = {}; settings[name] = value; } - if (inst = this._getInst(target._calId)) { - extendRemove(inst._settings, settings); + if (inst = $.data(target, PROP_NAME)) { + extendRemove(inst.settings, settings); this._updateDatepicker(inst); } }, @@ -260,8 +356,9 @@ $.extend(Datepicker.prototype, { @param date Date - the new date @param endDate Date - the new end date for a range (optional) */ _setDateDatepicker: function(target, date, endDate) { - if (inst = this._getInst(target._calId)) { - inst._setDate(date, endDate); + var inst = $.data(target, PROP_NAME); + if (inst) { + this._setDate(inst, date, endDate); this._updateDatepicker(inst); } }, @@ -271,158 +368,64 @@ $.extend(Datepicker.prototype, { @return Date - the current date or Date[2] - the current dates for a range */ _getDateDatepicker: function(target) { - var inst = this._getInst(target._calId); + var inst = $.data(target, PROP_NAME); if (inst) - inst._setDateFromField($(target)); - return (inst ? inst._getDate() : null); + this._setDateFromField(inst); + return (inst ? this._getDate(inst) : null); }, /* Handle keystrokes. */ _doKeyDown: function(e) { - var inst = $.datepicker._getInst(this._calId); + var inst = $.data(e.target, PROP_NAME); + var handled = true; if ($.datepicker._datepickerShowing) switch (e.keyCode) { case 9: $.datepicker._hideDatepicker(null, ''); break; // hide on tab out - case 13: $.datepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear, - $('td.ui-datepicker-days-cell-over', inst._datepickerDiv)[0]); + case 13: $.datepicker._selectDay(e.target, inst.selectedMonth, inst.selectedYear, + $('td.ui-datepicker-days-cell-over', inst.dpDiv)[0]); return false; // don't submit the form break; // select the value on enter - case 27: $.datepicker._hideDatepicker(null, inst._get('speed')); + case 27: $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'speed')); break; // hide on escape - case 33: $.datepicker._adjustDate(inst, - (e.ctrlKey ? -1 : -inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); + case 33: $.datepicker._adjustDate(e.target, (e.ctrlKey ? -1 : + -$.datepicker._get(inst, 'stepMonths')), (e.ctrlKey ? 'Y' : 'M')); break; // previous month/year on page up/+ ctrl - case 34: $.datepicker._adjustDate(inst, - (e.ctrlKey ? +1 : +inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); + case 34: $.datepicker._adjustDate(e.target, (e.ctrlKey ? +1 : + +$.datepicker._get(inst, 'stepMonths')), (e.ctrlKey ? 'Y' : 'M')); break; // next month/year on page down/+ ctrl - case 35: if (e.ctrlKey) $.datepicker._clearDate(inst); + case 35: if (e.ctrlKey) $.datepicker._clearDate(e.target); break; // clear on ctrl+end - case 36: if (e.ctrlKey) $.datepicker._gotoToday(inst); + case 36: if (e.ctrlKey) $.datepicker._gotoToday(e.target); break; // current on ctrl+home - case 37: if (e.ctrlKey) $.datepicker._adjustDate(inst, -1, 'D'); + case 37: if (e.ctrlKey) $.datepicker._adjustDate(e.target, -1, 'D'); break; // -1 day on ctrl+left - case 38: if (e.ctrlKey) $.datepicker._adjustDate(inst, -7, 'D'); + case 38: if (e.ctrlKey) $.datepicker._adjustDate(e.target, -7, 'D'); break; // -1 week on ctrl+up - case 39: if (e.ctrlKey) $.datepicker._adjustDate(inst, +1, 'D'); + case 39: if (e.ctrlKey) $.datepicker._adjustDate(e.target, +1, 'D'); break; // +1 day on ctrl+right - case 40: if (e.ctrlKey) $.datepicker._adjustDate(inst, +7, 'D'); + case 40: if (e.ctrlKey) $.datepicker._adjustDate(e.target, +7, 'D'); break; // +1 week on ctrl+down + default: handled = false; } else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home $.datepicker._showDatepicker(this); + else + handled = false; + if (handled) { + e.preventDefault(); + e.stopPropagation(); + } }, /* Filter entered characters - based on date format. */ _doKeyPress: function(e) { - var inst = $.datepicker._getInst(this._calId); - var chars = $.datepicker._possibleChars(inst._get('dateFormat')); + var inst = $.data(e.target, PROP_NAME); + var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')); var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode); return e.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1); }, - /* Attach the date picker to an input field. */ - _connectDatepicker: function(target, inst) { - var input = $(target); - if (input.hasClass(this.markerClassName)) - return; - var appendText = inst._get('appendText'); - var isRTL = inst._get('isRTL'); - if (appendText) - input[isRTL ? 'before' : 'after']('' + appendText + ''); - var showOn = inst._get('showOn'); - if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field - input.focus(this._showDatepicker); - if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked - input.wrap(''); - var buttonText = inst._get('buttonText'); - var buttonImage = inst._get('buttonImage'); - var trigger = $(inst._get('buttonImageOnly') ? - $('').addClass(this._triggerClass).attr({ src: buttonImage, alt: buttonText, title: buttonText }) : - $('').addClass(this._triggerClass).html(buttonImage != '' ? - $('').attr({ src:buttonImage, alt:buttonText, title:buttonText }) : buttonText)); - input[isRTL ? 'before' : 'after'](trigger); - trigger.click(function() { - if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target) - $.datepicker._hideDatepicker(); - else - $.datepicker._showDatepicker(target); - - return false; - }); - } - input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress) - .bind("setData.datepicker", function(event, key, value) { - inst._settings[key] = value; - }).bind("getData.datepicker", function(event, key) { - return inst._get(key); - }); - input[0]._calId = inst._id; - }, - - /* Attach an inline date picker to a div. */ - _inlineDatepicker: function(target, inst) { - var input = $(target); - if (input.hasClass(this.markerClassName)) - return; - input.addClass(this.markerClassName).append(inst._datepickerDiv) - .bind("setData.datepicker", function(event, key, value){ - inst._settings[key] = value; - }).bind("getData.datepicker", function(event, key){ - return inst._get(key); - }); - input[0]._calId = inst._id; - this._updateDatepicker(inst); - }, - - /* Tidy up after displaying the date picker. */ - _inlineShow: function(inst) { - var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers - inst._datepickerDiv.width(numMonths[1] * $('.ui-datepicker', inst._datepickerDiv[0]).width()); - }, - - /* Pop-up the date picker in a "dialog" box. - @param input element - ignored - @param dateText string - the initial date to display (in the current format) - @param onSelect function - the function(dateText) to call when a date is selected - @param settings object - update the dialog date picker instance's settings (anonymous object) - @param pos int[2] - coordinates for the dialog's position within the screen or - event - with x/y coordinates or - leave empty for default (screen centre) - @return the manager object */ - _dialogDatepicker: function(input, dateText, onSelect, settings, pos) { - var inst = this._dialogInst; // internal instance - if (!inst) { - inst = this._dialogInst = new DatepickerInstance({}, false); - this._dialogInput = $(''); - this._dialogInput.keydown(this._doKeyDown); - $('body').append(this._dialogInput); - this._dialogInput[0]._calId = inst._id; - } - extendRemove(inst._settings, settings || {}); - this._dialogInput.val(dateText); - - this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); - if (!this._pos) { - var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; - var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; - var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; - var scrollY = document.documentElement.scrollTop || document.body.scrollTop; - this._pos = // should use actual width/height below - [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; - } - - // move input on screen for focus, but hidden behind dialog - this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px'); - inst._settings.onSelect = onSelect; - this._inDialog = true; - this._datepickerDiv.addClass(this._dialogClass); - this._showDatepicker(this._dialogInput[0]); - if ($.blockUI) - $.blockUI(this._datepickerDiv); - return this; - }, - /* Pop-up the date picker for a given input field. @param input element - the input field attached to the date picker or event - if triggered by focus */ @@ -432,12 +435,12 @@ $.extend(Datepicker.prototype, { input = $('input', input.parentNode)[0]; if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here return; - var inst = $.datepicker._getInst(input._calId); - var beforeShow = inst._get('beforeShow'); - extendRemove(inst._settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); + var inst = $.data(input, PROP_NAME); + var beforeShow = $.datepicker._get(inst, 'beforeShow'); + extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); $.datepicker._hideDatepicker(null, ''); $.datepicker._lastInput = input; - inst._setDateFromField(input); + $.datepicker._setDateFromField(inst); if ($.datepicker._inDialog) // hide cursor input.value = ''; if (!$.datepicker._pos) { // position below input @@ -455,76 +458,70 @@ $.extend(Datepicker.prototype, { } var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; $.datepicker._pos = null; - inst._rangeStart = null; + inst.rangeStart = null; // determine sizing offscreen - inst._datepickerDiv.css({position: 'absolute', display: 'block', top: '-1000px'}); + inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'}); $.datepicker._updateDatepicker(inst); // fix width for dynamic number of date pickers - inst._datepickerDiv.width(inst._getNumberOfMonths()[1] * - $('.ui-datepicker', inst._datepickerDiv[0])[0].offsetWidth); + inst.dpDiv.width($.datepicker._getNumberOfMonths(inst)[1] * + $('.ui-datepicker', inst.dpDiv[0])[0].offsetWidth); // and adjust position before showing offset = $.datepicker._checkOffset(inst, offset, isFixed); - inst._datepickerDiv.css({position: ($.datepicker._inDialog && $.blockUI ? + inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', left: offset.left + 'px', top: offset.top + 'px'}); - if (!inst._inline) { - var showAnim = inst._get('showAnim') || 'show'; - var speed = inst._get('speed'); + if (!inst.inline) { + var showAnim = $.datepicker._get(inst, 'showAnim') || 'show'; + var speed = $.datepicker._get(inst, 'speed'); var postProcess = function() { $.datepicker._datepickerShowing = true; if ($.browser.msie && parseInt($.browser.version) < 7) // fix IE < 7 select problems - $('iframe.ui-datepicker-cover').css({width: inst._datepickerDiv.width() + 4, - height: inst._datepickerDiv.height() + 4}); + $('iframe.ui-datepicker-cover').css({width: inst.dpDiv.width() + 4, + height: inst.dpDiv.height() + 4}); }; - inst._datepickerDiv[showAnim](speed, postProcess); + inst.dpDiv[showAnim](speed, postProcess); if (speed == '') postProcess(); - if (inst._input[0].type != 'hidden') - inst._input[0].focus(); + if (inst.input[0].type != 'hidden') + inst.input[0].focus(); $.datepicker._curInst = inst; } }, /* Generate the date picker content. */ _updateDatepicker: function(inst) { - var dims = {width: inst._datepickerDiv.width() + 4, - height: inst._datepickerDiv.height() + 4}; - inst._datepickerDiv.empty().append(inst._generateDatepicker()). + var dims = {width: inst.dpDiv.width() + 4, + height: inst.dpDiv.height() + 4}; + inst.dpDiv.empty().append(this._generateDatepicker(inst)). find('iframe.ui-datepicker-cover'). css({width: dims.width, height: dims.height}); - var numMonths = inst._getNumberOfMonths(); - if (numMonths[0] != 1 || numMonths[1] != 1) - inst._datepickerDiv.addClass('ui-datepicker-multi'); - else - inst._datepickerDiv.removeClass('ui-datepicker-multi'); - - if (inst._get('isRTL')) - inst._datepickerDiv.addClass('ui-datepicker-rtl'); - else - inst._datepickerDiv.removeClass('ui-datepicker-rtl'); - - if (inst._input && inst._input[0].type != 'hidden') - $(inst._input[0]).focus(); + var numMonths = this._getNumberOfMonths(inst); + inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') + + 'Class']('ui-datepicker-multi'); + inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') + + 'Class']('ui-datepicker-rtl'); + if (inst.input && inst.input[0].type != 'hidden') + $(inst.input[0]).focus(); }, /* Check positioning to remain on screen. */ _checkOffset: function(inst, offset, isFixed) { - var pos = inst._input ? $.datepicker._findPos(inst._input[0]) : null; + var pos = inst.input ? this._findPos(inst.input[0]) : null; var browserWidth = window.innerWidth || document.documentElement.clientWidth; var browserHeight = window.innerHeight || document.documentElement.clientHeight; var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; var scrollY = document.documentElement.scrollTop || document.body.scrollTop; // reposition date picker horizontally if outside the browser window - if ((offset.left + inst._datepickerDiv.width() - scrollX) > browserWidth) + if ((offset.left + inst.dpDiv.width() - scrollX) > browserWidth) offset.left = Math.max((isFixed ? 0 : scrollX), - pos[0] + (inst._input ? inst._input.width() : 0) - (isFixed ? scrollX : 0) - inst._datepickerDiv.width() - + pos[0] + (inst.input ? inst.input.width() : 0) - (isFixed ? scrollX : 0) - inst.dpDiv.width() - (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0)); else offset.left -= (isFixed ? scrollX : 0); // reposition date picker vertically if outside the browser window - if ((offset.top + inst._datepickerDiv.height() - scrollY) > browserHeight) + if ((offset.top + inst.dpDiv.height() - scrollY) > browserHeight) offset.top = Math.max((isFixed ? 0 : scrollY), - pos[1] - (isFixed ? scrollY : 0) - (this._inDialog ? 0 : inst._datepickerDiv.height()) - + pos[1] - (isFixed ? scrollY : 0) - (this._inDialog ? 0 : inst.dpDiv.height()) - (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0)); else offset.top -= (isFixed ? scrollY : 0); @@ -547,32 +544,32 @@ $.extend(Datepicker.prototype, { var inst = this._curInst; if (!inst) return; - var rangeSelect = inst._get('rangeSelect'); + var rangeSelect = this._get(inst, 'rangeSelect'); if (rangeSelect && this._stayOpen) - this._selectDate(inst, inst._formatDate( - inst._currentDay, inst._currentMonth, inst._currentYear)); + this._selectDate('#' + inst.id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); this._stayOpen = false; if (this._datepickerShowing) { - speed = (speed != null ? speed : inst._get('speed')); - var showAnim = inst._get('showAnim'); - inst._datepickerDiv[(showAnim == 'slideDown' ? 'slideUp' : + speed = (speed != null ? speed : this._get(inst, 'speed')); + var showAnim = this._get(inst, 'showAnim'); + inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' : (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))](speed, function() { $.datepicker._tidyDialog(inst); }); if (speed == '') this._tidyDialog(inst); - var onClose = inst._get('onClose'); + var onClose = this._get(inst, 'onClose'); if (onClose) - onClose.apply((inst._input ? inst._input[0] : null), - [inst._getDate(), inst]); // trigger custom callback + onClose.apply((inst.input ? inst.input[0] : null), + [this._getDate(inst), inst]); // trigger custom callback this._datepickerShowing = false; this._lastInput = null; - inst._settings.prompt = null; + inst.settings.prompt = null; if (this._inDialog) { this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); if ($.blockUI) { $.unblockUI(); - $('body').append(this._datepickerDiv); + $('body').append(this.dpDiv); } } this._inDialog = false; @@ -582,8 +579,8 @@ $.extend(Datepicker.prototype, { /* Tidy up after a dialog display. */ _tidyDialog: function(inst) { - inst._datepickerDiv.removeClass(this._dialogClass).unbind('.ui-datepicker'); - $('.' + this._promptClass, inst._datepickerDiv).remove(); + inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker'); + $('.' + this._promptClass, inst.dpDiv).remove(); }, /* Close date picker if clicked elsewhere. */ @@ -600,44 +597,49 @@ $.extend(Datepicker.prototype, { /* Adjust one of the date sub-fields. */ _adjustDate: function(id, offset, period) { - var inst = this._getInst(id); - inst._adjustDate(offset, period); + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + this._adjustInstDate(inst, offset, period); this._updateDatepicker(inst); }, /* Action for current link. */ _gotoToday: function(id) { + var target = $(id); + var inst = $.data(target[0], PROP_NAME); var date = new Date(); - var inst = this._getInst(id); - inst._selectedDay = date.getDate(); - inst._drawMonth = inst._selectedMonth = date.getMonth(); - inst._drawYear = inst._selectedYear = date.getFullYear(); - this._adjustDate(inst); - inst._notifyChange(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + this._adjustDate(target); + this._notifyChange(inst); }, /* Action for selecting a new month/year. */ _selectMonthYear: function(id, select, period) { - var inst = this._getInst(id); + var target = $(id); + var inst = $.data(target[0], PROP_NAME); inst._selectingMonthYear = false; - inst[period == 'M' ? '_drawMonth' : '_drawYear'] = + inst[period == 'M' ? 'drawMonth' : 'drawYear'] = select.options[select.selectedIndex].value - 0; - this._adjustDate(inst); - inst._notifyChange(); + this._adjustDate(target); + this._notifyChange(inst); }, /* Restore input focus after not changing month/year. */ _clickMonthYear: function(id) { - var inst = this._getInst(id); - if (inst._input && inst._selectingMonthYear && !$.browser.msie) - inst._input[0].focus(); + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + if (inst.input && inst._selectingMonthYear && !$.browser.msie) + inst.input[0].focus(); inst._selectingMonthYear = !inst._selectingMonthYear; }, /* Action for changing the first week day. */ _changeFirstDay: function(id, day) { - var inst = this._getInst(id); - inst._settings.firstDay = day; + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + inst.settings.firstDay = day; this._updateDatepicker(inst); }, @@ -645,8 +647,9 @@ $.extend(Datepicker.prototype, { _selectDay: function(id, month, year, td) { if ($(td).hasClass(this._unselectableClass)) return; - var inst = this._getInst(id); - var rangeSelect = inst._get('rangeSelect'); + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + var rangeSelect = this._get(inst, 'rangeSelect'); if (rangeSelect) { this._stayOpen = !this._stayOpen; if (this._stayOpen) { @@ -654,80 +657,82 @@ $.extend(Datepicker.prototype, { $(td).addClass(this._currentClass); } } - inst._selectedDay = inst._currentDay = $('a', td).html(); - inst._selectedMonth = inst._currentMonth = month; - inst._selectedYear = inst._currentYear = year; + inst.selectedDay = inst.currentDay = $('a', td).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; if (this._stayOpen) { - inst._endDay = inst._endMonth = inst._endYear = null; + inst.endDay = inst.endMonth = inst.endYear = null; } else if (rangeSelect) { - inst._endDay = inst._currentDay; - inst._endMonth = inst._currentMonth; - inst._endYear = inst._currentYear; + inst.endDay = inst.currentDay; + inst.endMonth = inst.currentMonth; + inst.endYear = inst.currentYear; } - this._selectDate(id, inst._formatDate( - inst._currentDay, inst._currentMonth, inst._currentYear)); + this._selectDate(id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); if (this._stayOpen) { - inst._rangeStart = new Date(inst._currentYear, inst._currentMonth, inst._currentDay); + inst.rangeStart = new Date(inst.currentYear, inst.currentMonth, inst.currentDay); this._updateDatepicker(inst); } else if (rangeSelect) { - inst._selectedDay = inst._currentDay = inst._rangeStart.getDate(); - inst._selectedMonth = inst._currentMonth = inst._rangeStart.getMonth(); - inst._selectedYear = inst._currentYear = inst._rangeStart.getFullYear(); - inst._rangeStart = null; - if (inst._inline) + inst.selectedDay = inst.currentDay = inst.rangeStart.getDate(); + inst.selectedMonth = inst.currentMonth = inst.rangeStart.getMonth(); + inst.selectedYear = inst.currentYear = inst.rangeStart.getFullYear(); + inst.rangeStart = null; + if (inst.inline) this._updateDatepicker(inst); } }, /* Erase the input field and hide the date picker. */ _clearDate: function(id) { - var inst = this._getInst(id); - if (inst._get('mandatory')) + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + if (this._get(inst, 'mandatory')) return; this._stayOpen = false; - inst._endDay = inst._endMonth = inst._endYear = inst._rangeStart = null; - this._selectDate(inst, ''); + inst.endDay = inst.endMonth = inst.endYear = inst.rangeStart = null; + this._selectDate(target, ''); }, /* Update the input field with the selected date. */ _selectDate: function(id, dateStr) { - var inst = this._getInst(id); - dateStr = (dateStr != null ? dateStr : inst._formatDate()); - if (inst._get('rangeSelect') && dateStr) - dateStr = (inst._rangeStart ? inst._formatDate(inst._rangeStart) : - dateStr) + inst._get('rangeSeparator') + dateStr; - if (inst._input) - inst._input.val(dateStr); + var target = $(id); + var inst = $.data(target[0], PROP_NAME); + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (this._get(inst, 'rangeSelect') && dateStr) + dateStr = (inst.rangeStart ? this._formatDate(inst, inst.rangeStart) : + dateStr) + this._get(inst, 'rangeSeparator') + dateStr; + if (inst.input) + inst.input.val(dateStr); this._updateAlternate(inst); - var onSelect = inst._get('onSelect'); + var onSelect = this._get(inst, 'onSelect'); if (onSelect) - onSelect.apply((inst._input ? inst._input[0] : null), [dateStr, inst]); // trigger custom callback - else if (inst._input) - inst._input.trigger('change'); // fire the change event - if (inst._inline) + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback + else if (inst.input) + inst.input.trigger('change'); // fire the change event + if (inst.inline) this._updateDatepicker(inst); else if (!this._stayOpen) { - this._hideDatepicker(null, inst._get('speed')); - this._lastInput = inst._input[0]; - if (typeof(inst._input[0]) != 'object') - inst._input[0].focus(); // restore focus + this._hideDatepicker(null, this._get(inst, 'speed')); + this._lastInput = inst.input[0]; + if (typeof(inst.input[0]) != 'object') + inst.input[0].focus(); // restore focus this._lastInput = null; } }, /* Update any alternate field to synchronise with the main field. */ _updateAlternate: function(inst) { - var altField = inst._get('altField'); + var altField = this._get(inst, 'altField'); if (altField) { // update alternate field too - var altFormat = inst._get('altFormat'); - var date = inst._getDate(); + var altFormat = this._get(inst, 'altFormat'); + var date = this._getDate(inst); dateStr = (isArray(date) ? (!date[0] && !date[1] ? '' : - $.datepicker.formatDate(altFormat, date[0], inst._getFormatConfig()) + - inst._get('rangeSeparator') + $.datepicker.formatDate( - altFormat, date[1] || date[0], inst._getFormatConfig())) : - $.datepicker.formatDate(altFormat, date, inst._getFormatConfig())); + this.formatDate(altFormat, date[0], this._getFormatConfig(inst)) + + this._get(inst, 'rangeSeparator') + this.formatDate( + altFormat, date[1] || date[0], this._getFormatConfig(inst))) : + this.formatDate(altFormat, date, this._getFormatConfig(inst))); $(altField).each(function() { $(this).val(dateStr); }); } }, @@ -766,7 +771,8 @@ $.extend(Datepicker.prototype, { @param inst the current datepicker instance @return the status display text for this date */ dateStatus: function(date, inst) { - return $.datepicker.formatDate(inst._get('dateStatus'), date, inst._getFormatConfig()); + return $.datepicker.formatDate($.datepicker._get(inst, 'dateStatus'), + date, $.datepicker._getFormatConfig(inst)); }, /* Parse a string value into a date object. @@ -1007,80 +1013,59 @@ $.extend(Datepicker.prototype, { chars += format.charAt(iFormat); } return chars; - } -}); + }, -/* Individualised settings for date picker functionality applied to one or more related inputs. - Instances are managed and manipulated through the Datepicker manager. */ -function DatepickerInstance(settings, inline) { - this._id = $.datepicker._register(this); - this._selectedDay = 0; // Current date for selection - this._selectedMonth = 0; // 0-11 - this._selectedYear = 0; // 4-digit year - this._drawMonth = 0; // Current month at start of datepicker - this._drawYear = 0; - this._input = null; // The attached input field - this._inline = inline; // True if showing inline, false if used in a popup - this._datepickerDiv = (!inline ? $.datepicker._datepickerDiv : - $('' + this._get('weekHeader') + ' | ' : ''); - var firstDay = this._get('firstDay'); - var changeFirstDay = this._get('changeFirstDay'); - var dayNames = this._get('dayNames'); - var dayNamesShort = this._get('dayNamesShort'); - var dayNamesMin = this._get('dayNamesMin'); + (showWeeks ? '' + this._get(inst, 'weekHeader') + ' | ' : ''); for (var dow = 0; dow < 7; dow++) { // days of the week var day = (dow + firstDay) % 7; - var status = this._get('dayStatus') || ' '; - status = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) : + var dayStatus = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) : status.replace(/D/, dayNamesShort[day])); html += '= 5 ? ' class="ui-datepicker-week-end-cell"' : '') + '>' + (!changeFirstDay ? '' + + '' + dayNamesMin[day] + (changeFirstDay ? '' : '') + ' | '; } html += '
' + calculateWeek(printDate) + ' | ' : ''); for (var dow = 0; dow < 7; dow++) { // create date picker days var daySettings = (beforeShowDay ? - beforeShowDay.apply((this._input ? this._input[0] : null), [printDate]) : [true, '']); + beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']); var otherMonth = (printDate.getMonth() != drawMonth); var unselectable = otherMonth || !daySettings[0] || (minDate && printDate < minDate) || (maxDate && printDate > maxDate); html += '' + // actions + inst.id + '\').html(\' \');') + '" onclick="jQuery.datepicker._selectDay(\'#' + + inst.id + '\',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions (otherMonth ? (showOtherMonths ? printDate.getDate() : ' ') : // display for other months (unselectable ? printDate.getDate() : '' + printDate.getDate() + '')) + ' | '; // display for this month - printDate.setDate(printDate.getDate() + 1); + printDate.setUTCDate(printDate.getUTCDate() + 1); } html += '