diff options
Diffstat (limited to 'libs/jquery/original lib/jquery-calendar.js')
-rw-r--r-- | libs/jquery/original lib/jquery-calendar.js | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/libs/jquery/original lib/jquery-calendar.js b/libs/jquery/original lib/jquery-calendar.js new file mode 100644 index 0000000000..e7100e2d93 --- /dev/null +++ b/libs/jquery/original lib/jquery-calendar.js @@ -0,0 +1,814 @@ +/* jQuery Calendar v2.7 + Written by Marc Grabanski (m@marcgrabanski.com) and enhanced by Keith Wood (kbwood@iprimus.com.au). + + Copyright (c) 2007 Marc Grabanski (http://marcgrabanski.com/code/jquery-calendar) + Dual licensed under the GPL (http://www.gnu.org/licenses/gpl-3.0.txt) and + CC (http://creativecommons.org/licenses/by/3.0/) licenses. "Share or Remix it but please Attribute the authors." + Date: 09-03-2007 */ + +/* PopUp Calendar manager. + Use the singleton instance of this class, popUpCal, to interact with the calendar. + Settings for (groups of) calendars are maintained in an instance object + (PopUpCalInstance), allowing multiple different settings on the same page. */ +function PopUpCal() { + this._nextId = 0; // Next ID for a calendar instance + this._inst = []; // List of instances indexed by ID + this._curInst = null; // The current instance in use + this._disabledInputs = []; // List of calendar inputs that have been disabled + this._popUpShowing = false; // True if the popup calendar 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 + closeText: 'Close', // Display text for close link + prevText: '<Prev', // Display text for previous month link + nextText: 'Next>', // Display text for next month link + currentText: 'Today', // Display text for current month link + dayNames: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Names of days starting at Sunday + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], // Names of months + dateFormat: 'DMY/' // First three are day, month, year in the required order, + // fourth (optional) is the separator, e.g. US would be 'MDY/', ISO would be 'YMD-' + }; + this._defaults = { // Global defaults for all the calendar instances + autoPopUp: 'focus', // 'focus' for popup on focus, + // 'button' for trigger button, or 'both' for either + 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 + 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) + firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... + 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 + minDate: null, // The earliest selectable date, or null for no limit + maxDate: null, // The latest selectable date, or null for no limit + speed: 'medium', // Speed of display/closure + customDate: 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. popUpCal.noWeekends + fieldSettings: null, // Function that takes an input field and + // returns a set of custom settings for the calendar + onSelect: null // Define a callback function when a date is selected + }; + $.extend(this._defaults, this.regional['']); + this._calendarDiv = $('<div id="calendar_div"></div>'); + $(document.body).append(this._calendarDiv); + $(document.body).mousedown(this._checkExternalClick); +} + +$.extend(PopUpCal.prototype, { + /* Register a new calendar instance - with custom settings. */ + _register: function(inst) { + var id = this._nextId++; + this._inst[id] = inst; + return id; + }, + + /* Retrieve a particular calendar instance based on its ID. */ + _getInst: function(id) { + return this._inst[id] || id; + }, + + /* Override the default settings for all instances of the calendar. + @param settings object - the new settings to use as defaults (anonymous object) + @return void */ + setDefaults: function(settings) { + $.extend(this._defaults, settings || {}); + }, + + /* Handle keystrokes. */ + _doKeyDown: function(e) { + var inst = popUpCal._getInst(this._calId); + if (popUpCal._popUpShowing) { + switch (e.keyCode) { + case 9: popUpCal.hideCalendar(inst, ''); + break; // hide on tab out + case 13: popUpCal._selectDate(inst); + break; // select the value on enter + case 27: popUpCal.hideCalendar(inst, inst._get('speed')); + break; // hide on escape + case 33: popUpCal._adjustDate(inst, -1, (e.ctrlKey ? 'Y' : 'M')); + break; // previous month/year on page up/+ ctrl + case 34: popUpCal._adjustDate(inst, +1, (e.ctrlKey ? 'Y' : 'M')); + break; // next month/year on page down/+ ctrl + case 35: if (e.ctrlKey) popUpCal._clearDate(inst); + break; // clear on ctrl+end + case 36: if (e.ctrlKey) popUpCal._gotoToday(inst); + break; // current on ctrl+home + case 37: if (e.ctrlKey) popUpCal._adjustDate(inst, -1, 'D'); + break; // -1 day on ctrl+left + case 38: if (e.ctrlKey) popUpCal._adjustDate(inst, -7, 'D'); + break; // -1 week on ctrl+up + case 39: if (e.ctrlKey) popUpCal._adjustDate(inst, +1, 'D'); + break; // +1 day on ctrl+right + case 40: if (e.ctrlKey) popUpCal._adjustDate(inst, +7, 'D'); + break; // +1 week on ctrl+down + } + } + else if (e.keyCode == 36 && e.ctrlKey) { // display the calendar on ctrl+home + popUpCal.showFor(this); + } + }, + + /* Filter entered characters. */ + _doKeyPress: function(e) { + var inst = popUpCal._getInst(this._calId); + var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode); + return (chr < ' ' || chr == inst._get('dateFormat').charAt(3) || + (chr >= '0' && chr <= '9')); // only allow numbers and separator + }, + + /* Attach the calendar to an input field. */ + _connectCalendar: function(target, inst) { + var $input = $(target); + var appendText = inst._get('appendText'); + if (appendText) { + $input.after('<span class="calendar_append">' + appendText + '</span>'); + } + var autoPopUp = inst._get('autoPopUp'); + if (autoPopUp == 'focus' || autoPopUp == 'both') { // pop-up calendar when in the marked field + $input.focus(this.showFor); + } + if (autoPopUp == 'button' || autoPopUp == 'both') { // pop-up calendar when button clicked + var buttonText = inst._get('buttonText'); + var buttonImage = inst._get('buttonImage'); + var buttonImageOnly = inst._get('buttonImageOnly'); + var trigger = $(buttonImageOnly ? '<img class="calendar_trigger" src="' + + buttonImage + '" alt="' + buttonText + '" title="' + buttonText + '"/>' : + '<button type="button" class="calendar_trigger">' + (buttonImage != '' ? + '<img src="' + buttonImage + '" alt="' + buttonText + '" title="' + buttonText + '"/>' : + buttonText) + '</button>'); + $input.wrap('<span class="calendar_wrap"></span>').after(trigger); + trigger.click(this.showFor); + } + $input.keydown(this._doKeyDown).keypress(this._doKeyPress); + $input[0]._calId = inst._id; + }, + + /* Attach an inline calendar to a div. */ + _inlineCalendar: function(target, inst, defaultDate) { + $(target).append(inst._calendarDiv); + target._calId = inst._id; + var date = defaultDate; + inst._selectedDay = date.getDate(); + inst._selectedMonth = date.getMonth(); + inst._selectedYear = date.getFullYear(); + popUpCal._adjustDate(inst); + }, + + /* Pop-up the calendar in a "dialog" box. + @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 calendar instance's settings (anonymous object) + @param pos int[2] - coordinates for the dialog's position within the screen + leave empty for default (screen centre) + @return void */ + dialogCalendar: function(dateText, onSelect, settings, pos) { + var inst = this._dialogInst; // internal instance + if (!inst) { + inst = this._dialogInst = new PopUpCalInstance({}, false); + this._dialogInput = $('<input type="text" size="1" style="position: absolute; top: -100px;"/>'); + this._dialogInput.keydown(this._doKeyDown); + $('body').append(this._dialogInput); + this._dialogInput[0]._calId = inst._id; + } + $.extend(inst._settings, settings || {}); + this._dialogInput.val(dateText); + + /* Cross Browser Positioning */ + if (self.innerHeight) { // all except Explorer + windowWidth = self.innerWidth; + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowWidth = document.documentElement.clientWidth; + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowWidth = document.body.clientWidth; + windowHeight = document.body.clientHeight; + } + this._pos = pos || // should use actual width/height below + [(windowWidth / 2) - 100, (windowHeight / 2) - 100]; + + // 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._calendarDiv.addClass('calendar_dialog'); + this.showFor(this._dialogInput[0]); + if ($.blockUI) { + $.blockUI(this._calendarDiv); + } + }, + + /* Enable the input field(s) for entry. + @param inputs element/object - single input field or jQuery collection of input fields + @return void */ + enableFor: function(inputs) { + inputs = (inputs.jquery ? inputs : $(inputs)); + inputs.each(function() { + this.disabled = false; + $('../button.calendar_trigger', this).each(function() { this.disabled = false; }); + $('../img.calendar_trigger', +this).css({opacity:'1.0',cursor:''}); + var $this = this; + popUpCal._disabledInputs = $.map(popUpCal._disabledInputs, + function(value) { return (value == $this ? null : value); }); // delete entry + }); + }, + + /* Disable the input field(s) from entry. + @param inputs element/object - single input field or jQuery collection of input fields + @return void */ + disableFor: function(inputs) { + inputs = (inputs.jquery ? inputs : $(inputs)); + inputs.each(function() { + this.disabled = true; + $('../button.calendar_trigger', this).each(function() { this.disabled = true; }); + $('../img.calendar_trigger', this).css({opacity:'0.5',cursor:'default'}); + var $this = this; + popUpCal._disabledInputs = $.map(popUpCal._disabledInputs, + function(value) { return (value == $this ? null : value); }); // delete entry + popUpCal._disabledInputs[popUpCal._disabledInputs.length] = this; + }); + }, + + /* Update the settings for a calendar attached to an input field or division. + @param control element - the input field or div/span attached to the calendar + @param settings object - the new settings to update + @return void */ + reconfigureFor: function(control, settings) { + var inst = this._getInst(control._calId); + if (inst) { + $.extend(inst._settings, settings || {}); + this._updateCalendar(inst); + } + }, + + /* Set the date for a calendar attached to an input field or division. + @param control element - the input field or div/span attached to the calendar + @param date Date - the new date + @return void */ + setDateFor: function(control, date) { + var inst = this._getInst(control._calId); + if (inst) { + inst._setDate(date); + } + }, + + /* Retrieve the date for a calendar attached to an input field or division. + @param control element - the input field or div/span attached to the calendar + @return Date - the current date */ + getDateFor: function(control) { + var inst = this._getInst(control._calId); + return (inst ? inst._getDate() : null); + }, + + /* Pop-up the calendar for a given input field. + @param target element - the input field attached to the calendar + @return void */ + showFor: function(target) { + var input = (target.nodeName && target.nodeName.toLowerCase() == 'input' ? target : this); + if (input.nodeName.toLowerCase() != 'input') { // find from button/image trigger + input = $('input', input.parentNode)[0]; + } + if (popUpCal._lastInput == input) { // already here + return; + } + for (var i = 0; i < popUpCal._disabledInputs.length; i++) { // check not disabled + if (popUpCal._disabledInputs[i] == input) { + return; + } + } + var inst = popUpCal._getInst(input._calId); + popUpCal.hideCalendar(inst, ''); + popUpCal._lastInput = input; + inst._setDateFromField(input); + if (popUpCal._inDialog) { // hide cursor + input.value = ''; + } + if (!popUpCal._pos) { // position below input + popUpCal._pos = popUpCal._findPos(input); + popUpCal._pos[1] += input.offsetHeight; + } + inst._calendarDiv.css('position', (popUpCal._inDialog && $.blockUI ? 'static' : 'absolute')). + css('left', popUpCal._pos[0] + 'px').css('top', popUpCal._pos[1] + 'px'); + popUpCal._pos = null; + var fieldSettings = inst._get('fieldSettings'); + $.extend(inst._settings, (fieldSettings ? fieldSettings(input) : {})); + popUpCal._showCalendar(inst); + }, + + /* Construct and display the calendar. */ + _showCalendar: function(id) { + var inst = this._getInst(id); + popUpCal._updateCalendar(inst); + if (!inst._inline) { + var speed = inst._get('speed'); + inst._calendarDiv.show(speed, function() { + popUpCal._popUpShowing = true; + popUpCal._afterShow(inst); + }); + if (speed == '') { + popUpCal._popUpShowing = true; + popUpCal._afterShow(inst); + } + if (inst._input[0].type != 'hidden') { + inst._input[0].focus(); + } + this._curInst = inst; + } + }, + + /* Generate the calendar content. */ + _updateCalendar: function(inst) { + inst._calendarDiv.empty().append(inst._generateCalendar()); + if (inst._input && inst._input != 'hidden') { + inst._input[0].focus(); + } + }, + + /* Tidy up after displaying the calendar. */ + _afterShow: function(inst) { + if ($.browser.msie) { // fix IE < 7 select problems + $('#calendar_cover').css({width: inst._calendarDiv[0].offsetWidth + 4, + height: inst._calendarDiv[0].offsetHeight + 4}); + } + /*// re-position on screen if necessary + var calDiv = inst._calendarDiv[0]; + var pos = popUpCal._findPos(inst._input[0]); + if ((calDiv.offsetLeft + calDiv.offsetWidth) > + (document.body.clientWidth + document.body.scrollLeft)) { + inst._calendarDiv.css('left', (pos[0] + inst._input[0].offsetWidth - calDiv.offsetWidth) + 'px'); + } + if ((calDiv.offsetTop + calDiv.offsetHeight) > + (document.body.clientHeight + document.body.scrollTop)) { + inst._calendarDiv.css('top', (pos[1] - calDiv.offsetHeight) + 'px'); + }*/ + }, + + /* Hide the calendar from view. + @param id string/object - the ID of the current calendar instance, + or the instance itself + @param speed string - the speed at which to close the calendar + @return void */ + hideCalendar: function(id, speed) { + var inst = this._getInst(id); + if (popUpCal._popUpShowing) { + speed = (speed != null ? speed : inst._get('speed')); + inst._calendarDiv.hide(speed, function() { + popUpCal._tidyDialog(inst); + }); + if (speed == '') { + popUpCal._tidyDialog(inst); + } + popUpCal._popUpShowing = false; + popUpCal._lastInput = null; + inst._settings.prompt = null; + if (popUpCal._inDialog) { + popUpCal._dialogInput.css('position', 'absolute'). + css('left', '0px').css('top', '-100px'); + if ($.blockUI) { + $.unblockUI(); + $('body').append(this._calendarDiv); + } + } + popUpCal._inDialog = false; + } + popUpCal._curInst = null; + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function(inst) { + inst._calendarDiv.removeClass('calendar_dialog'); + $('.calendar_prompt', inst._calendarDiv).remove(); + }, + + /* Close calendar if clicked elsewhere. */ + _checkExternalClick: function(event) { + if (!popUpCal._curInst) { + return; + } + var target = $(event.target); + if( (target.parents("#calendar_div").length == 0) + && (target.attr('class') != 'calendar_trigger') + && popUpCal._popUpShowing + && !(popUpCal._inDialog && $.blockUI) ) + { + popUpCal.hideCalendar(popUpCal._curInst, ''); + } + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(id, offset, period) { + var inst = this._getInst(id); + inst._adjustDate(offset, period); + this._updateCalendar(inst); + }, + + /* Action for current link. */ + _gotoToday: function(id) { + var date = new Date(); + var inst = this._getInst(id); + inst._selectedDay = date.getDate(); + inst._selectedMonth = date.getMonth(); + inst._selectedYear = date.getFullYear(); + this._adjustDate(inst); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function(id, select, period) { + var inst = this._getInst(id); + inst._selectingMonthYear = false; + inst[period == 'M' ? '_selectedMonth' : '_selectedYear'] = + select.options[select.selectedIndex].value - 0; + this._adjustDate(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(); + } + inst._selectingMonthYear = !inst._selectingMonthYear; + }, + + /* Action for changing the first week day. */ + _changeFirstDay: function(id, a) { + var inst = this._getInst(id); + var dayNames = inst._get('dayNames'); + var value = a.firstChild.nodeValue; + for (var i = 0; i < 7; i++) { + if (dayNames[i] == value) { + inst._settings.firstDay = i; + break; + } + } + this._updateCalendar(inst); + }, + + /* Action for selecting a day. */ + _selectDay: function(id, td) { + var inst = this._getInst(id); + inst._selectedDay = $("a", td).html(); + this._selectDate(id); + }, + + /* Erase the input field and hide the calendar. */ + _clearDate: function(id) { + this._selectDate(id, ''); + }, + + /* 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._input) { + inst._input.val(dateStr); + } + var onSelect = inst._get('onSelect'); + if (onSelect) { + onSelect(dateStr); // trigger custom callback + } + else { + inst._input.trigger('change'); // fire the change event + } + if (inst._inline) { + this._updateCalendar(inst); + } + else { + this.hideCalendar(inst, inst._get('speed')); + } + }, + + /* Set as customDate function to prevent selection of weekends. + @param date Date - the date to customise + @return [boolean, string] - is this date selectable?, what is its CSS class? */ + noWeekends: function(date) { + var day = date.getDay(); + return [(day > 0 && day < 6), '']; + }, + + /* Find an object's position on the screen. */ + _findPos: function(obj) { + if (obj.type == 'hidden') { + obj = obj.nextSibling; + } + var curleft = curtop = 0; + if (obj.offsetParent) { + curleft = obj.offsetLeft; + curtop = obj.offsetTop; + while (obj = obj.offsetParent) { + var origcurleft = curleft; + curleft += obj.offsetLeft; + if (curleft < 0) { + curleft = origcurleft; + } + curtop += obj.offsetTop; + } + } + return [curleft,curtop]; + } +}); + +/* Individualised settings for calendars applied to one or more related inputs. + Instances are managed and manipulated through the PopUpCal manager. */ +function PopUpCalInstance(settings, inline, defaultDate) { + this._id = popUpCal._register(this); + this._selectedDay = 0; + this._selectedMonth = 0; // 0-11 + this._selectedYear = 0; // 4-digit year + this._input = null; // The attached input field + this._inline = inline; // True if showing inline, false if used in a popup + this._calendarDiv = (!inline ? popUpCal._calendarDiv : + $('<div id="calendar_div_' + this._id + '" class="calendar_inline"></div>')); + if (inline) { + var date = defaultDate; + this._currentDate = defaultDate; + this._currentDay = date.getDate(); + this._currentMonth = date.getMonth(); + this._currentYear = date.getFullYear(); + } + // customise the calendar object - uses manager defaults if not overridden + this._settings = $.extend({}, settings || {}); // clone +} + +$.extend(PopUpCalInstance.prototype, { + /* Get a setting value, defaulting if necessary. */ + _get: function(name) { + return (this._settings[name] != null ? this._settings[name] : popUpCal._defaults[name]); + }, + + /* Parse existing date and initialise calendar. */ + _setDateFromField: function(input) { + this._input = $(input); + var dateFormat = this._get('dateFormat'); + var currentDate = this._input.val().split(dateFormat.charAt(3)); + if (currentDate.length == 3) { + this._currentDay = parseInt(currentDate[dateFormat.indexOf('D')], 10); + this._currentMonth = parseInt(currentDate[dateFormat.indexOf('M')], 10) - 1; + this._currentYear = parseInt(currentDate[dateFormat.indexOf('Y')], 10); + } + else { + var date = new Date(); + this._currentDay = date.getDate(); + this._currentMonth = date.getMonth(); + this._currentYear = date.getFullYear(); + } + this._selectedDay = this._currentDay; + this._selectedMonth = this._currentMonth; + this._selectedYear = this._currentYear; + this._adjustDate(); + }, + + /* Set the date directly. */ + _setDate: function(date) { + this._selectedDay = this._currentDay = date.getDate(); + this._selectedMonth = this._currentMonth = date.getMonth(); + this._selectedYear = this._currentYear = date.getFullYear(); + this._adjustDate(); + }, + + /* Retrieve the date directly. */ + _getDate: function() { + return new Date(this._currentYear, this._currentMonth, this._currentDay); + }, + + /* Generate the HTML for the current state of the calendar. */ + _generateCalendar: function() { + var today = this._currentDate; + today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time + // build the calendar HTML + var controls = '<div class="calendar_control">' + + '<a class="calendar_clear" onclick="popUpCal._clearDate(' + this._id + ');">' + + this._get('clearText') + '</a>' + + '<a class="calendar_close" onclick="popUpCal.hideCalendar(' + this._id + ');">' + + this._get('closeText') + '</a></div>'; + var prompt = this._get('prompt'); + var closeAtTop = this._get('closeAtTop'); + var hideIfNoPrevNext = this._get('hideIfNoPrevNext'); + // controls and links + var html = (prompt ? '<div class="calendar_prompt">' + prompt + '</div>' : '') + + (closeAtTop && !this._inline ? controls : '') + '<div class="calendar_links">' + + (this._canAdjustMonth(-1) ? '<a class="calendar_prev" ' + + 'onclick="popUpCal._adjustDate(' + this._id + ', -1, \'M\');">' + this._get('prevText') + '</a>' : + (hideIfNoPrevNext ? '' : '<label class="calendar_prev">' + this._get('prevText') + '</label>')) + + (this._isInRange(today) ? '<a class="calendar_current" ' + + 'onclick="popUpCal._gotoToday(' + this._id + ');">' + this._get('currentText') + '</a>' : '') + + (this._canAdjustMonth(+1) ? '<a class="calendar_next" ' + + 'onclick="popUpCal._adjustDate(' + this._id + ', +1, \'M\');">' + this._get('nextText') + '</a>' : + (hideIfNoPrevNext ? '' : '<label class="calendar_next">' + this._get('nextText') + '</label>')) + + '</div><div class="calendar_header">'; + var minDate = this._get('minDate'); + var maxDate = this._get('maxDate'); + // month selection + var monthNames = this._get('monthNames'); + if (!this._get('changeMonth')) { + html += monthNames[this._selectedMonth] + ' '; + } + else { + var inMinYear = (minDate && minDate.getFullYear() == this._selectedYear); + var inMaxYear = (maxDate && maxDate.getFullYear() == this._selectedYear); + html += '<select class="calendar_newMonth" ' + + 'onchange="popUpCal._selectMonthYear(' + this._id + ', this, \'M\');" ' + + 'onclick="popUpCal._clickMonthYear(' + this._id + ');">'; + for (var month = 0; month < 12; month++) { + if ((!inMinYear || month >= minDate.getMonth()) && + (!inMaxYear || month <= maxDate.getMonth())) { + html += '<option value="' + month + '"' + + (month == this._selectedMonth ? ' selected="selected"' : '') + + '>' + monthNames[month] + '</option>'; + } + } + html += '</select>'; + } + // year selection + if (!this._get('changeYear')) { + html += this._selectedYear; + } + else { + // determine range of years to display + var years = this._get('yearRange').split(':'); + var year = 0; + var endYear = 0; + if (years.length != 2) { + year = this._selectedYear - 10; + endYear = this._selectedYear + 10; + } + else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') { + year = this._selectedYear + parseInt(years[0], 10); + endYear = this._selectedYear + parseInt(years[1], 10); + } + else { + year = parseInt(years[0], 10); + endYear = parseInt(years[1], 10); + } + year = (minDate ? Math.max(year, minDate.getFullYear()) : year); + endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); + html += '<select class="calendar_newYear" onchange="popUpCal._selectMonthYear(' + + this._id + ', this, \'Y\');" ' + 'onclick="popUpCal._clickMonthYear(' + + this._id + ');">'; + for (; year <= endYear; year++) { + html += '<option value="' + year + '"' + + (year == this._selectedYear ? ' selected="selected"' : '') + + '>' + year + '</option>'; + } + html += '</select>'; + } + html += '</div><table class="calendar" cellpadding="0" cellspacing="0"><thead>' + + '<tr class="calendar_titleRow">'; + var firstDay = this._get('firstDay'); + var changeFirstDay = this._get('changeFirstDay'); + var dayNames = this._get('dayNames'); + for (var dow = 0; dow < 7; dow++) { // days of the week + html += '<td>' + (!changeFirstDay ? '' : '<a onclick="popUpCal._changeFirstDay(' + + this._id + ', this);">') + dayNames[(dow + firstDay) % 7] + + (changeFirstDay ? '</a>' : '') + '</td>'; + } + html += '</tr></thead><tbody>'; + var daysInMonth = this._getDaysInMonth(this._selectedYear, this._selectedMonth); + this._selectedDay = Math.min(this._selectedDay, daysInMonth); + var leadDays = (this._getFirstDayOfMonth(this._selectedYear, this._selectedMonth) - firstDay + 7) % 7; + var currentDate = new Date(this._currentYear, this._currentMonth, this._currentDay); + var selectedDate = new Date(this._selectedYear, this._selectedMonth, this._selectedDay); + var printDate = new Date(this._selectedYear, this._selectedMonth, 1 - leadDays); + var numRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate + var customDate = this._get('customDate'); + var showOtherMonths = this._get('showOtherMonths'); + for (var row = 0; row < numRows; row++) { // create calendar rows + html += '<tr class="calendar_daysRow">'; + for (var dow = 0; dow < 7; dow++) { // create calendar days + var customSettings = (customDate ? customDate(printDate) : [true, '']); + var otherMonth = (printDate.getMonth() != this._selectedMonth); + var unselectable = otherMonth || !customSettings[0] || + (minDate && printDate < minDate) || (maxDate && printDate > maxDate); + html += '<td class="calendar_daysCell' + + ((dow + firstDay + 6) % 7 >= 5 ? ' calendar_weekEndCell' : '') + // highlight weekends + (otherMonth ? ' calendar_otherMonth' : '') + // highlight days from other months + //(printDate.getTime() == selectedDate.getTime() ? ' calendar_daysCellOver' : '') + // highlight selected day + (unselectable ? ' calendar_unselectable' : '') + // highlight unselectable days + (!otherMonth || showOtherMonths ? ' ' + customSettings[1] : '') + // highlight custom dates + (printDate.getTime() == currentDate.getTime() ? ' calendar_currentDay' : // highlight current day + (printDate.getTime() == today.getTime() ? ' calendar_today' : '')) + '"' + // highlight today (if different) + (unselectable ? '' : ' onmouseover="$(this).addClass(\'calendar_daysCellOver\');"' + + ' onmouseout="$(this).removeClass(\'calendar_daysCellOver\');"' + + ' onclick="popUpCal._selectDay(' + this._id + ', this);"') + '>' + // actions + (otherMonth ? (showOtherMonths ? printDate.getDate() : ' ') : // display for other months + (unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month + printDate.setDate(printDate.getDate() + 1); + } + html += '</tr>'; + } + html += '</tbody></table>' + (!closeAtTop && !this._inline ? controls : '') + + '<div style="clear: both;"></div>' + (!$.browser.msie ? '' : + '<!--[if lte IE 6.5]><iframe src="javascript:false;" class="calendar_cover"></iframe><![endif]-->'); + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(offset, period) { + var date = new Date(this._selectedYear + (period == 'Y' ? offset : 0), + this._selectedMonth + (period == 'M' ? offset : 0), + this._selectedDay + (period == 'D' ? offset : 0)); + // ensure it is within the bounds set + var minDate = this._get('minDate'); + var maxDate = this._get('maxDate'); + date = (minDate && date < minDate ? minDate : date); + date = (maxDate && date > maxDate ? maxDate : date); + this._selectedDay = date.getDate(); + this._selectedMonth = date.getMonth(); + this._selectedYear = date.getFullYear(); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function(year, month) { + return 32 - new Date(year, month, 32).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function(year, month) { + return new Date(year, month, 1).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function(offset) { + var date = new Date(this._selectedYear, this._selectedMonth + offset, 1); + if (offset < 0) { + date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + } + return this._isInRange(date); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function(date) { + var minDate = this._get('minDate'); + var maxDate = this._get('maxDate'); + return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate)); + }, + + /* Format the given date for display. */ + _formatDate: function() { + var day = this._currentDay = this._selectedDay; + var month = this._currentMonth = this._selectedMonth; + var year = this._currentYear = this._selectedYear; + month++; // adjust javascript month + var dateFormat = this._get('dateFormat'); + var dateString = ''; + for (var i = 0; i < 3; i++) { + dateString += dateFormat.charAt(3) + + (dateFormat.charAt(i) == 'D' ? (day < 10 ? '0' : '') + day : + (dateFormat.charAt(i) == 'M' ? (month < 10 ? '0' : '') + month : + (dateFormat.charAt(i) == 'Y' ? year : '?'))); + } + return dateString.substring(dateFormat.charAt(3) ? 1 : 0); + } +}); + +/* Attach the calendar to a jQuery selection. + @param settings object - the new settings to use for this calendar instance (anonymous) + @return jQuery object - for chaining further calls */ +$.fn.calendar = function(settings, defaultDate) { + return this.each(function() { + // check for settings on the control itself - in namespace 'cal:' + var inlineSettings = null; + for (attrName in popUpCal._defaults) { + var attrValue = this.getAttribute('cal:' + attrName); + if (attrValue) { + inlineSettings = inlineSettings || {}; + try { + inlineSettings[attrName] = eval(attrValue); + } + catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + var nodeName = this.nodeName.toLowerCase(); + if (nodeName == 'input') { + var instSettings = (inlineSettings ? $.extend($.extend({}, settings || {}), + inlineSettings || {}) : settings); // clone and customise + var inst = (inst && !inlineSettings ? inst : + new PopUpCalInstance(instSettings, false, defaultDate)); + popUpCal._connectCalendar(this, inst); + } + else if (nodeName == 'div' || nodeName == 'span') { + var instSettings = $.extend($.extend({}, settings || {}), + inlineSettings || {}); // clone and customise + var inst = new PopUpCalInstance(instSettings, true, defaultDate); + popUpCal._inlineCalendar(this, inst, defaultDate); + } + }); +}; + +/* Initialise the calendar. */ +$(document).ready(function() { + popUpCal = new PopUpCal(); // singleton instance +}); |