/**
* Classes included in this file:
*     CalendarHeaderFooterBase : VisibleControl
*     CalendarHeader : CalendarHeaderFooterBase
*     CalendarFooter : CalendarHeaderFooterBase
*     CalendarWeekDayHeaders : VisibleControl
*     CalendarWeekNumbers : VisibleControl
*     CalendarBase : Control
* Enumerators included in this file:
*     WeekDayFormat
* This file requires the use of the following files:
*     base.js
*     base_classes.js
**/

WeekDayFormat = {};
WeekDayFormat.Long = 1;
WeekDayFormat.Short = 2;
WeekDayFormat.Letters = 3;

/**************************************************************************/

function CalendarHeaderFooterBase(p_parent, p_elementId, p_instanceName)
{
	CalendarHeaderFooterBase.baseConstructor.call(this, p_elementId, p_instanceName);
	this.Parent  = p_parent;
	this.Colspan = (this.Parent.ShowWeekend ? 7 : 5);
	this.ElementIdExt = '';
	this.PercentWidth = 100;
	return;
}
Extend(CalendarHeaderFooterBase, VisibleControl);
CalendarHeaderFooterBase.prototype.GetType = function() { return 'CalendarHeaderFooterBase'; }
CalendarHeaderFooterBase.prototype.ToString = function() { return 'CalendarHeaderFooterBase: ' + this.Name; }
CalendarHeaderFooterBase.prototype.DefaultSize = function() { return new Size(0, 20); }
CalendarHeaderFooterBase.prototype.DefaultBorder = function() { return new RectangleBorder(0, BorderStyle.None, '#FFFFFF'); }
CalendarHeaderFooterBase.prototype.DefaultBackground = function() { return new Background('#C3D9FF'); }
CalendarHeaderFooterBase.prototype.GetSizeStyleString = function(p_stringBuilder)
{
	p_stringBuilder.Append(Utils.CreateCssPropertyString('width', this.PercentWidth + '%'))
		.Append(Utils.CreateCssPropertyString('height', (this.Size.Height > 0) ? this.Size.Height.ToCssString() : ''));
	return;
}
CalendarHeaderFooterBase.prototype.Render = function(p_stringBuilder)
{
	p_stringBuilder.Append('<td')
		.Append(Utils.CreateAttributeString('colspan', this.Colspan.toString()))
		.Append(Utils.CreateAttributeString('id', this.ElementId + this.ElementIdExt))
		.Append(Utils.CreateAttributeString('title', this.ToolTipText))
		.Append(Utils.CreateAttributeString('class', this.CssClassName));
	this.GetEventAttributeString(p_stringBuilder);
	p_stringBuilder.Append(' style="');
	this.GetStyleString(p_stringBuilder);
	p_stringBuilder.Append('">').Append(this.Text).Append('</td>');
	this.IsRendered = true;
	return;
}

/**************************************************************************/

function CalendarHeader(p_parent)
{
	CalendarHeader.baseConstructor.call(this, p_parent, p_parent.ElementId + '_Header', p_parent.Name + '.Header');
	this.ToolTipText = 'Click to select the entire month';
	this.ButtonsVisible = true;
	return;
}
Extend(CalendarHeader, CalendarHeaderFooterBase);
CalendarHeader.prototype.GetType = function() { return 'CalendarHeader'; }
CalendarHeader.prototype.ToString = function() { return 'CalendarHeader: ' + this.Name; }
CalendarHeader.prototype.DefaultFont = function() { return new Font('verdana', '12px', FontWeight.Bold); }
CalendarHeader.prototype.DefaultForeColor = function() { return '#1229D3'; }
CalendarHeader.prototype.DefaultCursor = function() { return 'pointer'; }
CalendarHeader.prototype.GetDateFromElementId = function(p_id) { var v_parts = p_id.replace(this.ElementId + '_', '').split('_'); return new Date(v_parts[0], v_parts[1], v_parts[2], 0, 0, 0, 0); }
CalendarHeader.prototype.OnClick = function(e, p_element)
{
	if(this.Parent.SelectionEnabled && this.Parent.MultipleSelection) { this.Parent.SelectMonth(this.GetDateFromElementId(p_element.id)); }
	CalendarHeader.superClass.OnClick.call(this, e, p_element);
	return;
}

/**************************************************************************/

function CalendarFooter(p_parent)
{
	CalendarFooter.baseConstructor.call(this, p_parent, p_parent.ElementId + '_Footer', p_parent.Name + '.Footer');
	return;
}
Extend(CalendarFooter, CalendarHeaderFooterBase);
CalendarFooter.prototype.GetType = function() { return 'CalendarFooter'; }
CalendarFooter.prototype.ToString = function() { return 'CalendarFooter: ' + this.Name; }

/**************************************************************************/

function CalendarWeekDayHeaders(p_parent)
{
	CalendarWeekDayHeaders.baseConstructor.call(this, p_parent.ElementId + '_WeekDayHeaders', p_parent.Name + '.WeekDayHeaders');
	this.Parent  = p_parent;
	this.VerticalBorders = new Border(0, BorderStyle.None, '#FFFFFF');
    this.TextAlign = ContentAlignment.MiddleCenter;
	this.TextFormat = WeekDayFormat.Letters;
	this.WeekNumberHeaderText = 'N';
	this.WeekNumberToolTipText = 'Week Number in Year';
	this.SunToolTipText = Date.LongWeekDayName(0);
	this.MonToolTipText = Date.LongWeekDayName(1);
	this.TueToolTipText = Date.LongWeekDayName(2);
	this.WedToolTipText = Date.LongWeekDayName(3);
	this.ThuToolTipText = Date.LongWeekDayName(4);
	this.FriToolTipText = Date.LongWeekDayName(5);
	this.SatToolTipText = Date.LongWeekDayName(6);
	this._WNHeaderFlag = false;
	return;
}
Extend(CalendarWeekDayHeaders, VisibleControl);
CalendarWeekDayHeaders.prototype.GetType = function() { return 'CalendarWeekDayHeaders'; }
CalendarWeekDayHeaders.prototype.ToString = function() { return 'CalendarWeekDayHeaders: ' + this.Name; }
CalendarWeekDayHeaders.prototype.DefaultSize = function() { return new Size(30, 25); }
CalendarWeekDayHeaders.prototype.DefaultBorder = function() { return new RectangleBorder(0, BorderStyle.None, '#FFFFFF'); }
CalendarWeekDayHeaders.prototype.DefaultBackground = function() { return new Background('#C3D9FF'); }
CalendarWeekDayHeaders.prototype.GetBorderStyleString = function(p_stringBuilder) { return; }
CalendarWeekDayHeaders.prototype.GetSizeStyleString = function(p_stringBuilder)
{
	var v_width = this._WNHeaderFlag ? this.Parent.WeekNumbers.Size.Width : this.Size.Width;
	p_stringBuilder.Append(Utils.CreateCssPropertyString('width', (v_width > 0) ? v_width.ToCssString() : (this._WNHeaderFlag ? '1%' : '14.1%')))
		.Append(Utils.CreateCssPropertyString('height', (this.Size.Height > 0) ? this.Size.Height.ToCssString() : ''));
	return;
}
CalendarWeekDayHeaders.prototype.Render = function(p_stringBuilder, p_date)
{
    if (this.Parent.WeekNumbers.Visible) { this._WNHeaderFlag = true; this._RenderColumn(p_stringBuilder, -1, p_date, true, false); this._WNHeaderFlag = false; }
	if (this.Parent.ShowWeekend)
	    for (var i = 0; i < 7; i++) { this._RenderColumn(p_stringBuilder, i, p_date, !this.Parent.WeekNumbers.Visible && i == 0, i == 6); }
	else
	    for (var i = 1; i < 6; i++) { this._RenderColumn(p_stringBuilder, i, p_date, !this.Parent.WeekNumbers.Visible && i == 1, i == 5); }
	this.IsRendered = true;
	return;
}
CalendarWeekDayHeaders.prototype._RenderColumn = function(p_stringBuilder, p_weekDay, p_date, p_isLeft, p_isRight)
{
	p_stringBuilder.Append('<td')
		.Append(Utils.CreateAttributeString('id', this.ElementId + '_' + p_date.getFullYear().Pad(4) + '_' + p_date.getMonth().Pad(2) + '_01_' + (p_weekDay == -1 ? 'N' : Date.ShortWeekDayName(p_weekDay))))
		.Append(Utils.CreateAttributeString('title', p_weekDay == -1 ? this.WeekNumberToolTipText : this._GetWeekDayToolTipText(p_weekDay)))
		.Append(Utils.CreateAttributeString('class', this.CssClassName));
	this.GetEventAttributeString(p_stringBuilder);
	p_stringBuilder.Append(' style="');
	this.GetStyleString(p_stringBuilder);
	if(p_isLeft) { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-left', this.Border.Left.ToCssString())); }
	if(p_isRight) { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-right', this.Border.Right.ToCssString())); }
	else { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-right', this.VerticalBorders.ToCssString())); }
	p_stringBuilder.Append(Utils.CreateCssPropertyString('border-top', this.Border.Top.ToCssString()))
		.Append(Utils.CreateCssPropertyString('border-bottom', this.Border.Bottom.ToCssString()))
        .Append(ContentAlignment.CreateCssString(this.TextAlign))
		.Append('">').Append(p_weekDay == -1 ? this.WeekNumberHeaderText : this._GetWeekDayText(p_weekDay)).Append('</td>');
	return;
}
CalendarWeekDayHeaders.prototype._GetWeekDayToolTipText = function(p_weekDay)
{
	switch(p_weekDay)
	{
		case 0: return this.SunToolTipText;
		case 1: return this.MonToolTipText;
		case 2: return this.TueToolTipText;
		case 3: return this.WedToolTipText;
		case 4: return this.ThuToolTipText;
		case 5: return this.FriToolTipText;
		case 6: return this.SatToolTipText;
	}
	return '';
}
CalendarWeekDayHeaders.prototype._GetWeekDayText = function(p_weekDay)
{
	switch(this.TextFormat)
	{
		case WeekDayFormat.Long: return Date.LongWeekDayName(p_weekDay);
		case WeekDayFormat.Short: return Date.ShortWeekDayName(p_weekDay);
		case WeekDayFormat.Letters: return Date.WeekDayLetter(p_weekDay);
	}
	return '';
}

/**************************************************************************/

function CalendarWeekNumbers(p_parent)
{
	CalendarWeekNumbers.baseConstructor.call(this, p_parent.ElementId + '_wn_', p_parent.Name + '.WeekNumbers');
	this.Parent = p_parent;
	this.ToolTipText = 'Week [WN] of the year [YEAR] (click to select this week)';
	this.HorizontalBorders = new Border(0, BorderStyle.None, '#FFFFFF');
	this.Border.Top.Set(1, BorderStyle.Solid, 'blue');
	this.Border.Bottom.Set(1, BorderStyle.Solid, 'blue');
	return;
}
Extend(CalendarWeekNumbers, VisibleControl);
CalendarWeekNumbers.prototype.GetType = function() { return 'CalendarWeekNumbers'; }
CalendarWeekNumbers.prototype.ToString = function() { return 'CalendarWeekNumbers: ' + this.Name; }
CalendarWeekNumbers.prototype.DefaultSize = function() { return new Size(30, 25); }
CalendarWeekNumbers.prototype.DefaultBorder = function() { return new RectangleBorder(0, BorderStyle.None, '#FFFFFF'); }
CalendarWeekNumbers.prototype.DefaultBackground = function() { return new Background('#C3D9FF'); }
CalendarWeekNumbers.prototype.DefaultCursor = function() { return 'pointer'; }
CalendarWeekNumbers.prototype.GetBorderStyleString = function(p_stringBuilder) { return; }
CalendarWeekNumbers.prototype.GetDateFromElementId = function(p_id) { var v_parts = p_id.replace(this.ElementId, '').split('_'); return new Date(v_parts[1], v_parts[2], v_parts[3], 0, 0, 0, 0); }
CalendarWeekNumbers.prototype.GetSizeStyleString = function(p_stringBuilder)
{
	p_stringBuilder.Append(Utils.CreateCssPropertyString('width', (this.Size.Width > 0) ? this.Size.Width.ToCssString() : '1%'))
		.Append(Utils.CreateCssPropertyString('height', (this.Size.Height > 0) ? this.Size.Height.ToCssString() : '16.2%'));
	return;
}
CalendarWeekNumbers.prototype.Render = function(p_stringBuilder, p_idPrefix, p_date, p_currentMonth, p_isTop, p_isBottom)
{
	var v_cm_end = new Date(p_currentMonth.getFullYear(), p_currentMonth.getMonth(), Date.DaysInMonth(p_currentMonth.getFullYear(), p_currentMonth.getMonth()), 0, 0, 0, 0);
	var v_next_month = v_cm_end.Clone(); v_next_month.AddDays(1);
	p_date = p_date.Clone();
	if(p_date.IsLessThan(p_currentMonth)) { p_date.setDate(1); p_date.AddMonths(1); }
	else if(p_date.IsGreaterThan(v_cm_end) && !p_date.IsGreaterThanOrEqualTo(v_next_month)) { p_date.setDate(Date.DaysInMonth(p_date.getFullYear(), p_date.getMonth())); p_date.AddMonths(-1); }
	p_stringBuilder.Append('<td')
		.Append(Utils.CreateAttributeString('id', this.ElementId + p_idPrefix + p_date.getFullYear().toString() + '_' + p_date.getMonth() + '_' + p_date.getDate()))
		.Append(Utils.CreateAttributeString('title', this.ToolTipText.replace('[WN]', p_date.GetIsoWeekNumber().toString()).replace('[YEAR]', p_date.getFullYear().Pad(4))))
		.Append(Utils.CreateAttributeString('class', this.CssClassName));
	this.GetEventAttributeString(p_stringBuilder);
	p_stringBuilder.Append(' style="');
	this.GetStyleString(p_stringBuilder);
	if(p_isTop) { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-top', this.Border.Top.ToCssString())); }
	else { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-top', this.HorizontalBorders.ToCssString())); }
	if(p_isBottom) { p_stringBuilder.Append(Utils.CreateCssPropertyString('border-bottom', this.Border.Bottom.ToCssString())); }
	p_stringBuilder.Append(Utils.CreateCssPropertyString('border-left', this.Border.Left.ToCssString()))
		.Append(Utils.CreateCssPropertyString('border-right', this.Border.Right.ToCssString()))
		.Append('">').Append(p_date.GetIsoWeekNumber().toString()).Append('</td>');
	this.IsRendered = true;
	return;
}
CalendarWeekNumbers.prototype.OnClick = function(e, p_element)
{
	if(this.Parent.SelectionEnabled && this.Parent.MultipleSelection)
	{
		var v_date = this.GetDateFromElementId(p_element.id);
		if(v_date.IsLessThan(this.Parent.VisibleStartDate)) { v_date = this.Parent.VisibleStartDate.Clone(); }
		if(!v_date.IsGreaterThan(this.Parent.VisibleEndDate)) { this.Parent.SelectWeek(v_date); }
	}
	CalendarWeekNumbers.superClass.OnClick.call(this, e, p_element);
	return;
}

/**************************************************************************/

function CalendarBase(p_elementId, p_instanceName, p_date)
{
	CalendarBase.baseConstructor.call(this, p_elementId, p_instanceName);
	this.Header            = new CalendarHeader(this);
	this.WeekDayHeaders    = new CalendarWeekDayHeaders(this);
	this.Footer            = new CalendarFooter(this);
	this.WeekNumbers       = new CalendarWeekNumbers(this);
	this.SelectedDays      = new DateCollection();
	this.DayText           = new HashArray();
	this.CurrentDate       = TypeOf(p_date) == 'Date' ? p_date : Date.Today();
	this.SelectionEnabled  = true;
	this.MultipleSelection = true;
	this.ToggleOnDayClick  = false;
	this.MouseOverEnabled  = true;
	this.ShowToday         = true;
	this.VisibleStartDate  = null;
	this.VisibleEndDate    = null;
	this.ShowWeekend       = true;
	this.DayCursor              = this.DefaultDayCursor();
	this.DayBoxSize             = this.DefaultDayBoxSize();
	this.DayPadding             = this.DefaultDayPadding();
	this.DayBorder              = this.DefaultDayBorder();
	this.NormalDayBackground    = this.DefaultNormalDayBackground();
	this.InactiveDayBackground  = this.DefaultInactiveDayBackground();
	this.WeekendDayBackground   = this.DefaultWeekendDayBackground();
	this.TodayBackground        = this.DefaultTodayBackground();
	this.SelectedDayBackground  = this.DefaultSelectedDayBackground();
	this.MouseOverDayBackground = this.DefaultMouseOverDayBackground();
	this.InactiveDayForeColor   = this.DefaultInactiveDayForeColor();
	this.WeekendDayForeColor    = this.DefaultWeekendDayForeColor();
	this.TodayDayForeColor      = this.DefaultTodayDayForeColor();
	this.SelectedDayForeColor   = this.DefaultSelectedDayForeColor();
	this.MouseOverDayForeColor  = this.DefaultMouseOverDayForeColor();
	this.DaySelected    = null;
	this.DayDeselected  = null;
	this.WeekSelected   = null;
	this.MonthSelected  = null;
	this.DayClick       = null;
	this.DayDoubleClick = null;
	this.DayMouseDown   = null;
	this.DayMouseUp     = null;
	this.DayMouseOver   = null;
	this.DayMouseMove   = null;
	this.DayMouseOut    = null;
	this.DayKeyPress    = null;
	this.DayKeyDown     = null;
	this.DayKeyUp       = null;
	this.UpdateVisibleDates();
	this.CurrentDate.setDate(1);
	return;
}
Extend(CalendarBase, Control);
CalendarBase.prototype.GetType = function() { return 'CalendarBase'; }
CalendarBase.prototype.ToString = function() { return 'CalendarBase: ' + this.Name; }
CalendarBase.prototype.DefaultSize = function() { return new Size(0, 0); }
CalendarBase.prototype.DefaultBorder = function() { return new RectangleBorder(1, BorderStyle.Solid, 'blue'); }
CalendarBase.prototype.DefaultDayCursor = function() { return 'pointer'; }
CalendarBase.prototype.DefaultDayBoxSize = function() { return new Size(22, 20); }
CalendarBase.prototype.DefaultDayPadding = function() { return new Padding(0, 0, 0, 0); }
CalendarBase.prototype.DefaultDayBorder = function() { return new Border(0, BorderStyle.None, '#FFFFFF'); }
CalendarBase.prototype.DefaultNormalDayBackground = function() { return new Background('#FFFFFF'); }
CalendarBase.prototype.DefaultInactiveDayBackground = function() { return new Background('#FFFFFF'); }
CalendarBase.prototype.DefaultWeekendDayBackground = function() { return new Background('#E8EEF7'); }
CalendarBase.prototype.DefaultTodayBackground = function() { return new Background('#99AABB'); }
CalendarBase.prototype.DefaultSelectedDayBackground = function() { return new Background('#AACCEE'); }
CalendarBase.prototype.DefaultMouseOverDayBackground = function() { return new Background('#C3D9FF'); }
CalendarBase.prototype.DefaultInactiveDayForeColor = function() { return '#888888'; }
CalendarBase.prototype.DefaultWeekendDayForeColor = function() { return '#000000'; }
CalendarBase.prototype.DefaultTodayDayForeColor = function() { return '#000000'; }
CalendarBase.prototype.DefaultSelectedDayForeColor = function() { return '#000000'; }
CalendarBase.prototype.DefaultMouseOverDayForeColor = function() { return '#000000'; }
CalendarBase.prototype.OnDaySelected = function(p_date) { if(this.DaySelected) { this.DaySelected(p_date, this); } return; }
CalendarBase.prototype.OnDayDeselected = function(p_date) { if(this.DayDeselected) { this.DayDeselected(p_date, this); } return; }
CalendarBase.prototype.OnWeekSelected = function(p_date) { if(this.WeekSelected) { this.WeekSelected(p_date, this); } return; }
CalendarBase.prototype.OnMonthSelected = function(p_date) { if(this.MonthSelected) { this.MonthSelected(p_date, this); } return; }
CalendarBase.prototype.OnDayDoubleClick = function(e, p_element) { if(this.DayDoubleClick) { this.DayDoubleClick(e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayMouseDown = function(e, p_element) { if(this.DayMouseDown) { this.DayMouseDown(Utils.GetMouseLocation(e), e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayMouseUp = function(e, p_element) { if(this.DayMouseUp) { this.DayMouseUp(Utils.GetMouseLocation(e), e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayMouseMove = function(e, p_element) { if(this.DayMouseMove) { this.DayMouseMove(Utils.GetMouseLocation(e), e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayKeyPress = function(e, p_element) { if(this.DayKeyPress) { this.DayKeyPress(e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayKeyDown = function(e, p_element) { if(this.DayKeyDown) { this.DayKeyDown(e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayKeyUp = function(e, p_element) { if(this.DayKeyUp) { this.DayKeyUp(e, this, this.ParseDateFromElementId(e.currentTarget.id)); } return; }
CalendarBase.prototype.OnDayClick = function(e, p_element)
{
	var v_date = this.ParseDateFromElementId(p_element.id);
	if(this.SelectionEnabled) { if(this.ToggleOnDayClick) { this.ToggleSelectDay(v_date, false); } else { this.SelectDay(v_date, false); } }
	if(this.DayClick) { this.DayClick(e, this, v_date); }
	return;
}
CalendarBase.prototype.OnDayMouseOver = function(e, p_element)
{
	var v_date = this.ParseDateFromElementId(p_element.id);
	if(this.MouseOverEnabled) { this.RefreshDayCore(v_date, this.IsSelected(v_date), true); }
	if(this.DayMouseOver) { this.DayMouseOver(e, this, v_date); }
	return;
}
CalendarBase.prototype.OnDayMouseOut = function(e, p_element)
{
	var v_date = this.ParseDateFromElementId(p_element.id);
	if(this.MouseOverEnabled) { this.RefreshDayCore(v_date, this.IsSelected(v_date), false); }
	if(this.DayMouseOut) { this.DayMouseOut(e, this, v_date); }
	return;
}
CalendarBase.prototype.AddText = function(p_date, p_text) {
    var v_key = 'd' + p_date.getFullYear().Pad(4) + p_date.getMonth().Pad(2) + p_date.getDate().Pad(2);
    if (this.DayText.Contains(v_key)) { this.DayText.Item(v_key, this.DayText.Item(v_key) + p_text); }
    else { this.DayText.Add(v_key, p_text); }
    return;
}
CalendarBase.prototype.ClearDayText = function(p_date) {
    var v_key = 'd' + p_date.getFullYear().Pad(4) + p_date.getMonth().Pad(2) + p_date.getDate().Pad(2);
    if (this.DayText.Contains(v_key)) { this.DayText.Remove(v_key); }
}
CalendarBase.prototype.ClearAllText = function() {
    this.DayText.Clear();
}
CalendarBase.prototype.IsSelected = function(p_date)
{
	p_date = p_date.DateValue();
	return this.SelectionEnabled ? this.SelectedDays.Contains(p_date) : false;
}
CalendarBase.prototype.ToggleSelectDay = function(p_date, p_append)
{
	if(this.SelectionEnabled)
	{
		p_date = p_date.DateValue();
		if(!this.IsSelected(p_date)) { this.SelectDay(p_date, p_append, false); }
		else { this.DeselectDay(p_date, false); }
	}
	return;
}
CalendarBase.prototype.SelectDay = function(p_date, p_append, p_skipEvent)
{
	if(this.SelectionEnabled)
	{
		p_date = p_date.DateValue();
		if(!this.IsSelected(p_date) || (this.SelectedDays.Count > 1 && !p_append))
		{
			if((!this.MultipleSelection || !p_append) && this.SelectedDays.Count > 0) { this.DeselectAll(); }
			this.SelectedDays.Add(p_date);
			this.RefreshDayCore(p_date, true, false);
			if(!p_skipEvent) { this.OnDaySelected(p_date); }
		}
	}
	return;
}
CalendarBase.prototype.Select = function(p_startDate, p_endDate, p_append, p_skipEvent)
{
	if(!this.SelectionEnabled) { return; }
	if(!p_append || !this.MultipleSelection) { this.DeselectAll(p_skipEvent); }
	p_startDate = p_startDate.DateValue();
	if(!p_endDate || !this.MultipleSelection) { this.SelectDay(p_startDate, true, p_skipEvent); }
	else
	{
		p_endDate = p_endDate.DateValue();
		var v_diff = Date.DateDiff('d', p_endDate, p_startDate);
		var v_cur_date = p_startDate.Clone();
		for(var i = 0; i <= v_diff; i++)
		{
			this.SelectDay(v_cur_date, true, p_skipEvent);
			v_cur_date = v_cur_date.Clone();
			v_cur_date.AddDays(1);
		}
	}
	return;
}
CalendarBase.prototype.SelectWeek = function(p_date)
{
	if(!this.SelectionEnabled || !this.MultipleSelection) { return; }
	var v_start = p_date.GetFirstWeekDay();
	var v_flag = this.SelectedDays.Count != 7;
	if(!v_flag)
	{
		var v_current_date = v_start.Clone();
		for(var i = 0; i < 7; i++)
		{
			if(!this.IsSelected(v_current_date)) { v_flag = true; break; }
			v_current_date.AddDays(1);
		}
	}
	if(v_flag) { this.OnWeekSelected(v_start); } // We only need to raise the event if the week is not already selected.
	this.Select(v_start, p_date.GetLastWeekDay(), false, true);
	return;
}
CalendarBase.prototype.SelectMonth = function(p_date)
{
	if(!this.SelectionEnabled || !this.MultipleSelection) { return; }
	var v_start = p_date.GetFirstMonthDay();
	var v_days_in_month = p_date.GetDaysInMonth();
	var v_flag = this.SelectedDays.Count != v_days_in_month;
	if(!v_flag)
	{
		var v_current_date = v_start.Clone();
		for(var i = 0; i < v_days_in_month; i++)
		{
			if(!this.IsSelected(v_current_date)) { v_flag = true; break; }
			v_current_date.AddDays(1);
		}
	}
	if(v_flag) { this.OnMonthSelected(v_start); } // We only need to raise the event if the month is not already selected.
	this.Select(v_start, p_date.GetLastMonthDay(), false, true);
	return;
}
CalendarBase.prototype.DeselectDay = function(p_date, p_remove, p_skipEvent)
{
	p_date = p_date.DateValue();
	p_remove = TypeOf(p_remove) != 'Boolean' ? true : p_remove;
	if(this.SelectionEnabled && this.IsSelected(p_date))
	{
		if(p_remove) { this.SelectedDays.Remove(p_date); }
		this.RefreshDayCore(p_date, false, false);
		if(!p_skipEvent) { this.OnDayDeselected(p_date); }
	}
	return;
}
CalendarBase.prototype.Deselect = function(p_startDate, p_endDate, p_skipEvent)
{
	if(!this.SelectionEnabled) { return; }
	p_startDate = p_startDate.DateValue();
	if(!p_endDate) { this.DeselectDay(p_startDate, true, p_skipEvent); }
	else
	{
		p_endDate = p_endDate.DateValue();
		var v_diff = Date.DateDiff('d', p_endDate, p_startDate);
		for(var i = 0; i <= v_diff; i++) { this.DeselectDay(p_startDate, true, p_skipEvent); p_startDate.AddDays(1); }
	}
	return;
}
CalendarBase.prototype.DeselectAll = function(p_skipEvent)
{
	if(!this.SelectionEnabled || this.SelectedDays.Count == 0) { return; }
	for(var i = 0; i < this.SelectedDays.Count; i++) { this.DeselectDay(this.SelectedDays.Item(i), false, p_skipEvent); }
	this.SelectedDays.Clear();
	return;
}
CalendarBase.prototype.Next = function() { return; }
CalendarBase.prototype.Previous = function() { return; }
CalendarBase.prototype.NextMonth = function() { this.CurrentDate.AddMonths(1); this.Refresh(); return; }
CalendarBase.prototype.PreviousMonth = function() { this.CurrentDate.AddMonths(-1); this.Refresh(); return; }
CalendarBase.prototype.NextYear = function() { this.CurrentDate.AddYears(1); this.Refresh(); return; }
CalendarBase.prototype.PreviousYear = function() { this.CurrentDate.AddYears(-1); this.Refresh(); return; }
CalendarBase.prototype.ShowMonth = function(p_year, p_month)
{
	if(this.CurrentDate.getFullYear() != p_year || this.CurrentDate.getMonth() != p_month)
	{
		this.CurrentDate.setFullYear(p_year, p_month, 1);
		this.Refresh();
	}
	return;
}
CalendarBase.prototype.Refresh = function() { this.Render(); return; }
CalendarBase.prototype.Render = function() { this.RenderSetup(); return; }
CalendarBase.prototype.RenderSetup = function()
{
	this.UpdateVisibleDates();
	if(this.DayBoxSize.Width > 0) { this.Size.Width = this.CalculateWidth(); }
	if(this.DayBoxSize.Height > 0) { this.Size.Height = this.CalculateHeight(); }
	this.Header.Colspan = this.GetColumnCount();
	this.Footer.Colspan = this.GetColumnCount();
	this.Header.Size.Width = 0;
	this.Footer.Size.Width = 0;
	this.IsRendered = true;
	return;
}
CalendarBase.prototype.GetRenderHtml = function() { return ''; }
CalendarBase.prototype.RefreshDayCore = function(p_date, p_selected, p_mouseOver)
{
	try {
	p_date = p_date.DateValue();
	var v_day_id = this.ElementId + '_day_' + p_date.getFullYear().toString() + '_' + p_date.getMonth().toString() + '_' + p_date.getDate().toString();
	var v_day = Utils.GetElement(v_day_id);
	var v_today = p_date.Equals(Date.Today());
	var v_weekend = p_date.getDay() == 0 || p_date.getDay() == 6;
	if(this.MouseOverEnabled && p_mouseOver)
	{
		v_day.style.background = this.MouseOverDayBackground.ToCssString();
		v_day.style.color = this.MouseOverDayForeColor;
	}
	else if(this.ShowToday && v_today)
	{
		v_day.style.background = this.TodayBackground.ToCssString();
		v_day.style.color = this.TodayDayForeColor;
	}
	else if(this.SelectionEnabled && p_selected)
	{
		v_day.style.background = this.SelectedDayBackground.ToCssString();
		v_day.style.color = this.SelectedDayForeColor;
	}
	else if(v_weekend)
	{
		v_day.style.background = this.WeekendDayBackground.ToCssString();
		v_day.style.color = this.WeekendDayForeColor;
	}
	else
	{
		v_day.style.background = this.NormalDayBackground.ToCssString();
		v_day.style.color = this.ForeColor;
	}
	this.RefreshDay(v_day, p_date, p_mouseOver, v_today, p_selected, v_weekend);
	} catch(e) {  }
	return;
}
CalendarBase.prototype.RefreshDay = function(p_dayElement, p_date, p_isMouseOver, p_isToday, p_isSelected, p_isWeekend) { return; }
CalendarBase.prototype.UpdateVisibleDates = function()
{
	this.VisibleStartDate = this.CurrentDate.GetFirstMonthDay();
	this.VisibleEndDate = this.CurrentDate.GetLastMonthDay();
	return;
}
CalendarBase.prototype.ParseDateFromElementId = function(p_elementId)
{
	var v_datestring = p_elementId.replace(this.ElementId + '_day_', '');
	var v_parts = v_datestring.split('_');
	return new Date(v_parts[0], v_parts[1], v_parts[2], 0, 0, 0, 0);
}
CalendarBase.prototype.GetColumnCount = function() { return this.WeekNumbers.Visible ? 8 : 7; }
CalendarBase.prototype.GetRowCount = function()
{
	var v_rows = 6;
	if(this.Header.Visible) { v_rows++; }
	if(this.WeekDayHeaders.Visible) { v_rows++; }
	if(this.Footer.Visible) { v_rows++; }
	return v_rows;
}
CalendarBase.prototype.CalculateWidth = function() { return ((this.ShowWeekend ? 7 : 5) * this.DayBoxSize.Width) + (this.WeekNumbers.Visible ? this.WeekNumbers.Size.Width : 0); }
CalendarBase.prototype.CalculateHeight = function() { return (6 * this.DayBoxSize.Height) + (this.Header.Visible ? this.Header.Size.Height : 0) + (this.Footer.Visible ? this.Footer.Size.Height : 0) + (this.WeekDayHeaders.Visible ? this.WeekDayHeaders.Size.Height : 0); }
