/* * jQuery UI Datepicker * * Copyright (c) 2006, 2007, 2008 Marc Grabanski * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * http://docs.jquery.com/UI/Datepicker * * Depends: * ui.core.js * * Marc Grabanski (m@marcgrabanski.com) and Keith Wood (kbwood@virginbroadband.com.au). */ (function($) { // hide the namespace /* 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. */ 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.regional = []; // Available regional settings, indexed by language code this.regional[''] = { // Default regional settings clearText: 'Clear', // Display text for clear link clearStatus: 'Erase the current date', // Status text for clear link closeText: 'Close', // Display text for close link closeStatus: 'Close without change', // Status text for close link prevText: '<Prev', // Display text for previous month link prevStatus: 'Show the previous month', // Status text for previous month link nextText: 'Next>', // Display text for next month link nextStatus: 'Show the next month', // Status text for next month link currentText: 'Today', // Display text for current month link currentStatus: 'Show the current month', // Status text for current month link monthNames: ['January','February','March','April','May','June', 'July','August','September','October','November','December'], // Names of months for drop-down and formatting monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting monthStatus: 'Show a different month', // Status text for selecting a month yearStatus: 'Show a different year', // Status text for selecting a year weekHeader: 'Wk', // Header for the week of the year column weekStatus: 'Week of the year', // Status text for the week of the year column dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday dayStatus: 'Set DD as first week day', // Status text for the day of the week selection dateStatus: 'Select DD, M d', // Status text for the date selection dateFormat: 'mm/dd/yy', // See format options on parseDate firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... initStatus: 'Select a date', // Initial Status text on opening isRTL: false // True if right-to-left language, false if left-to-right }; this._defaults = { // Global defaults for all the date picker instances showOn: 'focus', // 'focus' for popup on focus, // 'button' for trigger button, or 'both' for either showAnim: 'show', // Name of jQuery animation for popup defaultDate: null, // Used when field is blank: actual date, // +/-number for offset from today, null for today appendText: '', // Display text following the input box, e.g. showing the format buttonText: '...', // Text for trigger button buttonImage: '', // URL for trigger button image buttonImageOnly: false, // True if the image appears alone, false if it appears on a button closeAtTop: true, // True to have the clear/close at the top, // false to have them at the bottom mandatory: false, // True to hide the Clear link, false to include it hideIfNoPrevNext: false, // True to hide next/previous month links // if not applicable, false to just disable them changeMonth: true, // True if month can be selected directly, false if only prev/next changeYear: true, // True if year can be selected directly, false if only prev/next yearRange: '-10:+10', // Range of years to display in drop-down, // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn) changeFirstDay: true, // True to click on day name to change, false to remain as set showOtherMonths: false, // True to show dates in other months, false to leave blank showWeeks: false, // True to show week of the year, false to omit calculateWeek: this.iso8601Week, // How to calculate the week of the year, // takes a Date and returns the number of the week for it shortYearCutoff: '+10', // Short year values < this are in the current century, // > this are in the previous century, // string value starting with '+' for current year + value showStatus: false, // True to show status bar at bottom, false to not show it statusForDate: this.dateStatus, // Function to provide status text for a date - // takes date and instance as parameters, returns display text minDate: null, // The earliest selectable date, or null for no limit maxDate: null, // The latest selectable date, or null for no limit speed: 'normal', // Speed of display/closure beforeShowDay: null, // Function that takes a date and returns an array with // [0] = true if selectable, false if not, // [1] = custom CSS class name(s) or '', e.g. $.datepicker.noWeekends beforeShow: null, // Function that takes an input field and // returns a set of custom settings for the date picker onSelect: null, // Define a callback function when a date is selected onClose: null, // Define a callback function when the datepicker is closed numberOfMonths: 1, // Number of months to show at a time stepMonths: 1, // Number of months to step back/forward rangeSelect: false, // Allows for selecting a date range on one date picker rangeSeparator: ' - ' // Text between two dates in a range }; $.extend(this._defaults, this.regional['']); this._datepickerDiv = $('
'); } $.extend(Datepicker.prototype, { /* Class name added to elements to indicate already configured with a date picker. */ markerClassName: 'hasDatepicker', /* Debug logging (if enabled). */ log: function () { if (this.debug) 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 */ setDefaults: function(settings) { extendRemove(this._defaults, settings || {}); return this; }, /* Attach the date picker to a jQuery selection. @param target element - the target input field or division or span @param settings object - the new settings to use for this date picker instance (anonymous) */ _attachDatepicker: function(target, settings) { // check for settings on the control itself - in namespace 'date:' var inlineSettings = null; for (attrName in this._defaults) { var attrValue = target.getAttribute('date:' + attrName); if (attrValue) { inlineSettings = inlineSettings || {}; try { inlineSettings[attrName] = eval(attrValue); } catch (err) { inlineSettings[attrName] = attrValue; } } } var nodeName = target.nodeName.toLowerCase(); var instSettings = (inlineSettings ? $.extend(settings || {}, inlineSettings || {}) : settings); 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); this._inlineDatepicker(target, inst); } }, /* 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; target._calId = null; var $target = $(target); if (nodeName == 'input') { $target.siblings('.ui-datepicker-append').replaceWith('').end() .siblings('.ui-datepicker-trigger').replaceWith('').end() .removeClass(this.markerClassName) .unbind('focus', this._showDatepicker) .unbind('keydown', this._doKeyDown) .unbind('keypress', this._doKeyPress); var wrapper = $target.parents('.ui-datepicker-wrap'); if (wrapper) wrapper.replaceWith(wrapper.html()); } 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.ui-datepicker-trigger').each(function() { this.disabled = false; }).end() .siblings('img.ui-datepicker-trigger').css({opacity: '1.0', cursor: ''}); this._disabledInputs = $.map(this._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry }, /* Disable the date picker to a jQuery selection. @param target element - the target input field or division or span */ _disableDatepicker: function(target) { target.disabled = true; $(target).siblings('button.ui-datepicker-trigger').each(function() { this.disabled = true; }).end() .siblings('img.ui-datepicker-trigger').css({opacity: '0.5', cursor: 'default'}); this._disabledInputs = $.map($.datepicker._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry this._disabledInputs[$.datepicker._disabledInputs.length] = target; }, /* Is the first field in a jQuery collection disabled as a datepicker? @param target element - the target input field or division or span @return boolean - true if disabled, false if enabled */ _isDisabledDatepicker: function(target) { if (!target) return false; for (var i = 0; i < this._disabledInputs.length; i++) { if (this._disabledInputs[i] == target) return true; } return false; }, /* 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 value any - the new value for the setting (omit if above is an object) */ _changeDatepicker: function(target, name, value) { var settings = name || {}; if (typeof name == 'string') { settings = {}; settings[name] = value; } if (inst = this._getInst(target._calId)) { extendRemove(inst._settings, settings); this._updateDatepicker(inst); } }, /* Set the dates for a jQuery selection. @param target element - the target input field or division or span @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); this._updateDatepicker(inst); } }, /* Get the date(s) for the first entry in a jQuery selection. @param target element - the target input field or division or span @return Date - the current date or Date[2] - the current dates for a range */ _getDateDatepicker: function(target) { var inst = this._getInst(target._calId); if (inst) { inst._setDateFromField($(target)); } return (inst ? inst._getDate() : null); }, /* Handle keystrokes. */ _doKeyDown: function(e) { var inst = $.datepicker._getInst(this._calId); 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]); return false; // don't submit the form break; // select the value on enter case 27: $.datepicker._hideDatepicker(null, inst._get('speed')); break; // hide on escape case 33: $.datepicker._adjustDate(inst, (e.ctrlKey ? -1 : -inst._get('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')); break; // next month/year on page down/+ ctrl case 35: if (e.ctrlKey) $.datepicker._clearDate(inst); break; // clear on ctrl+end case 36: if (e.ctrlKey) $.datepicker._gotoToday(inst); break; // current on ctrl+home case 37: if (e.ctrlKey) $.datepicker._adjustDate(inst, -1, 'D'); break; // -1 day on ctrl+left case 38: if (e.ctrlKey) $.datepicker._adjustDate(inst, -7, 'D'); break; // -1 week on ctrl+up case 39: if (e.ctrlKey) $.datepicker._adjustDate(inst, +1, 'D'); break; // +1 day on ctrl+right case 40: if (e.ctrlKey) $.datepicker._adjustDate(inst, +7, 'D'); break; // +1 week on ctrl+down } else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home $.datepicker._showDatepicker(this); }, /* Filter entered characters - based on date format. */ _doKeyPress: function(e) { var inst = $.datepicker._getInst(this._calId); var chars = $.datepicker._possibleChars(inst._get('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.is('.' + this.markerClassName)) return; var appendText = inst._get('appendText'); var isRTL = inst._get('isRTL'); if (appendText) { if (isRTL) input.before('' + appendText); else input.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('ui-datepicker-trigger').attr({ src: buttonImage, alt: buttonText, title: buttonText }) : $('