

(function($){
    
$.fn.extend({
renderCalendar  :   function(s)
{
var dc = function(a)
{
return document.createElement(a);
};

s = $.extend({}, $.fn.datePicker.defaults, s);

if (s.showHeader != $.dpConst.SHOW_HEADER_NONE) {
var headRow = $(dc('tr'));
for (var i=Date.firstDayOfWeek; i<Date.firstDayOfWeek+7; i++) {
var weekday = i%7;
var day = Date.dayNames[weekday];
headRow.append(
jQuery(dc('th')).attr({'scope':'col', 'abbr':day, 'title':day, 'class':(weekday == 0 || weekday == 6 ? 'weekend' : 'weekday')}).html(s.showHeader == $.dpConst.SHOW_HEADER_SHORT ? day.substr(0, 1) : day)
);
}
};

var calendarTable = $(dc('table'))
.attr(
{
'cellspacing':2
}
)
.addClass('jCalendar')
.append(
(s.showHeader != $.dpConst.SHOW_HEADER_NONE ? 
$(dc('thead'))
.append(headRow)
:
dc('thead')
)
);
var tbody = $(dc('tbody'));

var today = (new Date()).zeroTime();
today.setHours(12);

var month = s.month == undefined ? today.getMonth() : s.month;
var year = s.year || today.getFullYear();

var currentDate = (new Date(year, month, 1, 12, 0, 0));


var firstDayOffset = Date.firstDayOfWeek - currentDate.getDay() + 1;
if (firstDayOffset > 1) firstDayOffset -= 7;
var weeksToDraw = Math.ceil(( (-1*firstDayOffset+1) + currentDate.getDaysInMonth() ) /7);
currentDate.addDays(firstDayOffset-1);

var doHover = function(firstDayInBounds)
{
return function()
{
if (s.hoverClass) {
var $this = $(this);
if (!s.selectWeek) {
$this.addClass(s.hoverClass);
} else if (firstDayInBounds && !$this.is('.disabled')) {
$this.parent().addClass('activeWeekHover');
}
}
}
};
var unHover = function()
{
if (s.hoverClass) {
var $this = $(this);
$this.removeClass(s.hoverClass);
$this.parent().removeClass('activeWeekHover');
}
};

var w = 0;
while (w++<weeksToDraw) {
var r = jQuery(dc('tr'));
var firstDayInBounds = s.dpController ? currentDate > s.dpController.startDate : false;
for (var i=0; i<7; i++) {
var thisMonth = currentDate.getMonth() == month;
var d = $(dc('td'))
.text(currentDate.getDate() + '')
.addClass((thisMonth ? 'current-month ' : 'other-month ') +
(currentDate.isWeekend() ? 'weekend ' : 'weekday ') +
(thisMonth && currentDate.getTime() == today.getTime() ? 'today ' : '')
)
.data('datePickerDate', currentDate.asString())
.hover(doHover(firstDayInBounds), unHover)
;
r.append(d);
if (s.renderCallback) {
s.renderCallback(d, currentDate, month, year);
}
currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()+1, 12, 0, 0);
}
tbody.append(r);
}
calendarTable.append(tbody);

return this.each(
function()
{
$(this).empty().append(calendarTable);
}
);
},

datePicker : function(s)
{
if (!$.event._dpCache) $.event._dpCache = [];

s = $.extend({}, $.fn.datePicker.defaults, s);

return this.each(
function()
{
var $this = $(this);
var alreadyExists = true;

if (!this._dpId) {
this._dpId = $.event.guid++;
$.event._dpCache[this._dpId] = new DatePicker(this);
alreadyExists = false;
}

if (s.inline) {
s.createButton = false;
s.displayClose = false;
s.closeOnSelect = false;
$this.empty();
}

var controller = $.event._dpCache[this._dpId];

controller.init(s);

if (!alreadyExists && s.createButton) {
controller.button = $('<a href="#" class="dp-choose-date" title="' + $.dpText.TEXT_CHOOSE_DATE + '">' + $.dpText.TEXT_CHOOSE_DATE + '</a>')
.bind(
'click',
function()
{
$this.dpDisplay(this);
this.blur();
return false;
}
);
$this.after(controller.button);
}

if (!alreadyExists && $this.is(':text')) {
$this
.bind(
'dateSelected',
function(e, selectedDate, $td)
{
this.value = selectedDate.asString();
}
).bind(
'change',
function()
{
if (this.value == '') {
controller.clearSelected();
} else {
var d = Date.fromString(this.value);
if (d) {
controller.setSelected(d, true, true);
}
}
}
);
if (s.clickInput) {
$this.bind(
'click',
function()
{
// The change event doesn't happen until the input loses focus so we need to manually trigger it...
$this.trigger('change');
$this.dpDisplay();
}
);
}
var d = Date.fromString(this.value);
if (this.value != '' && d) {
controller.setSelected(d, true, true);
}
}

$this.addClass('dp-applied');

}
)
},

dpSetDisabled : function(s)
{
return _w.call(this, 'setDisabled', s);
},

dpSetStartDate : function(d)
{
return _w.call(this, 'setStartDate', d);
},

dpSetEndDate : function(d)
{
return _w.call(this, 'setEndDate', d);
},

dpGetSelected : function()
{
var c = _getController(this[0]);
if (c) {
return c.getSelected();
}
return null;
},

dpSetSelected : function(d, v, m, e)
{
if (v == undefined) v=true;
if (m == undefined) m=true;
if (e == undefined) e=true;
return _w.call(this, 'setSelected', Date.fromString(d), v, m, e);
},

dpSetDisplayedMonth : function(m, y)
{
return _w.call(this, 'setDisplayedMonth', Number(m), Number(y), true);
},

dpDisplay : function(e)
{
return _w.call(this, 'display', e);
},

dpSetRenderCallback : function(a)
{
return _w.call(this, 'setRenderCallback', a);
},

dpSetPosition : function(v, h)
{
return _w.call(this, 'setPosition', v, h);
},

dpSetOffset : function(v, h)
{
return _w.call(this, 'setOffset', v, h);
},

dpClose : function()
{
return _w.call(this, '_closeCalendar', false, this[0]);
},
_dpDestroy : function()
{
}
});

var _w = function(f, a1, a2, a3, a4)
{
return this.each(
function()
{
var c = _getController(this);
if (c) {
c[f](a1, a2, a3, a4);
}
}
);
};

function DatePicker(ele)
{
this.ele = ele;

this.displayedMonth=null;
this.displayedYear=null;
this.startDate=null;
this.endDate=null;
this.showYearNavigation=null;
this.closeOnSelect=null;
this.displayClose=null;
this.rememberViewedMonth=null;
this.selectMultiple=null;
this.numSelectable=null;
this.numSelected=null;
this.verticalPosition=null;
this.horizontalPosition=null;
this.verticalOffset=null;
this.horizontalOffset=null;
this.button=null;
this.renderCallback=[];
this.selectedDates={};
this.inline=null;
this.context='#dp-popup';
this.settings={};
};
$.extend(
DatePicker.prototype,
{
init : function(s)
{
this.setStartDate(s.startDate);
this.setEndDate(s.endDate);
this.setDisplayedMonth(Number(s.month), Number(s.year));
this.setRenderCallback(s.renderCallback);
this.showYearNavigation = s.showYearNavigation;
this.closeOnSelect = s.closeOnSelect;
this.displayClose = s.displayClose;
this.rememberViewedMonth =s.rememberViewedMonth;
this.selectMultiple = s.selectMultiple;
this.numSelectable = s.selectMultiple ? s.numSelectable : 1;
this.numSelected = 0;
this.verticalPosition = s.verticalPosition;
this.horizontalPosition = s.horizontalPosition;
this.hoverClass = s.hoverClass;
this.setOffset(s.verticalOffset, s.horizontalOffset);
this.inline = s.inline;
this.settings = s;
if (this.inline) {
this.context = this.ele;
this.display();
}
},
setStartDate : function(d)
{
if (d) {
this.startDate = Date.fromString(d);
}
if (!this.startDate) {
this.startDate = (new Date()).zeroTime();
}
this.setDisplayedMonth(this.displayedMonth, this.displayedYear);
},
setEndDate : function(d)
{
if (d) {
this.endDate = Date.fromString(d);
}
if (!this.endDate) {
this.endDate = (new Date('12/31/2999')); // using the JS Date.parse function which expects mm/dd/yyyy
}
if (this.endDate.getTime() < this.startDate.getTime()) {
this.endDate = this.startDate;
}
this.setDisplayedMonth(this.displayedMonth, this.displayedYear);
},
setPosition : function(v, h)
{
this.verticalPosition = v;
this.horizontalPosition = h;
},
setOffset : function(v, h)
{
this.verticalOffset = parseInt(v) || 0;
this.horizontalOffset = parseInt(h) || 0;
},
setDisabled : function(s)
{
$e = $(this.ele);
$e[s ? 'addClass' : 'removeClass']('dp-disabled');
if (this.button) {
$but = $(this.button);
$but[s ? 'addClass' : 'removeClass']('dp-disabled');
$but.attr('title', s ? '' : $.dpText.TEXT_CHOOSE_DATE);
}
if ($e.is(':text')) {
$e.attr('disabled', s ? 'disabled' : '');
}
},
setDisplayedMonth : function(m, y, rerender)
{
if (this.startDate == undefined || this.endDate == undefined) {
return;
}
var s = new Date(this.startDate.getTime());
s.setDate(1);
var e = new Date(this.endDate.getTime());
e.setDate(1);

var t;
if ((!m && !y) || (isNaN(m) && isNaN(y))) {
t = new Date().zeroTime();
t.setDate(1);
} else if (isNaN(m)) {
t = new Date(y, this.displayedMonth, 1);
} else if (isNaN(y)) {
t = new Date(this.displayedYear, m, 1);
} else {
t = new Date(y, m, 1)
}
if (t.getTime() < s.getTime()) {
t = s;
} else if (t.getTime() > e.getTime()) {
t = e;
}
var oldMonth = this.displayedMonth;
var oldYear = this.displayedYear;
this.displayedMonth = t.getMonth();
this.displayedYear = t.getFullYear();

if (rerender && (this.displayedMonth != oldMonth || this.displayedYear != oldYear))
{
this._rerenderCalendar();
$(this.ele).trigger('dpMonthChanged', [this.displayedMonth, this.displayedYear]);
}
},
setSelected : function(d, v, moveToMonth, dispatchEvents)
{
if (d < this.startDate || d > this.endDate) {
return;
}
var s = this.settings;
if (s.selectWeek)
{
d = d.addDays(- (d.getDay() - Date.firstDayOfWeek + 7) % 7);
if (d < this.startDate) 
{
return;
}
}
if (v == this.isSelected(d)) 
{
return;
}
if (this.selectMultiple == false) {
this.clearSelected();
} else if (v && this.numSelected == this.numSelectable) {
return;
}
if (moveToMonth && (this.displayedMonth != d.getMonth() || this.displayedYear != d.getFullYear())) {
this.setDisplayedMonth(d.getMonth(), d.getFullYear(), true);
}
this.selectedDates[d.asString()] = v;
this.numSelected += v ? 1 : -1;
var selectorString = 'td.' + (d.getMonth() == this.displayedMonth ? 'current-month' : 'other-month');
var $td;
$(selectorString, this.context).each(
function()
{
if ($(this).data('datePickerDate') == d.asString()) {
$td = $(this);
if (s.selectWeek)
{
$td.parent()[v ? 'addClass' : 'removeClass']('selectedWeek');
}
$td[v ? 'addClass' : 'removeClass']('selected'); 
}
}
);
$('td', this.context).not('.selected')[this.selectMultiple &&  this.numSelected == this.numSelectable ? 'addClass' : 'removeClass']('unselectable');

if (dispatchEvents)
{
var s = this.isSelected(d);
$e = $(this.ele);
var dClone = Date.fromString(d.asString());
$e.trigger('dateSelected', [dClone, $td, s]);
$e.trigger('change');
}
},
isSelected : function(d)
{
return this.selectedDates[d.asString()];
},
getSelected : function()
{
var r = [];
for(s in this.selectedDates) {
if (this.selectedDates[s] == true) {
r.push(Date.fromString(s));
}
}
return r;
},
clearSelected : function()
{
this.selectedDates = {};
this.numSelected = 0;
$('td.selected', this.context).removeClass('selected').parent().removeClass('selectedWeek');
},
display : function(eleAlignTo)
{
if ($(this.ele).is('.dp-disabled')) return;

eleAlignTo = eleAlignTo || this.ele;
var c = this;
var $ele = $(eleAlignTo);
var eleOffset = $ele.offset();

var $createIn;
var attrs;
var attrsCalendarHolder;
var cssRules;

if (c.inline) {
$createIn = $(this.ele);
attrs = {
'id':'calendar-' + this.ele._dpId,
'class':'dp-popup dp-popup-inline'
};

$('.dp-popup', $createIn).remove();
cssRules = {
};
} else {
$createIn = $('body');
attrs = {
'id':'dp-popup',
'class':'dp-popup'
};
cssRules = {
'top':eleOffset.top + c.verticalOffset,
'left':eleOffset.left + c.horizontalOffset
};

var _checkMouse = function(e)
{
var el = e.target;
var cal = $('#dp-popup')[0];

while (true){
if (el == cal) {
return true;
} else if (el == document) {
c._closeCalendar();
return false;
} else {
el = $(el).parent()[0];
}
}
};
this._checkMouse = _checkMouse;

c._closeCalendar(true);
$(document).bind(
'keydown.datepicker', 
function(event)
{
if (event.keyCode == 27) {
c._closeCalendar();
}
}
);
}

if (!c.rememberViewedMonth)
{
var selectedDate = this.getSelected()[0];
if (selectedDate) {
selectedDate = new Date(selectedDate);
this.setDisplayedMonth(selectedDate.getMonth(), selectedDate.getFullYear(), false);
}
}

$createIn
.append(
$('<div></div>')
.attr(attrs)
.css(cssRules)
.append(
//$('<a href="#" class="selecteee">aaa</a>'),
$('<h2></h2>'),
$('<div class="dp-nav-prev"></div>')
.append(
$('<a class="dp-nav-prev-year" href="#" title="' + $.dpText.TEXT_PREV_YEAR + '">&lt;&lt;</a>')
.bind(
'click',
function()
{
return c._displayNewMonth.call(c, this, 0, -1);
}
),
$('<a class="dp-nav-prev-month" href="#" title="' + $.dpText.TEXT_PREV_MONTH + '">&lt;</a>')
.bind(
'click',
function()
{
return c._displayNewMonth.call(c, this, -1, 0);
}
)
),
$('<div class="dp-nav-next"></div>')
.append(
$('<a class="dp-nav-next-year" href="#" title="' + $.dpText.TEXT_NEXT_YEAR + '">&gt;&gt;</a>')
.bind(
'click',
function()
{
return c._displayNewMonth.call(c, this, 0, 1);
}
),
$('<a class="dp-nav-next-month" href="#" title="' + $.dpText.TEXT_NEXT_MONTH + '">&gt;</a>')
.bind(
'click',
function()
{
return c._displayNewMonth.call(c, this, 1, 0);
}
)
),
$('<div class="dp-calendar"></div>')
)
.bgIframe()
);

var $pop = this.inline ? $('.dp-popup', this.context) : $('#dp-popup');

if (this.showYearNavigation == false) {
$('.dp-nav-prev-year, .dp-nav-next-year', c.context).css('display', 'none');
}
if (this.displayClose) {
$pop.append(
$('<a href="#" id="dp-close">' + $.dpText.TEXT_CLOSE + '</a>')
.bind(
'click',
function()
{
c._closeCalendar();
return false;
}
)
);
}
c._renderCalendar();

$(this.ele).trigger('dpDisplayed', $pop);

if (!c.inline) {
if (this.verticalPosition == $.dpConst.POS_BOTTOM) {
$pop.css('top', eleOffset.top + $ele.height() - $pop.height() + c.verticalOffset);
}
if (this.horizontalPosition == $.dpConst.POS_RIGHT) {
$pop.css('left', eleOffset.left + $ele.width() - $pop.width() + c.horizontalOffset);
}
$(document).bind('mousedown.datepicker', this._checkMouse);
}

},
setRenderCallback : function(a)
{
if (a == null) return;
if (a && typeof(a) == 'function') {
a = [a];
}
this.renderCallback = this.renderCallback.concat(a);
},
cellRender : function ($td, thisDate, month, year) {
var c = this.dpController;
var d = new Date(thisDate.getTime());

$td.bind(
'click',
function()
{
var $this = $(this);
if (!$this.is('.disabled')) {
c.setSelected(d, !$this.is('.selected') || !c.selectMultiple, false, true);
if (c.closeOnSelect) {
c._closeCalendar();
}
if (!$.browser.msie)
{
$(c.ele).trigger('focus', [$.dpConst.DP_INTERNAL_FOCUS]);
}
}
}
);
if (c.isSelected(d)) {
$td.addClass('selected');
if (c.settings.selectWeek)
{
$td.parent().addClass('selectedWeek');
}
} else  if (c.selectMultiple && c.numSelected == c.numSelectable) {
$td.addClass('unselectable');
}

},
_applyRenderCallbacks : function()
{
var c = this;
$('td', this.context).each(
function()
{
for (var i=0; i<c.renderCallback.length; i++) {
$td = $(this);
c.renderCallback[i].apply(this, [$td, Date.fromString($td.data('datePickerDate')), c.displayedMonth, c.displayedYear]);
}
}
);
return;
},
_displayNewMonth : function(ele, m, y) 
{
if (!$(ele).is('.disabled')) {
this.setDisplayedMonth(this.displayedMonth + m, this.displayedYear + y, true);
}
ele.blur();
return false;
},
_rerenderCalendar : function()
{
this._clearCalendar();
this._renderCalendar();
},
_renderCalendar : function()
{
$('h2', this.context).html((new Date(this.displayedYear, this.displayedMonth, 1)).asString($.dpText.HEADER_FORMAT));

$('.dp-calendar', this.context).renderCalendar(
$.extend(
{},
this.settings, 
{
month: this.displayedMonth,
year: this.displayedYear,
renderCallback: this.cellRender,
dpController: this,
hoverClass: this.hoverClass
})
);

if (this.displayedYear == this.startDate.getFullYear() && this.displayedMonth == this.startDate.getMonth()) {
$('.dp-nav-prev-year', this.context).addClass('disabled');
$('.dp-nav-prev-month', this.context).addClass('disabled');
$('.dp-calendar td.other-month', this.context).each(
function()
{
var $this = $(this);
if (Number($this.text()) > 20) {
$this.addClass('disabled');
}
}
);
var d = this.startDate.getDate();
$('.dp-calendar td.current-month', this.context).each(
function()
{
var $this = $(this);
if (Number($this.text()) < d) {
$this.addClass('disabled');
}
}
);
} else {
$('.dp-nav-prev-year', this.context).removeClass('disabled');
$('.dp-nav-prev-month', this.context).removeClass('disabled');
var d = this.startDate.getDate();
if (d > 20) {
var st = this.startDate.getTime();
var sd = new Date(st);
sd.addMonths(1);
if (this.displayedYear == sd.getFullYear() && this.displayedMonth == sd.getMonth()) {
$('.dp-calendar td.other-month', this.context).each(
function()
{
var $this = $(this);
if (Date.fromString($this.data('datePickerDate')).getTime() < st) {
$this.addClass('disabled');
}
}
);
}
}
}
if (this.displayedYear == this.endDate.getFullYear() && this.displayedMonth == this.endDate.getMonth()) {
$('.dp-nav-next-year', this.context).addClass('disabled');
$('.dp-nav-next-month', this.context).addClass('disabled');
$('.dp-calendar td.other-month', this.context).each(
function()
{
var $this = $(this);
if (Number($this.text()) < 14) {
$this.addClass('disabled');
}
}
);
var d = this.endDate.getDate();
$('.dp-calendar td.current-month', this.context).each(
function()
{
var $this = $(this);
if (Number($this.text()) > d) {
$this.addClass('disabled');
}
}
);
} else {
$('.dp-nav-next-year', this.context).removeClass('disabled');
$('.dp-nav-next-month', this.context).removeClass('disabled');
var d = this.endDate.getDate();
if (d < 13) {
var ed = new Date(this.endDate.getTime());
ed.addMonths(-1);
if (this.displayedYear == ed.getFullYear() && this.displayedMonth == ed.getMonth()) {
$('.dp-calendar td.other-month', this.context).each(
function()
{
var $this = $(this);
var cellDay = Number($this.text());
if (cellDay < 13 && cellDay > d) {
$this.addClass('disabled');
}
}
);
}
}
}
this._applyRenderCallbacks();
},
_closeCalendar : function(programatic, ele)
{
if (!ele || ele == this.ele)
{
$(document).unbind('mousedown.datepicker');
$(document).unbind('keydown.datepicker');
this._clearCalendar();
$('#dp-popup a').unbind();
$('#dp-popup').empty().remove();
if (!programatic) {
$(this.ele).trigger('dpClosed', [this.getSelected()]);
}
}
},
_clearCalendar : function()
{
$('.dp-calendar td', this.context).unbind();
$('.dp-calendar', this.context).empty();
}
}
);

$.dpConst = {
SHOW_HEADER_NONE:0,
SHOW_HEADER_SHORT:1,
SHOW_HEADER_LONG:2,
POS_TOP:0,
POS_BOTTOM:1,
POS_LEFT:0,
POS_RIGHT:1,
DP_INTERNAL_FOCUS:'dpInternalFocusTrigger'
};
$.dpText = {
TEXT_PREV_YEAR:'Previous year',
TEXT_PREV_MONTH:'Previous month',
TEXT_NEXT_YEAR:'Next year',
TEXT_NEXT_MONTH:'Next month',
TEXT_CLOSE:'Close',
TEXT_CHOOSE_DATE:'Choose date',
HEADER_FORMAT:'mmmm yyyy'
};
$.dpVersion = '$Id: jquery.datePicker.js 84 2009-08-05 17:54:35Z kelvin.luck@gmail.com $';

$.fn.datePicker.defaults = {
month: undefined,
year: undefined,
showHeader: $.dpConst.SHOW_HEADER_SHORT,
startDate: undefined,
endDate: undefined,
inline: false,
renderCallback: null,
createButton: true,
showYearNavigation: true,
closeOnSelect: true,
displayClose: false,
selectMultiple: false,
numSelectable: Number.MAX_VALUE,
clickInput: false,
rememberViewedMonth: true,
selectWeek: false,
verticalPosition: $.dpConst.POS_TOP,
horizontalPosition: $.dpConst.POS_LEFT,
verticalOffset: 0,
horizontalOffset: 0,
hoverClass: 'dp-hover'
};

function _getController(ele)
{
if (ele._dpId) return $.event._dpCache[ele._dpId];
return false;
};

if ($.fn.bgIframe == undefined) {
$.fn.bgIframe = function() {return this; };
};

$(window)
.bind('unload', function() {
var els = $.event._dpCache || [];
for (var i in els) {
$(els[i].ele)._dpDestroy();
}
});


})(jQuery);
