

var __aspxCalendarWeekCount = 6;
var __aspxCalendarMsPerDay = 86400000;
var __aspxActiveCalendar = null;
ASPxClientCalendar = _aspxCreateClass(ASPxClientEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.SelectionChanging = new ASPxClientEvent();
        this.SelectionChanged = new ASPxClientEvent();
        this.VisibleMonthChanged = new ASPxClientEvent();
        
        this.isMouseDown = false;        
        this.forceMouseDown = false;
        
        this.selection = new ASPxClientCalendarSelection();
        this.selectionTransaction = null;        
        this.selectionStartDate = null;
        this.selectionPrevStartDate = null;
        this.lastSelectedDate = null;
        
        this.selectionCtrl = false;
        this.selectionByWeeks = false;        
        
        // set from server
        this.visibleDate = new Date();
        this.firstDayOfWeek = 0;                
        this.readonly = false;
        this.enabled = true;        
        this.columns = 1;
        this.rows = 1;
        this.enableFast = true;
        this.enableMulti = false;
        this.minDate = null;
        this.maxDate = null;
        this.customDraw = false;        
        this.showWeekNumbers = true;
        this.showDayHeaders = true;
        this.isDateEditCalendar = false;
        
        // for KB support
        this.isDateChangingByKeyboard = false;
        // for DateEdit
        this.MainElementClick = new ASPxClientEvent();                        
    },
    Initialize: function() {
        this.nodeCache = { };
        
        this.CreateViews();
        if(this.enableFast)
            this.fastNavigation = new ASPxClientCalendarFastNavigation(this);
        this.selectionTransaction = new ASPxClientCalendarSelectionTransaction(this);
        this.selectionPrevStartDate = this.selection.GetFirstDate();
        _aspxAttachEventToDocument("mouseup", aspxCalDocMouseUp);
        _aspxAttachEventToElement(this.GetMainElement(), "click", new Function("aspxCalMainElemClick('" + this.name + "');"));        
        this.InitKeyboardInput();
        this.SaveClientState();	// for callbacks
        ASPxClientEdit.prototype.Initialize.call(this);
    },
    InitKeyboardInput: function(){
        var kbInput = this.GetInputElement();        
        if (_aspxIsExistsElement(kbInput)) {
            kbInput.autocomplete = "off";
            _aspxAttachKBSupportEventsToElement(kbInput, this.name);
        }
    },
    
    // Base
    FindInputElement: function() {
        return this.GetChild("_KBS");
    },
    FindStateInputElement: function() {
        return _aspxGetElementById(this.name + "_STATE");
    },
    GetValue: function() {
        return this.selection.GetFirstDate();
    },
    GetValueString: function() {
        var value = this.GetValue();
        return value == null ? null : ASPxClientCalendar.GetInvariantDateString(value);
    },
    SetValue: function(date) {
        if(date != null)
            this.SetVisibleDate(date);
        this.SetSelectedDate(date);
    },
    
    GetFastNavigation: function() {
        return this.fastNavigation;
    },          
    // Views
    GetViewKey: function(row, column) {
        return row + "x" + column;
    },
    GetView: function(row, column) {
        var key = this.GetViewKey(row, column);
        return this.views[key];
    },
    CreateViews: function() {
        this.views = { };
        var key;
        for(var row = 0 ; row < this.rows; row++) {            
            for(var col = 0; col < this.columns; col++) {
                key = this.GetViewKey(row, col);
                var view = new ASPxClientCalendarView(this, row, col);
                view.Initialize();
                this.views[key] = view;
            }
        }
    },
    IsFastNavigationActive: function() {
        if (_aspxIsExists(this.fastNavigation))
            return this.fastNavigation.GetPopup().IsVisible();
        return false;
    },
    
    // Logic
    IsDateSelected: function(date) {
        return this.selection.Contains(date);
    },
    IsDateVisible: function(date) {
        var startDate = ASPxClientCalendar.CloneDate(this.GetView(0, 0).visibleDate);
        startDate.setDate(1);        
        var endDate = ASPxClientCalendar.CloneDate(this.GetView(this.rows - 1, this.columns - 1).visibleDate);
        endDate.setDate(ASPxClientCalendar.GetDaysInMonth(endDate.getMonth(), endDate.getFullYear()));
        return (date >= startDate) && (date < endDate);
    },
    IsDateWeekend: function(date) {
        var day = date.getDay();
        return day == 0 || day == 6;
    },    
    IsMultiView: function() {
        return this.columns > 1 || this.rows > 1;
    },
    IsDateInRange: function(date) {
        return date == null || 
            ((this.minDate == null || date >= this.minDate) && 
             (this.maxDate == null || date <= this.maxDate));
    },
    
    // Cache
    GetCachedElementById: function(id) {
        if(!_aspxIsExistsElement(this.nodeCache[id]))
            this.nodeCache[id] = _aspxGetElementById(id);
        return this.nodeCache[id]; 
    },
        
    // Rendering
    Update: function() {
        if(this.customDraw) 
            this.SendPostBack("");
        else {
            for(var key in this.views) {
                if (_aspxIsExists(this.views[key].Update))
                    this.views[key].Update();
            }
        }
    },    
    ApplySelectionByDiff: function(selection, save) {
        var toShow = [ ];
        var toHide = [ ];
        
        var dates =  selection.GetDates();
        var oldDates = this.selection.GetDates();
        var date;

        for(var i = 0; i < dates.length; i++) {
            date = dates[i];
            if(!this.selection.Contains(date));
                toShow.push(date);
        }
        for(var i = 0; i < oldDates.length; i++) {
            date = oldDates[i];
            if(!selection.Contains(date))
                toHide.push(date);
        }
    
        for(var key in this.views) {
            if (_aspxIsExists(this.views[key].UpdateSelection)) {
                this.views[key].UpdateSelection(toHide, false);
                this.views[key].UpdateSelection(toShow, true);
            }
        }
        
        this.selection.Assign(selection);
        if(save)
            this.SaveClientState();
    },
    
    // Style
    ImportEtalonStyle: function(info, suffix) {
        var cell = this.GetCachedElementById(this.name + "_EC_" + suffix);
        if(_aspxIsExistsElement(cell))
            info.Import(cell);            
    },

    // Client sync    
    SaveClientState: function() {
        var element = this.FindStateInputElement();
        if (element != null) {
            var state = ASPxClientCalendar.GetInvariantDateString(this.visibleDate);            
            if(this.selection.count > 0) 
                state += ":" + this.FormatDates(this.selection.GetDates(), ":");
            element.value = state;
        }
    },        
    FormatDates: function(dates, separator) {
        var result = "";
        for(var i = 0; i < dates.length; i++) {
            if (result.length > 0)
                result += separator;
            result += ASPxClientCalendar.GetInvariantDateString(dates[i]);           
        }
        return result;
    },
    // Events
    OnArrowUp: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {
            var newDate = this.GetNearestDayForToday();
            
            if (_aspxIsExists(this.lastSelectedDate))
                newDate = ASPxClientCalendar.GetPrevWeekDate(this.lastSelectedDate);
            this.CorrectVisibleMonth(newDate, false);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;
    },
    OnArrowDown: function(evt) {        
        if (!this.readonly && !this.IsFastNavigationActive()) {
            var newDate = this.GetNearestDayForToday();
                
            if (_aspxIsExists(this.lastSelectedDate))        
                newDate = ASPxClientCalendar.GetNextWeekDate(this.lastSelectedDate);
            this.CorrectVisibleMonth(newDate, true);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;
    },
    OnArrowLeft: function(evt) {    
        if (!this.readonly && !this.IsFastNavigationActive()) {  
            var newDate = this.GetNearestDayForToday();
            
            if (_aspxIsExists(this.lastSelectedDate))    
                newDate = ASPxClientCalendar.GetTomorrowDate(this.lastSelectedDate);
            this.CorrectVisibleMonth(newDate, false);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;
    },
    OnArrowRight: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {        
            var newDate = this.GetNearestDayForToday();
            
            if (_aspxIsExists(this.lastSelectedDate))
                newDate = ASPxClientCalendar.GetYesterDate(this.lastSelectedDate);
            this.CorrectVisibleMonth(newDate, true);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;
    },
    OnPageUp: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {            
            var newDate = this.GetNearestDayForToday();
            if (_aspxIsExists(this.lastSelectedDate)) {
                if (evt.ctrlKey)
                    newDate = ASPxClientCalendar.GetPrevYearDate(this.lastSelectedDate);
                else
                    newDate = ASPxClientCalendar.GetPrevMonthDate(this.lastSelectedDate);            
            }
            this.CorrectVisibleMonth(newDate, false);        
            this.DoKeyboardSelection(newDate);
        }
        return true;    
    },
    OnPageDown: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {
            var newDate = this.GetNearestDayForToday();
            if (_aspxIsExists(this.lastSelectedDate)) {
                if (evt.ctrlKey)
                    newDate = ASPxClientCalendar.GetNextYearDate(this.lastSelectedDate);
                else
                    newDate = ASPxClientCalendar.GetNextMonthDate(this.lastSelectedDate);
            }
            this.CorrectVisibleMonth(newDate, true);
            this.DoKeyboardSelection(newDate);
        }
        return true;
    },
    OnEndKeyDown: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {    
            var newDate = this.GetNearestDayForToday();
            if (_aspxIsExists(this.lastSelectedDate))            
                newDate = ASPxClientCalendar.CloneDate(this.lastSelectedDate);
                
            newDate = ASPxClientCalendar.GetLastDayInMonthDate(newDate);
            this.CorrectVisibleMonth(newDate, false);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;
    },
    OnHomeKeyDown: function(evt) {
        if (!this.readonly && !this.IsFastNavigationActive()) {
            var newDate = this.GetNearestDayForToday();
            if (_aspxIsExists(this.lastSelectedDate))            
                newDate = ASPxClientCalendar.CloneDate(this.lastSelectedDate);
            newDate = ASPxClientCalendar.GetFirstDayInMonthDate(newDate);            
                
            this.CorrectVisibleMonth(newDate, false);
            this.DoKeyboardSelection(newDate, evt.shiftKey);
        }
        return true;    
    },
    OnEscape: function() {
        if (this.IsFastNavigationActive())
            this.GetFastNavigation().Hide();
        else
            return ASPxClientEdit.prototype.OnEscape.call(this);
        return true;
    },
    
    OnShiftMonth: function(offset) {
        if(offset) {
            var date = ASPxClientCalendar.AddMonths(this.visibleDate, offset);            
            this.OnVisibleMonthChanged(date);
        }
    },
    OnDayMouseDown: function(date, shift, ctrl, byWeeks) {
        this.isMouseDown = true;
        this.selectionByWeeks = byWeeks;
        this.selectionTransaction.Start();
                
        if(this.enableMulti) {
            if(ctrl) {
                this.selectionCtrl = true;
                this.selectionTransaction.CopyFromBackup();
            } else
                this.selectionCtrl = false;

            if(shift && _aspxIsExists(this.selectionPrevStartDate)) {
                this.selectionStartDate = this.selectionPrevStartDate;                                    
                this.selectionTransaction.selection.AddRange(this.selectionStartDate, date);
                if(byWeeks)
                    this.selectionTransaction.selection.AddWeek(date);
            } else {
                this.selectionStartDate = date;
                this.selectionPrevStartDate = date;
                if(byWeeks)
                    this.selectionTransaction.selection.AddWeek(date);
                else
                    this.selectionTransaction.selection.Add(date);
            }
        } else
            this.selectionTransaction.selection.Add(date);
                
        this.ApplySelectionByDiff(this.selectionTransaction.selection, false);
    },
    OnDayMouseOver: function(date) {
        if(this.enableMulti) {
            if(this.selectionCtrl)
                this.selectionTransaction.CopyFromBackup();
            else
                this.selectionTransaction.selection.Clear();
            this.selectionTransaction.selection.AddRange(this.selectionStartDate, date);
            if(this.selectionByWeeks) {
                this.selectionTransaction.selection.AddWeek(date);
                this.selectionTransaction.selection.AddWeek(this.selectionStartDate);
            }
        } else {
            this.selectionTransaction.selection.Clear();
            this.selectionTransaction.selection.Add(date);
        }
                    
        this.ApplySelectionByDiff(this.selectionTransaction.selection, false);
    },
    OnDayMouseUp: function(date) {
        if (!__aspxIE && this.isMouseDown)
            this.OnMainElementClick();
    
        this.isMouseDown = false;

        if(this.enableMulti && this.selectionCtrl && this.selectionTransaction.backup.Contains(date) &&
            ASPxClientCalendar.AreDatesEqual(date, this.selectionStartDate)) {
            if(this.selectionByWeeks)
                this.selectionTransaction.selection.RemoveWeek(date);
            else
                this.selectionTransaction.selection.Remove(date);
        }
        this.lastSelectedDate = ASPxClientCalendar.CloneDate(date);
        this.OnSelectionChanging();            
    },
        
    OnTodayClick: function() {
        var now = new Date();    
        var date = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        if(this.IsDateInRange(date)) {
            this.selectionTransaction.Start();
            this.selectionTransaction.selection.Add(date);
            this.OnSelectionChanging();
            
            if(!ASPxClientCalendar.AreDatesOfSameMonth(date, this.visibleDate))
                this.OnVisibleMonthChanged(date);        
        }
    },
    OnClearClick: function() {
        this.selectionTransaction.Start();
        this.OnSelectionChanging();
        
        this.selectionStartDate = null;
        this.selectionPrevStartDate = null;       
        this.lastSelectedDate = null;
    },
    OnSelectMonth: function(row, column) {        
        var txn = this.selectionTransaction;
        txn.Start();
        var date = ASPxClientCalendar.CloneDate(this.GetView(row, column).visibleDate);
        date.setDate(1);
        do {        
            if(this.IsDateInRange(date))
                txn.selection.Add(date);
            date = ASPxClientCalendar.AddDays(date, 1);
        } while(date.getDate() > 1);
        this.OnSelectionChanging();
    },
    OnTitleClick: function(row, column) {
        this.fastNavigation.activeView = this.GetView(row, column);
        this.fastNavigation.Prepare();
        this.fastNavigation.Show();
    },
    OnMainElementClick: function() {
        this.SetFocus();
        this.MainElementClick.FireEvent(this);
    },
    OnSelectionChanging: function() {
        if(!this.SelectionChanging.IsEmpty()){
            var args = new ASPxClientCalendarSelectionEventArgs(false, this.selectionTransaction.selection);
            this.SelectionChanging.FireEvent(this, args);        
        }
        var changed = this.selectionTransaction.IsChanged();
        this.selectionTransaction.Commit();
        if(changed)
            this.OnValueChanged();        
    },
    OnVisibleMonthChanged: function(date) {
        var offsetInternal = ASPxClientCalendar.GetOffsetInMonths(this.visibleDate, date);
        this.SetVisibleDate(date);
        var processOnServer = this.autoPostBack;        
        if(_aspxIsExists(this.RaiseVisibleMonthChanged))
            processOnServer = this.RaiseVisibleMonthChanged(offsetInternal);
        if(processOnServer)
            this.SendPostBackInternal("");        
    },
    OnSelectionCancelled: function() {
        this.isMouseDown = false;        
        this.selectionTransaction.Rollback();
    },
    RaiseValueChangedEvent: function() {
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        if(_aspxIsExists(this.RaiseSelectionChanged))
            processOnServer = this.RaiseSelectionChanged(processOnServer);
        return processOnServer;
    },
    
    // Value
    SetVisibleDate: function(date) {
        var old = this.visibleDate;
        this.visibleDate = date;
        this.SaveClientState();
        if(!ASPxClientCalendar.AreDatesOfSameMonth(date, old))
            this.Update(); // here PostBack if custom draw
    },
    SetSelectedDate: function(date) {
        if(this.IsDateInRange(date)) {            
            var selection = new ASPxClientCalendarSelection();
            if(date != null) {
                selection.Add(date);
                this.lastSelectedDate = ASPxClientCalendar.CloneDate(date);
            }
            this.ApplySelectionByDiff(selection, true);
        }
    },
    // Utils
    CorrectVisibleMonth: function(newDate, isForwardDirection) {
        var offset = ASPxClientCalendar.GetOffsetInMonths(this.visibleDate, newDate);
        if (this.IsMultiView() && offset != 0) {
            var view = isForwardDirection ? this.GetView(this.rows - 1, this.columns - 1) : 
                                                this.GetView(0, 0);
            offset = this.IsDateVisible(newDate) ? 0 :
                            ASPxClientCalendar.GetOffsetInMonths(view.visibleDate, newDate);
                            
        }
        if (!this.IsDateInRange(newDate))
            offset = 0;
        if (offset != 0)
            this.OnShiftMonth(offset);
    },
    DoKeyboardSelection: function(date, shift) {
        if (this.IsDateInRange(date)) {
            this.isDateChangingByKeyboard = true;
        
            this.selectionTransaction.Start();

            if(this.enableMulti && shift && _aspxIsExists(this.selectionStartDate))
                this.selectionTransaction.selection.AddRange(this.selectionStartDate, date);
            else {
                this.selectionTransaction.selection.Add(date);
                this.selectionStartDate = date;
            }
            this.lastSelectedDate = ASPxClientCalendar.CloneDate(date);
            this.OnSelectionChanging();
            
            this.isDateChangingByKeyboard = false;
        }
    },
    GetNearestDayForToday: function() {
        var now = new Date();
        var ret = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        
        if (_aspxIsExists(this.minDate) && !this.IsDateInRange(ret))
            ret = ASPxClientCalendar.CloneDate(this.minDate);
        return ret;
    },
    // API
    GetSelectedDate: function() {
        return this.GetValue();
    },
    GetVisibleDate: function() {
        return this.visibleDate;
    },
    SelectDate: function(date) {
        if(_aspxIsExists(date)) {
            this.selection.Add(date);
            this.Update();
        }
    },
    SelectRange: function(start, end) {
        if(_aspxIsExists(start) && _aspxIsExists(end)) {
            this.selection.AddRange(start, end);
            this.Update();
        }
    },
    DeselectDate: function(date) {
        if(_aspxIsExists(date)) {
            this.selection.Remove(date);
            this.Update();    
        }
    },
    DeselectRange: function(start, end) {
        if(_aspxIsExists(start) && _aspxIsExists(end)) {
            this.selection.RemoveRange(start, end);
            this.Update();
        }
    },
    ClearSelection: function() {
        this.selection.Clear();
        this.Update();
    },
    GetSelectedDates: function() {
        return this.selection.GetDates();
    },

    RaiseSelectionChanged: function(processOnServer){
        if(!this.SelectionChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);        
            this.SelectionChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    RaiseVisibleMonthChanged: function(offsetInternal){
        var processOnServer = this.autoPostBack;
        if(!this.VisibleMonthChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            args.offsetInternal = offsetInternal;
            this.VisibleMonthChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    }
});

ASPxClientCalendar.AreDatesEqual = function(date1, date2) {
    if(date1 == date2)  // if are empty
        return true;
    if(!date1 || !date2)
        return false;
    return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate();
}
ASPxClientCalendar.AreDatesOfSameMonth = function(date1, date2) {
    if(!date1 || !date2)
        return false;
    return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth();
}
ASPxClientCalendar.GetUTCTime = function(date) {
    return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
}
ASPxClientCalendar.GetFirstDayOfYear = function(date) {
    return new Date(date.getFullYear(), 0, 1);        
}
ASPxClientCalendar.GetDayOfYear = function(date) {
    var ms = ASPxClientCalendar.GetUTCTime(date) - 
        ASPxClientCalendar.GetUTCTime(ASPxClientCalendar.GetFirstDayOfYear(date));
    return 1 + Math.floor(ms / __aspxCalendarMsPerDay);
}
ASPxClientCalendar.GetInvariantDateString = function(date) {
    if(date) {
        var day = date.getDate();
        var month = date.getMonth() + 1;
        var year = date.getFullYear();
        var result = "";
        if(month < 10)
            result += "0";
        result += month.toString() + "/";
        if(day < 10)
            result += "0";
        result += day.toString() + "/" + year.toString();
        return result;
    }
    return "01/01/0001";
}
ASPxClientCalendar.GetISO8601WeekOfYear = function(date) {
    var firstDate = new Date(date.getFullYear(), 0, 1);
    var firstDayOfWeek = firstDate.getDay();
    if(firstDayOfWeek == 0)
        firstDayOfWeek = 7;
    var daysInFirstWeek = 8 - firstDayOfWeek;
            
    var lastDate = new Date(date.getFullYear(), 11, 31);            
    var lastDayOfWeek = lastDate.getDay();
    if(lastDayOfWeek == 0)
        lastDayOfWeek = 7;
    var daysInLastWeek = 8 - lastDayOfWeek;    

    var fullWeeks = Math.ceil((ASPxClientCalendar.GetDayOfYear(date) - daysInFirstWeek) / 7);
    var result = fullWeeks;            
            
    if(daysInFirstWeek > 3)
        result++;
        
    var isThursday = firstDayOfWeek == 4 || lastDayOfWeek == 4;
    if(result > 52 && !isThursday)
        result = 1;
    if(result == 0)
        return ASPxClientCalendar.GetISO8601WeekOfYear(new Date(date.getFullYear() - 1, 11, 31));
    return result;
}
ASPxClientCalendar.GetNextWeekDate = function(date) {
    var ret = new Date(date.getTime());    
    var newDay = date.getDate() + 7;
    ret.setDate(newDay);
    return ret;
}
ASPxClientCalendar.GetPrevWeekDate = function(date) {
    var ret = new Date(date.getTime());
    var newDay = date.getDate() - 7;
    ret.setDate(newDay);
    return ret;
}
ASPxClientCalendar.GetTomorrowDate = function(date) {
    var ret = new Date(date.getTime());
    ret.setDate(ret.getDate() - 1);
    return ret;
}
ASPxClientCalendar.GetYesterDate = function(date) {
    var ret = new Date(date.getTime());
    ret.setDate(ret.getDate() + 1);
    return ret;
}
ASPxClientCalendar.GetNextMonthDate = function(date) {
    var ret = new Date(date.getTime());
    var maxDateInNextMonth = ASPxClientCalendar.GetDaysInMonth(ret.getMonth() + 1, ret.getFullYear());
    if (ret.getDate() > maxDateInNextMonth)
        ret.setDate(maxDateInNextMonth);
    ret.setMonth(ret.getMonth() + 1);
    return ret;
}
ASPxClientCalendar.GetNextYearDate = function(date) {
    var ret = new Date(date.getTime());
    var maxDateInPrevYearMonth = ASPxClientCalendar.GetDaysInMonth(ret.getMonth(), ret.getFullYear() + 1);
    if (ret.getDate() > maxDateInPrevYearMonth)
        ret.setDate(maxDateInPrevYearMonth);
    ret.setFullYear(ret.getFullYear() + 1);
    return ret;
}
ASPxClientCalendar.GetPrevMonthDate = function(date) {
    var ret = new Date(date.getTime());
    var maxDateInPrevMonth = ASPxClientCalendar.GetDaysInMonth(ret.getMonth() - 1, ret.getFullYear());
    if (ret.getDate() > maxDateInPrevMonth)
        ret.setDate(maxDateInPrevMonth);
    ret.setMonth(ret.getMonth() - 1);
    return ret;
}
ASPxClientCalendar.GetPrevYearDate = function(date) {
    var ret = new Date(date.getTime());
    var maxDateInPrevYearMonth = ASPxClientCalendar.GetDaysInMonth(ret.getMonth(), ret.getFullYear() - 1);
    if (ret.getDate() > maxDateInPrevYearMonth)
        ret.setDate(maxDateInPrevYearMonth);
    ret.setFullYear(ret.getFullYear() - 1);
    return ret;
}
ASPxClientCalendar.GetFirstDayInMonthDate = function(date) {
    var ret = new Date(date.getTime());
    ret.setDate(1);
    return ret;
}
ASPxClientCalendar.GetLastDayInMonthDate = function(date) {
    var ret = new Date(date.getTime());
    var maxDateInYearMonth = ASPxClientCalendar.GetDaysInMonth(ret.getMonth(), ret.getFullYear());
    ret.setDate(maxDateInYearMonth);
    return ret;
}

ASPxClientCalendar.AddDays = function(date, count) {
    var newDate = ASPxClientCalendar.CloneDate(date);
    newDate.setDate(count + newDate.getDate());
    return newDate;
}
ASPxClientCalendar.AddMonths = function(date, count) {
    var newDate = ASPxClientCalendar.CloneDate(date);
    newDate.setMonth(count + newDate.getMonth());
    // new month may not have sufficient days
    if(newDate.getDate() < date.getDate())
        newDate = ASPxClientCalendar.AddDays(newDate, -newDate.getDate());    
    return newDate;
}
ASPxClientCalendar.CloneDate = function(date) {
    var cloned = new Date();
    cloned.setTime(date.getTime());
    return cloned;
}
ASPxClientCalendar.GetDecadeStartYear = function(year) {
    return 10 * Math.floor(year / 10);
}
ASPxClientCalendar.GetDaysInRange = function(start, end) {
    return 1 + (ASPxClientCalendar.GetUTCTime(end) - ASPxClientCalendar.GetUTCTime(start)) / __aspxCalendarMsPerDay;
};
ASPxClientCalendar.GetDaysInMonth = function(month, year) {
    var d = new Date(year, month + 1, 0);
    return d.getDate();
};
ASPxClientCalendar.GetOffsetInMonths = function(start, end) {
    return end.getMonth() - start.getMonth() + 12 * (end.getFullYear() - start.getFullYear());
};


/////////////////////////////////////////////////////////////////

ASPxClientCalendarSelection = _aspxCreateClass(null, {
    constructor: function() {
        this.dates = { };
        this.count = 0;  
    },

    Assign: function(source) {
        this.Clear();
        for(var key in source.dates)
            this.Add(source.dates[key]);
    },
    Clear: function() {
        if(this.count > 0) {
            this.dates = { };
            this.count = 0;
        }
    },
    Equals: function(selection) {
        if(this.count != selection.count)
            return false;
        for(var key in this.dates)
            if(!selection.ContainsKey(key))
                return false;
        return true;
    },
    Contains: function(date) {
        var key = this.GetKey(date);
        return this.ContainsKey(key);
    },
    ContainsKey: function(key) {
        return _aspxIsExists(this.dates[key]);
    },
        
    Add: function(date) {
        var key = this.GetKey(date);
        if(!this.ContainsKey(key)) {
            this.dates[key] = ASPxClientCalendar.CloneDate(date);
            this.count++;
        }
    },
    AddArray: function(dates) {
        for(var i = 0; i < dates.length; i++)
            this.Add(dates[i]);
    },
    AddRange: function(start, end)  {
        if(end < start) {
            this.AddRange(end, start);
            return;
        }
        var count = ASPxClientCalendar.GetDaysInRange(start, end);
        var date = ASPxClientCalendar.CloneDate(start);        
        for(var i = 0; i < count; i++) {
            this.Add(date);
            date = ASPxClientCalendar.AddDays(date, 1);
        }
    },
    AddWeek: function(startDate) {
        this.AddRange(startDate, ASPxClientCalendar.AddDays(startDate, 6));
    },
    
    Remove: function(date) {
        var key = this.GetKey(date);
        if(this.ContainsKey(key)) {
            delete this.dates[key];
            this.count--;
        }
    },
    RemoveArray: function(dates) {
        for(var i = 0; i < dates.length; i++)
            this.Remove(dates[i]);
    },
    RemoveRange: function(start, end) {
        if(end < start) {
            this.RemoveRange(end, start);
            return;
        }
        var count = ASPxClientCalendar.GetDaysInRange(start, end);
        var date = ASPxClientCalendar.CloneDate(start);        
        for(var i = 0; i < count; i++) {
            this.Remove(date);
            date = ASPxClientCalendar.AddDays(date, 1);
        }
    },
    RemoveWeek: function(startDate) {
        this.RemoveRange(startDate, ASPxClientCalendar.AddDays(startDate, 6));
    },
    
    GetDates: function() {
        var result = [ ];
        for(var key in this.dates) {
            var date = this.dates[key];
            if (ASPxIdent.IsDate(date))
                result.push(ASPxClientCalendar.CloneDate(date));
        }
        return result;        
    },
    GetFirstDate: function() {
        if(this.count == 0)
            return null;
        for(var key in this.dates) {
            var date = this.dates[key];
            if (ASPxIdent.IsDate(date))
                return ASPxClientCalendar.CloneDate(date);
        }
        return null;
    },
        
    GetKey: function(date) {        
        return ASPxClientCalendar.GetInvariantDateString(date);
        // Note: Opera is buggy with numeric keys
    }
});

/////////////////////////////////////////////////////////////////

ASPxClientCalendarSelectionTransaction = _aspxCreateClass(null, {
    constructor: function(calendar) {
        this.calendar = calendar;
        this.isActive = false;
    
        this.backup = new ASPxClientCalendarSelection();
        this.selection = new ASPxClientCalendarSelection;
    },
    
    Start: function() {
        if(this.isActive)
            this.Rollback();
        this.backup.Assign(this.calendar.selection);
        this.selection.Clear();
        this.isActive = true;
        __aspxActiveCalendar = this.calendar;
    },
    Commit: function() {        
        this.calendar.ApplySelectionByDiff(this.selection, true);
        this.Reset();
    },
    Rollback: function() {
        this.calendar.ApplySelectionByDiff(this.backup);        
        this.Reset();
    },
    
    Reset: function() {
        this.backup.Clear();
        this.selection.Clear();
        this.isActive = false;
        __aspxActiveCalendar = null;
    },
    CopyFromBackup: function() {
        this.selection.Assign(this.backup);
    },
    IsChanged: function() {
        return !this.backup.Equals(this.selection);
    }
});

/////////////////////////////////////////////////////////////////
         
ASPxClientCalendarView = _aspxCreateClass(null, {
    constructor: function(calendar, row, column) {
        this.row = row;
        this.column = column;
        this.calendar = calendar;    
        
        var temp = column + row;
        this.isLowBoundary = temp == 0;
        this.isHighBoundary = temp == calendar.rows + calendar.columns - 2; 
        
        this.visibleDate = null;
        this.startDate = null;        
    },
    
    Initialize: function() {
        this.dayCellCache = { };
        this.dayStyleCache = { };    
    
        this.UpdateDate();
        this.UpdateSelection(this.calendar.selection.GetDates(), true);        
        var cell = this.GetMonthCell();
        if(_aspxIsExistsElement(cell))
            _aspxPreventElementDragAndSelect(cell, false);
        if(this.calendar.enabled && !this.calendar.readonly)
            this.AttachMouseEvents();
    },
    AttachMouseEvents: function() {
        var index;
        var cell;
        if(this.calendar.showDayHeaders) {
            var headCells = this.GetMonthTable().rows[0].cells; 
            var dayNameIndex = 0;
            // cross cell
            if(this.calendar.showWeekNumbers) {
                dayNameIndex++;
                cell = headCells[0];
                if(this.calendar.enableMulti) {
                    _aspxAttachEventToElement(cell, "click", 
                        new Function("aspxCalSelectMonth('" + this.calendar.name + "', " + this.row + ", " + this.column + ")"));                    
                    _aspxSetPointerCursor(cell);
                }
                this.AttachCancelSelect(cell);
            }            
            // day headers
            for(var j = 0; j < 7; j++)
                this.AttachCancelSelect(headCells[dayNameIndex++]);
        }
        for(var i = 0; i < __aspxCalendarWeekCount; i++) {
            // week number
            if(this.calendar.showWeekNumbers) {
                cell = this.GetWeekNumberCell(i);
                if(this.calendar.enableMulti)
                    this.AttachDayMouseEvents(cell, this.GetDayMouseEventFunction(7 * i, true));                
                else
                    this.AttachCancelSelect(cell);
            }
            // days
            var date;
            for(var j = 0; j < 7; j++) {
                index = i * 7 + j;
                cell = this.GetDayCell(index);
                date = this.GetDateByIndex(index);
                if(!this.calendar.enableMulti && this.IsDateVisible(date) && this.calendar.IsDateInRange(date))
                    _aspxSetPointerCursor(cell);
                this.AttachDayMouseEvents(cell, this.GetDayMouseEventFunction(index, false));                
            }
        }
    },
    AttachDayMouseEvents: function(cell, handler) {
        var types = ["down", "up", "over"];
        for(var i = 0; i < types.length; i++)
            _aspxAttachEventToElement(cell, "mouse" + types[i], handler);
    },
    AttachCancelSelect: function(element) {
        _aspxAttachEventToElement(element, "mouseup", aspxCalCancelSelect);
    },
    GetDayMouseEventFunction: function(index, selectWeeks) {
        return new Function("e", "aspxCalDayMouseEvt('" + this.calendar.name + "', " + this.row + ", " + this.column + ", " + index + ", e, " + selectWeeks + ");");
    },
        
    // Dates
    UpdateDate: function() {
        this.visibleDate = ASPxClientCalendar.AddMonths(this.calendar.visibleDate, 
            this.row * this.calendar.columns + this.column);
        var date = ASPxClientCalendar.CloneDate(this.visibleDate);
        date.setDate(1);
        var offset = date.getDay() - this.calendar.firstDayOfWeek;
        if(offset < 0)
            offset += 7;
        this.startDate = ASPxClientCalendar.AddDays(date, -offset);
    },
    GetDateByIndex: function(index) {
        return ASPxClientCalendar.AddDays(this.startDate, index);
    },
    GetIndexByDate: function(date) {
        return ASPxClientCalendar.GetDaysInRange(this.startDate, date) - 1;
    },
    IsDateOtherMonth: function(date) {
        if(date == null)
            return false;
        return date.getMonth() != this.visibleDate.getMonth() ||
            date.getFullYear() != this.visibleDate.getFullYear();
    },
    
    // Elements
    GetDayCell: function(index) {
        if(_aspxIsExists(this.dayCellCache[index]))
            return this.dayCellCache[index];
        var mt = this.GetMonthTable();
        var colIndex = index % 7;
        var rowIndex = (index - colIndex) / 7;
        if(this.calendar.showDayHeaders)
            rowIndex++;        
        if(this.calendar.showWeekNumbers)
            colIndex++;
        var cell = mt.rows[rowIndex].cells[colIndex];
        this.dayCellCache[index] = cell;
        return cell;
    },
    GetMonthTable: function() {
        return this.GetCachedElementById("mt");
    },    
    GetMonthCell: function() {
        return this.GetCachedElementById("mc");
    },
    GetWeekNumberCell: function(index) {
        if(this.calendar.showDayHeaders)
            index++;
        return this.GetMonthTable().rows[index].cells[0];
    },
    GetTitleElement: function() {
        return this.GetCachedElementById("t");
    },
                
    // Render
    Update: function() {
        this.dayStyleCache = { };
        this.UpdateDate();        
        this.UpdateDays();
        this.UpdateTitle();        
        this.UpdateSelection(this.calendar.selection.GetDates(), true);
    },
    UpdateDays: function() {  
        var date = ASPxClientCalendar.CloneDate(this.startDate);
        var offset = this.calendar.firstDayOfWeek - 1;
        if(offset < 0)
            offset += 7;                
        var weekNumber = ASPxClientCalendar.GetISO8601WeekOfYear(ASPxClientCalendar.AddDays(date, offset));
        var cell;
        for(var i = 0; i < __aspxCalendarWeekCount; i++) {
            if(this.calendar.showWeekNumbers)                
                this.GetWeekNumberCell(i).innerHTML = (weekNumber < 10 ? "0" : "") + weekNumber.toString();            
            for(var j = 0; j < 7; j++) {                
                cell = this.GetDayCell(i * 7 + j);                
                cell.innerHTML = this.IsDateVisible(date) ? date.getDate() : "&nbsp;";
                this.ApplyDayCellStyle(cell, date);
                date = ASPxClientCalendar.AddDays(date, 1);
            }    
            if(++weekNumber > 52)
                weekNumber = ASPxClientCalendar.GetISO8601WeekOfYear(ASPxClientCalendar.AddDays(date, offset));
        }      
    },
    UpdateTitle: function() {        
        this.GetTitleElement().innerHTML = __aspxDateFormatInfo.monthNames[this.visibleDate.getMonth()] +
            " " + this.visibleDate.getFullYear(); 
    },
    UpdateSelection: function(dates, showSelection) {  
        var index;        
        var maxIndex = 7 * __aspxCalendarWeekCount - 1;        
        for(var i = 0; i < dates.length; i++) {
            index = this.GetIndexByDate(dates[i]);
            if(index < 0 || index > maxIndex || !this.IsDateVisible(dates[i]))
                continue;
            this.ApplySelectionToCell(index, showSelection);
        }
    },
    ApplySelectionToCell: function(index, showSelection) {
        var cell = this.GetDayCell(index);
        if(showSelection) {
            var info;
            if(!_aspxIsExists(this.dayStyleCache[index])) {
                var backup = new ASPxClientCalendarStyleInfo();
                backup.Import(cell);                
                this.dayStyleCache[index] = backup;
                info = backup.Clone();
            } else
                info = this.dayStyleCache[index].Clone();
            this.calendar.ImportEtalonStyle(info, "DS");
        } else
            info = this.dayStyleCache[index];
        info.Apply(cell);
    },    
                
    // Styles
    ApplyDayCellStyle: function(cell, date) {
        cell.style.cursor = "";
        var cal = this.calendar;
        var info = new ASPxClientCalendarStyleInfo();
        var needPointer = false;
        cal.ImportEtalonStyle(info, "D");
        if(this.IsDateVisible(date)) {
            if(cal.IsDateWeekend(date))
                cal.ImportEtalonStyle(info, "DW");                
            if(this.IsDateOtherMonth(date))
                cal.ImportEtalonStyle(info, "DA");                
            if(!cal.IsDateInRange(date))
                cal.ImportEtalonStyle(info, "DO");
            if(ASPxClientCalendar.AreDatesEqual(new Date(), date))
                cal.ImportEtalonStyle(info, "DT");
            else if(!cal.enableMulti)
                needPointer = true;
        }
        info.Apply(cell);
        if(needPointer)
            _aspxSetPointerCursor(cell);
    },
            
    GetIDPostfix: function() {
        return "_" + this.row.toString() + "x" + this.column.toString();
    },
    GetCachedElementById: function(postfix) {
        if(this.calendar.IsMultiView())
            postfix += this.GetIDPostfix();    
        return this.calendar.GetCachedElementById(this.calendar.name + "_" + postfix);
    },
    
    IsDateVisible: function(date) {
        var result = !this.calendar.IsMultiView() || !this.IsDateOtherMonth(date);
        if(!result) {            
            result = result || this.isLowBoundary && date <= this.visibleDate ||
                this.isHighBoundary && date >= this.visibleDate;
        }        
        return result;
    }
});

/////////////////////////////////////////////////////////////////

ASPxClientCalendarFastNavigation = _aspxCreateClass(null, {
    constructor: function(calendar) {
        this.calendar = calendar;
        this.activeMonth = -1;
        this.activeYear = -1;
        this.startYear = -1;
        this.activeView = null;
        this.InitializeUI();        
    },
    InitializeUI: function() {
        var item;
        var prefix = this.GetId();
        for(var m = 0; m < 12; m++) {
            item = this.GetMonthItem(m);
            if(!_aspxIsExistsElement(item))
                break;
            item.id = prefix + "_M" + m;
            _aspxAttachEventToElement(item, "click", new Function("aspxCalFNMClick('" + this.calendar.name + "', " + m + ")"));
        }
        for(var i = 0; i < 10; i++) {
            item = this.GetYearItem(i);
            if(!_aspxIsExistsElement(item))
                break;            
            item.id = prefix + "_Y" + i;
            _aspxAttachEventToElement(item, "click", new Function("aspxCalFNYClick('" + this.calendar.name + "', " + i + ")"));
        }
        _aspxAttachEventToElement(this.GetPopup().GetWindowElement(-1), "click", new Function("aspxCalMainElemClick('" + this.calendar.name + "')"));
    },
        
    Show: function() {
        this.GetPopup().ShowAtElement(this.activeView.GetTitleElement());
    },
    Hide: function() {
        this.GetPopup().Hide();
    },
        
    Prepare: function() {
        var date = this.activeView.visibleDate;
        this.activeYear = date.getFullYear();
        this.activeMonth = date.getMonth();
        this.startYear = ASPxClientCalendar.GetDecadeStartYear(this.activeYear);
        this.PrepareMonthList();
        this.PrepareYearList();
    },    
    PrepareMonthList: function() {        
        var item;
        for(var month = 0; month < 12; month++) {
            item = this.GetMonthItem(month);
            if(item == null)
                return;
            this.ApplyItemStyle(item, month == this.activeMonth, "M");
        }        
    },
    PrepareYearList: function() {
        var year = this.startYear;
        var item;
        for(var index = 0; index < 10; index++) {
            item = this.GetYearItem(index);
            if(item == null)
                return;
            item.innerHTML = year;
            this.ApplyItemStyle(item, year == this.activeYear, "Y");
            year++;
        }         
    },

    GetMonthItem: function(month) {
        var t = this.GetCachedElementById("m");
        if(!_aspxIsExistsElement(t))
            return null;
        var colIndex = month % 4;
        var rowIndex = (month - colIndex) / 4;
        return t.rows[rowIndex].cells[colIndex];
    },
    GetYearItem: function(index) {
        var t = this.GetCachedElementById("y");
        if(!_aspxIsExistsElement(t) || index < 0 || index > 9)
            return null;
        var colIndex = index % 5;
        var rowIndex = (index - colIndex) / 5;
        if(rowIndex == 0)
            colIndex++;
        return t.rows[rowIndex].cells[colIndex];
    },
    GetPopup: function() {
        return aspxGetControlCollection().Get(this.GetId());
    },
        
    ApplyItemStyle: function(item, isSelected, type) {
        var info = new ASPxClientCalendarStyleInfo();
        this.calendar.ImportEtalonStyle(info, "FN" + type);
        if(isSelected)
            this.calendar.ImportEtalonStyle(info, "FN" + type + "S");
        info.Apply(item);        
    },
        
    GetCachedElementById: function(postfix) {    
        return this.calendar.GetCachedElementById(this.GetId() + "_" + postfix);
    },
    GetId: function() {
        return this.calendar.name + "_FNP";
    },
    
    // Events    
    OnMonthClick: function(month) {
        if(month != this.activeMonth) {
            var prevCell = this.GetMonthItem(this.activeMonth);
            var cell = this.GetMonthItem(month);
            if(_aspxIsExistsElement(prevCell))
                this.ApplyItemStyle(prevCell, false, "M");
            this.ApplyItemStyle(cell, true, "M");
            this.activeMonth = month;            
        }    
    },
    OnYearClick: function(index) {
        var prevIndex = this.activeYear - this.startYear;
        if(index != prevIndex) {
            var prevCell = this.GetYearItem(prevIndex);
            if(_aspxIsExistsElement(prevCell))
                this.ApplyItemStyle(prevCell, false, "Y");
            var cell = this.GetYearItem(index);
            this.ApplyItemStyle(cell, true, "Y");
            this.activeYear = index + this.startYear;
        }    
    },
    OnYearShuffle: function(offset) {
        this.startYear += offset;        
        this.PrepareYearList();
    },
    OnOkClick: function() {
        this.GetPopup().Hide();        
        var offset = ASPxClientCalendar.GetOffsetInMonths(this.calendar.visibleDate, new Date(this.activeYear, this.activeMonth, 1));
        offset -= this.activeView.row * this.calendar.columns + this.activeView.column;        
        if(offset != 0) {
            var date = ASPxClientCalendar.AddMonths(this.calendar.visibleDate, offset);
            this.calendar.OnVisibleMonthChanged(date);        
        }
        this.calendar.OnMainElementClick();
    },
    OnCancelClick: function() {
        this.GetPopup().Hide();
        this.calendar.OnMainElementClick();
    }
});

/////////////////////////////////////////////////////////////////

ASPxClientCalendarStyleInfo = _aspxCreateClass(null, {
    constructor: function() {
        this.className = "";
        this.cssText = "";
    },
    
    Clone: function() {
        var clone = new ASPxClientCalendarStyleInfo();
        clone.className = this.className;
        clone.cssText = this.cssText;
        return clone;
    },
    Apply: function(element) {
        if(element.className != this.className)
            element.className = this.className;
        if(element._style != this.cssText) {
            element.style.cssText = this.cssText;    
            element._style = this.cssText; 
        }    
    },
    Import: function(element) {
        if(element.className.length > 0) {
            if(this.className.length > 1)
                this.className += " ";
            this.className +=  element.className;
        }        
        var cssText = element.style.cssText;
        if(cssText.length > 0) {
            var pos = cssText.length - 1;
            // ensure ";" at the end
            while(pos > -1 && cssText.charAt(pos) == " ")
                --pos;
            if(cssText.charAt(pos) != ";")
                cssText += ";";
            this.cssText += cssText;
        }
    }        
});

/////////////////////////////////////////////////////////////////

ASPxClientCalendarSelectionEventArgs = _aspxCreateClass(ASPxClientProcessingModeEventArgs, {
    constructor: function(processOnServer, selection){
        this.constructor.prototype.constructor.call(this, processOnServer);
        this.selection = selection;
    }
});

/////////////////////////////////////////////////////////////////

function aspxCalShiftMonth(name, monthOffset) {
    if(monthOffset != 0) {
        var edit = aspxGetControlCollection().Get(name);
        if(edit != null) 
            edit.OnShiftMonth(monthOffset);        
    }
}

function aspxCalDayMouseEvt(name, row, column, index, e, byWeeks) {
    var cal = aspxGetControlCollection().Get(name);
    if(cal != null) {
        var view = cal.GetView(row, column);
        var date = view.GetDateByIndex(index);
        if(byWeeks)
            date = ASPxClientCalendar.AddDays(date, cal.firstDayOfWeek - date.getDay());
        var allowed = cal.IsDateInRange(date) && (view.IsDateVisible(date) || byWeeks);
        switch(e.type) {
            case "mousedown":
                if(allowed)
                    cal.OnDayMouseDown(date, e.shiftKey, e.ctrlKey, byWeeks);
                break;
            case "mouseover":
                if(allowed) {
                    if(cal.forceMouseDown)
                        cal.OnDayMouseDown(date, false, false, false);
                    else if(cal.isMouseDown)
                        cal.OnDayMouseOver(date);
                }
                break;
            case "mouseup":
                if(cal.isMouseDown) {
                    if(allowed)
                        cal.OnDayMouseUp(date);
                    else
                        cal.OnSelectionCancelled();
                }
                break;
        }
    }
}

function aspxCalTodayClick(name) {    
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnTodayClick();
}

function aspxCalClearClick(name) {    
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnClearClick();        
}
function aspxCalSelectMonth(name, row, column) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnSelectMonth(row, column);
}

function aspxCalTitleClick(name, row, column) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnTitleClick(row, column);
}

function aspxCalFNMClick(name, month) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.fastNavigation.OnMonthClick(month);
}

function aspxCalFNYClick(name, index) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.fastNavigation.OnYearClick(index);
}

function aspxCalFNYShuffle(name, offset) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.fastNavigation.OnYearShuffle(offset);
}

function aspxCalFNBClick(name, action) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) {
        switch(action) {
            case "ok":
                edit.fastNavigation.OnOkClick(); 
                break;
            case "cancel":
                edit.fastNavigation.OnCancelClick();
                break;
        }                
    }
}

function aspxCalMainElemClick(name) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnMainElementClick();
}

function aspxCalDocMouseUp(evt) {
    var target = _aspxGetEventSource(evt);
    if(__aspxActiveCalendar != null && _aspxIsExistsElement(target)) {
        __aspxActiveCalendar.forceMouseDown = false;
        if(__aspxActiveCalendar.isMouseDown) {            
            for(var key in __aspxActiveCalendar.views) {            
                if (_aspxIsExists(__aspxActiveCalendar.views[key].GetMonthCell)) {
                    var monthCell = __aspxActiveCalendar.views[key].GetMonthCell();
                    var parent = target.parentNode;
                    while(_aspxIsExistsElement(parent)) {
                        if(parent == monthCell)
                            return;
                        parent = parent.parentNode;
                    }
                }
            }
            __aspxActiveCalendar.OnSelectionCancelled();            
        }
        __aspxActiveCalendar = null;
    }
}
function aspxCalCancelSelect() {
    if(__aspxActiveCalendar != null) {
        __aspxActiveCalendar.forceMouseDown = false;
        __aspxActiveCalendar.OnSelectionCancelled();        
    }
}