

var __aspxClientValidationStateNameSuffix = "$CVS";
ASPxClientEditBase = _aspxCreateClass(ASPxClientControl, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    InlineInitialize: function(){
        this.InitializeEnabled(); // need to call even if enabled - assign initially attributes, events, etc
    },
    InitializeEnabled: function() {
        this.SetEnabledInternal(this.clientEnabled, true);
    },
    GetValue: function() {
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element))
            return element.innerHTML;
        return "";
    },
    GetValueString: function(){
        var value = this.GetValue();
        return (value == null) ? null : value.toString();
    },
    SetValue: function(value) {
        if(value == null)
            value = "";
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element))
            element.innerHTML = value;
    }, 
    GetEnabled: function(){
        return this.enabled && this.clientEnabled;
    }, 
    SetEnabled: function(enabled){
        if(this.clientEnabled != enabled) {
            var errorFrameRequiresUpdate = this.GetIsValid && !this.GetIsValid();
            
            // While the editor is enabled, hiding its error frame if required
            if(errorFrameRequiresUpdate && !enabled)
                this.UpdateErrorFrameAndFocus(false /* setFocusOnError */, null /* ignoreVisibilityCheck */, true /* isValid */);
            
            // Changing editor's enabled state
            this.clientEnabled = enabled;
            this.SetEnabledInternal(enabled, false);
            
            // After the editor has been enabled, showing its error frame if required
            if(errorFrameRequiresUpdate && enabled)
                this.UpdateErrorFrameAndFocus(false /* setFocusOnError */);
        }
    },
    SetEnabledInternal: function(enabled, initialization){
        if(!this.enabled) return;
        if(!initialization || !enabled)
            this.ChangeEnabledStateItems(enabled);
        this.ChangeEnabledAttributes(enabled);
    },
    ChangeEnabledAttributes: function(enabled){
    },
    ChangeEnabledStateItems: function(enabled){
    }
});

ASPxValidationPattern = _aspxCreateClass(null, {
    constructor: function(errorText) {
        this.errorText = errorText;
    }
});

ASPxRequiredFieldValidationPattern = _aspxCreateClass(ASPxValidationPattern, {
    constructor: function(errorText) {
        this.constructor.prototype.constructor.call(this, errorText);
    },
    EvaluateIsValid: function(value) {
        return value != null && (value.constructor == Array || _aspxTrim(value.toString()) != "");
    }
});

ASPxRegularExpressionValidationPattern = _aspxCreateClass(ASPxValidationPattern, {
    constructor: function(errorText, pattern) {
        this.constructor.prototype.constructor.call(this, errorText);
        this.pattern = pattern;
    },
    EvaluateIsValid: function(value) {
        if (value == null) 
            return true;
        var strValue = value.toString();
        if (_aspxTrim(strValue).length == 0)
            return true;
        var regEx = new RegExp(this.pattern);
        var matches = regEx.exec(strValue);
        return matches != null && strValue == matches[0];
    }
});

function _aspxIsEditorFocusable(inputElement) {
    return _aspxIsFocusableCore(inputElement, function(container) {
        return container.getAttribute("errorFrame") == "errorFrame";
    });
}

var __aspxInvalidEditorToBeFocused = null;

ASPxValidationType = {
    PersonalOnValueChanged: "ValueChanged",
    PersonalViaScript: "CalledViaScript",
    MassValidation: "MassValidation"
};

ASPxErrorFrameDisplay = {
    Static: "Static",
    Dynamic: "Dynamic"
};

ASPxEditElementSuffix = {
    ExternalTable: "_ET",
    ControlCell: "_CC",
    ErrorCell: "_EC",
    ErrorTextCell: "_ETC",
    ErrorImage: "_EI"
};

var __aspxFocusedEditorName = "";
function __aspxGetFocusedEditor(){
    var focusedEditor = aspxGetControlCollection().Get(__aspxFocusedEditorName);
    if(focusedEditor && !focusedEditor.focused){
        __aspxSetFocusedEditor("");
        focusedEditor = null;
    }
    return focusedEditor;
}
function __aspxSetFocusedEditor(editor){
    __aspxFocusedEditorName = editor ? editor.name : "";
}
ASPxClientEdit = _aspxCreateClass(ASPxClientEditBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.isASPxClientEdit = true;
        
        this.inputElement = null;
        this.elementCache = { };
        
        this.convertEmptyStringToNull = true;
        this.readOnly = false;
        this.focused = false;
        this.focusEventsLocked = false;
        this.receiveGlobalMouseWheel = true;
        
        // size correction
        this.widthCorrectionRequired = false;
        this.heightCorrectionRequired = false;
        
        // validation
        this.customValidationEnabled = false;
        this.display = ASPxErrorFrameDisplay.Static;
        this.initialErrorText = "";
        this.causesValidation = false;
        this.validateOnLeave = true;
        this.validationGroup = "";
        this.sendPostBackWithValidation = null;
        this.validationPatterns = [];
        this.setFocusOnError = false;
        this.errorDisplayMode = "it";
        this.errorText = "";
        this.isValid = true;
        this.errorImageIsAssigned = false;
        this.clientValidationStateElement = null;
        
        // Keyboard support
        this.enterProcessed = false;
        this.keyDownHandlers = {};
        this.keyPressHandlers = {};
        this.keyUpHandlers = {};
        this.specialKeyboardHandlingUsed = false;
        this.onKeyDownHandler = null;
        this.onKeyPressHandler = null;
        this.onKeyUpHandler = null;
        this.onGotFocusHandler = null;
        this.onLostFocusHandler = null;
        this.GotFocus = new ASPxClientEvent();
        this.LostFocus = new ASPxClientEvent();
        this.Validation = new ASPxClientEvent();
        this.ValueChanged = new ASPxClientEvent();
        
        this.KeyDown = new ASPxClientEvent();
        this.KeyPress = new ASPxClientEvent();
        this.KeyUp = new ASPxClientEvent();
    },
    Initialize: function() {
        this.initialErrorText = this.errorText;
        ASPxClientEditBase.prototype.Initialize.call(this);
        this.InitializeKeyHandlers();
        this.UpdateClientValidationState();
    },
    InitSpecialKeyboardHandling: function(){
        this.onKeyDownHandler = _aspxCreateEventHandlerFunction("aspxKBSIKeyDown", this.name, true);
        this.onKeyPressHandler = _aspxCreateEventHandlerFunction("aspxKBSIKeyPress", this.name, true);
        this.onKeyUpHandler = _aspxCreateEventHandlerFunction("aspxKBSIKeyUp", this.name, true);
        this.onGotFocusHandler = _aspxCreateEventHandlerFunction("aspxESGotFocus", this.name, false);
        this.onLostFocusHandler = _aspxCreateEventHandlerFunction("aspxESLostFocus", this.name, false);
        this.specialKeyboardHandlingUsed = true;
    },
    InitializeKeyHandlers: function() {
    },
    AddKeyDownHandler: function(key, handler) {
        this.keyDownHandlers[key] = handler;
    },
    ChangeSpecialInputEnabledAttributes: function(element, method){
        element.autocomplete = "off";
        if(this.onKeyDownHandler != null)
            method(element, "keydown", this.onKeyDownHandler);
        if(this.onKeyPressHandler != null)
            method(element, "keypress", this.onKeyPressHandler);
        if(this.onKeyUpHandler != null)
            method(element, "keyup", this.onKeyUpHandler);
        if(this.onGotFocusHandler != null)
            method(element, "focus", this.onGotFocusHandler);
        if(this.onLostFocusHandler != null)
            method(element, "blur", this.onLostFocusHandler);
    },
    UpdateClientValidationState: function() {
        var mainElement = this.GetMainElement();
        if (_aspxIsExists(mainElement)) {
            var hiddenField = this.GetClientValidationStateHiddenField();
            if(_aspxIsExists(hiddenField))
                hiddenField.value = !this.GetIsValid() ? ("-" + this.GetErrorText()) : "";
        }
    },

    GetCachedElementByIdSuffix: function(idSuffix) {
        var element = this.elementCache[idSuffix];
        if(!_aspxIsExistsElement(element)) {
            element = _aspxGetElementById(this.name + idSuffix);
            this.elementCache[idSuffix] = element;
        }
        return element;
    },
    FindInputElement: function(){
        return null;
    },
    GetInputElement: function(){
        if(!_aspxIsExistsElement(this.inputElement))
            this.inputElement = this.FindInputElement();
        return this.inputElement;
    },
    GetErrorImage: function() {
        return this.GetCachedElementByIdSuffix(ASPxEditElementSuffix.ErrorImage);
    },
    GetExternalTable: function() {
        return this.GetCachedElementByIdSuffix(ASPxEditElementSuffix.ExternalTable);
    },
    GetControlCell: function() {
        return this.GetCachedElementByIdSuffix(ASPxEditElementSuffix.ControlCell);
    },
    GetErrorCell: function() {
        return this.GetCachedElementByIdSuffix(ASPxEditElementSuffix.ErrorCell);
    },
    GetErrorTextCell: function() {
        return this.GetCachedElementByIdSuffix(this.errorImageIsAssigned ?
            ASPxEditElementSuffix.ErrorTextCell : ASPxEditElementSuffix.ErrorCell);
    },
    GetClientValidationStateHiddenField: function() {
        if(!_aspxIsExists(this.clientValidationStateElement))
            this.clientValidationStateElement = this.CreateClientValidationStateHiddenField();
        return this.clientValidationStateElement;
    },
    CreateClientValidationStateHiddenField: function() {
        var mainElement = this.GetMainElement();
        var hiddenField = _aspxCreateHiddenField(this.uniqueID + __aspxClientValidationStateNameSuffix);
        mainElement.parentNode.appendChild(hiddenField);
        return hiddenField;
    },
    SetVisible: function(isVisible){
        if(this.clientVisible == isVisible)
            return;

        if(this.customValidationEnabled) {
            var errorFrame = this.GetExternalTable();
            if(_aspxIsExists(errorFrame)) {
                _aspxSetElementDisplay(errorFrame, isVisible);
                
                // If we hide an editor than we must hide its error frame too
                var isValid = !isVisible ? true : void(0);
                this.UpdateErrorFrameAndFocus(false /* setFocusOnError */, true /* ignoreVisibilityCheck */, isValid /* isValid */);
            }
        }

        ASPxClientControl.prototype.SetVisible.call(this, isVisible);
    },
    GetValueInputToValidate: function() {
        return this.GetInputElement();
    },
    IsVisible: function() {
        if (!this.clientVisible)
            return false;
        var element = this.GetMainElement();
        while(_aspxIsExists(element) && element.tagName != "BODY") {
            if (element.getAttribute("errorFrame") != "errorFrame" && (!_aspxGetElementVisibility(element) || !_aspxGetElementDisplay(element)))
                return false;
            element = element.parentNode;
        }
        return true;
    },
    // Size correction
    AdjustControlCore: function() {
        var mainElement = this.GetMainElement();
        var mainElementCurStyle = _aspxGetCurrentStyle(mainElement);
        // collapse control
        this.CollapseControl();
        // correct width
        if (this.widthCorrectionRequired && mainElementCurStyle.width != "" && mainElementCurStyle.width != "auto")
            this.CorrectEditorWidth();
        else
            this.UnstretchInputElement();
        // correct height
        if (this.heightCorrectionRequired)
            this.CorrectEditorHeight();
    },
    CorrectEditorWidth: function() {
    },
    CorrectEditorHeight: function() {
    },
    UnstretchInputElement: function() {
    },
    IsFocusEventsLocked: function() {
        return this.focusEventsLocked;
    },
    LockFocusEvents: function() {
        if(!this.focused) return;
        this.focusEventsLocked = true;
    },
    UnlockFocusEvents: function() {
        this.focusEventsLocked = false;
    },
    OnFocusCore: function() {
        if (!this.IsFocusEventsLocked()){
            this.focused = true;
            __aspxSetFocusedEditor(this);
            if(this.isInitialized)
                this.RaiseFocus();
        }
        else
            this.UnlockFocusEvents();
    },
    OnLostFocusCore: function() {
        if (!this.IsFocusEventsLocked()){
            this.focused = false;
            __aspxSetFocusedEditor(null);
            this.RaiseLostFocus();
            if (this.validateOnLeave)
                this.SetFocusOnError();
        }
    },
    OnFocus: function() {
        if(!this.specialKeyboardHandlingUsed)
            this.OnFocusCore();
    },
    OnLostFocus: function() {
        if (this.isInitialized && !this.specialKeyboardHandlingUsed)
            this.OnLostFocusCore();
    },
    OnSpecialFocus: function() {
        if (this.isInitialized)
            this.OnFocusCore();
    },
    OnSpecialLostFocus: function() {
        if (this.isInitialized)
            this.OnLostFocusCore();
    },
    OnMouseWheel: function(evt){
    },
    OnValidation: function(validationType) {
        if (this.customValidationEnabled && this.isInitialized && _aspxIsExistsElement(this.GetExternalTable())) {
            this.BeginErrorFrameUpdate();
            try {
                this.SetIsValid(true);
                this.SetErrorText(this.initialErrorText);

                if(this.validateOnLeave || validationType != ASPxValidationType.PersonalOnValueChanged) {
                    this.ValidateWithPatterns();
                    this.RaiseValidation();
                }
                
                this.UpdateErrorFrameAndFocus(validationType == ASPxValidationType.PersonalOnValueChanged && this.validateOnLeave && !this.GetIsValid());
            } finally {
                this.EndErrorFrameUpdate();
            }
        }
    },
    OnValueChanged: function() {
        var processOnServer = this.RaiseValidationInternal();
        processOnServer = this.RaiseValueChangedEvent() && processOnServer;
        if (processOnServer)
            this.SendPostBackInternal("");
    },
    ParseValue: function() {
    },
    RaisePersonalStandardValidation: function() {
        if (_aspxIsFunction(window.ValidatorOnChange)) {
            var inputElement = this.GetValueInputToValidate();
            if (_aspxIsExists(inputElement.Validators))
                window.ValidatorOnChange({ srcElement: inputElement });
        }
    },
    RaiseValidationInternal: function() {
        if (this.autoPostBack && this.causesValidation && this.validateOnLeave)
            return ASPxClientEdit.ValidateGroup(this.validationGroup);
        else {
            this.OnValidation(ASPxValidationType.PersonalOnValueChanged);
            return this.GetIsValid();
        }
    },
    RaiseValueChangedEvent: function(){
        return this.RaiseValueChanged();
    },
    SendPostBackInternal: function(postBackArg) {
        if (_aspxIsFunction(this.sendPostBackWithValidation))
            this.sendPostBackWithValidation(postBackArg);
        else
            this.SendPostBack(postBackArg);
    },
    SetElementToBeFocused: function() {
        if (this.IsVisible())
            __aspxInvalidEditorToBeFocused = this;
    },
    SetFocus: function(){
        var inputElement = this.GetInputElement();
        if (_aspxGetActiveElement() != inputElement && _aspxIsEditorFocusable(inputElement)) 
            _aspxSetFocus(inputElement);
    },
    SetFocusOnError: function() {
        if (__aspxInvalidEditorToBeFocused == this) {
            this.SetFocus();
            __aspxInvalidEditorToBeFocused = null;
        }
    },
    BeginErrorFrameUpdate: function() {
        if(!this.errorFrameUpdateLocked)
            this.errorFrameUpdateLocked = true;
    },
    EndErrorFrameUpdate: function() {
        this.errorFrameUpdateLocked = false;
        var args = this.updateErrorFrameAndFocusLastCallArgs;
        if(args) {
            this.UpdateErrorFrameAndFocus(args[0], args[1]);
            delete this.updateErrorFrameAndFocusLastCallArgs;
        }
    },
    UpdateErrorFrameAndFocus: function(setFocusOnError, ignoreVisibilityCheck, isValid) {
        if(!this.GetEnabled() || !ignoreVisibilityCheck && !this.GetVisible() /* client-visible */)
            return;

        if(this.errorFrameUpdateLocked) {
            this.updateErrorFrameAndFocusLastCallArgs = [ setFocusOnError, ignoreVisibilityCheck ];
            return;
        }

        if(typeof(isValid) == "undefined")
            isValid = this.GetIsValid();

        var externalTable = this.GetExternalTable();
        var isStaticDisplay = this.display == ASPxErrorFrameDisplay.Static;
        if(isValid) {
            if(isStaticDisplay) {
                externalTable.style.visibility = "hidden";
            } else {
                this.HideErrorCell();
                this.SaveErrorFrameStyles();
                this.ClearErrorFrameElementsStyles();
            }
        } else {
            var editorLocatedWithinVisibleContainer = this.IsVisible();

            if(this.widthCorrectionRequired) {
                if(editorLocatedWithinVisibleContainer)
                    this.CollapseControl(); // 100% width support
                else
                    this.sizeCorrectedOnce = false;
            }

            this.UpdateErrorCellContent();
            if(isStaticDisplay) {
                externalTable.style.visibility = "visible";
            } else {
                this.EnsureErrorFrameStylesLoaded();
                this.RestoreErrorFrameElementsStyles();
                this.ShowErrorCell();
            }

            if(editorLocatedWithinVisibleContainer) {
                if(this.widthCorrectionRequired)
                    this.AdjustControl(); // 100% width support

                if(setFocusOnError && this.setFocusOnError && __aspxInvalidEditorToBeFocused == null)
                    this.SetElementToBeFocused();
            }
        }
    },
    ShowErrorCell: function() {
        var errorCell = this.GetErrorCell();
        if(_aspxIsExists(errorCell))
            _aspxSetElementDisplay(errorCell, true);
    },
    HideErrorCell: function() {
        var errorCell = this.GetErrorCell();
        if(_aspxIsExists(errorCell))
            _aspxSetElementDisplay(errorCell, false);
    },
    SaveErrorFrameStyles: function() {
        this.EnsureErrorFrameStylesLoaded();
    },
    EnsureErrorFrameStylesLoaded: function() {
        if(typeof(this.errorFrameStyles) == "undefined") {
            var externalTable = this.GetExternalTable();
            var controlCell = this.GetControlCell();
            this.errorFrameStyles = {
                errorFrame: {
                    cssClass: externalTable.className,
                    style: this.ExtractElementStyleStringIgnoringVisibilityProps(externalTable)
                },
                controlCell: {
                    cssClass: controlCell.className,
                    style: this.ExtractElementStyleStringIgnoringVisibilityProps(controlCell)
                }
            };
        }
    },
    ClearErrorFrameElementsStyles: function() {
        this.ClearElementStyle(this.GetExternalTable());
        this.ClearElementStyle(this.GetControlCell());
    },
    RestoreErrorFrameElementsStyles: function() {
        var externalTable = this.GetExternalTable();
        externalTable.className = this.errorFrameStyles.errorFrame.cssClass;
        externalTable.style.cssText = this.errorFrameStyles.errorFrame.style;
        
        var controlCell = this.GetControlCell();
        controlCell.className = this.errorFrameStyles.controlCell.cssClass;
        controlCell.style.cssText = this.errorFrameStyles.controlCell.style;
    },
    ExtractElementStyleStringIgnoringVisibilityProps: function(element) {
        var savedVisibility = element.style.visibility;
        var savedDisplay = element.style.display;
        element.style.visibility = "";
        element.style.display = "";
        var styleStr = element.style.cssText;
        element.style.visibility = savedVisibility;
        element.style.display = savedDisplay;
        return styleStr;
    },
    ClearElementStyle: function(element) {
        if(!_aspxIsExists(element))
            return;
        element.className = "";
        var savedVisibility = element.style.visibility;
        var savedDisplay = element.style.display;
        var savedWidth = element.style.width;
        
        element.style.cssText = "";
        
        element.style.visibility = savedVisibility;
        element.style.display = savedDisplay;
        element.style.width = savedWidth;
    },
    UpdateErrorCellContent: function() {
        if (this.errorDisplayMode.indexOf("t") > -1)
            this.UpdateErrorText();
        if (this.errorDisplayMode == "i")
            this.UpdateErrorImage();
    },
    UpdateErrorImage: function() {
        var image = this.GetErrorImage();
        if (_aspxIsExistsElement(image)) {
            image.alt = this.errorText;
            image.title = this.errorText;
        } else {
            this.UpdateErrorText();
        }
    },
    UpdateErrorText: function() {
        var errorTextCell = this.GetErrorTextCell();
        if(_aspxIsExistsElement(errorTextCell))
            errorTextCell.innerHTML = this.errorText;
    },
    ValidateWithPatterns: function() {
        if (this.validationPatterns.length > 0) {
            var value = this.GetValue();
            for (var i = 0; i < this.validationPatterns.length; i++) {
                var validator = this.validationPatterns[i];
                if (!validator.EvaluateIsValid(value)) {
                    this.SetIsValid(false);
                    this.SetErrorText(validator.errorText);
                    return;
                }
            }
        }
    },
    // Keyboard support
    OnSpecialKeyDown: function(evt){
        this.RaiseKeyDown(evt);
        
        var handler = this.keyDownHandlers[evt.keyCode];
        if(_aspxIsExists(handler)) 
            return this[handler](evt);
        return false;
    },
    OnSpecialKeyPress: function(evt){
        this.RaiseKeyPress(evt);
        
        var handler = this.keyPressHandlers[evt.keyCode];
        if(_aspxIsExists(handler)) 
            return this[handler](evt);
        if(__aspxNS || __aspxOpera){
            if(evt.keyCode == ASPxKey.Enter)
                return this.enterProcessed;
        }
        return false;
    },
    OnSpecialKeyUp: function(evt){
        this.RaiseKeyUp(evt);
        
        var handler = this.keyUpHandlers[evt.keyCode];
        if(_aspxIsExists(handler)) 
            return this[handler](evt);
        return false;
    },
    OnKeyDown: function(evt) {
        if(!this.specialKeyboardHandlingUsed)
            this.RaiseKeyDown(evt);
    },
    OnKeyPress: function(evt) {
        if(!this.specialKeyboardHandlingUsed)
            this.RaiseKeyPress(evt);
    },
    OnKeyUp: function(evt) {
        if(!this.specialKeyboardHandlingUsed)
            this.RaiseKeyUp(evt);
    },
    // API
    RaiseKeyDown: function(evt){
        if(!this.KeyDown.IsEmpty()){
            var args = new ASPxClientEditKeyEventArgs(evt);
            this.KeyDown.FireEvent(this, args);
        }
    },
    RaiseKeyPress: function(evt){
        if(!this.KeyPress.IsEmpty()){
            var args = new ASPxClientEditKeyEventArgs(evt);
            this.KeyPress.FireEvent(this, args);
        }
    },
    RaiseKeyUp: function(evt){
        if(!this.KeyUp.IsEmpty()){
            var args = new ASPxClientEditKeyEventArgs(evt);
            this.KeyUp.FireEvent(this, args);
        }
    },
    RaiseFocus: function(){
        if(!this.GotFocus.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.GotFocus.FireEvent(this, args);
        }
    },
    RaiseLostFocus: function(){
        if(!this.LostFocus.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.LostFocus.FireEvent(this, args);
        }
    },
    RaiseValidation: function() {
        if (this.customValidationEnabled && !this.Validation.IsEmpty()) {
            var currentValue = this.GetValue();
            var args = new ASPxClientEditValidationEventArgs(currentValue, this.errorText, this.GetIsValid());
            this.Validation.FireEvent(this, args);
            this.SetErrorText(args.errorText);
            this.SetIsValid(args.isValid);
            if (args.value != currentValue)
                this.SetValue(args.value);
        }
    },
    RaiseValueChanged: function(){
        var processOnServer = this.autoPostBack;
        if(!this.ValueChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.ValueChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;  
    },
    Focus: function(){
        this.SetFocus();
    },
    GetIsValid: function(){
        // B95671: requires the external table existence check
        var externalTable = this.GetExternalTable();
        return _aspxIsExistsElement(externalTable) ? this.isValid : true;
    },
    GetErrorText: function(){
        return this.errorText;
    },
    SetIsValid: function(isValid){
        if (this.customValidationEnabled) {
            this.isValid = isValid;
            this.UpdateErrorFrameAndFocus(false /* setFocusOnError */);
            this.UpdateClientValidationState();
        }
    },
    SetErrorText: function(errorText){
        if (this.customValidationEnabled) {
            this.errorText = errorText;
            this.UpdateErrorFrameAndFocus(false /* setFocusOnError */);
            this.UpdateClientValidationState();
        }
    },
    Validate: function(){
        this.ParseValue();
        this.OnValidation(ASPxValidationType.PersonalViaScript);
    }
});
ASPxClientEdit.ClearEditorsInContainer = function(container, validationGroup, clearInvisibleEditors) {
    __aspxInvalidEditorToBeFocused = null;
    _aspxProcessEditorsInContainer(container, _aspxClearProcessingProc, _aspxClearChoiceCondition, validationGroup, clearInvisibleEditors, true /* processDisabledEditors */);
}
ASPxClientEdit.ClearEditorsInContainerById = function(containerId, validationGroup, clearInvisibleEditors) {
    var container = document.getElementById(containerId);
    this.ClearEditorsInContainer(container, validationGroup, clearInvisibleEditors);
}
ASPxClientEdit.ClearGroup = function(validationGroup, clearInvisibleEditors) {
    return this.ClearEditorsInContainer(null, validationGroup, clearInvisibleEditors);
}
ASPxClientEdit.ValidateEditorsInContainer = function(container, validationGroup, validateInvisibleEditors) {
    var validationResult = _aspxProcessEditorsInContainer(container, _aspxValidateProcessingProc, _aspxValidateChoiceCondition, validationGroup, validateInvisibleEditors,
        false /* processDisabledEditors */);
    if(_aspxIsExistsType(typeof(aspxGetGlobalEvents))) {
        if(typeof(validateInvisibleEditors) == "undefined")
            validateInvisibleEditors = false;
        if(typeof(validationGroup) == "undefined")
            validationGroup = null;
        validationResult.isValid = aspxGetGlobalEvents().OnValidationCompleted(container, validationGroup,
            validateInvisibleEditors, validationResult.isValid, validationResult.firstInvalid, validationResult.firstVisibleInvalid);
    }
    return validationResult.isValid;
}
ASPxClientEdit.ValidateEditorsInContainerById = function(containerId, validationGroup, validateInvisibleEditors) {
    var container = document.getElementById(containerId);
    return this.ValidateEditorsInContainer(container, validationGroup, validateInvisibleEditors);
}
ASPxClientEdit.ValidateGroup = function(validationGroup, validateInvisibleEditors) {
    return this.ValidateEditorsInContainer(null, validationGroup, validateInvisibleEditors);
}
ASPxClientEditKeyEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(htmlEvent) {
        this.constructor.prototype.constructor.call(this);
        this.htmlEvent = htmlEvent;
    }
});
ASPxClientEditValidationEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(value, errorText, isValid) {
        this.constructor.prototype.constructor.call(this);
        this.errorText = errorText;
        this.isValid = isValid;
        this.value = value;
    }
});

function aspxEGotFocus(name){
    var edit = aspxGetControlCollection().Get(name); 
    if(edit != null)
        edit.OnFocus();
}
function aspxELostFocus(name){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) 
        edit.OnLostFocus();
}
function aspxESGotFocus(name){
    var edit = aspxGetControlCollection().Get(name); 
    if(edit != null)
        edit.OnSpecialFocus();
}
function aspxESLostFocus(name){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) 
        edit.OnSpecialLostFocus();
}
function aspxEValueChanged(name){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnValueChanged();
}

_aspxAttachEventToDocument(__aspxNS ? "DOMMouseScroll" : "mousewheel", aspxEMouseWheel);
function aspxEMouseWheel(evt) {
    var editor = __aspxGetFocusedEditor();
    if (editor != null && editor.focused && editor.receiveGlobalMouseWheel)
        editor.OnMouseWheel(evt);
}

// Keyboard Support
function aspxKBSIKeyDown(name, evt){
    var control = aspxGetControlCollection().Get(name);
    if(control != null){
        var isProcessed = control.OnSpecialKeyDown(evt);
        if(isProcessed)
            return _aspxPreventEventAndBubble(evt);
    }
}
function aspxKBSIKeyPress(name, evt){
    var control = aspxGetControlCollection().Get(name);
    if(control != null){
        var isProcessed = control.OnSpecialKeyPress(evt);
        if(isProcessed)
            return _aspxPreventEventAndBubble(evt);
    }
}
function aspxKBSIKeyUp(name, evt){
    var control = aspxGetControlCollection().Get(name);
    if(control != null){
        var isProcessed = control.OnSpecialKeyUp(evt);
        if(isProcessed)
            return _aspxPreventEventAndBubble(evt);
    }
}
function aspxEKeyDown(name, evt){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnKeyDown(evt);
}
function aspxEKeyPress(name, evt){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnKeyPress(evt);
}
function aspxEKeyUp(name, evt){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null)
        edit.OnKeyUp(evt);
}

ASPxValidationResult = _aspxCreateClass(null, {
    constructor: function(isValid, firstInvalid, firstVisibleInvalid) {
        this.isValid = isValid;
        this.firstInvalid = firstInvalid;
        this.firstVisibleInvalid = firstVisibleInvalid;
    }
});

function _aspxProcessEditorsInContainer(container, processingProc, choiceCondition, validationGroup, processInvisibleEditors, processDisabledEditors) {
    var allProcessedSuccessfull = true;
    var firstInvalid = null;
    var firstVisibleInvalid = null;
    var invalidEditorToBeFocused = null;
    var collection = aspxGetControlCollection();
    for (var key in collection.elements) {
        var control = collection.elements[key];
        if (control != null && ASPxIdent.IsASPxClientEdit(control) && (processDisabledEditors || control.GetEnabled())) {
            var mainElement = control.GetMainElement();
            if (_aspxIsExists(mainElement) &&
                (container == null || _aspxGetIsParent(container, mainElement)) &&
                (processInvisibleEditors || control.IsVisible()) &&
                choiceCondition(control, validationGroup)) {
                var isSuccess = processingProc(control);
                if(!isSuccess) {
                    allProcessedSuccessfull = false;
                    if(firstInvalid == null)
                        firstInvalid = control;
                    var isVisible = control.IsVisible();
                    if(isVisible && firstVisibleInvalid == null)
                        firstVisibleInvalid = control;
                    if (control.setFocusOnError && invalidEditorToBeFocused == null && isVisible)
                        invalidEditorToBeFocused = control;
                }
            }
        }
    }
    if (invalidEditorToBeFocused != null)
        invalidEditorToBeFocused.SetFocus();
    return new ASPxValidationResult(allProcessedSuccessfull, firstInvalid, firstVisibleInvalid);
}

function _aspxClearChoiceCondition(edit, validationGroup) {
    return !_aspxIsExists(validationGroup) || (edit.validationGroup == validationGroup);
}
function _aspxValidateChoiceCondition(edit, validationGroup) {
    return _aspxClearChoiceCondition(edit, validationGroup) && edit.customValidationEnabled;
}
function _aspxClearProcessingProc(edit) {
    edit.SetValue(null);
    edit.SetIsValid(true);
    return true;
}
function _aspxValidateProcessingProc(edit) {
    edit.OnValidation(ASPxValidationType.MassValidation);
    return edit.GetIsValid();
}

// Selection Utils
function _aspxGetCaretPosition(inputElement) {
    return _aspxGetSelection(inputElement).startPos;
}
function _aspxSetSelectionCore(inputElement, startPos, endPos) {
    if (__aspxIE) {
        var range = inputElement.createTextRange();
        range.collapse(true);
        range.moveStart("character", startPos);
        range.moveEnd("character", endPos - startPos);
        range.select();
    } else
        inputElement.setSelectionRange(startPos, endPos);
}
function _aspxSetSelection(inputElement, startPos, endPos, scrollToSelection) {
    var textLen = inputElement.value.length;
    if (endPos == -1 || endPos > textLen) 
        endPos = textLen;
    if (startPos > textLen) 
        startPos = textLen;
    if (startPos > endPos)
        return;
    
    _aspxSetSelectionCore(inputElement, startPos, endPos);
    
    if (scrollToSelection && inputElement.tagName == 'TEXTAREA') {
        var scrollHeight = inputElement.scrollHeight;
        var approxCaretPos = startPos;
        var scrollTop = Math.max(Math.round(approxCaretPos * scrollHeight / textLen  - inputElement.clientHeight / 2), 0);
        inputElement.scrollTop = scrollTop;
    }
}
function _aspxSetCaretPosition(inputElement, caretPos, scrollToSelection) {
    if(!scrollToSelection)
        scrollToSelection = true;
    if (caretPos == -1)
        caretPos = inputElement.value.length;
    _aspxSetSelection(inputElement, caretPos, caretPos, scrollToSelection);
}

var __aspxLBSerializingSeparator = "|";
var __aspxLBSerializingSeparatorLength = __aspxLBSerializingSeparator.length;
var __aspxLoadRangeItemsCallbackPrefix = "LBCRI";
var __aspxLBIPostfixes = ['I', 'T'];
var __aspxLBIIdSuffix = "LBI";
var __aspxLBSIIdSuffix = __aspxLBIIdSuffix + "-1";
var __aspxLBTSIdSuffix = "_TS";
var __aspxLBBSIdSuffix = "_BS";
var __aspxLBHeaderDivIdSuffix = "_H";
var __aspxLTableIdSuffix = "_LBT";
var __aspxLEVISuffix = "_VI";
var __aspxLBDSuffix = "_D";
var __aspxEmptyItemsRange = "0:-1";
var __aspxNbsp = "&nbsp;";
var __aspxNbspChar = String.fromCharCode(160);

var __aspxCachedHoverItemKind = "cached" + __aspxHoverItemKind;
var __aspxCachedSelectedItemKind = "cached" + __aspxSelectedItemKind;
var __aspxCachedDisabledItemKind = "cached" + __aspxDisabledItemKind;
ASPxClientListEdit = _aspxCreateClass(ASPxClientEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);        
        this.SelectedIndexChanged = new ASPxClientEvent();
        
        //set from server
	    this.savedSelectedIndex = -1;
	},
    RaiseValueChangedEvent: function() {
        if(!this.isInitialized) return false;
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseSelectedIndexChanged(processOnServer);
        return processOnServer;
    },
    FindInputElement: function() {
        return this.FindStateInputElement();
    },
    FindStateInputElement: function(){
        return document.getElementById(this.name + __aspxLEVISuffix);
    },
    GetItemValue: function(index) {
        throw "Not implemented";
    },
    GetValue: function(){
        return this.GetItemValue(this.GetSelectedIndexInternal());
    },    
    GetSelectedIndexInternal: function(){
        return this.savedSelectedIndex;
    }, 
    SetSelectedIndexInternal: function(index){
        this.savedSelectedIndex = index;
    },
    // API
    RaiseItemClick: function() {
        var processOnServer = this.autoPostBack;
        if(!this.ItemClick.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.ItemClick.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    RaiseItemDoubleClick: function() {
        var processOnServer = this.autoPostBack;
        if(!this.ItemDoubleClick.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.ItemDoubleClick.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    RaiseSelectedIndexChanged: function(processOnServer) {
        if(!this.SelectedIndexChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.SelectedIndexChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    UpdateHiddenInputs: function(index){
        var element = this.FindStateInputElement();
        if(_aspxIsExistsElement(element)) {
            var value = this.GetItemValue(index);
            if (value == null)
                value = "";
            element.value = value;
        }
    },
    GetSelectedItem: function(){
        var index = this.GetSelectedIndexInternal();
        return this.GetItem(index);
    },
    GetSelectedIndex: function(){
        return this.GetSelectedIndexInternal();
    },
    SetSelectedItem: function(item){
        var index = (item != null) ? item.index : -1;
        this.SetSelectedIndex(index);
    },
    SetSelectedIndex: function(index){
        this.SelectIndexSilent(index);
    }
});
    // [Seleznyov] The GetColumnText and GetColumnText methods defined in the ASPxClientListBoxItem.
    // The ASPxClientListBoxItem is unde
    // This is made to simplify the help
ASPxClientListEditItem = _aspxCreateClass(null, {
	constructor: function(listEditBase, index, text, value, imageUrl){
	    this.listEditBase = listEditBase;
        this.index = index;
        this.imageUrl = imageUrl;
        this.text = text;
        this.value = value;
    }
});
ASPxClientListBoxItem = _aspxCreateClass(ASPxClientListEditItem, {
	constructor: function(listEditBase, index, texts, value, imageUrl){
	    this.constructor.prototype.constructor.call(this, listEditBase, index, null, value, imageUrl);
        
        this.texts = texts;
        this.text = listEditBase.FormatText(texts);
    },
    GetColumnText: function(columnIndexOrFieldName){
        var columnIndex = -1;
        if(typeof(columnIndexOrFieldName) == "string")
            columnIndex = _aspxArrayIndexOf(this.listEditBase.columnFieldNames, columnIndexOrFieldName);
        else if(typeof(columnIndexOrFieldName) == "number")
            columnIndex = columnIndexOrFieldName;
            
        return this.GetColumnTextByIndex(columnIndex);
    },
    GetColumnTextByIndex: function(columnIndex){
        if(0 <= columnIndex && columnIndex < this.texts.length)
            return this.texts[columnIndex];
        else
            return null;
    }
});

_aspxListBoxScrollCallbackHelperBase = _aspxCreateClass(null, {
    constructor: function(listBoxControl) {
        this.listBoxControl = listBoxControl;
        this.itemsRange = "";
        this.defaultItemsRange = "0:" + (this.listBoxControl.callbackPageSize - 1);
    },
    OnScroll: function(){ },
    Reset: function(){ },
    IsScrolledToTopSpacer: function(){ return false; },
    IsScrolledToBottomSpacer: function(){ return false; },
    GetIsNeedToHideTopSpacer: function(){ return false; },
    GetIsNeedCallback: function(){ return false; },
    GetItemsRangeForLoad: function(){ return this.defaultItemsRange; },
    SetItemsRangeForLoad: function(){}
});
_aspxListBoxScrollCallbackHelper = _aspxCreateClass(_aspxListBoxScrollCallbackHelperBase, {
    constructor: function(listBoxControl) {
        this.constructor.prototype.constructor.call(this, listBoxControl);
        this.isScrolledToTopSpacer = false;
        this.isScrolledToBottomSpacer = false;
    },
    OnScroll: function(){
        this.DetectScrollDirection();
        this.ResetItemsRange();
        if(this.GetIsAnySpacerVisible())
            this.RecalcItemsRangeForLoad();
    },
    DetectScrollDirection: function(){
        var listBoxControl = this.listBoxControl;
        var divElement = listBoxControl.GetScrollDivElement();
        var listTable = listBoxControl.GetListTable();
        var scrollTop = divElement.scrollTop;
        var scrollBottom = divElement.scrollTop + divElement.clientHeight;
        var isTopSpacerVisible = listBoxControl.GetScrollSpacerVisibility(true);
        var isBottomSpacerVisible = listBoxControl.GetScrollSpacerVisibility(false);
        
        var topSpacerHeight = listBoxControl.GetScrollSpacerVisibility(true) ? parseInt(listBoxControl.GetScrollSpacerElement(true).clientHeight) : 0;
        
        this.isScrolledToTopSpacer = (scrollTop < topSpacerHeight) && isTopSpacerVisible;
        this.isScrolledToBottomSpacer = (scrollBottom >= topSpacerHeight + listTable.clientHeight) && isBottomSpacerVisible;
    },
    Reset: function(){
        this.ResetItemsRange();
        this.isScrolledToTopSpacer = false;
        this.isScrolledToBottomSpacer = false;
    },
    ResetItemsRange: function(){
        this.itemsRange = "";
    },
    RecalcItemsRangeForLoad: function(){
        if(this.listBoxControl.isCallbackMode) {
            if(this.isScrolledToTopSpacer || this.isScrolledToBottomSpacer)
                this.SetItemsRangeForLoad(this.isScrolledToTopSpacer);
        }
    },
    IsScrolledToTopSpacer: function(){
        return this.isScrolledToTopSpacer;
    },
    IsScrolledToBottomSpacer: function(){
        return this.isScrolledToBottomSpacer;
    },
    GetIsAnySpacerVisible: function(){
        return this.isScrolledToTopSpacer || this.isScrolledToBottomSpacer;
    },
    GetIsNeedCallback: function(){
        return !this.GetIsItemsRangeEmpty();
    },
    GetIsNeedToHideTopSpacer: function(){
        return this.isScrolledToTopSpacer && this.GetIsItemsRangeEmpty();
    },
    GetItemsRangeForLoad: function(){
        return (!this.GetIsItemsRangeEmpty() ? this.itemsRange : this.defaultItemsRange);
    },
    SetItemsRangeForLoad: function(isForTop){
        var listbox = this.listBoxControl;
        var beginIndex = isForTop ? 
            listbox.serverIndexOfFirstItem - listbox.callbackPageSize : 
            listbox.serverIndexOfFirstItem + listbox.GetItemCount();
        beginIndex = beginIndex < 0 ? 0 : beginIndex;
        
        var endIndex = isForTop ? 
            listbox.serverIndexOfFirstItem - 1 : 
            beginIndex + listbox.callbackPageSize - 1;
    
        this.itemsRange = beginIndex + ":" + endIndex;
        this.isScrolledToTopSpacer = isForTop;
        this.isScrolledToBottomSpacer = !isForTop;
    },
    GetIsItemsRangeEmpty: function(){
        return (this.itemsRange == "" || this.itemsRange == __aspxEmptyItemsRange);
    }
});
ASPxClientListBoxBase = _aspxCreateClass(ASPxClientListEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.APILockCount = 0;
        this.autoScrollLockCount = 0;
        this.isComboBoxList = false;
        this.isSyncEnabled = true;
        this.ownerName = "";
        
        this.serializingHelper = null;
        this.deletedItems = [];
        this.insertedItems = [];
        this.itemsValue = [];    
        this.ItemDoubleClick = new ASPxClientEvent();

        this.ItemClick = new ASPxClientEvent();
    },
    
    LockAutoScroll: function(){
        this.autoScrollLockCount++;
    },
    UnlockAutoScroll: function(){
        this.autoScrollLockCount--;
    },
    GetItemCount: function(){
        return 0;
    },
    GetItemValue: function(index){
        if(0 <= index && index < this.GetItemCount())
            return this.PrepareItemValue(this.itemsValue[index]);
        return null;
    },
    GetItemTexts: function(item) {
        return item.text;
    },
    PrepareItemValue: function(value) {
        return (typeof(value) == "string" && value == "" && this.convertEmptyStringToNull) ? null : value;
    },
    LoadItemsFromCallback: function(isToTop, itemStrings){
    },
    SetValue: function(value){
        var index = this.GetItemIndexByValue(value);
        this.SelectIndexSilentAndMakeVisible(index, false);
    },
    GetItemIndexByValue: function(value){
        for(var i = this.GetItemCount() - 1; i >= 0 ; i--){
            if(this.GetItemValue(i) == value)
                break;
        }
        return i;
    },
    SelectIndex: function (index){
        if(this.SelectIndexSilentAndMakeVisible(index, false)){
            this.RaisePersonalStandardValidation();
            this.OnValueChanged();
        }
    },
    SelectIndexSilent: function(index, initialize){
    },
    SelectIndexSilentAndMakeVisible: function(index, initialize){
        var selectionChanged = this.SelectIndexSilent(index, initialize);
        if(this.autoScrollLockCount == 0)
            this.MakeItemVisible(index);
        return selectionChanged;
    },

    // API
    MakeItemVisible: function(index){
    },
    InitOnContainerMadeVisible: function(){
    },
    AddItem: function(texts, value, imageUrl){
        var index = this.GetItemCount();
        this.InsertItemInternal(index, texts, value, imageUrl);
        return index;
    },
    InsertItem: function(index, texts, value, imageUrl){
        this.InsertItemInternal(index, texts, value, imageUrl);
    },
    InsertItemInternal: function(index, text, value, imageUrl){
    },
    BeginUpdate: function(){
        this.APILockCount ++;
    },
    EndUpdate: function(){
        this.APILockCount --;
        this.Synchronize();
    },
    ClearItems: function(){
        this.BeginUpdate();
        this.UpdateArraysItemsCleared();
        this.ClearItemsCore();
        this.EndUpdate();
    },
    ClearItemsCore: function(){
    },
    ClearItemsForPerformCallback: function(){
        this.itemsValue = [];
        this.ClearItemsCore();
    },
    RemoveItem: function(index){
    },
    GetItem: function(index){
        return null;
    },
    PerformCallback: function(arg) {
    },
    GetCallbackArguments: function(){
        var args = this.GetCustomCallbackArg();
        args += this.GetLoadItemsRangeCallbackArg();
        return args;
    },
    GetLoadItemsRangeCallbackArg: function(){
        return this.FormatCallbackArg(__aspxLoadRangeItemsCallbackPrefix, this.GetItemsRangeForLoad());
    },
    FormatCallbackArg: function(prefix, arg) { // REFACTOR with ASPxGrigView, replace to Callback.js
        arg = arg.toString();
        return (_aspxIsExists(arg) ? prefix + "|" + arg.length + ';' + arg + ';' : "");
    },
    GetItemsRangeForLoad: function(){
        return __aspxEmptyItemsRange;
    },
    GetCallbackOwnerControl: function(){
        if(this.ownerName != "" && !_aspxIsExists(this.ownerControl))
            this.ownerControl = aspxGetControlCollection().Get(this.ownerName);
        return this.ownerControl;
    },
    GetCustomCallbackArg: function(){
        return this.GetSyncHiddenInput("CustomCallback").value;
    },
    SetCustomCallbackArg: function(arg){
        this.GetSyncHiddenInput("CustomCallback").value = arg;
    },
    FormatAndSetCustomCallbackArg: function(arg){
        var formatArg = this.FormatCallbackArg("LECC", _aspxIsExists(arg) ? arg : "");
        this.SetCustomCallbackArg(formatArg);
    },
    SendCallback: function(){
    },
    
    RegisterInsertedItem: function(index, text, value, imageUrl){
        if(this.isSyncEnabled){
            this.RefreshSynchroArraysIndex(index, true);
            var item = this.CreateItem(index, text, value, imageUrl);
            _aspxArrayPush(this.insertedItems, item);
            this.Synchronize();
        }
    },
    CreateItem: function(index, text, value, imageUrl){
        return new ASPxClientListEditItem(this, index, text, value, imageUrl);
    },
    
    UpdateSyncArraysItemDeleted: function(item, isValueRemovingRequired){
        if(isValueRemovingRequired)
            _aspxArrayRemoveAt(this.itemsValue, item.index);
        if(this.isSyncEnabled){
            var index = this.FindItemInArray(this.insertedItems, item);
            if(index == -1){
                this.RefreshSynchroArraysIndex(item.index, false);
                _aspxArrayPush(this.deletedItems, item);
            } else {
                this.RefreshSynchroArraysIndex(item.index, false);
                _aspxArrayRemoveAt(this.insertedItems, index);
            }
            this.Synchronize();
        }
    },
    UpdateArraysItemsCleared: function(){
        if(this.isSyncEnabled){
            for(var i = this.GetItemCount() - 1; i >= 0; i --)
                this.UpdateSyncArraysItemDeleted(this.GetItem(i), false);
        } 
        this.itemsValue = [];
    },
    RefreshSynchroArraysIndex: function(startIndex, isIncrease){
        this.RefreshSynchroArrayIndexIndex(this.deletedItems, startIndex, isIncrease);
        this.RefreshSynchroArrayIndexIndex(this.insertedItems, startIndex, isIncrease);
    },
    RefreshSynchroArrayIndexIndex: function(array, startIndex, isIncrease){
       var delta = isIncrease ? 1 : -1;
       for(var i = 0; i < array.length; i ++){
            if(array[i].index >= startIndex)
                array[i].index += delta;
        }   
    },
    FindItemInArray: function(array, item){
	    for(var i = array.length - 1; i >= 0; i--){
	        var currentItem = array[i];
	        if(currentItem.text == item.text && currentItem.value == item.value &&
	            currentItem.imageUrl == item.imageUrl)
	            break;
	    }
	    return i;
    },

    GetSyncHiddenInput: function(syncType){
        return _aspxGetElementById(this.name + syncType);
    },
    Synchronize: function(){
        if(this.APILockCount == 0){
            if(this.isSyncEnabled){
                this.SynchronizeItems(this.deletedItems, "DeletedItems");
                this.SynchronizeItems(this.insertedItems, "InsertedItems");
            }
            this.CorrectSizeByTimer();
        }
    },
    CorrectSizeByTimer: function(){
    },
    SynchronizeItems: function(items, syncType){
        // TODO think about lock items changes
        var inputElement = this.GetSyncHiddenInput(syncType);
        if(!_aspxIsExistsElement(inputElement))
            return;

        inputElement.value = _aspxEncodeHtml(this.SerializeItems(items));
    },
    
    GetSerializingHelper: function(){ 
        if(this.serializingHelper == null)
            this.serializingHelper = this.CreateSerializingHelper();
        return this.serializingHelper;
    },
    CreateSerializingHelper: function(){ // TODO override it in native and not native listbox with pattern behavior for _aspxListBoxItemsSerializingHelper
        return new _aspxListBoxBaseItemsSerializingHelper(this);
    },
    SerializeItems: function(items){
        var serialiser = this.GetSerializingHelper();
        return serialiser.SerializeItems(items);
    },
    DeserializeItems: function(serializedItems){
        var serialiser = this.GetSerializingHelper();
        return serialiser.DeserializeItems(serializedItems);
    }
});

_aspxListBoxBaseItemsSerializingHelper = _aspxCreateClass(null, {
    constructor: function(listBoxControl) {
        this.listBoxControl = listBoxControl;
        this.startPos = 0;
    },
    SerializeItems: function(items){
        var sb = new ASPxStringBuilder();
        for(var i = 0; i < items.length; i++)
            this.SerializeItem(sb, items[i]);
        
        return sb.ToString();
    },
    SerializeItem: function(sb, item) {
        if(!_aspxIsExists(item))
            return;

        this.SerializeAtomValue(sb, item.index);
        this.SerializeAtomValue(sb, item.value);
        this.SerializeAtomValue(sb, item.imageUrl);
        
        var texts = this.listBoxControl.GetItemTexts(item);
        if(typeof(texts) == "string")
            this.SerializeAtomValue(sb, texts);
        else {
            for(var i = 0; i < texts.length; i++)
                this.SerializeAtomValue(sb, texts[i]);
        }
    },
    SerializeAtomValue: function(sb, value) {
        var valueStr = _aspxIsExists(value) ? value.toString() : "";
        sb.Append(valueStr.length);
        sb.Append('|');
        sb.Append(valueStr);
    },
    
    DeserializeItems: function(serializedItems){
        var deserializedItems = [];
        var item;
        this.startPos = 0;
        while(this.startPos < serializedItems.length){
            var index = this.ParseItemIndex(serializedItems);
            var value = this.ParseItemValue(serializedItems);
            var imageUrl = this.ParseString(serializedItems);
            if(imageUrl == "")
                imageUrl = this.listBoxControl.defaultImageUrl;
            var texts = this.ParseTexts(serializedItems);

            item = this.listBoxControl.CreateItem(index, texts, value, imageUrl);
            _aspxArrayPush(deserializedItems, item);
        }
        return deserializedItems;
    },
    ParseItemIndex: function(serializedItem){
        return parseInt(this.ParseString(serializedItem));
    },
    ParseItemValue: function(serializedItem){
        return this.ParseString(serializedItem);
    },
    ParseString: function(str){
        var indexOfSeparator = str.indexOf(__aspxLBSerializingSeparator, this.startPos);
        var strLength = parseInt(str.substring(this.startPos, indexOfSeparator));
        var strStartPos = indexOfSeparator + __aspxLBSerializingSeparatorLength;
        this.startPos = strStartPos + strLength;
        
        return str.substring(strStartPos, strStartPos + strLength);
    },
    ParseTexts: function(serializedItems){
        return this.ParseString(serializedItems);
    }
});

_aspxListBoxItemsSerializingHelper = _aspxCreateClass(_aspxListBoxBaseItemsSerializingHelper, {
    constructor: function(listBoxControl) {
        this.constructor.prototype.constructor.call(this, listBoxControl);
    },
    ParseTexts: function(serializedItems){
        var textColumnCount = this.listBoxControl.GetItemTextCellCount();
        return (textColumnCount > 1) ? this.DeserializeItemTexts(serializedItems, textColumnCount) 
            : this.constructor.prototype.ParseTexts.call(this, serializedItems);
    },
    DeserializeItemTexts: function(serializedItem, textColumnCount){
        var text = "";
        var texts = [];
        for(var i = 0; i < textColumnCount; i++)
            _aspxArrayPush(texts, this.ParseString(serializedItem));
        return texts;
    }
});

ASPxClientListBox = _aspxCreateClass(ASPxClientListBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.freeUniqIndex = -1;
        this.isHasFakeRow = false;
        this.headerDiv = null;
        this.headerTable = null;
        this.listTable = null;
        this.sampleItemFirstTextCell = null;
        this.width = "";
        this.hasSampleItem = false;
        this.hoverClass = "";
        this.hoverCss = "";
        this.selectedClass = "";
        this.selectedCss = "";
        this.disabledClass = "";
        this.disabledCss = "";
        this.imageCellExists = false;
        this.scrollHandlerLockCount = 0;
        this.columnFieldNames = [];
        this.textFormatString = "";
        this.defaultImageUrl = "";
        this.itemHorizontalAlign = "";
        // Callback
        this.allowMultipleCallbacks = false;
        this.isCallbackMode = false;
        this.callbackPageSize = -1;
        this.isTopSpacerVisible = false;
        this.isBottomSpacerVisible = false;
        this.serverIndexOfFirstItem = 0;
        this.scrollHelper = null;
        this.changeSelectAfterCallback = 0;
        // Cache
        this.ownerControl = null;
        this.SampleItem = null;
        this.scrollDivElement = null;
        this.scrollPageSize = 4;
        this.itemsValue = [];
        
        aspxGetListBoxCollection().Add(this);
    },
    Initialize: function() {   
        this.LockScrollHandler();
        this.InitScrollPos();
        this.freeUniqIndex = this.GetItemCount();
        
        this.SelectIndexSilent(this.GetSelectedIndexInternal(), true);
        this.AdjustControl(false);
        this.InitializeLoadOnDemand();
        this.UnlockScrollHandler();
        ASPxClientEdit.prototype.Initialize.call(this);
    },
    InitScrollPos: function(){ // Fix auto-scroll
        if(!this.isComboBoxList && this.isCallbackMode && this.GetSelectedIndexInternal() == -1)
			this.GetScrollDivElement().scrollTop = 0;
    },
    InitializeLoadOnDemand: function(){
        var loadOnDemandRequired = this.isCallbackMode && this.GetEnabledByServer();
        this.scrollHelper = loadOnDemandRequired ? new _aspxListBoxScrollCallbackHelper(this) : new _aspxListBoxScrollCallbackHelperBase(this);
    },
    InlineInitialize: function(){
        this.LockScrollHandler();
        this.InitializeItemsAttributes();
        this.InitSpecialKeyboardHandling();
        this.GenerateStateItems();
        this.UnlockScrollHandler();
        ASPxClientEditBase.prototype.InlineInitialize.call(this);
    },
    
    GetItemTexts: function(item) {
        return item.texts ? item.texts : [ item.text ];
    },
    CallbackSpaceInit: function(isInitialization){
        if(this.isCallbackMode){
            this.SetBottomScrollSpacerVisibility(this.GetScrollSpacerVisibility(false));
            this.SetTopScrollSpacerVisibility(this.GetScrollSpacerVisibility(true));
            if(isInitialization || this.isComboBoxList){
                this.EnsureSelectedItemVisible();
                _aspxAttachEventToElement(this.GetScrollDivElement(), "scroll", aspxLBScroll);
            }
            
        }
    },
    GetItemCellCount: function(){
        if(this.hasSampleItem)
            return this.GetSampleItemRow().cells.length;
        else if(this.GetItemCount() > 0){
            var listTable = this.GetListTable();
            return listTable.rows[0].cells.length;
        }
        return 0;
    },
    GetItemTextCellCount: function(){
        var itemCellCount = this.GetItemCellCount();
        if(this.imageCellExists)
            itemCellCount --;
        return itemCellCount;
    },
    
    GetItemCellsIdPostfixes: function(){
        if(this.itemCellsIdPostfixes == null){
            this.itemCellsIdPostfixes = [];
            var i = 0;
            if(this.imageCellExists){
                this.itemCellsIdPostfixes.push(__aspxLBIPostfixes[0]);
                i = 1;
            }
            var cellCount = this.GetItemCellCount();
            for(; i < cellCount; i++)
                this.itemCellsIdPostfixes.push(__aspxLBIPostfixes[1] + i);
        }
        return this.itemCellsIdPostfixes;
    },
    InitializeItemsAttributes: function() { 
        var listTable = this.GetListTable();
        if(this.isHasFakeRow){
            var isSyncEnabled = this.isSyncEnabled;
            this.isSyncEnabled = false;
            this.ClearItems();
            this.isSyncEnabled = isSyncEnabled;
        }
        
        listTable.ListBoxId = this.name;  
        
        var rows = listTable.rows;
        var count = rows.length;
        
        var rowIdConst = this.name + "_";
        if(this.hasSampleItem)
            this.InitializeItemAttributes(this.GetSampleItemRow(), rowIdConst + __aspxLBSIIdSuffix);
        rowIdConst += __aspxLBIIdSuffix;
        for(var i = 0; i < count; i ++)
            this.InitializeItemAttributes(rows[i], rowIdConst + i);
    },
    InitializeItemAttributes: function(row, rowId) {
        var cells = row.cells;
        var itemCellsIdSuffixes = this.GetItemCellsIdPostfixes();
        for(var i = 0; i < row.cells.length; i++) {
            cells[i].style.textAlign = this.itemHorizontalAlign;
            cells[i].id = rowId + itemCellsIdSuffixes[i];
        }
    },
    InitializePageSize: function(){
        var divElement = this.GetScrollDivElement();
        var listTable = this.GetListTable();
        var rows = listTable.rows;
        var count = rows.length;

        if(_aspxIsExists(divElement) && count > 0)
            this.scrollPageSize = Math.round(divElement.clientHeight / rows[0].offsetHeight) - 1;
    },
    GenerateStateItems: function() {
        var itemCellsIdSuffixes = this.GetItemCellsIdPostfixes();
        var count = this.GetItemCount();
        var constName = this.name + "_" + __aspxLBIIdSuffix;
        var name = "";
        var controller = aspxGetStateController();
        var i = this.hasSampleItem ? -1 : 0 ;
        for(; i < count; i ++){
            name = constName + i;
            controller.AddHoverItem(name, this.hoverClass, this.hoverCss, itemCellsIdSuffixes, null, null);
            controller.AddSelectedItem(name, this.selectedClass, this.selectedCss, itemCellsIdSuffixes, null, null);
            controller.AddDisabledItem(name, this.disabledClass, this.disabledCss, itemCellsIdSuffixes, null, null);
        }
    },
    
    GetEnabledByServer: function(){
        return this.enabled;
    },
    SetEnabled: function(enabled){  
        ASPxClientListBoxBase.prototype.SetEnabled.call(this, enabled);
        this.CallbackSpaceInit(false);
    },
    AfterInitialize: function(){
        this.CallbackSpaceInit(true);
        this.constructor.prototype.AfterInitialize.call(this);
    },
    AdjustControlCore: function(){
        ASPxClientEdit.prototype.AdjustControlCore.call(this);
        
        this.CorrectSize();
        this.EnsureSelectedItemVisible();
        if(!this.isComboBoxList && __aspxIE7) // FIX orevent to IE7 invisible scrollbar accounting
            this.CorrectWidth();
    },
    CorrectSize: function() {
        if(this.isComboBoxList)
            return;
        this.LockScrollHandler();
        this.CorrectHeight();
        this.CorrectWidth();
        this.InitializePageSize();
        this.UnlockScrollHandler();
    },
    OnCorrectSizeByTimer: function() {
        if(this.IsVisible())
            this.CorrectSize();
    }, 
    
    SetProtectionFromFlick_inFF: function(changeVisibility, changeDisplay){
        if(!__aspxFirefox) return;
        
		var listTable = this.GetListTable();
		if(changeVisibility)
        	listTable.style.visibility = "hidden";
		if(changeDisplay)
        	listTable.style.display = "none";
    },
    ResetProtectionFromFlick_inFF: function(){
        if(!__aspxFirefox) return;
        
        var listTable = this.GetListTable();
        listTable.style.visibility = "";
        listTable.style.display = "";
    },
    CorrectHeight: function(){
        if(__aspxFirefox && this.heightCorrected) return; // Prevent FF flicks
        this.heightCorrected = true;
    
        var mainElement = this.GetMainElement();
        var divElement = this.GetScrollDivElement();
        if(__aspxIE55) divElement.style.display = "none";
        divElement.style.height = "0px";
        var height = mainElement.offsetHeight;
        if(__aspxIE55) divElement.style.display = "";
        divElement.style.height = height + "px";
        var extrudedHeight = mainElement.offsetHeight;
        var heightCorrection = extrudedHeight - height;
        if(heightCorrection > 0){
            var divHeight = divElement.offsetHeight;

            this.SetProtectionFromFlick_inFF(true, false);
            divElement.style.height = (divHeight - heightCorrection) + "px";
            this.ResetProtectionFromFlick_inFF(); 

            extrudedHeight = mainElement.offsetHeight;
            var paddingsHeightCorrection = extrudedHeight - height;
            if(paddingsHeightCorrection > 0)
                divElement.style.height = (divHeight - heightCorrection - paddingsHeightCorrection) + "px";
        } 
    },
    IsMultiColumn: function(){
        return this.columnFieldNames.length > 0;
    },
    CorrectWidth: function(){
        if(this.IsMultiColumn())
            this.CorrectHeaderWidth();
        else
            this.CorrectWidthNonMultiColumn();
    },
    CorrectHeaderWidth: function(){
        var scrollDivElement = this.GetScrollDivElement();
        var headerDivElement = this.GetHeaderDivElement();
        var scrollBarWidth = this.GetVerticalScrollBarWidth(); // Prevent IE7 fail WidthCorrection test
        if(__aspxIE && !__aspxIE55)
            scrollDivElement.style.paddingRight = scrollBarWidth + "px";
        if(_aspxIsExistsElement(headerDivElement)){
            var headerTable;
            if(__aspxSafariFamily){
                headerTable = this.GetHeaderTableElement();
                if(!_aspxIsExistsElement(headerTable))
                    headerTable = null;
            }
            
            if(headerTable)
                headerTable.style.width = "0";
            if(__aspxIE)
                headerDivElement.style.width = scrollDivElement.style.width;
            headerDivElement.style.paddingRight = scrollBarWidth + "px";
            if(headerTable)
                window.setTimeout(function() { headerTable.style.width = "100%"; }, 0);
        }
    },
    CorrectWidthNonMultiColumn: function(){
        var divElement = this.GetScrollDivElement();
        if(__aspxIE){
            var mainElement = this.GetMainElement();
            var scrollBarWidth = this.GetVerticalScrollBarWidth(); // Prevent IE7 fail WidthCorrection test
            mainElement.style.width = "";
            divElement.style.width = "100%";
            if(!__aspxIE55)
                divElement.style.paddingRight = "0px";
            if(this.width != ""){
                mainElement.style.width = this.width;
                divElement.style.width = "0px";
                var widthCorrectrion = __aspxIE55 ? 0 : scrollBarWidth;
                divElement.style.width = (mainElement.clientWidth - widthCorrectrion) + "px";
            }
            else{
                var widthCorrectrion = __aspxIE55 ? scrollBarWidth : 0;
                if(this.IsListBoxWidthLessThenList())
                    widthCorrectrion -= scrollBarWidth;
                divElement.style.width = (mainElement.clientWidth + widthCorrectrion) + "px";
            }
            if(!__aspxIE55)
                divElement.style.paddingRight = scrollBarWidth + "px";
        } else {
            if(this.width == ""){
                var listTable = this.GetListTable();
                var mainElement = this.GetMainElement();
                if(listTable.offsetWidth != 0 || !__aspxMozilla){ // Mozilla: Prevent width collapse when item collection is empty
                    divElement.style.width = (listTable.offsetWidth + this.GetVerticalScrollBarWidth()) + "px";
                    if(__aspxFirefox) // Prevent right border invisibility
                        mainElement.style.width = divElement.offsetWidth + "px";
                }
            }
        }
    },
    EnsureSelectedItemVisible: function(){
        var index = this.GetSelectedIndexInternal();
        if(index != -1)
            this.MakeItemVisible(index);
    },
    MakeItemVisible: function(index){
        if(!this.IsItemVisible(index))
            this.ScrollItemToTop(index);
    },
    IsItemVisible: function(index){
        var scrollDiv = this.GetScrollDivElement();
        var itemElement = this.GetItemElement(index);
        var topVisible = false;
        var bottomVisible = false;
        if(itemElement != null){
            var itemOffsetTop = itemElement.offsetTop + this.GetTopScrollSpacerHeight();
            topVisible = itemOffsetTop >= scrollDiv.scrollTop; // TODO  >=apply fix to another version
            bottomVisible = itemOffsetTop + itemElement.offsetHeight < scrollDiv.scrollTop + scrollDiv.clientHeight;
        }
        return (topVisible && bottomVisible);
    },
    ScrollItemToTop: function(index){
        this.LockScrollHandler();
        this.SetScrollTop(this.GetItemTopOffset(index));
        this.UnlockScrollHandler();
    },
    ScrollToItemVisible: function(index){
        if(!this.IsItemVisible(index)){
            var scrollDiv = this.GetScrollDivElement();
            var scrollTop = scrollDiv.scrollTop;
            var scrollDivHeight = scrollDiv.clientHeight;
            var itemOffsetTop = this.GetItemTopOffset(index);
            var itemHeight = this.GetItemHeight(index);
            
            var itemAbove = scrollTop > itemOffsetTop;
            var itemBelow = scrollTop  + scrollDivHeight < itemOffsetTop + itemHeight;
            if(itemAbove)
                scrollDiv.scrollTop = itemOffsetTop;
            else if(itemBelow){
                var scrollPaddings = scrollDiv.scrollHeight - this.GetListTable().offsetHeight - 
                    this.GetTopScrollSpacerHeight() - this.GetBottomScrollSpacerHeight();
                scrollDiv.scrollTop = itemOffsetTop + itemHeight - scrollDivHeight + scrollPaddings;
            }
        }
    },
    SetScrollTop: function(scrollTop){
        var scrollDiv = this.GetScrollDivElement();
        if(_aspxIsExists(scrollDiv)){ // Render may be removed when callback processing
            scrollDiv.scrollTop = scrollTop;
            if(__aspxOpera) // B36477
                this.CachedScrollTop();
        }   
    },
    CachedScrollTop: function(){
        var scrollDiv = this.GetScrollDivElement();
        scrollDiv.cachedScrollTop = scrollDiv.scrollTop;
    },
    RestoreScrollTopFromCache: function(){
       var scrollDiv = this.GetScrollDivElement();
       if(_aspxIsExists(scrollDiv) && _aspxIsExists(scrollDiv.cachedScrollTop))
            scrollDiv.scrollTop = scrollDiv.cachedScrollTop;
    },
    GetItemElement: function(index){
        var itemElement = this.GetItemRow(index);
        return __aspxSafariFamily && itemElement != null ? itemElement.cells[0] : itemElement;
    },
    GetItemTopOffset: function(index){
        var itemElement = this.GetItemElement(index);
        return (itemElement != null) ? itemElement.offsetTop + this.GetTopScrollSpacerHeight() : 0;
    },
    GetItemHeight: function(index){
        var itemElement = this.GetItemElement(index);
        return (itemElement != null) ? itemElement.offsetHeight : 0;
    },
    IsListBoxWidthLessThenList: function(){
        var divElement = this.GetScrollDivElement();
        var listTable = this.GetListTable();
        var listTabelWidth = listTable.style.width;
        var isLess = false;
        
        listTable.style.width = "";
        isLess = listTable.offsetWidth < divElement.offsetWidth;
        listTable.style.width = listTabelWidth;
        return isLess;
    },
    GetScrollDivElement: function(){
        if(!_aspxIsExistsElement(this.scrollDivElement))
            this.scrollDivElement = document.getElementById(this.name + __aspxLBDSuffix);
        return this.scrollDivElement;
    },
    GetItemCount: function(){
        var lbt = this.GetListTable();
        if(_aspxIsExists(lbt))
            return this.GetListTable().rows.length;
        return 0;
    },
    GetItemFirstTextCell: function(index){
        var rowElement = this.GetItemRow(index);
        if(rowElement == null) 
            return null;
        var cells = __aspxIE ? rowElement.childNodes : rowElement.cells;// Prevent IE cells undefining
        return this.imageCellExists ? cells[1] : cells[0];
    },
    GetItemRow: function(index){
        var listTable = this.GetListTable();
        if(_aspxIsExists(listTable)){
            if(0 <= index && index < listTable.rows.length)
                return listTable.rows[index];
        }
        return null;
    },
    
    GetHeaderDivElement: function(){
        if(!_aspxIsExistsElement(this.headerDiv))
            this.headerDiv = _aspxGetElementById(this.name + __aspxLBHeaderDivIdSuffix);
        return this.headerDiv;
    },
    GetHeaderTableElement: function(){
        if(!_aspxIsExistsElement(this.headerTable)){
            var headerDiv = this.GetHeaderDivElement();
            this.headerTable = _aspxGetChildByTagName(headerDiv, "table", 0);
        }
        return this.headerTable;
    },
    GetListTable: function(){
        if(!_aspxIsExistsElement(this.listTable))
            this.listTable = _aspxGetElementById(this.name + __aspxLTableIdSuffix);
        return this.listTable;
    },
    GetListTableHeight: function(){
        return this.GetListTable().offsetHeight;
    },
    SetValue: function(value){
        var index = this.GetItemIndexByValue(value);
        this.SelectIndexSilentAndMakeVisible(index, false);
    },
    GetVerticalScrollBarWidth: function(){
        var divElement = this.GetScrollDivElement();    
        if(!this.verticalScrollBarWidth || this.verticalScrollBarWidth <= 0){
            this.verticalScrollBarWidth = this.GetVerticalScrollBarWidthCore(divElement);
            return this.verticalScrollBarWidth;
        } else
            return this.GetIsVerticalScrollBarVisible(divElement) ? this.verticalScrollBarWidth : 0;
    },
    GetIsVerticalScrollBarVisible: function(divElement){
        var verticalOverflow = this.GetVerticalOverflow(divElement);
        if(verticalOverflow != "auto"){ 
            var listTable = this.GetListTable();
            return divElement.clientHeight < listTable.offsetHeight;
        } else {
            var borderWidthWithScroll = divElement.offsetWidth - divElement.clientWidth;
            return borderWidthWithScroll == this.scrollDivBordersWidthWithScroll;
        }
    },
    GetVerticalScrollBarWidthCore: function(divElement){
        var overflowYReserv = this.GetVerticalOverflow(divElement);
        this.SetVerticalOverflow(divElement, "auto");
        this.scrollDivBordersWidthWithScroll = divElement.offsetWidth - divElement.clientWidth;
        if(__aspxIE7) return this.scrollDivBordersWidthWithScroll; // Prevent IE7 Border invisible
        this.SetProtectionFromFlick_inFF(false, true);
        this.SetVerticalOverflow(divElement, "hidden");
        var bordersWidthWithoutScroll = divElement.offsetWidth - divElement.clientWidth;
        this.SetVerticalOverflow(divElement, overflowYReserv);
		this.ResetProtectionFromFlick_inFF();
        return this.scrollDivBordersWidthWithScroll - bordersWidthWithoutScroll;
    },
    GetVerticalOverflow: function(element){
        if(__aspxIE || __aspxSafariVersionNonLessThan3 || __aspxChrome)
            return element.style.overflowY;
        return element.style.overflow;
    },
    SetVerticalOverflow: function(element, value){
        if(__aspxIE || __aspxSafariVersionNonLessThan3 || __aspxChrome)
            element.style.overflowY = value;
        else
            element.style.overflow = value;
    },

    OnItemClick: function(index){
        if(!this.isInitialized) 
            return;
            
        this.SelectIndex(index);
        this.SetFocus();
        this.RaiseItemClick();
    },
    OnItemDblClick: function(){
        this.RaiseItemDoubleClick();
    },
    SelectIndexSilent: function(index, initialize){
        var selectedIndex = this.GetSelectedIndexInternal();
        var isValidIndex = (-1 <= index && index < this.GetItemCount());
        if((selectedIndex != index && isValidIndex && !this.readOnly) || initialize){
            if (!initialize)
                this.SetHoverElement(null);
            
            var itemSelection = this.SetItemSelectedState(selectedIndex, index);
            
            this.SetSelectedIndexInternal(index);
            this.UpdateHiddenInputs(index);
            if (!initialize)
                this.SetHoverElement(itemSelection);
            return true;
        }
        return false;
    },
    SetItemSelectedState: function(oldSelectedIndex, newSelectedIndex){
        var itemSelection = this.GetItemFirstTextCell(newSelectedIndex);
        var itemSelected = this.GetItemFirstTextCell(oldSelectedIndex);
        var controller = aspxGetStateController();
        controller.DeselectElementBySrcElement(itemSelected);
        controller.SelectElementBySrcElement(itemSelection);
        return itemSelection;
    },
    // LoadingPanel
    ShowLoadingPanel: function() {    
        if(!this.isComboBoxList){
            var loadingParentElement = this.GetScrollDivElement().parentNode;
            this.CreateLoadingDiv(loadingParentElement);
            this.CreateLoadingPanelWithAbsolutePosition(loadingParentElement);
        }
    },
    // CallbackMode
    ParseCallbackResult: function(result){
        var gottenEgdeOfCollection = false;
        var nothingToLoad = result == "";
        var isLoadindToTopByScroll = this.scrollHelper.IsScrolledToTopSpacer();
        if(!nothingToLoad){
            var deserializedItems = this.DeserializeItems(result);
            this.LoadItemsFromCallback(isLoadindToTopByScroll, deserializedItems);
            gottenEgdeOfCollection = deserializedItems.length != this.callbackPageSize;
        }
        var noMoreItemsForLoadThisDirection = nothingToLoad || gottenEgdeOfCollection;
        this.SetScrollSpacerVisibility(isLoadindToTopByScroll, !noMoreItemsForLoadThisDirection);
        this.scrollHelper.Reset();
    },
    LoadItemsFromCallback: function(isToTop, deserializedItems){
        this.BeginUpdate();
        if(isToTop){
            var scrollHeightCorrection = 0;
            for(var i = deserializedItems.length - 1; i >= 0; i --){
                this.InsertItem(0, deserializedItems[i].texts, deserializedItems[i].value, deserializedItems[i].imageUrl);
                scrollHeightCorrection += this.GetItemHeight(0);
            } 
            this.GetScrollDivElement().scrollTop += scrollHeightCorrection;
            this.serverIndexOfFirstItem -= deserializedItems.length;
            if(this.serverIndexOfFirstItem < 0) this.serverIndexOfFirstItem = 0;
        } else {
            for(var i = 0; i < deserializedItems.length; i ++){
                this.AddItem(deserializedItems[i].texts, deserializedItems[i].value, deserializedItems[i].imageUrl);
            } 
        }
        if(this.changeSelectAfterCallback != 0) {
            var newIndex = this.GetSelectedIndexInternal() + this.changeSelectAfterCallback;
            newIndex = this.GetAdjustedIndex(newIndex);
            this.SelectIndexSilent(newIndex, false);
            
            if(this.scrollHelper.isScrolledToTopSpacer)
                this.ScrollItemToTop(newIndex);
            else
                this.ScrollToItemVisible(newIndex);
        }
        this.EndUpdate();
    },
    CreateSerializingHelper: function(){ // TODO override it in native and not native listbox with pattern behavior for _aspxListBoxItemsSerializingHelper
        return new _aspxListBoxItemsSerializingHelper(this);
    },
    InCallback: function(){
        var callbackOwner = this.GetCallbackOwnerControl();
        if(callbackOwner != null)
            return callbackOwner.InCallback();
        return ASPxClientListEdit.prototype.InCallback.call(this);
    },
    GetItemsRangeForLoad: function(){
        return this.scrollHelper.GetItemsRangeForLoad();
    },
    GetScrollSpacerElement: function(isTop){
        return document.getElementById(this.name + (isTop ? __aspxLBTSIdSuffix : __aspxLBBSIdSuffix));
    },
    GetScrollSpacerVisibility: function(isTop){
        if(!this.clientEnabled)
            return false;
        return isTop ? this.isTopSpacerVisible : this.isBottomSpacerVisible;
    },
    SetScrollSpacerVisibility: function(isTop, visibility){
        this.LockScrollHandler();
        
        var spacer = this.GetScrollSpacerElement(isTop);
        if(_aspxIsExists(spacer)){
            if(visibility)
                spacer.style.height = this.GetScrollDivElement().clientHeight + "px";
            
            if(this.clientEnabled){
                if(isTop)
                    this.isTopSpacerVisible = visibility;
                else
                    this.isBottomSpacerVisible = visibility;
            }
                
            if(_aspxGetElementDisplay(spacer) != visibility){
                _aspxSetElementDisplay(spacer, visibility);
                _aspxGetElementVisibility(spacer, visibility);
            }
        }
        
        this.UnlockScrollHandler();
    },
    SetTopScrollSpacerVisibility: function(visibility){
        this.SetScrollSpacerVisibility(true, visibility);
    },
    SetBottomScrollSpacerVisibility: function(visibility){
        this.SetScrollSpacerVisibility(false, visibility);
    },
    GetTopScrollSpacerHeight: function(){
        return this.GetScrollSpacerVisibility(true) ? this.GetScrollSpacerElement(true).clientHeight : 0;
    },
    GetBottomScrollSpacerHeight: function(){
        return this.GetScrollSpacerVisibility(false) ? this.GetScrollSpacerElement(false).clientHeight : 0;
    },

    SendCallback: function(){
        if(!this.InCallback()){
            this.ShowLoadingPanel();
            var callbackOwner = this.GetCallbackOwnerControl();
            if(callbackOwner != null)
                callbackOwner.SendCallback();
             else {
                var argument = this.GetCallbackArguments();
                this.CreateCallback(argument);
            }
        }
    },
    OnCallback: function(result) {
        this.ParseCallbackResult(result);
        this.OnCallbackFinally();
    },
    OnCallbackError: function(result){
        ASPxClientListBoxBase.prototype.OnCallbackError.call(this, result);
        this.OnCallbackFinally();
    },
    OnCallbackFinally: function(){
        this.HideLoadingPanel();
        this.changeSelectAfterCallback = 0;
    },

    LockScrollHandler: function(){
        this.scrollHandlerLockCount ++;
    },
    UnlockScrollHandler: function(){
        this.scrollHandlerLockCount --;
    },
    IsScrollHandlerLocked: function(){
        return this.scrollHandlerLockCount > 0;
    },
    OnScroll: function(){
        if(this.IsScrollHandlerLocked()) return;
        
        if(this.IsVisible() && !this.InCallback() && ( this.GetScrollSpacerVisibility(true) || this.GetScrollSpacerVisibility(false))) {
            this.scrollHelper.OnScroll();
            if(this.scrollHelper.GetIsNeedToHideTopSpacer())
                this.SetTopScrollSpacerVisibility(false);
            if(this.scrollHelper.GetIsNeedCallback())
                this.SendCallback();
        }
    },
    OnResize: function(){
        var mainElement = this.GetMainElement();
        if(_aspxIsExistsElement(mainElement) && mainElement.style.width.indexOf("%") > -1 && this.IsVisible()){
            this.CorrectSize();
        }
    },
    // Keyboard support
    InitializeKeyHandlers: function() {
        this.AddKeyDownHandler(ASPxKey.PageUp, "OnPageUp");
        this.AddKeyDownHandler(ASPxKey.PageDown, "OnPageDown");
        this.AddKeyDownHandler(ASPxKey.End, "OnEndKeyDown");
        this.AddKeyDownHandler(ASPxKey.Home, "OnHomeKeyDown");
        this.AddKeyDownHandler(ASPxKey.Up, "OnArrowUp");
        this.AddKeyDownHandler(ASPxKey.Down, "OnArrowDown");
    },
    OnArrowUp: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(-1);
        return true;
    },
    OnArrowDown: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(1);
        return true;
    },
    OnPageUp: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(-this.scrollPageSize);
        return true;
    },
    OnPageDown: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(this.scrollPageSize);
        return true;
    },
    OnHomeKeyDown: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(-this.GetItemCount());
        return true;
    },
    OnEndKeyDown: function(evt){
        if(this.isInitialized)
            this.SelectNeighbour(this.GetItemCount());
        return true;
    },
    
    GetAdjustedIndex: function(index){
        if(index < 0) index = 0;
        else{
            var itemCount = this.GetItemCount();
            if(index >= itemCount) index = itemCount - 1;
        }
        return index;
    },
    SelectNeighbour: function (step){
        var itemCount = this.GetItemCount();
        if(itemCount > 0){
            this.changeSelectAfterCallback = 0;
            var selectedIndex = this.GetSelectedIndexInternal();
            var isFirstPageDown = selectedIndex == -1 && step == this.scrollPageSize;
            selectedIndex = isFirstPageDown ? step : selectedIndex + step;

            selectedIndex = this.GetAdjustedIndex(selectedIndex);
            this.LockAutoScroll();
            this.SelectIndex(selectedIndex);
            this.UnlockAutoScroll();

            if(this.GetIsNeedToCallbackLoadItemsToTop(selectedIndex, step, itemCount)){
                this.LoadItemsOnCallback(true, selectedIndex);
            } else if(this.GetIsNeedToCallbackLoadItemsToBottom(selectedIndex, step, itemCount)){
                this.LoadItemsOnCallback(false, selectedIndex);
            }
            this.ScrollToItemVisible(selectedIndex);
        }
    },
    GetIsNeedToCallbackLoadItemsToTop: function(selectedIndex, step, itemCount){
        return this.isCallbackMode && this.GetScrollSpacerVisibility(true) && 
            this.serverIndexOfFirstItem > 0 && ((step < 0 && selectedIndex <= 0) || step <= -itemCount);
    },
    GetIsNeedToCallbackLoadItemsToBottom: function(selectedIndex, step, itemCount){
        return this.isCallbackMode && this.GetScrollSpacerVisibility(false) && 
            ((step > 0 && selectedIndex >= itemCount - 1) || step >= itemCount);
    },
    LoadItemsOnCallback: function(isToTop, index){
        this.changeSelectAfterCallback = index - this.GetSelectedIndexInternal();
        this.scrollHelper.SetItemsRangeForLoad(isToTop);
        this.SendCallback();
    },
    FindInputElement: function(){
        return document.getElementById(this.name + "_KBS");
    },
    
    // TODO Refactor this function - coincide with ASPxMenu and ASPxNavBar
    SetHoverElement: function(element){
        aspxGetStateController().SetCurrentHoverElementBySrcElement(element);
    },
    InitOnContainerMadeVisible: function(){
        this.AdjustControl(false);
    },
    SetSelectedIndex: function(index){
        if(index != this.GetSelectedIndexInternal())
            this.SelectIndexSilentAndMakeVisible(index, false);
    },
    ClearItemsCore: function(){
        this.ClearListTableContent();
        this.SetSelectedIndexInternal(-1);
        this.SetValue(null);
    },
    CopyCellWidths: function(sourceRowIndex, destinationRowIndex){
        var cellCount = this.GetItemCellCount();
        var sourceRow = this.GetItemRow(sourceRowIndex);
        var destRow = this.GetItemRow(destinationRowIndex);
        for(var i = 0; i < cellCount; i++)
            destRow.cells[i].style.width = sourceRow.cells[i].style.width;
    },
    RemoveItem: function(index){
        if(index == 0 && this.GetItemCount() > 1)
            this.CopyCellWidths(0, 1);
            
        if(0 <= index && index < this.GetItemCount()){
            this.UpdateSyncArraysItemDeleted(this.GetItem(index), true);
            var row = this.GetItemRow(index);
            if(_aspxIsExistsElement(row))
                row.parentNode.removeChild(row);
                
            var selectedIndex = this.GetSelectedIndexInternal();
            if(index < selectedIndex)
                this.SetSelectedIndexInternal(selectedIndex - 1);
            else if(index == selectedIndex)
                this.SetSelectedIndexInternal(-1);

        }
    },
    GetItem: function(index){
        var listTable = this.GetListTable();
        if(!_aspxIsExists(listTable) || index < 0 || index >= listTable.rows.length)
            return null;
        
        var row = listTable.rows[index];
        var image = this.imageCellExists ? _aspxGetChildByTagName(row.cells[0], "IMG", 0) : null;
        var src = image == null ? "" : ASPxImageUtils.GetImageSrc(image);
        
        var i = this.imageCellExists ? 1 : 0;
        var texts = [];
        for(;i < row.cells.length; i ++){
            var textCell = row.cells[i];
            
            if(typeof(textCell.attributes["DXText"]) != "undefined")
                texts.push(_aspxGetAttribute(textCell, "DXText"));
            else {
                var text = _aspxGetInnerText(textCell);
                text = text.replace(new RegExp(__aspxNbspChar, "g"), " ");
                texts.push(text);
            }
        }
        
        return new ASPxClientListBoxItem(this, index, texts, this.itemsValue[index], src);
    },
    
    PerformCallback: function(arg) {
        this.SetScrollSpacerVisibility(true, false);
        this.SetScrollSpacerVisibility(false, false);
        
        this.ClearItemsForPerformCallback();
        this.serverIndexOfFirstItem = 0;
        this.SetScrollSpacerVisibility(true, false);
        this.SetScrollSpacerVisibility(false, false);
        
        this.FormatAndSetCustomCallbackArg(arg);
        this.SendCallback();
    },

    GetTableRowParent: function(table){
        if(table.tBodies.length > 0)
            return table.tBodies[0];
        return table;
    },
    ProtectWhitespaceSerieses: function(text){
        if(text == "") 
            text = __aspxNbsp;
        else {
          if(text.charAt(0) == ' ')
                text = __aspxNbsp + text.slice(1);
            if(text.charAt(text.length - 1) == ' ')
                text = text.slice(0, -1) + __aspxNbsp;
            text = text.replace(new RegExp("  ", "g"), " &nbsp;");
        }
        return text;
    },

    CreateItem: function(index, texts, value, imageUrl){
        return new ASPxClientListBoxItem(this, index, texts, value, imageUrl);
    },
    InsertItemInternal: function(index, texts, value, imageUrl){ // TODO texts
        if(!_aspxIsExists(texts) || texts.length == 0)
            texts = [""];
        else if(typeof(texts) == "string")
            texts = [ texts ];
            
        if(!_aspxIsExists(value))
            value = texts[0];
        if(!_aspxIsExists(imageUrl))
            imageUrl = "";
        
        var newItemRow = this.CreateNewItem();
        _aspxRemoveAttribute(newItemRow, "id");
        
        var listTable = this.GetListTable();
        var tbody = this.GetTableRowParent(listTable);
        var isAdd = listTable.rows.length <= index;
        if(isAdd)
            tbody.appendChild(newItemRow);
        else
            tbody.insertBefore(newItemRow, this.GetItemRow(index));
        
        var newIndex = this.FindFreeIndex();
        var newId = this.CreateItemId(newIndex);
        var newClientId = this.CreateItemClientId(newIndex);
        this.InitializeItemAttributes(newItemRow, newClientId, true);
        var sampleItemFirstTextCell = this.GetSampleItemFirstTextCell();
        var itemCellsIdPostfixes = this.GetItemCellsIdPostfixes();
        
        var styleController = aspxGetStateController();
        aspxAddHoverItems(this.name, this.CreateStyleClasses(newId, itemCellsIdPostfixes, 
            styleController.GetHoverElement(sampleItemFirstTextCell), __aspxHoverItemKind));
        aspxAddSelectedItems(this.name, this.CreateStyleClasses(newId, itemCellsIdPostfixes, 
            styleController.GetSelectedElement(sampleItemFirstTextCell), __aspxSelectedItemKind));
        aspxAddDisabledItems(this.name, this.CreateStyleClasses(newId, itemCellsIdPostfixes, 
            styleController.GetDisabledElement(sampleItemFirstTextCell), __aspxDisabledItemKind));
        
        _aspxRemoveAttribute(sampleItemFirstTextCell, __aspxCachedHoverItemKind);
        _aspxRemoveAttribute(sampleItemFirstTextCell, __aspxCachedSelectedItemKind);
        _aspxRemoveAttribute(sampleItemFirstTextCell, __aspxCachedDisabledItemKind);

        this.PrepareItem(newItemRow, texts, imageUrl); 
        
        _aspxArrayInsert(this.itemsValue, value, index);
        this.RegisterInsertedItem(index, texts, value, imageUrl); // TODO make possible to remove
        
        this.AdjustSelectedIndex(index);
    },
    AdjustSelectedIndex: function(index){
        var selectedIndex = this.GetSelectedIndexInternal();
        if(index <= selectedIndex && selectedIndex != -1)
            this.SetSelectedIndexInternal(selectedIndex + 1);
    },
    PrepareItem: function(newItemRow, texts, imageUrl){ 
        var i = 0;
        if(this.imageCellExists) {
            this.PrepareItemImage(newItemRow, imageUrl);
            i = 1;
        }
        var cellCount = this.GetItemCellCount();
        for(var j = 0; i < cellCount; i++, j++)
            this.PrepareItemTextCell(newItemRow.cells[i], texts[j])
    },
    PrepareItemImage: function(newItemRow, imageUrl){
        var imageCell = newItemRow.cells[0];
        var image = _aspxGetChildByTagName(imageCell, "IMG", 0);
        if(!_aspxIsExists(image)){
            image = document.createElement("IMG");
            imageCell.innerHTML = ""; // TODO clarify for what do we need this
            imageCell.appendChild(image);
        }
        ASPxImageUtils.SetImageSrc(image, imageUrl);
    }, 
    PrepareItemTextCell: function(cell, text){
        if(!_aspxIsExists(text)) 
            text = "";
        
        cell.innerHTML = this.ProtectWhitespaceSerieses(text);
        if(text == "")
            _aspxSetAttribute(cell, "DXText", text);
    },
    ClearListTableContent: function(){
        var tBody = this.GetTableRowParent(this.GetListTable());
        if(__aspxIE)
            tBody.innerText = "";
        else
            tBody.innerHTML = "";
    },
    FormatText: function(texts){
        if(typeof(texts) == "string")
            return texts;
        else if(!this.IsMultiColumn())
            return texts[0];
        else {
            var text = this.textFormatString;
            for(var i = 0; i <= texts.length; i++)
                text = text.replace(new RegExp("\\{" + i + "\\}", "g"), texts[i]);
            return text;
        }
    },

    CreateItemId: function(index){
        return __aspxLBIIdSuffix + index;
    },
    CreateItemClientId: function(index){
        return this.name + "_" + __aspxLBIIdSuffix + index;
    },
    CreateNewItem: function(){
        var newItemRow = this.GetSampleItemRow();
        if (_aspxIsExistsElement(newItemRow)) 
            newItemRow = newItemRow.cloneNode(true);
        return newItemRow;
    },
    CreateStyleClasses: function(id, postfixes, item, kind){
        var classes = [];
        if(_aspxIsExists(item) && _aspxIsExists(item[kind])){
            classes[0] = [];
            classes[0][0] = item[kind].className;
            classes[0][1] = item[kind].cssText;
            classes[0][2] = [];
            classes[0][2][0] = id;
            classes[0][3] = postfixes;
        }
        return classes;
    },
    CorrectSizeByTimer: function(){
        if(this.APILockCount == 0 && this.IsDisplayed())
            _aspxSetTimeout("aspxLBCorrectSizeByTimer(\""+this.name+"\");", 0);
    },
    FindFreeIndex: function(){
        return this.freeUniqIndex ++;
    },
    GetSampleItemRowID: function(){
        return this.name + "_" + __aspxLBSIIdSuffix;
    },
    GetSampleItemRow: function(){
        if(this.SampleItem == null)
            this.SampleItem = _aspxGetElementById(this.GetSampleItemRowID());
        return this.SampleItem;
    },
    GetSampleItemFirstTextCell: function(){
        if(!_aspxIsExistsElement(this.sampleItemFirstTextCell)){
			var sampleItemRow = this.GetSampleItemRow();
			if(_aspxIsExistsElement(sampleItemRow))
			    this.sampleItemFirstTextCell = sampleItemRow.cells[this.imageCellExists ? 1 : 0];
		}
        return this.sampleItemFirstTextCell;
    },
    ChangeEnabledAttributes: function(enabled){
        this.ChangeListTableEvents(this.GetListTable(), _aspxChangeEventsMethod(enabled));
        var inputElement = this.GetInputElement();
        if(_aspxIsExists(inputElement)) 
            this.ChangeSpecialInputEnabledAttributes(inputElement, _aspxChangeEventsMethod(enabled));
    },
    ChangeEnabledStateItems: function(enabled){
        var controller = aspxGetStateController();
        controller.SetElementEnabled(this.GetMainElement(), enabled);

        var count = this.GetItemCount();
        var i = this.hasSampleItem ? -1 : 0 ;
        for(; i < count; i ++){
            var element = this.GetItemFirstTextCell(i);
            if(_aspxIsExists(element))
                controller.SetElementEnabled(element, enabled);
        }
    },
    ChangeListTableEvents: function(listTable, method){
        if(this.isComboBoxList){
            method(listTable, "mouseup", aspxLBIClick);
            if(__aspxFirefox)// FIX prevent to wront continues selection by item click
                method(listTable, "mousedown", _aspxPreventEvent); 
        }
        else{
            method(listTable, "click", aspxLBIClick);   
            method(listTable, "dblclick", aspxLBIClick);    
        }
    }
});

ASPxClientNativeListBox = _aspxCreateClass(ASPxClientListBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    
    SetMainElement: function(mainElement){
        this.mainElement = mainElement;
    },
    FindInputElement: function(){
        return this.GetMainElement();
    },    

    GetOptionCount: function(){
        return this.GetMainElement().options.length;
    },
    GetOption: function(index){
        return this.GetMainElement().options[index];
    },
    
    GetItemCount: function(){
        return this.GetOptionCount();
    },
    SelectIndexSilent: function(index, initialize){
        var selectedIndex = this.GetSelectedIndexInternal();
        var isValidIndex = (-1 <= index && index < this.GetItemCount());
        if((selectedIndex != index && isValidIndex) || initialize){
            this.SetSelectedIndexInternal(index);
            return true;
        }
        return false;
    },
    GetSelectedIndexInternal: function(){
        return this.GetMainElement().selectedIndex; 
    },
    SetSelectedIndexInternal: function(index){
        this.GetMainElement().selectedIndex = index; 
    },
    ClearItemsCore: function(){
        this.GetMainElement().innerHTML = "";
    },
    RemoveItem: function(index){
        if(0 <= index && index < this.GetItemCount()){
            var oldSelectedIndex = this.GetSelectedIndexInternal();
            this.UpdateSyncArraysItemDeleted(this.GetItem(index), true);
            
            var option = this.GetOption(index);
            this.GetMainElement().removeChild(option);
            
            this.UpdateOptionValues();
            if(index == oldSelectedIndex)
                this.SetSelectedIndexInternal(-1);
        }
    },
    GetItem: function(index){
        if(0 <= index && index < this.GetOptionCount()) {
            var text = this.GetOption(index).text;
            if(_aspxIsExists(text))
                return new ASPxClientListEditItem(this, index, text, this.itemsValue[index], "");
        }
        return null;
    },
    PerformCallback: function(arg) {
        this.ClearItemsForPerformCallback();
        this.FormatAndSetCustomCallbackArg(arg);
        this.SendCallback();
    },
    SendCallback: function(){
        if(!this.InCallback()){
            var callbackOwner = this.GetCallbackOwnerControl();
            if(callbackOwner != null)
                callbackOwner.SendCallback();
             else {
                var argument = this.GetCallbackArguments();
                this.CreateCallback(argument);
            }
        }
    },
    ParseCallbackResult: function(result){
        var deserializedItems = this.DeserializeItems(result);
        this.LoadItemsFromCallback(true, deserializedItems);
    },
    
    InsertItemInternal: function(index, text, value, imageUrl){
        if(!_aspxIsExists(value))     
            value = text;
        var oldSelectedIndex = this.GetSelectedIndexInternal();
            
        var isAdd = this.GetOptionCount() <= index;
        var newOption = document.createElement("OPTION");
        if(isAdd)
            this.GetMainElement().appendChild(newOption);
        else
            this.GetMainElement().insertBefore(newOption, this.GetOption(index));
        newOption.innerHTML = text;
        this.UpdateOptionValues();
        
        _aspxArrayInsert(this.itemsValue, value, index);
        this.RegisterInsertedItem(index, text, value, imageUrl); // TODO make possible to remove
        if(index == oldSelectedIndex && index != -1)
            this.SetSelectedIndex(index + 1);
    },
    UpdateOptionValues: function() {
        for(var i = 0; i < this.GetOptionCount(); i++)
            this.GetOption(i).value = i;
    },
    ChangeEnabledAttributes: function(enabled){
        if(!this.isComboBoxList)
            this.GetMainElement().disabled = !enabled;
    },
    OnCallback: function(result) {
        this.ParseCallbackResult(result);
    },
    OnItemDblClick: function(){
        this.RaiseItemDoubleClick();
    },
    LoadItemsFromCallback: function(isToTop, deserializedItems){
        this.BeginUpdate();
        for(var i = deserializedItems.length - 1; i >= 0; i --)
            this.InsertItem(0, deserializedItems[i].text, deserializedItems[i].value, deserializedItems[i].imageUrl);
        this.EndUpdate();
    }
});
ASPxClientRadioButtonList = _aspxCreateClass(ASPxClientListEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);                                        
        
        this.items = [];
	},
	InlineInitialize: function() {
	    var selectedIndex = this.GetSelectedIndex();
	    this.UpdateHiddenInputs(selectedIndex);
	    this.SetSelectedIndex(this.GetSelectedIndex());//B134441
	    
	    ASPxClientListEdit.prototype.InlineInitialize.call(this);
	},
    SetFocus: function() {
        var index = this.GetSelectedIndexInternal();
        if(index == -1)
            index = 0;
        var itemElement = this.GetItemElement(index);
        if (itemElement != null && _aspxGetActiveElement() != itemElement && _aspxIsEditorFocusable(itemElement)) 
            _aspxSetFocus(itemElement);
    },
    GetInputElement: function() {
        var index = this.GetSelectedIndexInternal();
        return this.GetItemElement(index);
    },
    GetValueInputElement: function() {
        if(this.valueInput == null) {
            this.valueInput = _aspxCreateHiddenField(null, this.name + "_ValueInput");
            var stateInput = this.FindStateInputElement();
            stateInput.parentNode.insertBefore(this.valueInput, stateInput);
        }
        return this.valueInput;
    },
    GetValueInputToValidate: function() {
        return this.GetValueInputElement();
    },
    GetItemElement: function(index) {
        return this.GetChild("_RB" + index + "_I");
    },
    GetItemMainElement: function(index) {
        return this.GetChild("_RB" + index);
    },
    GetItemCount: function() {
        return this.items.length;
    },
    OnItemClick: function(index) {
        if(this.GetSelectedIndexInternal() != index) {
            this.SelectIndexSilent(index);
            this.RaisePersonalStandardValidation();
            this.OnValueChanged();
        }
    },
    OnItemClickReadonly: function() {
        var index = this.GetSelectedIndexInternal();
        this.SelectIndexSilent(index);
    },
    UpdateHiddenInputs: function(index) {
        var stateInput = this.FindStateInputElement();
        if(_aspxIsExistsElement(stateInput))
            stateInput.value = index;
        
        // Standard validation support
        var valueInput = this.GetValueInputElement();
        if(_aspxIsExistsElement(valueInput)) {
            // [Victor] Standard validation mechanism trying to receive a value of an editor doens't
            // treat empty value string as a value at all and continue searching. So, we must store
            // at least one whitespace character to force validators to use our value input.
            var value = this.GetValue();
            valueInput.value = _aspxIsExists(value) ? value : " ";
        }
    },
    SelectIndexSilent: function(index) {
        var itemCount = this.GetItemCount();
        var isValidIndex = (-1 <= index && index < itemCount);
        if(isValidIndex) {
            for(var i = 0; i < itemCount; i++) {
                if(_aspxIsExists(this.GetItemElement(i)))
                    this.GetItemElement(i).checked = (i == index);
            }
            this.SetSelectedIndexInternal(index);
            this.UpdateHiddenInputs(index);
        }
    },
        
    GetItemValue: function(index){
        if (index > -1 && index < this.items.length) {
            if (typeof(this.items[index].value) == "string" && this.items[index].value == "" && this.convertEmptyStringToNull)
                return null;
            else
                return this.items[index].value;
        }
        return null;
    },
    // value
    SetValue: function(value) {
        for (var i = 0; i < this.items.length; i++) {
            if(this.GetItemValue(i) == value) {            
                this.SelectIndexSilent(i);
                return;
            }
        } 
        this.SelectIndexSilent(-1);       
    },
    // API
    CreateItems: function(itemsProperties){
        for(var i = 0; i < itemsProperties.length; i ++)
            this.CreateItem(i, itemsProperties[i][0], itemsProperties[i][1], itemsProperties[i][2]);
    },
    CreateItem: function(index, text, value, imageUrl){
        var item = new ASPxClientListEditItem(this, index, text, value, imageUrl);
        _aspxArrayPush(this.items, item);
    },
    GetItem: function(index){
        return (0 <= index && index < this.items.length) ? this.items[index] : null;
    },
    
    ChangeEnabledAttributes: function(enabled){
        for(var i = 0; i < this.GetItemCount(); i++){        
            var element = this.GetItemElement(i);
            if(_aspxIsExists(element)){
                this.ChangeItemEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
                element.disabled = !enabled;
            }
        }
    },
    ChangeEnabledStateItems: function(enabled){
        aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
        for(var i = 0; i < this.GetItemCount(); i++){        
            var element = this.GetItemMainElement(i);
            if(_aspxIsExists(element))
                aspxGetStateController().SetElementEnabled(element, enabled);
        }
    },
	ChangeItemEnabledAttributes: function(element, method){
        method(element, "onclick");
	}
});

ASPxClientListBoxCollection = _aspxCreateClass(ASPxClientControlCollection, {
    OnResize: function(){
        for(var name in this.elements){
            var control = this.elements[name];
            control.OnResize();
        }
    }
});
var __aspxListBoxCollection = null;
function aspxGetListBoxCollection(){
    if(__aspxListBoxCollection == null)
        __aspxListBoxCollection  = new ASPxClientListBoxCollection();
    return __aspxListBoxCollection;
}
_aspxAttachEventToElement(window, "resize", aspxLBWindowResize);
function aspxLBWindowResize(evt){
    aspxGetListBoxCollection().OnResize(evt);	
}

function aspxLBIClick(evt){
    var element = _aspxGetEventSource(evt);
    while(element != null && element.tagName != "BODY"){
        if(element.tagName == "TR"){
		    var table = element.offsetParent;
		    if(_aspxIsExists(table) && _aspxIsExists(table.ListBoxId)){
		        var lb = aspxGetControlCollection().Get(table.ListBoxId);
		        if(lb != null) {
		            if(evt.type == "dblclick") 
		                lb.OnItemDblClick();
		            else if(!lb.isComboBoxList || _aspxGetIsLeftButtonPressed(evt)) 
		                lb.OnItemClick(element.rowIndex);
		        }
        		break;
		    }
		}
        element = element.parentNode;
	}
}
function aspxNLBIDClick(evt){
    var element = _aspxGetEventSource(evt);
    if(element != null && element.tagName == "SELECT"){
		var lb = aspxGetControlCollection().Get(element.id);
		if(lb != null)
		    lb.OnItemDblClick();
	}
}

function aspxLBScroll(evt){
    var sourceId = _aspxGetEventSource(evt).id;
    if(sourceId.slice(-__aspxLBDSuffix.length) == __aspxLBDSuffix){
        var name = sourceId.slice(0, -2);
        var lb = aspxGetControlCollection().Get(name);
        if(lb != null && lb.isInitialized) 
            lb.OnScroll();
    }
}
function aspxLBCorrectSizeByTimer(name){
    var lb = aspxGetControlCollection().Get(name);
    if(lb != null)
        lb.OnCorrectSizeByTimer();
}

function aspxERBLIClick(name, index) {
    var list = aspxGetControlCollection().Get(name);
    if(list != null)
        list.OnItemClick(index);
}
function aspxERBLICancel(name) {
    var list = aspxGetControlCollection().Get(name);
    if(list != null)
        list.OnItemClickReadonly();
}	
ASPxClientCheckBox = _aspxCreateClass(ASPxClientEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.stateInput = null;

        this.valueChecked = true;
        this.valueUnchecked = false;
        this.CheckedChanged = new ASPxClientEvent();
    },
    Initialize: function() {
        ASPxClientEdit.prototype.Initialize.call(this);
        this.previousChecked = this.GetInputElement().checked;
    },
        
    FindInputElement: function() {
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element) && element.tagName != "INPUT")
            element = this.GetChild("_I");
        return element;
    },
    GetStateInput: function() {
        if(!_aspxIsExistsElement(this.stateInput))
            this.stateInput = this.GetChild("_S");
        return this.stateInput;
    },
    RaiseValueChangedEvent: function() {
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseCheckedChanged(processOnServer);
        return processOnServer;
    },    
    OnClick: function() {
        var value = this.previousChecked ? this.valueUnchecked : this.valueChecked;        
        this.SetValue(value);
        this.OnValueChanged();
    },    
    // value
    GetValue: function() {
        var value;
        switch(this.GetStateInput().value) {
            case "N":
                return null;
            case "C":
                value = this.valueChecked; break;
            case "U":
                value = this.valueUnchecked; break;                            
        }
        if(value === "" && this.convertEmptyStringToNull)
            value = null;                    
        return value;
    },        
    SetValue: function(value) {
        // hidden field
        var stateInput = this.GetStateInput();        
        if(value == null)
            stateInput.value = "N";
        else
            stateInput.value = value == this.valueChecked ? "C" : "U";        
        // presentation
        this.GetInputElement().checked = this.previousChecked = value == this.valueChecked;
        
        this.OnValueSet();
    },
    OnValueSet: function() {
        
    },
    // API
    RaiseCheckedChanged: function(processOnServer) {
        if(!this.CheckedChanged.IsEmpty()) {
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.CheckedChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    GetChecked: function() {
        return this.GetValue() == this.valueChecked;
    },
    SetChecked: function(isChecked) {
        this.SetValue(isChecked ? this.valueChecked : this.valueUnchecked);
    },
    
    ChangeEnabledAttributes: function(enabled){
        this.ChangeInputEnabledAttributes(this.GetInputElement(), _aspxChangeAttributesMethod(enabled));
        this.GetInputElement().disabled = !enabled;
    },
    ChangeEnabledStateItems: function(enabled){
        aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
    },
	ChangeInputEnabledAttributes: function(element, method){
        method(element, "onclick");
	}
});
ASPxClientRadioButton = _aspxCreateClass(ASPxClientCheckBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);

        this.isASPxClientRadioButton = true;
    },
    OnValueSet: function() {
        if(this.GetInputElement().checked)
            this.UncheckOtherGroupMemebers(true /* suppressEvents */);
    },
    OnClick: function() {
        if(!this.previousChecked) {
            this.UncheckOtherGroupMemebers();
            ASPxClientCheckBox.prototype.OnClick.call(this);
        }
    },
    UncheckOtherGroupMemebers: function(suppressEvents) {
        var members = this.GetGroupMembers();
        for(var i = 0; i < members.length; i++) {
            var radioButton = members[i];
            if(radioButton != this && radioButton.GetValue()){
                radioButton.SetValue(false);
                if(!suppressEvents)
                    radioButton.RaiseValueChangedEvent();
            }
        }
    },
    OnReadonlyClick: function() {
        if(!this.previousChecked) {            
            var members = this.GetGroupMembers();            
            for(var i = 0; i < members.length; i++) {
                var radioButton = members[i];
                radioButton.SetValue(radioButton.GetValue());
            }
        }
    },    
    GetGroupName: function() {
        var inputElement = this.GetInputElement();
        if (!_aspxIsExistsElement(inputElement))
            return null;
        var name = inputElement.name;
        if(!name.length)
            name = "";
        return name;
    },
    GetGroupMembers: function() {
        var result = [ ];
        var groupName = this.GetGroupName();
        if(groupName.length > 0) {
            var collection = aspxGetControlCollection();
            var control;
            for(var i in collection.elements) {
                control = collection.elements[i];
                if(ASPxIdent.IsASPxClientRadioButton(control)) {
                    var controlGroupName = control.GetGroupName();
                    if (controlGroupName != null && controlGroupName == groupName)
                        result.push(control);
                }
            }
        } else {
            result.push(this);
        }
        return result;        
    },
    // API
    GetChecked: function() {
        return this.GetValue() == true;
    },
    SetChecked: function(isChecked) {
        this.SetValue(isChecked);
    }    
});

ASPxIdent.IsASPxClientRadioButton = function(obj) {
    return _aspxIsExists(obj.isASPxClientRadioButton) && obj.isASPxClientRadioButton;
};

function aspxChkOnClick(name) {
    var edit = aspxGetControlCollection().Get(name);
    if(_aspxIsExists(edit))
        edit.OnClick();
}
function aspxERBOnReadonlyClick(name) {
    var rb = aspxGetControlCollection().Get(name);
    if(_aspxIsExists(rb))
        rb.OnReadonlyClick();
}

var __aspxTEInputSuffix = "_I";
var __aspxTERawInputSuffix = "_Raw";
var __aspxPasteCheckInterval = 20;
ASPxClientTextEdit = _aspxCreateClass(ASPxClientEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);                        
        
        this.nullText = "";
        this.raiseValueChangedOnEnter = true;
        
        this.maskInfo = null;        
        this.maskValueBeforeUserInput = "";
        this.maskPasteTimerID = -1;
        this.maskPasteLock = false;                
        this.maskTextBeforePaste = "";                
        this.maskHintHtml = "";
        this.maskHintTimerID = -1;
        this.TextChanged = new ASPxClientEvent();
    },
    InlineInitialize: function(){
        ASPxClientEdit.prototype.InlineInitialize.call(this);
        if(this.maskInfo != null)
            this.InitMask();
    },

    FindInputElement: function(){
        return this.isNative ? this.GetMainElement() : _aspxGetElementById(this.name + __aspxTEInputSuffix);
    },
    GetRawInputElement: function() {
		return _aspxGetElementById(this.name + __aspxTERawInputSuffix);
    },
    DecodeRawInputValue: function(value) {
		return value;
    },
    SyncRawInputValue: function() {
		if(this.maskInfo != null)
			this.GetRawInputElement().value = this.maskInfo.GetValue();
	    else
	        this.GetRawInputElement().value = this.GetInputElement().value;
    },
    
    SwapNullText: function() {	
		if(this.nullText == "")
			return;	
		if(this.maskInfo != null) {			
			this.ApplyMaskInfo(this.focused);
		} else {
			if(this.GetValue() == null) {
				this.GetInputElement().value = this.focused ? this.GetRawInputElement().value : this.nullText;
				// B36159
				if(this.focused)
					_aspxSetInputSelection(this.GetInputElement(), 0, 0);
			}
		}
    },    
    
    GetValue: function() {
        var value = null;
		if(this.maskInfo != null)
		    value = this.maskInfo.GetValue();
		else if(this.nullText != "")
		    value = this.GetRawInputElement().value;
		else
		    value = this.GetInputElement().value;
        return (value == "" && this.convertEmptyStringToNull) ? null : value;
    },
    SetValue: function(value) {
        if(value == null) value = "";
        if(this.maskInfo != null) {
			this.maskInfo.SetValue(value);
			this.ApplyMaskInfo(false);
			this.SavePrevMaskValue();
        } 
        else if(this.nullText != "") {
            this.GetRawInputElement().value = value;
            this.GetInputElement().value = (value == null || (value == "" && this.convertEmptyStringToNull)) ? this.nullText : value;
        }
        else
            this.GetInputElement().value = value;
    },
    // size correction
    CollapseControl: function(checkSizeCorrectedFlag) {
        if (checkSizeCorrectedFlag && this.sizeCorrectedOnce)
            return;
        var mainElement = this.GetMainElement();
        if (!_aspxIsExistsElement(mainElement))
            return;
        var mainElementCurStyle = _aspxGetCurrentStyle(mainElement);
        if (_aspxIsExistsElement(mainElement) && this.widthCorrectionRequired && mainElementCurStyle.width != "" && mainElementCurStyle.width != "auto")
            this.GetInputElement().style.width = "0";
    },
    CorrectEditorWidth: function() {
        var inputElement = this.GetInputElement();
        _aspxSetOffsetWidth(inputElement, _aspxGetClearClientWidth(_aspxFindOffsetParent(inputElement)));
    },
    UnstretchInputElement: function(){
        var inputElement = this.GetInputElement();
        var mainElement = this.GetMainElement();
        var mainElementCurStyle = _aspxGetCurrentStyle(mainElement);
        if (_aspxIsExistsElement(mainElement) && _aspxIsExistsElement(inputElement) && inputElement.style.width == "100%" &&
            (mainElementCurStyle.width == "" || mainElementCurStyle.width == "auto"))
            inputElement.style.width = "";
    },
    
    RaiseValueChangedEvent: function() {
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseTextChanged(processOnServer);
        return processOnServer;
    },
    
    // Mask    
    InitMask: function() {
		this.SetValue(this.DecodeRawInputValue(this.GetRawInputElement().value));
		this.validationPatterns.unshift(new ASPxMaskValidationPattern(this.maskInfo.errorText, this.maskInfo));
		this.maskPasteTimerID = _aspxSetInterval("aspxMaskPasteTimerProc('" + this.name + "')", __aspxPasteCheckInterval);
    },
    SavePrevMaskValue: function() {
        this.maskValueBeforeUserInput = this.maskInfo.GetValue();
    },
	FillMaskInfo: function() {
		var sel = _aspxGetSelectionInfo(this.GetInputElement());
	    this.maskInfo.SetCaret(sel.startPos, sel.endPos - sel.startPos);	    
	},
	ApplyMaskInfo: function(applyCaret) {
		this.SyncRawInputValue();
		var input = this.GetInputElement();		
		input.value = this.maskTextBeforePaste = this.GetMaskDisplayText();
		if(applyCaret)
			_aspxSetInputSelection(input, this.maskInfo.caretPos, this.maskInfo.caretPos + this.maskInfo.selectionLength);
	},
	GetMaskDisplayText: function() {
		if(!this.focused && this.nullText != "" && this.GetValue() == null)
			return this.nullText;
		return this.maskInfo.GetText();
	},
    ShouldCancelMaskKeyProcessing: function(htmlEvent, keyDownInfo) {
		return htmlEvent.returnValue === false;
    },	
	HandleMaskKeyDown: function(evt) {
		var keyInfo = _aspxMaskManager.CreateKeyInfoByEvent(evt);
		_aspxMaskManager.keyCancelled = this.ShouldCancelMaskKeyProcessing(evt, keyInfo);
		if(_aspxMaskManager.keyCancelled) {
			_aspxPreventEvent(evt);
			return;
		}
        this.maskPasteLock = true;
        this.FillMaskInfo();        
        var canHandle = _aspxMaskManager.CanHandleControlKey(keyInfo);            
        _aspxMaskManager.savedKeyDownKeyInfo = keyInfo;
        if(canHandle) {            
            _aspxMaskManager.OnKeyDown(this.maskInfo, keyInfo);
            this.ApplyMaskInfo(true);
            _aspxPreventEvent(evt);
        }
        _aspxMaskManager.keyDownHandled = canHandle;
        this.maskPasteLock = false;
        this.UpdateMaskHintHtml();
	},
	HandleMaskKeyPress: function(evt) {
		var keyInfo = _aspxMaskManager.CreateKeyInfoByEvent(evt);
		_aspxMaskManager.keyCancelled = _aspxMaskManager.keyCancelled || this.ShouldCancelMaskKeyProcessing(evt, _aspxMaskManager.savedKeyDownKeyInfo);
		if(_aspxMaskManager.keyCancelled) {
			_aspxPreventEvent(evt);
			return;
		}
        this.maskPasteLock = true;        
        var printable = _aspxMaskManager.savedKeyDownKeyInfo != null && _aspxMaskManager.IsPrintableKeyCode(_aspxMaskManager.savedKeyDownKeyInfo);
        if(printable) {
            _aspxMaskManager.OnKeyPress(this.maskInfo, keyInfo);
            this.ApplyMaskInfo(true);
        }
        if(printable || _aspxMaskManager.keyDownHandled)            
            _aspxPreventEvent(evt); // for browsers that cannot cancel key down
        this.maskPasteLock = false;
        this.UpdateMaskHintHtml();
	},
	MaskPasteTimerProc: function() {
	    if(this.maskPasteLock) return;
	    var inputElement = this.GetInputElement();
	    if(!_aspxIsExistsElement(inputElement)) {
			this.maskPasteTimerID = _aspxClearInterval(this.maskPasteTimerID);
			return;		
		}
	    if(this.maskTextBeforePaste != inputElement.value) {	        
	        this.maskInfo.ProcessPaste(inputElement.value, _aspxGetSelectionInfo(inputElement).endPos);
	        this.ApplyMaskInfo(true);
	    }
	},
	BeginShowMaskHint: function() {		
		if(!this.readOnly && this.maskHintTimerID == -1)
			this.maskHintTimerID = window.setInterval(aspxMaskHintTimerProc, 500);
	},
	EndShowMaskHint: function() {
		window.clearInterval(this.maskHintTimerID);
		this.maskHintTimerID = -1;
	},
	
	MaskHintTimerProc: function() {		
		this.FillMaskInfo();
		this.UpdateMaskHintHtml();
	},
	UpdateMaskHintHtml: function() {		
		var hint =  this.GetMaskHintElement();
		if(!_aspxIsExistsElement(hint))
			return;

		var html = _aspxMaskManager.GetHintHtml(this.maskInfo);
		if(html == this.maskHintHtml)
			return;
		
		if(html != "") {
			var mainElement = this.GetMainElement();
			if(_aspxIsExistsElement(mainElement)) {
				hint.innerHTML = html;
				hint.style.position = "absolute";		
				hint.style.left = _aspxGetAbsoluteX(mainElement) + "px";
				hint.style.top = (_aspxGetAbsoluteY(mainElement) + mainElement.offsetHeight + 2) + "px";
				hint.style.display = "block";				
			}			
		} else {
			hint.style.display = "none";
		}
		this.maskHintHtml = html;
	},
	HideMaskHint: function() {
		var hint =  this.GetMaskHintElement();
		if(_aspxIsExistsElement(hint))
			hint.style.display = "none";
		this.maskHintHtml = "";
	},
	GetMaskHintElement: function() {
		return _aspxGetElementById(this.name + "_MaskHint");
	},
	
	OnMouseWheel: function(evt){
	    if(this.readOnly || this.maskInfo == null) return;
	    
	    this.FillMaskInfo();
	    _aspxMaskManager.OnMouseWheel(this.maskInfo, _aspxGetWheelDelta(evt) < 0 ? -1 : 1);
	    this.ApplyMaskInfo(true);
	    _aspxPreventEvent(evt);
	    this.UpdateMaskHintHtml();
	},    
	
    // Keyboard support
    OnKeyDown: function(evt) {                       
        ASPxClientEdit.prototype.OnKeyDown.call(this, evt);
        if(!this.specialKeyboardHandlingUsed && this.raiseValueChangedOnEnter && evt.keyCode == ASPxKey.Enter) {
            this.RaiseStandardOnChange();
            return;
        }        
        if(!this.readOnly && this.maskInfo != null)
            this.HandleMaskKeyDown(evt);
    },
    OnKeyPress: function(evt) {
        ASPxClientEdit.prototype.OnKeyPress.call(this, evt);
        if(!this.readOnly && this.maskInfo != null)
            this.HandleMaskKeyPress(evt);
    },
    OnKeyUp: function(evt) {
        if(this.nullText != "")
			this.SyncRawInputValue();
        ASPxClientEdit.prototype.OnKeyUp.call(this, evt);
    },
    OnFocusCore: function() {
		ASPxClientEdit.prototype.OnFocusCore.call(this);
        if(this.maskInfo != null) {
            this.SavePrevMaskValue();
            this.BeginShowMaskHint();
        }
        this.SwapNullText();
    },
    OnLostFocusCore: function() {    
		ASPxClientEdit.prototype.OnLostFocusCore.call(this);
        if(this.maskInfo != null) {
			this.EndShowMaskHint();
			this.HideMaskHint();
			if(this.maskInfo.FixLastRangePart(null))
				this.ApplyMaskInfo(false);
            this.RaiseStandardOnChange();
        }
        this.SwapNullText();
    },    
    OnValueChanged: function() {    
        if(this.maskInfo != null) {
            if(this.maskInfo.GetValue() == this.maskValueBeforeUserInput)    
                return;
            this.SavePrevMaskValue();
        }
        if(this.nullText != "")
			this.SyncRawInputValue();
        ASPxClientEdit.prototype.OnValueChanged.call(this);
    },    
    RaiseStandardOnChange: function(){
        var element = this.GetInputElement();
        if(_aspxIsExists(element) && _aspxIsExists(element.onchange))
            element.onchange();        
    },
    // API
    RaiseTextChanged: function(processOnServer){
        if(!this.TextChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.TextChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;  
    },
    GetText: function(){
		if(this.maskInfo != null) {
			return this.maskInfo.GetText();
        } else {
			var value = this.GetValue();
			return value != null ? value : "";
		}
    },
    SetText: function (value){
		if(this.maskInfo != null) {
			this.maskInfo.SetText(value);
			this.ApplyMaskInfo(false);
			this.SavePrevMaskValue();
		} else {
			this.SetValue(value);
		}
    },
    SelectAll: function() {
        this.SetSelection(0, -1, false);
    },
    SetCaretPosition: function(pos) {
        var inputElement = this.GetInputElement();
        _aspxSetCaretPosition(inputElement, pos);
    },
    SetSelection: function(startPos, endPos, scrollToSelection) {    
        var inputElement = this.GetInputElement();
        _aspxSetSelection(inputElement, startPos, endPos, scrollToSelection);
    },
    ChangeEnabledAttributes: function(enabled){
        if(this.isNative)
            this.GetMainElement().disabled = !enabled;

        var inputElement = this.GetInputElement();
        if(_aspxIsExists(inputElement)){
            this.ChangeInputEnabledAttributes(inputElement, _aspxChangeAttributesMethod(enabled), enabled);
            if(this.specialKeyboardHandlingUsed)
                this.ChangeSpecialInputEnabledAttributes(inputElement, _aspxChangeEventsMethod(enabled));
            if(!this.isNative)
                this.ChangeReadOnlyAttribute(inputElement, enabled);
        }
    },
    ChangeEnabledStateItems: function(enabled){
        if(!this.isNative)
            aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
    },
    ChangeReadOnlyAttribute: function(element, enabled){
        element.readOnly = !enabled || this.readOnly;
    },
	ChangeInputEnabledAttributes: function(element, method, enabled){
	    method(element, "tabIndex");
	    if(!enabled) element.tabIndex = -1;
	    
        method(element, "onclick");
        method(element, "onfocus");
        method(element, "onblur");
        method(element, "onkeydown");
        method(element, "onkeypress");
        method(element, "onkeyup");
	}
});

ASPxMaskValidationPattern = _aspxCreateClass(ASPxValidationPattern, {
    constructor: function(errorText, maskInfo) {
        this.constructor.prototype.constructor.call(this, errorText);
        this.maskInfo = maskInfo;
    },
    EvaluateIsValid: function(value) {
        return this.maskInfo.IsValid();
    }
});
ASPxClientTextBoxBase = _aspxCreateClass(ASPxClientTextEdit, {
});
ASPxClientTextBox = _aspxCreateClass(ASPxClientTextBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.isASPxClientTextBox = true;
    }
});

ASPxIdent.IsASPxClientTextBox = function(obj) {
    return _aspxIsExists(obj.isASPxClientTextBox) && obj.isASPxClientTextBox;
};

var __aspxMMinHeight = 34;
ASPxClientMemo = _aspxCreateClass(ASPxClientTextEdit, { 
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);                                
        
        this.isASPxClientMemo = true;
        this.raiseValueChangedOnEnter = false;
    },

    CollapseControl: function(checkSizeCorrectedFlag) {
        if (checkSizeCorrectedFlag && this.sizeCorrectedOnce)
            return;
        var mainElement = this.GetMainElement();
        var inputElement = this.GetInputElement();
        if (!_aspxIsExistsElement(mainElement) || !_aspxIsExistsElement(inputElement))
            return;
        ASPxClientTextEdit.prototype.CollapseControl.call(this, checkSizeCorrectedFlag);
        var mainElementCurStyle = _aspxGetCurrentStyle(mainElement);
        if (this.heightCorrectionRequired && _aspxIsExists(mainElement) && _aspxIsExists(inputElement)) {
            if (mainElement.style.height == "100%" || mainElementCurStyle.height == "100%") {
                mainElement.style.height = "0";
                mainElement.wasCollapsed = true;
            }
            inputElement.style.height = "0";
        }
    },
    CorrectEditorHeight: function() {
        var mainElement = this.GetMainElement();
        if (mainElement.wasCollapsed) {
            mainElement.wasCollapsed = null;
            _aspxSetOffsetHeight(mainElement, _aspxGetClearClientHeight(_aspxFindOffsetParent(mainElement)));
        }
        if(!this.isNative){
            var inputElement = this.GetInputElement();
            var inputClearClientHeight = _aspxGetClearClientHeight(_aspxFindOffsetParent(inputElement)) - 2;
            if (__aspxIE) {
                var calculatedMainElementStyle = _aspxGetCurrentStyle(mainElement);
                inputClearClientHeight += _aspxPxToInt(calculatedMainElementStyle.borderTopWidth) + _aspxPxToInt(calculatedMainElementStyle.borderBottomWidth);
            }
            if (inputClearClientHeight < __aspxMMinHeight)
                inputClearClientHeight = __aspxMMinHeight;
            _aspxSetOffsetHeight(inputElement, inputClearClientHeight);
            mainElement.style.height = "100%";
        }
    }
});

ASPxIdent.IsASPxClientMemo = function(obj) {
    return _aspxIsExists(obj.isASPxClientMemo) && obj.isASPxClientMemo;
};	
ASPxClientButtonEditBase = _aspxCreateClass(ASPxClientTextBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);                                
        
        this.allowUserInput = true;
        this.buttonCount = 0;    
        this.ButtonClick = new ASPxClientEvent();
    },
    GetButton: function(number) {
        return this.GetChild("_B" + number);
    },
    ProcessInternalButtonClick: function(number) {
        return false;
    },
    OnButtonClick: function(number){
        var processOnServer = this.RaiseButtonClick(number);
        if (!this.ProcessInternalButtonClick(number) && processOnServer)
            this.SendPostBack('BC:' + number);
    },
    SelectInputElement: function() {
        var element = this.GetInputElement();
        if(_aspxIsExistsElement(element)) {
            _aspxSetFocus(element);
            element.select();        
        }
    },
    RaiseButtonClick: function(number){
        var processOnServer = this.autoPostBack || this.IsServerEventAssigned("ButtonClick");
        if(!this.ButtonClick.IsEmpty()){
            var args = new ASPxClientButtonEditClickEventArgs(processOnServer, number);
            this.ButtonClick.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    ChangeEnabledAttributes: function(enabled){
        ASPxClientTextEdit.prototype.ChangeEnabledAttributes.call(this, enabled);
        for(var i = 0; i < this.buttonCount; i++){
            var element = this.GetButton(i);
            if(_aspxIsExists(element)) 
                this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
        }
    },
    ChangeEnabledStateItems: function(enabled){
        ASPxClientTextEdit.prototype.ChangeEnabledStateItems.call(this, enabled);
        for(var i = 0; i < this.buttonCount; i++){
            var element = this.GetButton(i);
            if(_aspxIsExists(element)) 
                aspxGetStateController().SetElementEnabled(element, enabled);
        }
    },
	ChangeButtonEnabledAttributes: function(element, method){
        method(element, "onclick");
        method(element, "ondblclick");
        method(element, "onmousedown");
        method(element, "onmouseup");
	},
    ChangeReadOnlyAttribute: function(element, enabled){
        if(this.allowUserInput)
            ASPxClientTextEdit.prototype.ChangeReadOnlyAttribute.call(this, element, enabled);
    }
});
ASPxClientButtonEdit = _aspxCreateClass(ASPxClientButtonEditBase, {
});
ASPxClientButtonEditClickEventArgs = _aspxCreateClass(ASPxClientProcessingModeEventArgs, {
    constructor: function(processOnServer, buttonIndex){
        this.constructor.prototype.constructor.call(this, processOnServer);
        this.buttonIndex = buttonIndex;
    }
});

function aspxETextChanged(name) {    
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.OnTextChanged();    
}
function aspxBEClick(name,number){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.OnButtonClick(number);
}
function aspxMaskPasteTimerProc(name){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.MaskPasteTimerProc();
}
function aspxMaskHintTimerProc() {
    var focusedEditor = __aspxGetFocusedEditor();
	if(focusedEditor != null && _aspxIsFunction(focusedEditor.MaskHintTimerProc))
		focusedEditor.MaskHintTimerProc();
}
function _aspxSetFocusToTextEditWithDelay(name) {
    _aspxSetTimeout("var edit = aspxGetControlCollection().Get('" + name + "'); __aspxIE ? edit.SetCaretPosition(0) : edit.SetFocus();", 500);
}

var __aspxLoadFilteredItemsCallbackPrefix = "CBLF";
var __aspxCorrectFilterCallbackPrefix = "CBCF";
var __aspxDropDownNameSuffix = "_DDD";
var __aspxCalendarNameSuffix = "_C";
var __aspxListBoxNameSuffix = "_L";
ASPxClientDropDownEdit = _aspxCreateClass(ASPxClientButtonEditBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.DropDown = new ASPxClientEvent();
        this.CloseUp = new ASPxClientEvent();
        
        this.ddHeightCache = __aspxInvalidDimension;
        this.ddWidthCache = __aspxInvalidDimension;
        this.mainElementWidthCache = __aspxInvalidDimension;
        this.dropDownButtonIndex = -1;
        this.droppedDown = false;
        this.ddButtonPushed = false;
        this.lastSuccessText = "";

        aspxGetDropDownCollection().Add(this);
    },
    Initialize: function(){
        var pc = this.GetPopupControl();
        if(_aspxIsExists(pc))
            pc.allowCorrectYOffsetPosition = false;
        this.AssignClientAttributes();
        this.InitLastSuccessText();
        ASPxClientEdit.prototype.Initialize.call(this);
    },
    InitLastSuccessText: function(){
        var input = this.GetInputElement();        
        if (_aspxIsExistsElement(input))
            this.SetLastSuccessTest(input.value);
    },
    IsRenderExists: function(){
        return _aspxIsExistsElement(this.GetMainElement());
    },
    AssignClientAttributes: function(){
        var element = this.GetDropDownButton();
        if(_aspxIsExistsElement(element))
            _aspxPreventElementDragAndSelect(element, true);
    },
    GetDropDownButton: function(){
        return this.GetButton(this.dropDownButtonIndex);
    },
    GetPopupControl: function(){
        return aspxGetControlCollection().Get(this.name + __aspxDropDownNameSuffix);
    },
    GetDropDownInnerControlName: function(suffix){
        var pc = this.GetPopupControl();
        if(_aspxIsExists(pc))
            return this.GetPopupControl().name + suffix;
        return "";
    },
    GetIsControlWidthWasChanged: function(){
        return this.mainElementWidthCache == __aspxInvalidDimension || this.mainElementWidthCache != this.GetMainElement().clientWidth;
    },
    GetDropDownHeight: function(){
        return 0;
    },
    GetDropDownWidth: function(){
        return 0;
    },
    ShowDropDownArea: function(isRaiseEvent){
		aspxGetDropDownCollection().RegisterDroppedDownControl(this);
		
		this.lockListBoxClick = true;
		
        var pc = this.GetPopupControl();
        var element = this.GetMainElement();
        var pcwElement = pc.GetWindowElement(-1);
        
        _aspxSetElementDisplay(pcwElement, true);
        // Order is important. Width with scroll bar depends on Height
        var height = this.GetDropDownHeight();
        var width = this.GetDropDownWidth();
        if(this.ddHeightCache != height || this.ddWidthCache != width){
            pc.SetSize(width, height);
            this.ddHeightCache = height;
            this.ddWidthCache = width;
        }
        pc.popupVerticalOffset = - _aspxGetClientTop(element);
        pc.ShowAtElement(element);
        this.RaiseDropDownEventRequired = isRaiseEvent;
        
        this.droppedDown = true;
    },
    HideDropDownArea: function(isRaiseEvent){
        this.DropDownButtonPop();
        var pc = this.GetPopupControl();
        if (_aspxIsExists(pc)){
            pc.Hide();
            if(isRaiseEvent)
                this.RaiseCloseUp();
            aspxGetDropDownCollection().UnregisterDroppedDownControl(this);
            this.droppedDown = false;
        }
    },
    ProcessInternalButtonClick: function(number) {
        return this.dropDownButtonIndex == number;
    },
    ToggleDropDown: function(){
        this.OnApplyChanges();
        if(this.droppedDown)
            this.HideDropDownArea(true);
        else
            this.ShowDropDownArea(true);        
    },
    SetTextInternal: function(text){
        if(!this.readOnly)
            ASPxClientButtonEditBase.prototype.SetValue.call(this, text);
    },
    SetLastSuccessTest: function(text){
        if(text == null) text = "";
        this.lastSuccessText = text;
    },
    // base events
    OnValueChanged: function() {
        this.SetLastSuccessTest(this.GetInputElement().value);
        ASPxClientEdit.prototype.OnValueChanged.call(this);
    },
    
    OnApplyChanges: function(){
    },
    OnCancelChanges: function(){
        var isCancelProcessed = (this.GetInputElement().value != this.lastSuccessText);
        this.SetTextInternal(this.lastSuccessText);
        return isCancelProcessed;
    },
    OnFocus: function(){
        this.OnSetFocus(true);
        ASPxClientButtonEditBase.prototype.OnFocus.call(this);
    },
    OnLostFocus: function(){
        this.OnSetFocus(false);
        ASPxClientButtonEditBase.prototype.OnLostFocus.call(this);
    },
    OnSetFocus: function(isFocused){
        aspxGetDropDownCollection().SetFocusedDropDownName(isFocused ? this.name : "");
    },
    LockFocusEventsByMouseDown: function(srcElement) {
        if(!__aspxNS && srcElement != this.GetInputElement() && srcElement != this.GetDropDownButton())
            this.LockFocusEvents();
    },
    OnClick: function(){
        this.SetFocus();
        if(this.IsFocusEventsLocked())
            this.UnlockFocusEvents();
    },    
    OnPopupControlShown: function(){
        if(this.RaiseDropDownEventRequired){
            this.RaiseDropDownEventRequired = false;
            _aspxSetTimeout("aspxDDBRaiseDropDownByTimer(\"" + this.name + "\")", 0);
        }
    },
    IsCanToDropDown: function(){
        return true;
    },
    OnDropDown: function(evt) { 
        if(!this.IsCanToDropDown() || !this.isInitialized) return true;
        
        if(!__aspxNS)
            this.LockFocusEvents();
        this.SetFocus();
        if(!this.droppedDown)
            this.DropDownButtonPush();
        this.ToggleDropDown();
        return _aspxCancelBubble(evt);
    },
    DropDownButtonPush: function(){
        if(this.droppedDown || this.ddButtonPushed) return;
        this.ddButtonPushed = true;
        
        if(__aspxIE || __aspxOpera) 
            this.DropDownButtonPushPop(true);
        else
            this.DropDownButtonPushMozilla();
    },    
    DropDownButtonPop: function(){
        if(!this.droppedDown || !this.ddButtonPushed) return;
        this.ddButtonPushed = false;
        
        if(__aspxIE || __aspxOpera) 
            this.DropDownButtonPushPop(false);
        else
            this.DropDownButtonPopMozilla();
    },
    DropDownButtonPushPop: function(isPush){
        var buttonElement = this.GetDropDownButton();
        if(_aspxIsExists(buttonElement)){
            var controller = aspxGetStateController();
            var element = controller.GetPressedElement(buttonElement);
            if(_aspxIsExists(element)){
                if(isPush){
                    controller.SetCurrentHoverElement(null);
                    controller.DoSetPressedState(element);
                } else {
                    controller.DoClearPressedState(element);
                    controller.SetCurrentPressedElement(null);
                    controller.SetCurrentHoverElement(element);
                }
            }
        }
    },
    DropDownButtonPushMozilla: function(){
        this.DisableStyleControllerForDDButton();
        var controller = aspxGetStateController();
        controller.savedCurrentPressedElement = null;
    },
    DropDownButtonPopMozilla: function(){
        this.EnableStyleControllerForDDButton();
        var controller = aspxGetStateController();
        var buttonElement = this.GetDropDownButton();
        if(_aspxIsExists(buttonElement)){
            var element = controller.GetPressedElement(buttonElement);
            if(_aspxIsExists(element))
                controller.DoClearPressedState(element);
            controller.currentPressedElement = null;
            element = controller.GetHoverElement(buttonElement);
            if(_aspxIsExists(element))
                controller.SetCurrentHoverElement(element);
        }
    },
    EnableStyleControllerForDDButton: function(){
        var element = this.GetDropDownButton();
        if(_aspxIsExists(element)){
            var controller = aspxGetStateController();
            this.ReplaceElementControlStyleItem(controller.hoverItems, element, this.ddButtonHoverStyle);
            this.ReplaceElementControlStyleItem(controller.pressedItems, element, this.ddButtonPressedStyle);
            this.ReplaceElementControlStyleItem(controller.selectedItems, element, this.ddButtonSelectedStyle);
        }
    },
    DisableStyleControllerForDDButton: function(){
        var element = this.GetDropDownButton();
        if(_aspxIsExists(element)){
            var controller = aspxGetStateController();
            this.ddButtonHoverStyle = this.ReplaceElementControlStyleItem(controller.hoverItems, element, null);
            this.ddButtonPressedStyle = this.ReplaceElementControlStyleItem(controller.pressedItems, element, null);
            this.ddButtonSelectedStyle = this.ReplaceElementControlStyleItem(controller.selectedItems, element, null);
        }
    },
    ReplaceElementControlStyleItem: function(items, element, newStyleItem){
        var styleItem = items[element.id];
        items[element.id] = newStyleItem;
        return styleItem;
    },

    OnDocumentMouseDown: function() {
        this.HideDropDownArea(true);
    },
    OnDocumentMouseUp: function() {
        this.DropDownButtonPop();
    },
    OnDDButtonMouseMove: function(evt){
    },
    OnCloseUp: function(evt){
        this.HideDropDownArea(true);
    },
    OnOpenAnotherDropDown: function(){
        this.HideDropDownArea(true);
    },
    OnTextChanged: function() {
        this.ParseValue();
    },
    ChangeEnabledAttributes: function(enabled){
        ASPxClientButtonEditBase.prototype.ChangeEnabledAttributes.call(this, enabled);
        var btnElement = this.GetDropDownButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        var inputElement = this.GetInputElement();
        if(_aspxIsExists(inputElement))
            this.ChangeInputCellEnabledAttributes(inputElement.parentNode, _aspxChangeAttributesMethod(enabled));
    },
    ChangeEnabledStateItems: function(enabled){
        ASPxClientButtonEditBase.prototype.ChangeEnabledStateItems.call(this, enabled);
        var btnElement = this.GetDropDownButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
    },
	ChangeInputCellEnabledAttributes: function(element, method){
        method(element, "onclick");
        method(element, "onkeyup");
        method(element, "onmousedown");
        method(element, "onmouseup");
	},
    
    // Keyboard Support
    InitializeKeyHandlers: function() {
        this.AddKeyDownHandler(ASPxKey.Enter, "OnEnter");
        this.AddKeyDownHandler(ASPxKey.Esc, "OnEscape");
        this.AddKeyDownHandler(ASPxKey.PageUp, "OnPageUp");
        this.AddKeyDownHandler(ASPxKey.PageDown, "OnPageDown");
        this.AddKeyDownHandler(ASPxKey.End, "OnEndKeyDown");
        this.AddKeyDownHandler(ASPxKey.Home, "OnHomeKeyDown");
        this.AddKeyDownHandler(ASPxKey.Left, "OnArrowLeft");
        this.AddKeyDownHandler(ASPxKey.Right, "OnArrowRight");
        this.AddKeyDownHandler(ASPxKey.Up, "OnArrowUp");
        this.AddKeyDownHandler(ASPxKey.Down, "OnArrowDown");
        this.AddKeyDownHandler(ASPxKey.Tab, "OnTab");
    },
    OnArrowUp: function(evt){
        if(evt.altKey) {
            this.ToggleDropDown();
            return true;
        }
        return false;
    },
    OnArrowDown: function(evt){
        if(evt.altKey) {
            this.ToggleDropDown();
            return true;
        }
        return false;
    },
    OnPageUp: function(evt){
        return false;
    },    
    OnPageDown: function(evt){
        return false;
    },
    OnEndKeyDown: function(evt){
        return false;
    },
    OnHomeKeyDown: function(evt){
        return false;
    },
    OnArrowLeft: function(evt){
        return false;
    },
    OnArrowRight: function(evt){
        return false;
    },
    OnEscape: function(evt){
        var isCancelProcessed = this.OnCancelChanges() || this.droppedDown;
        this.HideDropDownArea(true);
        return isCancelProcessed;
    },
    OnEnter: function(evt){
        return false;
    },
    OnTab: function(evt){
        return false;
    },
    // API
    RaiseCloseUp: function(){
        if(!this.CloseUp.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.CloseUp.FireEvent(this, args);
        }
    },
    RaiseDropDown: function(){
        if(!this.DropDown.IsEmpty() && this.isInitialized){
            var args = new ASPxClientEventArgs();
            this.DropDown.FireEvent(this, args);
        }
    },
    ShowDropDown: function(){
        this.ShowDropDownArea(false);
    },
    HideDropDown: function(){
        this.HideDropDownArea(false);
    }
});

ASPxClientDropDownCollection = _aspxCreateClass(ASPxClientControlCollection, {
    constructor: function(){
		this.constructor.prototype.constructor.call(this);
		
		this.droppedControlName = "";
		this.focusedControlName = "";
    },
    SetFocusedDropDownName: function(name){
        this.focusedControlName = name;
    },
    ResetDroppedDownControl: function(){
        this.droppedControlName = "";
    },
    ResetFocusedControl: function(){
        this.focusedControlName = "";
    },
    GetFocusedDropDown: function(){
        var control = this.GetDropDownControlInternal(this.focusedControlName);
        if(control == null) this.ResetFocusedControl();
        return control;
    },
    GetDroppedDropDown: function(){
        var control = this.GetDropDownControlInternal(this.droppedControlName);
        if(control == null) this.ResetDroppedDownControl();
        return control;
    },
    GetDropDownControlInternal: function(name){
        var control = this.Get(name);
        var isControlExists = _aspxIsExists(control) && control.IsRenderExists();
        if(!isControlExists)
            control = null;
        return control;
    },
    OnDDButtonMouseMove: function(evt){
        var dropDownControl = this.GetDroppedDropDown();
        if(dropDownControl != null)
            dropDownControl.OnDDButtonMouseMove(evt);
    },
    OnDocumentMouseDown: function(evt){
        this.LockFocusEventsByDocumentEvent(evt);
        this.CloseDropDownByDocumentEvent(evt, false);
        this.ClearFocusedDropDownByDocumentEvent(evt);
    },
    OnDocumentMouseUp: function(evt){
        var dropDownControl = this.GetDroppedDropDown();
        if(dropDownControl != null)
            dropDownControl.OnDocumentMouseUp();
    },
    OnResize: function(evt){
        this.CloseDropDownByDocumentEvent(evt, true);
        this.AdjustControls();
    },
    
    LockFocusEventsByDocumentEvent: function(evt){
        var control = this.GetFocusedDropDown();
        if(control != null && !this.IsEventNotFromControlSelf(evt, control))
            control.LockFocusEventsByMouseDown(_aspxGetEventSource(evt));
    },
    CloseDropDownByDocumentEvent: function(evt, isResize){
        var dropDownControl = this.GetDroppedDropDown();
        if(dropDownControl != null && (this.IsEventNotFromControlSelf(evt, dropDownControl) || isResize))
            dropDownControl.OnDocumentMouseDown();
    },
    ClearFocusedDropDownByDocumentEvent: function(evt){
        var focusedDropDown = this.GetFocusedDropDown();
        if(focusedDropDown != null && this.IsEventNotFromControlSelf(evt, focusedDropDown))
            this.SetFocusedDropDownName("");        
    },
    AdjustControls: function(){
        for(var name in this.elements) {
            this.elements[name].AdjustControl(false);
        }
    },
    IsEventNotFromControlSelf: function(evt, control){
        // Prevent DocumentOnClick event fired by the DropDown control and DropDownElement(Mozilla only) click
        var srcElement = _aspxGetEventSource(evt);
        var mainElement = control.GetMainElement();
        var popupControl = control.GetPopupControl();
        if(srcElement == null || !_aspxIsExists(mainElement) || !_aspxIsExists(popupControl)) return true;
        
        return (!_aspxGetIsParent(mainElement, srcElement) &&
            !_aspxGetIsParent(popupControl.GetWindowElement(-1), srcElement));
    },
    RegisterDroppedDownControl: function(dropDownControl){
        var previousDropDownControl = this.GetDroppedDropDown();
        if(previousDropDownControl != null && previousDropDownControl != dropDownControl)
            previousDropDownControl.OnOpenAnotherDropDown();
        this.droppedControlName = dropDownControl.name;
    },
    UnregisterDroppedDownControl: function(dropDownControl){
        if(this.droppedControlName == dropDownControl.name)
            this.ResetDroppedDownControl();
    }
});
ASPxClientDateEdit = _aspxCreateClass(ASPxClientDropDownEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.dateFormatter = null;
        
        //set from server        
        this.date = null;
        this.dateOnError = "u";
        this.allowNull = true;
        this.calendarOwnerName = null;
        this.DateChanged = new ASPxClientEvent();        
        this.ParseDate = new ASPxClientEvent();
    },
    Initialize: function() {
		if(this.calendarOwnerName == null) {
            var calendar = this.GetCalendar();
            if(_aspxIsExists(calendar)) {
				calendar.SelectionChanging.AddHandler(ASPxClientDateEdit.HandleCalendarSelectionChanging);
				calendar.MainElementClick.AddHandler(ASPxClientDateEdit.HandleCalendarMainElementClick);
                if (__aspxNS)
                    calendar.GetMainElement().style.borderCollapse = "separate";
            }
		}
        ASPxClientDropDownEdit.prototype.Initialize.call(this);
    },
    InlineInitialize: function(){
        this.InitSpecialKeyboardHandling();
        ASPxClientDropDownEdit.prototype.InlineInitialize.call(this);
    },
        
    ShowDropDownArea: function(isRaiseEvent){
        var cal = this.GetCalendar();
        if(_aspxIsExists(cal))            
            cal.SetValue(this.date);
        cal.forceMouseDown = true;
        __aspxActiveCalendar = cal;
        ASPxClientDateEdit.active = this;
        
        ASPxClientDropDownEdit.prototype.ShowDropDownArea.call(this, isRaiseEvent);        
    },    	  
    GetPopupControl: function() { // overriden
		var calendarOwner = this.GetCalendarOwner();
		if(calendarOwner != null)
			return calendarOwner.GetPopupControl();
		return ASPxClientDropDownEdit.prototype.GetPopupControl.call(this);
    },    
    GetCalendar: function() {    
        var name = this.GetDropDownInnerControlName(__aspxCalendarNameSuffix);
        return aspxGetControlCollection().Get(name);
    },
    GetCalendarOwner: function() {
		if(!this.calendarOwnerName)
			return null;
		return aspxGetControlCollection().Get(this.calendarOwnerName);
    },
    GetFormattedDate: function() {
		if(this.maskInfo != null)
			return this.maskInfo.GetValue();
        if(this.date == null)
            return "";       
        return this.dateFormatter.Format(this.date);
    },
    RaiseValueChangedEvent: function() {
        if(!this.isInitialized) return false;
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseDateChanged(processOnServer);
        return processOnServer;
    },
    OnApplyChanges: function(){
        this.OnTextChanged();
    },
    OnCalendarSelectionChanging: function(date) {
        if(!this.GetCalendar().isDateChangingByKeyboard) {
            this.HideDropDownArea(true);
            if(date != null && this.date != null) {
                date.setHours(this.date.getHours());
                date.setMinutes(this.date.getMinutes());
                date.setSeconds(this.date.getSeconds());
                date.setMilliseconds(this.date.getMilliseconds());
            }         
            this.ChangeDate(date);
            this.SelectInputElement();
        }
    },
    OnArrowUp: function(evt){    
        var isProcessed = ASPxClientDropDownEdit.prototype.OnArrowUp.call(this, evt);
        if(!isProcessed && this.droppedDown)
            return this.GetCalendar().OnArrowUp(evt);                   
        return false;
    },
    OnArrowDown: function(evt){
        var isProcessed = ASPxClientDropDownEdit.prototype.OnArrowDown.call(this, evt);
        if(!isProcessed && this.droppedDown)
            return this.GetCalendar().OnArrowDown(evt);
        return false;
    },
    OnArrowLeft: function(evt){
        if (this.droppedDown) {
            this.GetCalendar().OnArrowLeft(evt);
            return true;
        }
        return false;
    },
    OnArrowRight: function(evt){
        if (this.droppedDown) {    
            this.GetCalendar().OnArrowRight(evt);
            return true;
        }
        return false;
    },
    
    OnPageUp: function(evt){
        if (this.droppedDown) {    
            this.GetCalendar().OnPageUp(evt);
            return true;
        }
        return false;        
    },
    OnPageDown: function(evt){
        if (this.droppedDown) {
            this.GetCalendar().OnPageDown(evt);
            return true;
        }
        return false;        
    },
    OnEndKeyDown: function(evt) {
        if (this.droppedDown) {
            this.GetCalendar().OnEndKeyDown(evt);
            return true;
        }
        return false;
    },
    OnHomeKeyDown: function(evt) {
        if (this.droppedDown) {
            this.GetCalendar().OnHomeKeyDown(evt);
            return true;
        }
        return false;    
    },
    
    OnEnter: function() {
        this.enterProcessed = false; // hack for defaultbutton
        if (this.droppedDown) {
			var calendar = this.GetCalendar();
            if (calendar.IsFastNavigationActive()) {
                calendar.GetFastNavigation().OnEnter();
            } else {
                this.OnCalendarSelectionChanging(this.GetCalendar().GetValue());
            }
            this.enterProcessed = true;
        }
        else
            this.OnApplyChanges();
        return this.enterProcessed;
    },
    OnEscape: function() {
        if (this.droppedDown){
            if (this.GetCalendar().IsFastNavigationActive())
                this.GetCalendar().OnEscape();
            else
                ASPxClientDropDownEdit.prototype.OnEscape.call(this);
        }
        return true;
    },
    OnTab: function(evt){
        if(!this.droppedDown) return;
        
		var calendar = this.GetCalendar();
        if (calendar.IsFastNavigationActive()) 
            calendar.GetFastNavigation().Hide();
            
        this.OnCalendarSelectionChanging(this.GetCalendar().GetValue());
    },
        
    ParseValue: function() {    
		var date;
		if(this.maskInfo != null) {
			date = _aspxMaskDateTimeHelper.GetDate(this.maskInfo);			
		} else {
			var text = this.GetInputElement().value;
			var userParseResult = this.GetUserParsedDate(text);
			if(userParseResult !== false) {
				date = userParseResult;
			} else {
				if(text == null || text == "")
					date = null;
				else
					date = this.dateFormatter.Parse(text);
			}			
		}
		this.ApplyParsedDate(date);
    },
    GetUserParsedDate: function(text) {
		if(!this.ParseDate.IsEmpty()) {
			var args = new ASPxClientParseDateEventArgs(text);
			this.ParseDate.FireEvent(this, args);
			if(args.handled)
				return args.date;
		}
		return false;
    },
    
    ApplyParsedDate: function(date) {
		if(date === false || !this.GetCalendar().IsDateInRange(date)) {
			switch(this.dateOnError) {
				case "n":
					date = null;
					break;
				case "t":
					date = new Date();
					break;
				default:
					date = this.date;
					break;
			}
		}
		if(!this.allowNull && date == null)
			date = this.date;
		this.ChangeDate(date);		
    },
    
    GetValue: function() {
        return this.date;
    },    
    GetValueString: function() {
        return this.date != null ? _aspxGetInvariantDateTimeString(this.date) : null;
    },
    SetValue: function(date) {        
        this.date = date;
        if(this.maskInfo != null) {
			_aspxMaskDateTimeHelper.SetDate(this.maskInfo, date);
			this.ApplyMaskInfo(false);
			this.SavePrevMaskValue();
        } else {
			this.UpdateDateEditInputs();        
		}
    },
    UpdateDateEditInputs: function() {
		ASPxClientButtonEditBase.prototype.SetValue.call(this, this.GetFormattedDate());		
		this.SyncRawInputValue();
    },
    ChangeDate: function(date) {    
		var changed = !this.AreDatesEqualExact(this.date, date);
		this.SetValue(date);        
		if(changed) {
            this.RaisePersonalStandardValidation();
            this.OnValueChanged();
        }
    },
    AreDatesEqualExact: function(date1, date2) {
        if(date1 == null && date2 == null)
            return true;
        if(date1 == null || date2 == null)
            return false;
        return date1.getTime() == date2.getTime();
    },
    GetText: function() {
		return this.GetFormattedDate();
    },
    SetText: function(value) {
		ASPxClientTextEdit.prototype.SetValue.call(this, value);
		if(this.maskInfo == null)
			this.ParseValue();
    },    
    LockFocusEventsByMouseDown: function(srcElement) {
        if(srcElement != this.GetInputElement() && srcElement != this.GetDropDownButton())
            this.LockFocusEvents();
    },
        
    // Mask
    ShouldCancelMaskKeyProcessing: function(htmlEvent, keyDownInfo) {
		if(htmlEvent.altKey)
			return true;
		if(ASPxClientDropDownEdit.prototype.ShouldCancelMaskKeyProcessing.call(this, htmlEvent, keyDownInfo))
			return true;		
		if(!this.droppedDown)
			return false;
		return !_aspxMaskManager.IsPrintableKeyCode(keyDownInfo) 
			&& keyDownInfo.keyCode != ASPxKey.Backspace
			&& keyDownInfo.keyCode != ASPxKey.Delete;
    },
    DecodeRawInputValue: function(value) {
		if(value == "N") return null;
		var date = new Date();
		date.setTime(Number(value));
		date.setTime(date.getTime() + 60000 * date.getTimezoneOffset());
		return date;
    },
    SyncRawInputValue: function() {
		this.GetRawInputElement().value = this.date == null ? "N" : this.date.valueOf() - 60000 * this.date.getTimezoneOffset();
    },
    GetMaskDisplayText: function() {
		if(!this.focused && this.date == null)
			return this.nullText;
		return this.maskInfo.GetText();
    },
    
    SwapNullText: function() {	
		if(this.maskInfo != null) {
			this.ApplyMaskInfo(this.focused);
		} else {			
			if(this.date == null)
				this.GetInputElement().value = this.focused ? this.GetFormattedDate() : this.nullText;
		}
    },    
    
    // API
    RaiseDateChanged: function(processOnServer) {
        if(!this.DateChanged.IsEmpty()) {
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.DateChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    SetDate: function(date) {
        this.SetValue(date);
    },
    GetDate: function() {
        return this.date;
    }
});
ASPxClientDateEdit.active = null;
ASPxClientDateEdit.HandleCalendarSelectionChanging = function(s, e) {
	if(ASPxClientDateEdit.active == null) return;
	ASPxClientDateEdit.active.OnCalendarSelectionChanging(e.selection.GetFirstDate());
};
ASPxClientDateEdit.HandleCalendarMainElementClick = function(s, e) {
	if(ASPxClientDateEdit.active == null) return;
	ASPxClientDateEdit.active.SetFocus();
};
ASPxClientParseDateEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(value) {
		this.constructor.prototype.constructor.call(this);
		this.value = value;
		this.date = null;
		this.handled = false;
	}	
});


__aspxCCValueInputSuffix = "VI";
ASPxClientComboBoxBase = _aspxCreateClass(ASPxClientDropDownEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.lbEventLockCount = 0;
        this.receiveGlobalMouseWheel = false;
        
        // Cache
        this.listBox = null;
        this.lastSuccessValue = "";
        this.islastSuccessValueInit = false;        
        this.SelectedIndexChanged = new ASPxClientEvent();
    },
    Initialize: function(){
        this.InitializeListBoxOwnerName();
        ASPxClientDropDownEdit.prototype.Initialize.call(this);
    },
    InitializeListBoxOwnerName: function(){
        var lb = this.GetListBoxControl();
        if(_aspxIsExists(lb))
            lb.ownerName = this.name;
    },
    GetDropDownInnerControlName: function(suffix){
        return "";
    },
    GetListBoxControl: function(){
        if(!_aspxIsExistsElement(this.listBox)){
            var name = this.GetDropDownInnerControlName(__aspxListBoxNameSuffix);
            this.listBox = aspxGetControlCollection().Get(name);
        }
        return this.listBox;
    },
    GetCallbackArguments: function(){
        return this.GetListBoxCallbackArguments();
    },
    GetListBoxCallbackArguments: function(){
        var lb = this.GetListBoxControl();
        return lb.GetCallbackArguments();
    },
    SendCallback: function(){
        this.CreateCallback(this.GetCallbackArguments());
    },
    SetText: function (text){
        var lb = this.GetListBoxControl();
        var index = this.FindItemIndexByText(lb, text);
        this.SelectIndex(index, false);
        this.SetTextInternal(text);
        this.SetLastSuccessTest(text);
        this.lastSuccessValue = index >= 0 ? lb.GetValue() : text;
        this.islastSuccessValueInit = true;
    },
    GetValue: function(){
        return this.islastSuccessValueInit ? this.lastSuccessValue : this.GetValueInternal();
    },
    GetValueInternal: function(){
        var text = this.GetInputElement().value;
        var lb = this.GetListBoxControl();
        if (_aspxIsExists(lb)){
            var index = this.FindItemIndexByText(lb, text);
            lb.SelectIndexSilent(index, false); // Prevent to direct input value changing
            if(index != -1)
                return lb.GetValue();
        }
        return ASPxClientDropDownEdit.prototype.GetValue.call(this);
    },
    SetValue: function(value){
        var lb = this.GetListBoxControl();
        lb.SetValue(value);
        // TODO Extract method with OnSelectChanged (params: value)
        var item = lb.GetSelectedItem();
        var text = _aspxIsExists(item) ? item.text : value;
        this.SetTextInternal(text);
        this.SetLastSuccessTest(text);
        this.lastSuccessValue = item != null ? item.value : text;
        this.islastSuccessValueInit = true;
        
        this.UpdateValueInput();
        //
    },
    
    FindItemIndexByText: function(lb, text){
        if (!_aspxIsExists(lb)) return;
        for(var i = 0; i < lb.GetItemCount(); i ++){
            if(lb.GetItem(i).text == text)
                return i;
        }
        return -1;
    },
    CollectionChanged: function(){
    },
    SelectIndex: function(index, initialize){
        var lb = this.GetListBoxControl();
        var isSelectionChanged = lb.SelectIndexSilentAndMakeVisible(index, initialize);
        var item = lb.GetSelectedItem();
        var text = item != null ? item.text : "";
        
        if(isSelectionChanged){
            this.SetTextInternal(text);
            this.SetLastSuccessTest(text);
            this.lastSuccessValue = item != null ? item.value : text;
            this.islastSuccessValueInit = true;
        }
        this.UpdateValueInput();
        
        return isSelectionChanged;
    },
    OnSelectChanged: function(){
        if(this.lbEventLockCount > 0) return;
        
        var lb = this.GetListBoxControl();
        // TODO Extract method with SetValue (params: value = "")
        var item = lb.GetSelectedItem();
        var text = item != null ? item.text : "";
        this.SetLastSuccessTest(text);
        this.lastSuccessValue = item != null ? item.value : text;
        this.islastSuccessValueInit = true;
        this.SetTextInternal(text);
        //
        this.OnChange(); 
        
        if(!this.isToolbarItem)
            this.SelectInputElement();
    },
    OnCallback: function(result) {
    },
    OnChange: function(){
        this.UpdateValueInput();
        this.RaisePersonalStandardValidation();
        this.OnValueChanged();
    },
    UpdateValueInput: function() {
    },
    
    RaiseValueChangedEvent: function() {
        if(!this.isInitialized) return;
        var processOnServer = ASPxClientTextEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseSelectedIndexChanged(processOnServer);
        return processOnServer;
    },
    RaiseSelectedIndexChanged: function(processOnServer) {
        if(!this.SelectedIndexChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.SelectedIndexChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    AddItem: function(text, value, imageUrl){
        var index = this.GetListBoxControl().AddItem(text, value, imageUrl);
        this.CollectionChanged();
        return index;
    },
    InsertItem: function(index, text, value, imageUrl){
        this.GetListBoxControl().InsertItem(index, text, value, imageUrl);
        this.CollectionChanged();
    },
    RemoveItem: function(index){
        this.GetListBoxControl().RemoveItem(index);
        this.CollectionChanged();
    },
    ClearItems: function(){
        this.GetListBoxControl().ClearItems();
        this.ClearItemsInternal();
    },
    BeginUpdate: function(){
         this.GetListBoxControl().BeginUpdate();
    },
    EndUpdate: function(){
        this.GetListBoxControl().EndUpdate();
        this.CollectionChanged();
    },
    MakeItemVisible: function(index){
    },
    GetItem: function(index){
        return this.GetListBoxControl().GetItem(index);
    },
    GetItemCount: function(){
        return this.GetListBoxControl().GetItemCount(); 
    },
    GetSelectedIndex: function(){
        return this.GetListBoxControl().GetSelectedIndexInternal();
    },
    SetSelectedIndex: function(index){
        this.SelectIndex(index, false);
    },
    GetSelectedItem: function(){
        var lb = this.GetListBoxControl();
        var index = lb.GetSelectedIndexInternal();
        return lb.GetItem(index);
    },
    SetSelectedItem: function(item){
        var index = (item != null) ? item.index : -1;
        this.SelectIndex(index, false);
    },
    GetText: function(){
        return this.lastSuccessText;
    },
    PerformCallback: function(arg) {
    },
    ClearItemsInternal: function(){
        this.SetValue(null);
        this.CollectionChanged();
    }
});

ASPxClientComboBox = _aspxCreateClass(ASPxClientComboBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.filterTimerId = -1;
        // Callback
        this.allowMultipleCallbacks = false;
        this.isApplyAndCloseAfterCallback = false;
        this.isCallbackMode = false;
        this.isPerformCallback = false;
        this.changeSelectAfterCallback = 0;
        // Filtering
        this.isFilterEnabled = false;
        this.isEnterLocked = false;
        this.isLastFilteredKeyWasTab = false; // B30455. FilteringHighlightComplitedText invoke setFocus().
        this.currentCallbackIsFiltration = false;
        this.filter = "";
        this.filterTimer = 100;
        
        this.isToolbarItem = false;
        this.isDropDownListStyle = true;
        this.defaultDropDownHeight = "";
        this.dropDownHeight = "";
        this.dropDownWidth = "";
    },
    Initialize: function(){
        var lb = this.GetListBoxControl();
        this.InitializeListBoxOwnerName();
        
        var mainElement = this.GetMainElement();
        if(_aspxIsExists(mainElement))
            _aspxAttachEventToElement(mainElement, __aspxNS ? "DOMMouseScroll" : "mousewheel", aspxCBMouseWheel);    
        var input = this.GetInputElement();
        var ddbutton = this.GetDropDownButton();
        if(_aspxIsExists(ddbutton))
            _aspxAttachEventToElement(ddbutton, "mousemove", aspxCBDDButtonMMove);
        if(this.isFilterEnabled)
            _aspxAttachEventToElement(input, "keyup", aspxCBKeyUp);
        if(this.isDropDownListStyle && __aspxIE){
            _aspxPreventElementDragAndSelect(mainElement, true);
            _aspxPreventElementDragAndSelect(input, true);
            if(_aspxIsExists(ddbutton))
                _aspxPreventElementDragAndSelect(ddbutton, true);
        }
        if(this.isToolbarItem){
            mainElement.unselectable="on";
            input.unselectable="on";
            if(_aspxIsExists(input.offsetParent))
                input.offsetParent.unselectable="on";
            if(_aspxIsExists(ddbutton))
                ddbutton.unselectable="on";
            if(_aspxIsExists(lb)){
                var table = lb.GetListTable();
                for(var i = 0; i < table.rows.length; i ++){
                    for(var j = 0; j < table.rows[i].cells.length; j ++)
                        _aspxSetElementAsUnselectable(table.rows[i].cells[j], true);
                }
            }
        }
        
        this.RemoveRaisePSValidationFromListBox();
        this.RedirectStandardValidators();
        this.UpdateValueInput();
        this.InitDropDownSize();
        this.InitListBoxScrollStyle();

        ASPxClientComboBoxBase.prototype.Initialize.call(this);
    },
    InlineInitialize: function(){
        this.InitSpecialKeyboardHandling();
        ASPxClientEditBase.prototype.InlineInitialize.call(this);
    },
        
    GetDropDownInnerControlName: function(suffix){
        return ASPxClientDropDownEdit.prototype.GetDropDownInnerControlName.call(this, suffix);
    },
    AdjustControlCore: function() {
        ASPxClientEdit.prototype.AdjustControlCore.call(this);
        this.ddHeightCache = __aspxInvalidDimension;
        this.ddWidthCache = __aspxInvalidDimension;
    },
    
    RemoveRaisePSValidationFromListBox: function() {
        var listBox = this.GetListBoxControl();
        if (_aspxIsExists(listBox))
            listBox.RaisePersonalStandardValidation = function() { };
    },
    RedirectStandardValidators: function() {
        var valueInput = this.GetValueInput();
        if(_aspxIsExistsElement(valueInput) && _aspxIsExists(valueInput.Validators)) {
            for(var i = 0; i < valueInput.Validators.length; i++)
                valueInput.Validators[i].controltovalidate = valueInput.id;
        }
    },
    GetValueInputToValidate: function(){
        return this.GetValueInput();
    },
    GetValueInput: function(){
        return document.getElementById(this.name + "_" + __aspxCCValueInputSuffix);
    },
    GetListBoxScrollDivElement: function(){
        return this.GetListBoxControl().GetScrollDivElement();
    },
    GetDropDownHeight: function(){
        return (this.ddHeightCache != __aspxInvalidDimension) ? this.ddHeightCache : this.InitListBoxHeight();
    },
    GetDropDownWidth: function(){
        return (this.ddWidthCache != __aspxInvalidDimension && !this.GetIsControlWidthWasChanged()) ? this.ddWidthCache : this.InitListBoxWidth();
    },
    UpdateValueInput: function() {
        var inputElement = this.GetValueInput();
        if(_aspxIsExists(inputElement)){
            var value = this.GetValue();
            inputElement.value = value != null ? value : "";
        }
    },
    
    VisibleCollectionChanged: function(){
		if(__aspxIE)
            this.CollectionChangedCore();
    },
    CollectionChanged: function(){
        this.CollectionChangedCore();
    },
    CollectionChangedCore: function(byTimer){
        if(this.GetListBoxControl().APILockCount == 0){
            this.InitDropDownSize();
            
            if(__aspxIE){ // Prevent wrong selected style render
                var lb = this.GetListBoxControl();
                var selectedIndex = lb.GetSelectedIndex();
                if(selectedIndex > -1)
                    lb.SetItemSelectedState(selectedIndex, selectedIndex);
            }
        }
    },
    InitListBoxScrollStyle: function(){
        this.PreventScrollSpoilDDShowing();
    },
    InitDropDownSize: function(){
        if(!this.enabled || this.GetItemCount() == 0) return; // B132741
        
        var pc = this.GetPopupControl();
        if(_aspxIsExists(pc) && this.IsDisplayed()) {
            var pcwElement = pc.GetWindowElement(-1);
            if(_aspxIsExistsElement(pcwElement)){
                var isPcwDisplayed = _aspxGetElementDisplay(pcwElement);
                if(!isPcwDisplayed)
                    pc.SetWindowDisplay(-1, true);
                this.ddHeightCache = this.InitListBoxHeight();
                this.ddWidthCache = this.InitListBoxWidth();
                pc.SetSize(this.ddWidthCache, this.ddHeightCache);
                if(!isPcwDisplayed)
                    pc.SetWindowDisplay(-1, false);
            }
        }
    },
    InitMainElementCache: function(){
        this.mainElementWidthCache = this.GetMainElement().clientWidth;
    },
    GetVisibleItemCount: function(lb){
        if(!this.isFilterEnabled || this.isCallbackMode)
            return this.GetItemCount();
        
        var lbTable = lb.GetListTable();
        var count = this.GetItemCount();
        var visibleItemCount = 0;
        for(var i = 0; i < count; i ++){
            if(_aspxGetElementDisplay(lbTable.rows[i]))
                visibleItemCount++;
        }
        return visibleItemCount;
    },
    GetDefaultDropDownHeight: function(listHeight, count){
        if(this.defaultDropDownHeight == ""){
            this.defaultDropDownHeight = ((listHeight / count) * 7) + "px";
        }
        return this.defaultDropDownHeight;
    },
    InitListBoxHeight: function(){
        var lbScrollDiv = this.GetListBoxScrollDivElement();
        var height = this.dropDownHeight;
        var lb = this.GetListBoxControl();
        lb.GetMainElement().style.height = "0px";
        var lbHeight = 0;
        if(height == ""){
            var listHeight = lb.GetListTableHeight();
            var count = this.GetVisibleItemCount(lb);
            if(count > 7)
                height = this.GetDefaultDropDownHeight(listHeight, count);
            else
                height = count == 0 ? "0px" : listHeight + "px";
            lbScrollDiv.style.height = height;
            lbHeight = lbScrollDiv.offsetHeight;
        } else {
            var lbMainElement = lb.GetMainElement();
            lbMainElement.style.height = "0px";
            lbScrollDiv.style.height = "0px";
            lbMainElement.style.height = height;
            var trueLbOffsetHeight = lbMainElement.offsetHeight;
            var trueLbClientHeight = lbMainElement.clientHeight;
            lbScrollDiv.style.height = lbMainElement.clientHeight + "px";
            lbHeightCorrection = lbMainElement.offsetHeight - trueLbOffsetHeight;
            lbScrollDiv.style.height = (trueLbClientHeight - lbHeightCorrection) + "px";
            lbHeight = lbMainElement.offsetHeight;
        }
        lb.InitializePageSize();
        return lbHeight;
    },
    InitListBoxWidth: function(){
        this.InitMainElementCache();
    
        var mainElement = this.GetMainElement();
        var lbScrollDiv = this.GetListBoxScrollDivElement();
        var lb = this.GetListBoxControl();
        var lbMainElement = lb.GetMainElement();
        var lbTable = lb.GetListTable();
        
		lbMainElement.style.width = "";
        lbScrollDiv.style.paddingRight = "0px";
        lbScrollDiv.style.width = "100%";

        if(this.dropDownWidth != ""){
            lbMainElement.style.width = this.dropDownWidth;
            var width = lbMainElement.clientWidth;
            width = this.SetLbScrollDivAndCorrectionForScroll(lb, width, false);
            if(!__aspxIE) {
                var difference = lbTable.offsetWidth - lbScrollDiv.clientWidth;
                if(difference > 0){
                    lbMainElement.style.width = (lbMainElement.offsetWidth + difference) + "px";
                    lbScrollDiv.style.width = (lbMainElement.clientWidth)  + "px";
                }
            }
        } else {
            var pc = this.GetPopupControl();
            var width = lbTable.offsetWidth;
            width = this.SetLbScrollDivAndCorrectionForScroll(lb, width, true);
            if(__aspxFirefox && lbMainElement.offsetWidth < lbScrollDiv.offsetWidth)
                lbMainElement.style.width = "0%"; // FIX B19014
            var widthDifference = mainElement.offsetWidth - lbMainElement.offsetWidth;
            if(widthDifference > 0){
                lbScrollDiv.style.width = (width + widthDifference) + "px";
                var twoBorderSize = (lbMainElement.offsetWidth - lbMainElement.clientWidth);
                lbMainElement.style.width = (width + widthDifference + twoBorderSize) + "px"; // prevent lbMainElement size hover ScrollBar
            }
        }
        if(lb.IsMultiColumn())
            lb.CorrectHeaderWidth();
        return lbScrollDiv.offsetWidth;
    },
    SetLbScrollDivAndCorrectionForScroll: function(lb, width, widthByContent){
        var lbScrollDiv = this.GetListBoxScrollDivElement();
        var scrollWidth = lb.GetVerticalScrollBarWidth();

        var BrowserPutsScrollOnContent = !__aspxIE || __aspxIE55;
        var BrowserCanHaveScroll = lb.GetVerticalOverflow(lbScrollDiv) == "auto" || this.IsScrollSpoilDDShowing();
        
        if(!BrowserPutsScrollOnContent){
            width -= scrollWidth;
            lbScrollDiv.style.paddingRight = scrollWidth + "px";
        } else if(widthByContent && BrowserCanHaveScroll)
            width += scrollWidth;
            
        lbScrollDiv.style.width = width + "px";
        return width;
    },
    SelectIndexSilent: function(lb, index){
        this.lbEventLockCount ++;
        lb.SelectIndexSilentAndMakeVisible(index);
        this.lbEventLockCount --;
    },
    SelectInputText: function(){
        var input = this.GetInputElement();
        _aspxSetInputSelection(input, 0, input.value.length);
    },
    ShowDropDownArea: function(isRaiseEvent){
        var lb = this.GetListBoxControl();
        if(!_aspxIsExists(lb) || lb.GetItemCount() == 0) 
            return;

        ASPxClientDropDownEdit.prototype.ShowDropDownArea.call(this, isRaiseEvent);
        
        var text = ASPxClientDropDownEdit.prototype.GetValue.call(this);
        var lbItem = lb.GetSelectedItem();
        var lbText = lbItem != null ? lbItem.text : "";
        if(text != lbText && text != null && lbText != ""){
            var newSelectedIndex = this.FindItemIndexByText(lb, text);
            lb.SelectIndexSilent(newSelectedIndex, false);
        }
        this.EnsureSelectedItemVisibleOnShow(lb);
        lb.CallbackSpaceInit();
    },
    HideDropDownArea: function(isRaiseEvent){
        this.FilteringStop();
        if(__aspxSafariFamily || __aspxOpera) // B36477
            this.GetListBoxControl().CachedScrollTop();
        ASPxClientDropDownEdit.prototype.HideDropDownArea.call(this, isRaiseEvent);

        this.PreventScrollSpoilDDShowing();
    },
    EnsureSelectedItemVisibleOnShow: function(listBox){
        if(__aspxSafariFamily || __aspxOpera) // B36477 Before EnsureSelectedItemVisible will make decision about item's visibility
            listBox.RestoreScrollTopFromCache();
        listBox.EnsureSelectedItemVisible();
    },
    IsScrollSpoilDDShowing: function (){
        var pc = this.GetPopupControl();
        return ((__aspxMozilla || __aspxFirefox)  && pc.enableAnimation); // Prevent mozilla wrong animation
    },
    EnableLBDivOverflow: function(){
        var divElement = this.GetListBoxScrollDivElement();
        divElement.style.overflow = "auto";
    },
    DisableLBDivOverflow: function(){
        var divElement = this.GetListBoxScrollDivElement();
        divElement.style.overflow = "hidden";
    },
    PreventScrollSpoilDDShowing: function(){
        if(this.IsScrollSpoilDDShowing())
            this.DisableLBDivOverflow();
    },
    ChangeReadOnlyAttribute: function(element, enabled){
        if(!this.isDropDownListStyle || this.isFilterEnabled)
            ASPxClientTextEdit.prototype.ChangeReadOnlyAttribute.call(this, element, enabled);
    },
    // Filtering
    IsCtrlV: function(evt, keyCode){
        return evt.ctrlKey && keyCode == 118;
    },
    EventKeyCodeChangesTheInput: function(evt){
        var keyCode = _aspxGetKeyCode(evt);
        if(evt.ctrlKey)
            return this.IsCtrlV(evt, keyCode);
            
        var isSystemKey = ASPxKey.Windows <= keyCode && keyCode <= ASPxKey.ContextMenu;
        var isFKey = ASPxKey.F1 <= keyCode && keyCode <= 127; // 127 - F16 for MAC
        return ASPxKey.Delete <= keyCode && !isSystemKey && !isFKey || keyCode == ASPxKey.Backspace || keyCode == ASPxKey.Space;
    },
    OnFilteringKeyUp: function(evt){
        if(!this.isFilterEnabled || this.InCallback()) return;
        
        if(this.EventKeyCodeChangesTheInput(evt)){
            this.FilterStopTimer();
            var input = this.GetInputElement();
            var newFilter = input.value.toLowerCase();
            if(evt.keyCode == ASPxKey.Backspace && _aspxHasInputSelection(input) && newFilter == this.filter)
                this.FilteringBackspace();
            else
                this.FilterStartTimer();
        }
    },
    IsFilterTimerActive: function(){
        return (this.filterTimerId != -1);
    },
    FilterStartTimer: function(){
        this.isEnterLocked = true;
        this.filterTimerId = _aspxSetTimeout("aspxCBFilterByTimer('" + this.name + "')", this.filterTimer);
    },
    FilterStopTimer: function(){
        this.filterTimerId = _aspxClearTimer(this.filterTimerId);
    },
    EnshureShowDropDownArea: function(){
        if(!this.droppedDown)
            this.ShowDropDownArea(true);
    },
    Filtering: function(){
        this.FilterStopTimer();
    
        var input = this.GetInputElement();
        var newFilter = input.value.toLowerCase();
        if(this.filter != newFilter){
            this.filter = newFilter;
            this.EnshureShowDropDownArea();

            if(this.isCallbackMode)
                this.FilteringOnServer();
            else
                this.FilteringOnClient(input); 
        } else 
            this.isEnterLocked = false;
    },
    FilteringBackspace: function(){
        if(this.isFilterEnabled){
            var input = this.GetInputElement();
            input.value = input.value.slice(0, -1);
            this.FilterStartTimer();
        } 
    },
    GetCallbackArguments: function(){
        var args = ASPxClientComboBoxBase.prototype.GetCallbackArguments.call(this);
        if(this.isCallbackMode)
            args += this.GetCallbackArgumentsInternal();
        return args;
    },
    GetCallbackArgumentsInternal: function(){
        var args = "";
        if(this.filter != "")
            args = this.GetCallbackArgumentFilter(this.filter);
        return args;
    },
    GetCallbackArgumentFilter: function(value){
        var callbackPrefix = this.isDropDownListStyle ? __aspxCorrectFilterCallbackPrefix : __aspxLoadFilteredItemsCallbackPrefix;
        return this.FormatCallbackArg(callbackPrefix, value);
    },
    FilteringOnServer: function(){
        if(!this.InCallback()){
            var listBox = this.GetListBoxControl();
            
            listBox.ClearItems(); // TODO Extract method in 8.x with ListBoxAPI
            listBox.serverIndexOfFirstItem = 0;
            listBox.SetScrollSpacerVisibility(true, false);
            listBox.SetScrollSpacerVisibility(false, false);
            
            this.SendFilteringCallback();
        }
    },
    SendFilteringCallback: function(){
        this.currentCallbackIsFiltration = true;
        this.SendCallback();
    },
    FilteringOnClient: function(input){
        var filter = this.filter.toLowerCase();
        var lb = this.GetListBoxControl();
        var listTable = lb.GetListTable();
        var count = lb.GetItemCount();
        var text = "";
        var isSatisfy = false;
        var isFirstSatisfyItemFinded = false;
        
        if(this.isDropDownListStyle){
            var coincide = new Array(count);
            var maxCoincide = 0;
            for(var i = count - 1; i >= 0; i--){
                coincide[i] = this.GetCoincideCharCount(lb.GetItem(i).text.toLowerCase(), filter);
                if(coincide[i] > maxCoincide)
                    maxCoincide = coincide[i];
            }
            filter = filter.substr(0, maxCoincide);
            input.value = filter;
        }
        for(var i = 0; i < count; i ++){
            text = lb.GetItem(i).text; //TODO create able to get server-cached text
            isSatisfy = this.isDropDownListStyle ? (coincide[i] == maxCoincide) : 
                (text.toLowerCase().indexOf(filter) == 0);
            _aspxSetElementDisplay(listTable.rows[i], isSatisfy);
            if(!isFirstSatisfyItemFinded && isSatisfy){
                var isTextClearing = !this.isDropDownListStyle && this.filter == "" && this.filter != text;
                if(!isTextClearing)
                    this.FilteringHighlightComplitedText(text);
                this.SelectIndexSilent(lb, isTextClearing ? -1 : i);
                isFirstSatisfyItemFinded = true;
            }
        }
        if(this.isDropDownListStyle)
            this.filter = filter;
        if(!isFirstSatisfyItemFinded)
            this.HideDropDownArea(true);
        else
            this.VisibleCollectionChanged();
        this.isEnterLocked = false;
    },
    GetCoincideCharCount: function(text, filter) {
        while(filter != "" && text.indexOf(filter) != 0) {
            filter = filter.slice(0, -1);
        }
        return filter.length;
    },
    FilteringHighlightComplitedText: function(filterItemText){
        var lb = this.GetListBoxControl();
        var input = this.GetInputElement();
        var text = input.value;
        var valueLenght = text.length;
        var itemTextLenght = filterItemText.length;
        
        input.value = filterItemText;
        if(valueLenght < itemTextLenght)
            _aspxSetInputSelection(input, valueLenght, itemTextLenght);
    },
    FilteringStop: function(){
        if(this.isFilterEnabled){
            this.isEnterLocked = false;
            if(!this.isCallbackMode)
                this.FilteringStopClient();
        }
    },
    FilteringStopClient: function(){
        var lb = this.GetListBoxControl();
        var listTable = lb.GetListTable();
        var count = lb.GetItemCount();
        for(var i = 0; i < count; i ++)
            _aspxSetElementDisplay(listTable.rows[i], true);
        this.VisibleCollectionChanged();
    },
    // LoadingPanel
    ShowLoadingPanel: function() {    
        var lb = this.GetListBoxControl();
        var loadingParentElement = lb.GetScrollDivElement().parentNode;
        if(!_aspxIsExists(this.loadingDivElement))
            this.CreateLoadingDiv(loadingParentElement);
        if(!_aspxIsExists(this.loadingPanelElement))
            this.CreateLoadingPanelWithAbsolutePosition(loadingParentElement, loadingParentElement);
    },
    HideLoadingPanelOnCallback: function(){
        return false;
    },
    // CallbackMode
    FormatCallbackArg: function(prefix, arg) {
        return (_aspxIsExists(arg) ? prefix + "|" + arg.length + ';' + arg + ';' : "");
    },
    FilterChanged: function(){
        return this.filter != this.GetInputElement().value.toLowerCase();
    },
    OnCallback: function(result) {
        if(this.currentCallbackIsFiltration && this.FilterChanged()) {
            this.preventEndCallbackRising = true;
            _aspxSetTimeout("aspxCBFilterByTimer('" + this.name + "')", 0);
        } else {
            this.OnCallbackBeforeListBox();
            this.GetListBoxControl().OnCallback(result);
            this.OnCallbackInternal(result);
            this.OnCallbackFinally(true);
        }
        this.currentCallbackIsFiltration = false;
    },
    DoEndCallback: function(){
        if(this.preventEndCallbackRising)
            this.preventEndCallbackRising = false;
        else
            ASPxClientDropDownEdit.prototype.DoEndCallback.call(this);
    },
    OnCallbackError: function(result, data){
        this.GetListBoxControl().OnCallbackError(result);
        this.OnCallbackFinally(false);
    },
    OnCallbackFinally: function(isSuccessful){
        this.currentCallbackIsFiltration = false;
        this.CollectionChanged();
        this.HideLoadingDiv();
        this.HideLoadingPanel();
        this.isPerformCallback = false;
        this.changeSelectAfterCallback = 0;
        if(isSuccessful){
            if(this.isApplyAndCloseAfterCallback){
                this.OnApplyChangesAndCloseWithEvents();
                this.isApplyAndCloseAfterCallback = false;
            }
        }
    },
    OnCallbackBeforeListBox: function(){
        var lb = this.GetListBoxControl();
        this.changeSelectAfterCallback = lb.changeSelectAfterCallback;
    },
    OnCallbackCorrectSelectedIndex: function(){
        var lb = this.GetListBoxControl();
        if(this.changeSelectAfterCallback != 0)
            this.SetTextInternal(lb.GetSelectedItem().text);
    },
    OnCallbackInternal: function(result){
        this.OnCallbackCorrectSelectedIndex();
        this.OnFilterCallback(result);
    },
    OnFilterCallback: function(result){
        var lb = this.GetListBoxControl();
        if(lb.GetItemCount() == 0)
            this.HideDropDownArea(true);
        else 
            this.OnFilterCallbackWithResult(lb);        
        this.isEnterLocked = false;
    },
    OnFilterCallbackWithResult: function(lb){
        this.OnFilterCallbackHighlightAndSelect(lb);
        var isNeedToKeepDropDownVisible = !this.isPerformCallback && !this.isLastFilteredKeyWasTab;
        if(isNeedToKeepDropDownVisible)
            this.EnshureShowDropDownArea();
    },
    OnFilterCallbackHighlightAndSelect: function(lb){
        var firstItemText = lb.GetItem(0).text;
        var isTextClearing = !this.isDropDownListStyle && this.filter == "" && this.filter != firstItemText;
        if(!isTextClearing){
            var isFilterRollBack = this.CheckForFilterRollback(lb, firstItemText);
            var isNonFilterChangingCallback = (lb.GetSelectedItem() == null);
            if(isFilterRollBack || isNonFilterChangingCallback){
                if(this.isLastFilteredKeyWasTab){
                    this.isLastFilteredKeyWasTab = false;
                    this.SelectIndex(0);
                    this.OnChange(); // B94483
                } else  {
                    this.FilteringHighlightComplitedText(firstItemText);
                    if(this.isPerformCallback )
                        this.SelectIndex(0); // this.isPerformCallback - Q96137, B30516 
                    else
                        this.SelectIndexSilent(lb, 0);
                }
            }
        }
    },
    CheckForFilterRollback: function(lb, firstItemText){
        var isHasCorrection = false;
        var filter = this.filter.toLowerCase();
        firstItemText = firstItemText.toLowerCase();
        
        while(firstItemText.substring(0, filter.length).toLowerCase() != filter){
            filter = filter.slice(0, -1);
            isHasCorrection = true;
        }
        if(isHasCorrection){
            this.filter = this.filter.substring(0, filter.length);
            this.GetInputElement().value = this.filter;
        } 
        return isHasCorrection;
    },
    SendCallback: function(){
        this.ShowLoadingPanel();
        ASPxClientComboBoxBase.prototype.SendCallback.call(this);
    },
    
    // Key Board support
    SelectNeighbour: function (step){
        if(this.isToolBarItem && !this.droppedDown) return;
        
        var lb = this.GetListBoxControl();
        var step = this.GetStepForClientFiltrationEnabled(lb, step);
        this.SelectNeighbourInternal(lb, step);
    },
    SelectNeighbourInternal: function(lb, step){
        if(this.droppedDown)
            this.lbEventLockCount ++;
        
        lb.SelectNeighbour(step);
        
        if(this.droppedDown){
            this.SetTextInternal(lb.GetSelectedItem().text);
            this.lbEventLockCount --;
        } 
    },
    GetStepForClientFiltrationEnabled: function(lb, step){
        var isClientFiltrationEnabled = this.isFilterEnabled && !this.isCallbackMode;
        if(isClientFiltrationEnabled){
            var stepDirection = step > 0 ? 1 : -1;
            var startIndex = this.GetSelectedIndex();
            var count = lb.GetItemCount();
            var listTable = lb.GetListTable();
            var needVisibleItemCount = Math.abs(step);
            var outermostVisibleIndex = startIndex;
            
            for(var index = startIndex + stepDirection; needVisibleItemCount > 0; index += stepDirection){
                if(index < 0 || count <= index) break;
                
                if(_aspxGetElementDisplay(listTable.rows[index])){
                    outermostVisibleIndex = index;
                    needVisibleItemCount --;
                }
            }
            step = outermostVisibleIndex - this.GetSelectedIndex();
        }
        return step;
    },
    
    OnSpecialKeyDown: function(evt){
        if(this.isFilterEnabled && this.EventKeyCodeChangesTheInput(evt))
            this.FilterStopTimer();

        return ASPxClientEdit.prototype.OnSpecialKeyDown.call(this, evt);
    },
    OnArrowUp: function(evt){
        if(!this.isInitialized) return true;
        var isProcessed = ASPxClientDropDownEdit.prototype.OnArrowUp.call(this, evt);
        if (!isProcessed)
            this.SelectNeighbour(-1);
        return true;
    },
    OnArrowDown: function(evt){
        if(!this.isInitialized) return true;
        var isProcessed = ASPxClientDropDownEdit.prototype.OnArrowDown.call(this, evt);
        if (!isProcessed)
            this.SelectNeighbour(1);
        return true;
    },
    OnPageUp: function(){
        if(!this.isInitialized) return true;
        return this.OnPageButtonDown(false);
    },
    OnPageDown: function(){
        if(!this.isInitialized) return true;
        return this.OnPageButtonDown(true);
    },
    OnPageButtonDown: function(isDown){
        if(!this.isInitialized) return true;
        var lb = this.GetListBoxControl();
        if(_aspxIsExists(lb)){
            var direction = isDown ? 1 : -1;
            this.SelectNeighbour(lb.scrollPageSize * direction);
        }
        return true;
    },
    OnHomeKeyDown: function(evt){
        if(!this.isInitialized) return true;
        return this.OnHomeEndKeyDown(evt, true);
    },
    OnEndKeyDown: function(evt){
        if(!this.isInitialized) return true;
        return this.OnHomeEndKeyDown(evt, false);
    },
    OnHomeEndKeyDown: function(evt, isHome){
        if(!this.isInitialized) return true;
        var input = this.GetValueInput();
        if(input.readOnly || evt.ctrlKey){
            var lb = this.GetListBoxControl();
            var count = lb.GetItemCount();
            this.SelectNeighbour(isHome ? -count : count);
            return true;
        }
        return false;
    },
    OnEnter: function(){
        if(!this.isInitialized) return true;
        this.enterProcessed = this.droppedDown; // hack for defaultbutton
        if(!this.isEnterLocked) //Prevent to extra fast push enter while filtering continue
            this.OnApplyChangesAndCloseWithEvents();
        return this.enterProcessed;
    },
    OnTab: function(evt){
        if(!this.isInitialized) 
            return true;
        if(this.IsFilterTimerActive() || this.currentCallbackIsFiltration){
            this.isLastFilteredKeyWasTab = true;
            this.Filtering(); // B91550 Tab sends KeyDown without KeyUp
        }
        
        if(this.InCallback())
            this.isApplyAndCloseAfterCallback = true;
        else
            this.OnApplyChangesAndCloseWithEvents();
    },

    OnApplyChanges: function(){
        if(this.isDropDownListStyle && !this.isFilterEnabled) return;
        this.OnApplyChangesInternal();
    },
    OnApplyChangesAndCloseWithEvents: function(){
        this.OnApplyChangesInternal();
        this.HideDropDownArea(true);
    },
    OnApplyChangesInternal: function(){
        var text = this.GetInputElement().value;
        var isChanged = this.lastSuccessText != text;
        if(isChanged){
            var lb = this.GetListBoxControl();
            if(this.isDropDownListStyle && this.FindItemIndexByText(lb, text) < 0){
                var lbItem = lb.GetSelectedItem();
                text = lbItem != null ? lbItem.text : this.lastSuccessText;
            }
        } 
        this.SetText(text);
        if(isChanged)
            this.OnChange();
    },
    OnButtonClick: function(number){
        if(number != this.dropDownButtonIndex){
            this.HideDropDownArea(true);
        }
        ASPxClientButtonEditBase.prototype.OnButtonClick.call(this, number);
    },
    OnCancelChanges: function(){
        var isCancelProcessed = ASPxClientDropDownEdit.prototype.OnCancelChanges.call(this);
        var lb = this.GetListBoxControl();
        var index = this.FindItemIndexByText(lb, this.lastSuccessText);
        this.SelectIndexSilent(lb, index);
        return isCancelProcessed;
    },
    OnCloseUp: function(evt){
        var evt = _aspxGetEvent(evt);
        var scrollDiv = this.GetListBoxControl().GetScrollDivElement();
        var scrollDivID = _aspxIsExists(scrollDiv) ? scrollDiv.id : "";
        if(__aspxFirefox && evt.type == "mouseup" && 
            (_aspxGetEventSource(evt).tagName == "DIV" && scrollDivID == _aspxGetEventSource(evt).id)) // Prevent FF closing DropDownArea by DropDownArea ScrollBar MouseUp
            return;
        ASPxClientDropDownEdit.prototype.OnCloseUp.call(this, evt);
    },
    OnDDButtonMouseMove: function(evt){
        return (this.droppedDown ? _aspxCancelBubble(evt) : true);
    },
    OnDocumentMouseDown: function(){
        this.OnApplyChangesInternal();
        ASPxClientDropDownEdit.prototype.OnDocumentMouseDown.call(this);
    },
    IsCanToDropDown: function(){
        return (this.GetListBoxControl().GetItemCount() > 0);
    },
    OnPopupControlShown: function(){
        if(!this.isInitialized) return;
        if(__aspxOpera) // B36477
            this.GetListBoxControl().RestoreScrollTopFromCache();
        if(this.IsScrollSpoilDDShowing())
            _aspxSetTimeout("aspxCBMozillaOverflowOn(\"" + this.name + "\")", 100);
        if(this.lockListBoxClick)
            delete this.lockListBoxClick;
        ASPxClientDropDownEdit.prototype.OnPopupControlShown.call(this);
    },
    OnLBSelectedIndexChanged: function(){
        if(!this.lockListBoxClick) {
            this.OnSelectChanged();
            this.OnClick();
        }
    },
    OnListBoxItemMouseUp: function(evt){
        if(!this.lockListBoxClick && !this.InCallback()){
            this.OnApplyChangesInternal();
            this.OnCloseUp(evt);
            this.OnClick();
        }
    },
    OnMouseWheel: function(evt){
        if(!this.droppedDown){
            var wheelDelta = _aspxGetWheelDelta(evt);
            if(wheelDelta > 0)
			    this.SelectNeighbour(-1);
		    else  if(wheelDelta < 0)
			    this.SelectNeighbour(1);
			return _aspxPreventEvent(evt);
		}
    },
    OnSetFocus: function(isFocused){
        if(isFocused && this.isFilterEnabled && !this.isToolbarItem)
            this.SelectInputText();
        ASPxClientDropDownEdit.prototype.OnSetFocus.call(this, isFocused);
    },
    OnOpenAnotherDropDown: function(){
        this.OnApplyChangesAndCloseWithEvents();
    },
    OnClick: function(){
        if(!this.isToolbarItem)
            ASPxClientDropDownEdit.prototype.OnClick.call(this);
    },    

    ParseValue: function() {
        var newText = ASPxClientButtonEditBase.prototype.GetValue.call(this);
        var oldText = this.GetText();
        if(oldText != newText){
            if(this.CanTextBeAccepted(newText, oldText)){
                this.SetText(newText);
                this.OnChange();
            } else
                this.SetTextInternal(oldText);
        }
    },
    CanTextBeAccepted: function(newText, oldText){
        var notAnyTextCanBeAccepted = this.isDropDownListStyle && oldText != "";
        if(notAnyTextCanBeAccepted){
            var lb = this.GetListBoxControl();
            var newTextPresentInItemCollection = this.FindItemIndexByText(lb, newText) != -1;
            return newTextPresentInItemCollection;
        }
        return true;
    },

    MakeItemVisible: function(index){
        var lb = this.GetListBoxControl();
        lb.MakeItemVisible(index);
    },
    PerformCallback: function(arg) {
        this.isPerformCallback = true;
        this.filter = "";
        this.ClearItemsInternal();
        this.GetListBoxControl().PerformCallback(arg);
    },

    ClearItemsInternal: function(){
        ASPxClientComboBoxBase.prototype.ClearItemsInternal.call(this);
        var lbScrollDiv = this.GetListBoxScrollDivElement();
        if(_aspxIsExists(lbScrollDiv))
            lbScrollDiv.scrollTop = "0px";
    }
});

ASPxClientNativeComboBox = _aspxCreateClass(ASPxClientComboBoxBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.initSelectedIndex = -1;
        this.raiseValueChangedOnEnter = false;
    },
    Initialize: function(){
        var lb = this.GetListBoxControl();
        if(lb != null) lb.SetMainElement(this.GetMainElement());
        
        ASPxClientComboBoxBase.prototype.Initialize.call(this);
    },
    InitLastSuccessText: function(){
        this.SelectIndex(this.initSelectedIndex, true);
    },
    FindInputElement: function(){
        return this.GetMainElement();
    },    
    GetDropDownInnerControlName: function(suffix){
        return this.name + suffix;
    },
    PerformCallback: function(arg) {
        this.GetListBoxControl().PerformCallback(arg);
    },
    SetText: function (text){
        var lb = this.GetListBoxControl();
        var index = this.FindItemIndexByText(lb, text);
        this.SelectIndex(index, false);
        this.SetLastSuccessTest((index > -1) ? text : "");
        this.lastSuccessValue = (index > -1) ? lb.GetValue() : null;
        this.islastSuccessValueInit = true;
    },
    GetValue: function(){
        var selectedItem = this.GetSelectedItem();
        return (selectedItem != null) ? selectedItem.value : null;
    },
    SetValue: function(value){
        var lb = this.GetListBoxControl();
        lb.SetValue(value);
        // TODO Extract method with OnSelectChanged (params: value)
        var item = lb.GetSelectedItem();
        var text = _aspxIsExists(item) ? item.text : value;
        this.SetLastSuccessTest((item != null) ? text : "");
        this.lastSuccessValue = (item != null) ? item.value : null;
        this.islastSuccessValueInit = true;
    },
    OnCallback: function(result) {
        this.GetListBoxControl().OnCallback(result);
        if(this.GetItemCount() > 0)
            this.SetSelectedIndex(0);
    },
    OnTextChanged: function() {
        this.OnChange();
    },
    SetTextInternal: function(text){
    },
    ChangeEnabledAttributes: function(enabled){
        this.GetMainElement().disabled = !enabled;
    }
});

// DropDown
var __aspxDropDownCollection = null;
function aspxGetDropDownCollection(){
    if(__aspxDropDownCollection == null)
        __aspxDropDownCollection  = new ASPxClientDropDownCollection();
    return __aspxDropDownCollection;
}

_aspxAttachEventToDocument("mousedown", aspxDropDownDocumentMouseDown);
function aspxDropDownDocumentMouseDown(evt){
    return aspxGetDropDownCollection().OnDocumentMouseDown(evt);
}
_aspxAttachEventToDocument("mouseup", aspxDropDownDocumentMouseUp);
function aspxDropDownDocumentMouseUp(evt){
    return aspxGetDropDownCollection().OnDocumentMouseUp(evt);
}
_aspxAttachEventToElement(window, "resize", aspxCBWindowResize);
function aspxCBWindowResize(evt){
    aspxGetDropDownCollection().OnResize(evt);	
}

function aspxDDDropDown(name, evt){
    if(_aspxGetIsLeftButtonPressed(evt)){
        var dd = aspxGetControlCollection().Get(name);
        if(_aspxIsExists(dd))
            return dd.OnDropDown(evt);
    }
}
function aspxDDCloseUp(name, evt){
    var dd = aspxGetControlCollection().Get(name);
    dd.OnCloseUp(evt);
}

// DropDownEdit
function aspxDDMainElementClick(name, evt){
    var dd = aspxGetControlCollection().Get(name);
    if(dd != null) dd.OnClick();
}
// ComboBox
function aspxDDBPCShown(name){
    var cb = aspxGetControlCollection().Get(name);
    if(cb != null) cb.OnPopupControlShown();
}
function aspxDDBRaiseDropDownByTimer(name){
    var cb = aspxGetControlCollection().Get(name);
    if(cb != null) cb.RaiseDropDown();
}
function aspxCBLBSelectedIndexChanged(name, evt){
    var cb = aspxGetControlCollection().Get(name);
    if(cb != null) cb.OnLBSelectedIndexChanged();
}
function aspxCBLBItemMouseUp(name, evt){
    var cb = aspxGetControlCollection().Get(name);
    if(cb != null) cb.OnListBoxItemMouseUp(evt);
}
function aspxCBMozillaOverflowOn(name){
    var cb = aspxGetControlCollection().Get(name);
    cb.EnableLBDivOverflow();
}
function aspxCBDDButtonMMove(evt){
    return aspxGetDropDownCollection().OnDDButtonMouseMove(evt);
}
function aspxCBMouseWheel(evt){
    var cb = aspxGetDropDownCollection().GetFocusedDropDown();
    if (cb != null) 
        return cb.OnMouseWheel(evt);
}
function aspxCBKeyUp(evt){
    var cb = aspxGetDropDownCollection().GetFocusedDropDown();
    if (cb != null) 
        cb.OnFilteringKeyUp(evt);
}
function aspxCBFilterByTimer(name){
    var cb = aspxGetControlCollection().Get(name);
    if(cb != null) cb.Filtering();
}

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;        
        
        this.nodeCache = { };
        
        // set from server
        this.visibleDate = new Date();
        this.firstDayOfWeek = 0;                
        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() {
        if(this.enableFast)
            this.fastNavigation = new ASPxClientCalendarFastNavigation(this);
        this.selectionTransaction = new ASPxClientCalendarSelectionTransaction(this);
        this.selectionPrevStartDate = this.selection.GetFirstDate();
        this.SaveClientState();	// for callbacks
        ASPxClientEdit.prototype.Initialize.call(this);
    },
    InlineInitialize: function(){
        this.CreateViews();
        this.InitSpecialKeyboardHandling();
        this.MakeDisabledStateItems();
        ASPxClientEditBase.prototype.InlineInitialize.call(this);
    },
    MakeDisabledStateItems: function(){
        for(var key in this.views) {
			var view = this.views[key];
			if(view.constructor != ASPxClientCalendarView) continue;
            view.MakeDisabledStateItems();
        }
    },
    // Base
    FindInputElement: function() {
        return this.GetChild("_KBS");
    },
    FindStateInputElement: function() {
        return _aspxGetElementById(this.name + "_STATE");
    },
    GetClearButton: function() {
        return this.GetChild("_BC");
    },
    GetTodayButton: function() {
        return this.GetChild("_BT");
    },
    GetValue: function() {
        return this.selection.GetFirstDate();
    },
    GetValueString: function() {
        var value = this.GetValue();
        return value == null ? null : _aspxGetInvariantDateString(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
            this.UpdateInternal();
    },    
    UpdateInternal: function() {
        for(var key in this.views) {
			var view = this.views[key];
			if(view.constructor != ASPxClientCalendarView) continue;
             view.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) {
			var view = this.views[key];
			if(view.constructor != ASPxClientCalendarView) continue;
            view.UpdateSelection(toHide, false);
            view.UpdateSelection(toShow, true);            
        }
        
        this.selection.Assign(selection);
        if(save)
            this.SaveClientState();
    },
    
    // Style
    ImportEtalonStyle: function(info, suffix) {
        var cell = this.GetEtalonStyleCell(suffix);
        if(_aspxIsExistsElement(cell))
            info.Import(cell);            
    },
    GetEtalonStyleCell: function(name) {
        return this.GetCachedElementById(this.name + "_EC_" + name);
    },

    // Client sync    
    SaveClientState: function() {
        var element = this.FindStateInputElement();
        if (element != null) {
            var state = _aspxGetInvariantDateString(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 += _aspxGetInvariantDateString(dates[i]);           
        }
        return result;
    },
    // Keyboard Support
    InitializeKeyHandlers: function() {
        this.AddKeyDownHandler(ASPxKey.Enter, "OnEnter");
        this.AddKeyDownHandler(ASPxKey.Esc, "OnEscape");
        this.AddKeyDownHandler(ASPxKey.PageUp, "OnPageUp");
        this.AddKeyDownHandler(ASPxKey.PageDown, "OnPageDown");
        this.AddKeyDownHandler(ASPxKey.End, "OnEndKeyDown");
        this.AddKeyDownHandler(ASPxKey.Home, "OnHomeKeyDown");
        this.AddKeyDownHandler(ASPxKey.Left, "OnArrowLeft");
        this.AddKeyDownHandler(ASPxKey.Right, "OnArrowRight");
        this.AddKeyDownHandler(ASPxKey.Up, "OnArrowUp");
        this.AddKeyDownHandler(ASPxKey.Down, "OnArrowDown");
    },
    OnArrowUp: function(evt) {
        if (this.IsFastNavigationActive()) 
            this.GetFastNavigation().OnArrowUp(evt);
        else if (!this.readOnly) {
            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.IsFastNavigationActive()) 
            this.GetFastNavigation().OnArrowDown(evt);
        else if (!this.readOnly) {
            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.IsFastNavigationActive()) 
            this.GetFastNavigation().OnArrowLeft(evt);
        else if (!this.readOnly) {  
            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.IsFastNavigationActive()) 
            this.GetFastNavigation().OnArrowRight(evt);
        else if (!this.readOnly) {        
            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.IsFastNavigationActive()) 
            this.GetFastNavigation().OnPageUp(evt);
        else if (!this.readOnly) {
            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.IsFastNavigationActive()) 
            this.GetFastNavigation().OnPageDown(evt);
        else if (!this.readOnly) {
            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;    
    },
    OnEnter: function() {
        if (this.IsFastNavigationActive())
            this.GetFastNavigation().OnEnter();
        return true;
    },
    OnEscape: function() {
        if (this.IsFastNavigationActive())
            this.GetFastNavigation().OnEscape();
        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.RaiseVisibleMonthChanged(offsetInternal);
        if(processOnServer)
            this.SendPostBackInternal("");        
    },
    OnSelectionCancelled: function() {
        this.isMouseDown = false;        
        this.selectionTransaction.Rollback();
    },
    RaiseValueChangedEvent: function() {
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        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;
    },
    ChangeEnabledAttributes: function(enabled){
        _aspxChangeDocumentEventsMethod(enabled)("mouseup", aspxCalDocMouseUp);
        _aspxChangeEventsMethod(enabled)(this.GetMainElement(), "click", new Function("aspxCalMainElemClick('" + this.name + "');"));
        var inputElement = this.GetInputElement();
        if(_aspxIsExists(inputElement)) 
            this.ChangeSpecialInputEnabledAttributes(inputElement, _aspxChangeEventsMethod(enabled));
        var btnElement = this.GetTodayButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        btnElement = this.GetClearButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        for(var key in this.views) {
			var view = this.views[key];
			if(view.constructor != ASPxClientCalendarView) continue;
            view.ChangeEnabledAttributes(enabled);
        }
    },
    ChangeEnabledStateItems: function(enabled){
        aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
        var btnElement = this.GetTodayButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
        btnElement = this.GetClearButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
        for(var key in this.views) {
			var view = this.views[key];
			if(view.constructor != ASPxClientCalendarView) continue;        
            view.ChangeEnabledStateItems(enabled);
        }
        this.UpdateInternal();            
    },
	ChangeButtonEnabledAttributes: function(element, method){
        method(element, "onclick");
        method(element, "ondblclick");
	}
});

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.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.setUTCDate(count + newDate.getUTCDate());

	// B31219, B95404, Q148106
	var diff = newDate.getTimezoneOffset() - date.getTimezoneOffset();
	if(diff != 0)
		newDate.setMinutes(newDate.getMinutes() + diff);
	if(count == 1 && newDate.getDate() == date.getDate() && newDate.getTimezoneOffset() == 240) {
		// Santiago timezone
		newDate.setHours(2 + newDate.getHours());
	}

    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) {
			var item = source.dates[key];
			if(item.constructor != Date) continue;
            this.Add(item);
        }
    },
    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(this.dates[key].constructor != Date) continue;
            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 item = this.dates[key];
			if(item.constructor != Date) continue;
            result.push(ASPxClientCalendar.CloneDate(item));
        }
        return result;        
    },
    GetFirstDate: function() {
        if(this.count == 0)
            return null;
        for(var key in this.dates) {
			var item = this.dates[key];
			if(item.constructor != Date) continue;
            return ASPxClientCalendar.CloneDate(item);
        }
        return null;
    },
        
    GetKey: function(date) {        
        return _aspxGetInvariantDateString(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;        
        
        this.dayFunctions = {};
        this.dayFunctionsWithWeekSelection = {};
    },
    
    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);
    },
    AttachMouseEvents: function(eventMethod, styleMethod) {
        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) {
                    eventMethod(cell, "click", 
                        new Function("aspxCalSelectMonth('" + this.calendar.name + "', " + this.row + ", " + this.column + ")"));                    
                    styleMethod(cell, "cursor", _aspxGetPointerCursor());
                }
                this.AttachCancelSelect(eventMethod, cell);
            }            
            // day headers
            for(var j = 0; j < 7; j++)
                this.AttachCancelSelect(eventMethod, 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(eventMethod, cell, this.GetDayMouseEventFunction(7 * i, true));                
                else
                    this.AttachCancelSelect(eventMethod, 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))
                    styleMethod(cell, "cursor", _aspxGetPointerCursor());
                this.AttachDayMouseEvents(eventMethod, cell, this.GetDayMouseEventFunction(index, false));                
            }
        }
    },
    AttachDayMouseEvents: function(method, cell, handler) {
        var types = ["down", "up", "over"];
        for(var i = 0; i < types.length; i++)
            method(cell, "mouse" + types[i], handler);
    },
    AttachCancelSelect: function(method, element) {
        method(element, "mouseup", aspxCalCancelSelect);
    },
    GetDayMouseEventFunction: function(index, selectWeeks) {
        var hash = selectWeeks ? this.dayFunctionsWithWeekSelection : this.dayFunctions;
        if(!_aspxIsExists(hash[index]))
            hash[index] = new Function("e", "aspxCalDayMouseEvt('" + this.calendar.name + "', " + this.row + ", " + this.column + ", " + index + ", e, " + selectWeeks + ");");
        return hash[index];
    },
        
    // 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];
    },
    GetPrevYearCell: function() {
        return this.GetCachedElementById("PYC");
    },
    GetPrevMonthCell: function() {
        return this.GetCachedElementById("PMC");
    },
    GetTitleCell: function() {
        return this.GetCachedElementById("TC");
    },
    GetTitleElement: function() {
        return this.GetCachedElementById("T");
    },
    GetNextMonthCell: function() {
        return this.GetCachedElementById("NMC");
    },
    GetNextYearCell: function() {
        return this.GetCachedElementById("NYC");
    },
                
    // 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 = __aspxCultureInfo.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");
            if(!cal.clientEnabled)
                cal.ImportEtalonStyle(info, "DD");
            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;
    },
    
    MakeDisabledStateItems: function() {
        var cells = this.GetAuxCells();
        for(var i = 0; i < cells.length; i++)
            this.AddAuxDisabledStateItem(cells[i], this.GetAuxId(i));

        var element = this.GetTitleCell();
        if(_aspxIsExists(element))
            this.AddHeaderDisabledStateItem(element);
        var element = this.GetTitleElement();
        if(_aspxIsExists(element))
            this.AddHeaderDisabledStateItem(element);
    },
    AddAuxDisabledStateItem: function(element, id){
        var cell = this.calendar.GetEtalonStyleCell("DD");
        element.id = id;
        aspxGetStateController().AddDisabledItem(id, cell.className, cell.style.cssText, null, null, null);
    },
    AddHeaderDisabledStateItem: function(element){
        var cell = this.calendar.GetEtalonStyleCell("DD");
        aspxGetStateController().AddDisabledItem(element.id, cell.className, cell.style.cssText, null, null, null);
    },

    ChangeEnabledAttributes: function(enabled){
        var element = this.GetPrevYearCell();
        if(_aspxIsExists(element))
            this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
        var element = this.GetPrevMonthCell();
        if(_aspxIsExists(element))
            this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
        var element = this.GetTitleElement();
        if(_aspxIsExists(element)){
            this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
            this.ChangeTitleElementEnabledAttributes(element, _aspxChangeStyleAttributesMethod(enabled));
        }
        var element = this.GetNextMonthCell();
        if(_aspxIsExists(element))
            this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));
        var element = this.GetNextYearCell();
        if(_aspxIsExists(element))
            this.ChangeButtonEnabledAttributes(element, _aspxChangeAttributesMethod(enabled));

        if(this.calendar.enabled && !this.calendar.readOnly)
            this.AttachMouseEvents(_aspxChangeEventsMethod(enabled), _aspxInitiallyChangeStyleAttributesMethod(enabled));
    },
    ChangeEnabledStateItems: function(enabled){
        this.SetAuxCellsEnabled(enabled);
        this.SetHeaderCellsEnabled(enabled);
    },    
	ChangeTitleElementEnabledAttributes: function(element, method){
        method(element, "cursor");
	},
	ChangeButtonEnabledAttributes: function(element, method){
        method(element, "onclick");
        method(element, "ondblclick");
	},
    SetAuxCellsEnabled: function(enabled){
        var cells = this.GetAuxCells();
        for(var i = 0; i < cells.length; i++)
            aspxGetStateController().SetElementEnabled(cells[i], enabled);
    },
    SetHeaderCellsEnabled: function(enabled){
        var element = this.GetPrevYearCell();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
        var element = this.GetPrevMonthCell();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
        var element = this.GetTitleCell();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
        var element = this.GetTitleElement();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
        var element = this.GetNextMonthCell();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
        var element = this.GetNextYearCell();
        if(_aspxIsExists(element))
            aspxGetStateController().SetElementEnabled(element, enabled);
    },   
    GetAuxCells: function(){
        if(this.auxCells == null){
            this.auxCells = [];
            var table = this.GetMonthTable();
            for(var i = 0; i < table.rows.length; i++) {
                var row = table.rows[i];
                if(i == 0 && this.calendar.showDayHeaders) {                
                    for(var j = 0; j < row.cells.length; j++)
                        this.auxCells.push(row.cells[j]);
                }
                if(i > 0 && this.calendar.showWeekNumbers)
                    this.auxCells.push(row.cells[0]);
            }
        }
        return this.auxCells;
    },
    GetAuxId: function(index) {
        return this.calendar.name + "_AUX_" + this.row + "_" + this.column + "_" + index;
    }
});

/////////////////////////////////////////////////////////////////

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();
    },
    SetMonth: 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;            
        }    
    },
    ShiftMonth: function(offset) {
        var month = (this.activeMonth + offset) % 12;
        month = (month < 0) ? month + 12 : month;
        this.SetMonth(month);
    },
    SetYear: function(year) {
        var startYear = Math.floor(year / 10) * 10;
        this.SetStartYear(startYear);
        this.SetYearIndex(year - startYear);
    },
    SetYearIndex: 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;
        }    
    },
    SetStartYear: function(year) {
        if(this.startYear == year) return;
        
        this.startYear = year;        
        this.PrepareYearList();
    },
    ShiftYear: function(offset) {
        this.SetYear(this.activeYear + offset);
    },
    ShiftStartYear: function(offset) {
        this.SetStartYear(this.startYear + offset);
    },
    ApplyChanges: 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();
    },
    CancelChanges: function() {
        this.GetPopup().Hide();
        this.calendar.OnMainElementClick();
    },
        
    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";
    },
    
    // Keyboard support
    OnArrowUp: function(evt) {
        if(!evt.shiftKey)
            this.ShiftYear(-5);
        else
            this.ShiftMonth(-4);
    },
    OnArrowDown: function(evt) {        
        if(!evt.shiftKey)
            this.ShiftYear(5);
        else
            this.ShiftMonth(4);
    },
    OnArrowLeft: function(evt) {    
        if(!evt.shiftKey)
            this.ShiftYear(-1);
        else
            this.ShiftMonth(-1);
    },
    OnArrowRight: function(evt) {
        if(!evt.shiftKey)
            this.ShiftYear(1);
        else
            this.ShiftMonth(1);
    },
    OnPageUp: function(evt) {
        this.ShiftYear(-10);
    },
    OnPageDown: function(evt) {
        this.ShiftYear(10);
    },
    OnEnter: function() {
        this.ApplyChanges();
    },
    OnEscape: function() {
        this.CancelChanges();
    },
    
    // Events    
    OnMonthClick: function(month) {
        this.SetMonth(month);
    },
    OnYearClick: function(index) {
        this.SetYearIndex(index);
    },
    OnYearShuffle: function(offset) {
        this.ShiftStartYear(offset);
    },
    OnOkClick: function() {
        this.ApplyChanges();
    },
    OnCancelClick: function() {
        this.CancelChanges();
    }
});

/////////////////////////////////////////////////////////////////

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 && _aspxGetIsLeftButtonPressed(e))
                    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) {            
				var view = __aspxActiveCalendar.views[key];
				if(view.constructor != ASPxClientCalendarView) continue;
                var monthCell = view.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();        
    }
}

var __aspxLabelValueSuffix = "_V";
ASPxClientStaticEdit = _aspxCreateClass(ASPxClientEditBase, { 
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.Click = new ASPxClientEvent();
    },

    OnClick: function(htmlEvent) {
        this.RaiseClick(this.GetMainElement(), htmlEvent);
    },
    RaiseClick: function(htmlElement, htmlEvent){
        if(!this.Click.IsEmpty()){
            var args = new ASPxClientEditClickEventArgs(htmlElement, htmlEvent);
            this.Click.FireEvent(this, args);
        }
    },
    
    ChangeEnabledAttributes: function(enabled){
        this.ChangeMainElementAttributes(this.GetMainElement(), _aspxChangeAttributesMethod(enabled));
    },
    ChangeEnabledStateItems: function(enabled){
        aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
    },
	ChangeMainElementAttributes: function(element, method){
        method(element, "onclick");
	}
});
ASPxClientEditClickEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(htmlElement, htmlEvent){
        this.constructor.prototype.constructor.call(this);
        this.htmlElement = htmlElement;
        this.htmlEvent = htmlEvent;
    }
});
ASPxClientHyperLink = _aspxCreateClass(ASPxClientStaticEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    GetNavigateUrl: function(){
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element))
            return element.href;
        return "";
    },
    SetNavigateUrl: function(url){
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element))
            element.href = url;
    },
    GetText: function(){
        return this.GetValue();
    },
    SetText: function(value){
        this.SetValue(value);
    },
    
	ChangeMainElementAttributes: function(element, method){
	    ASPxClientStaticEdit.prototype.ChangeMainElementAttributes.call(this, element, method);
        method(element, "href");
	}
});
ASPxClientImageBase = _aspxCreateClass(ASPxClientStaticEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    GetWidth: function(){
        return this.GetSize(true);
    },
    GetHeight: function(){
        return this.GetSize(false);
    },
    SetSize: function(width, height){
        var image = this.GetMainElement();
        if(_aspxIsExistsElement(image))
            ASPxImageUtils.SetSize(image, width, height);
    },
    GetSize: function(isWidth){
        var image = this.GetMainElement();
        if(_aspxIsExistsElement(image))
            return ASPxImageUtils.GetSize(image, isWidth);
        return -1;
    }
});
ASPxClientImage = _aspxCreateClass(ASPxClientImageBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.isEmpty = false;
    },
    GetValue: function() {
        if(this.isEmpty)
            return "";
        var image = this.GetMainElement();
        if(_aspxIsExistsElement(image))
             return ASPxImageUtils.GetImageSrc(image);
        return "";
    },
    SetValue: function(value) {
        if(value == null)
            value = "";
        this.isEmpty = value == "";            
        var image = this.GetMainElement();
        if(_aspxIsExistsElement(image))
            ASPxImageUtils.SetImageSrc(image, value);
    },
    GetImageUrl: function(url){
        return this.GetValue();
    },
    SetImageUrl: function(url){
        this.SetValue(url);
    }
});
ASPxClientBinaryImage = _aspxCreateClass(ASPxClientImageBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    GetValue: function() {
        return "";
    },
    SetValue: function(value) {
    }
});
ASPxClientLabel = _aspxCreateClass(ASPxClientStaticEdit, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);                               
    },
    GetValue: function() {    
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element))
            return element.innerHTML;
    },
    SetValue: function(value) {
        if(value == null)
            value = "";
        var element = this.GetMainElement();
        if(_aspxIsExistsElement(element)) 
            element.innerHTML = value;
    },
    SetVisible: function(visible){
        if(this.clientVisible != visible){
            this.clientVisible = visible;
            var element = this.GetMainElement();
            if(!visible)
                element.style.display = "none";
            else if((element.style.width != "" || element.style.height != "") && !__aspxNS)
                element.style.display = "inline-block";
            else
                element.style.display = "";
        }
    },
    GetText: function(){
        return this.GetValue();
    },
    SetText: function(value){
        this.SetValue(value);
    },
    
	ChangeMainElementAttributes: function(element, method){
	    ASPxClientStaticEdit.prototype.ChangeMainElementAttributes.call(this, element, method);
        method(element, "htmlFor");
	}
});

function aspxSEClick(name, evt){
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) 
        edit.OnClick(evt);
}
ASPxClientButton = _aspxCreateClass(ASPxClientControl, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);

        this.isASPxClientButton = true;

        this.allowFocus = true;
        this.autoPostBackFunction = null;
        this.causesValidation = true;
        this.checked = false;
        this.clickLocked = false;
        this.groupName = "";
        this.focusElementSelected = false;
        this.pressed = false;
        this.useSubmitBehavior = true;
        this.validationGroup = "";
        this.validationContainerID = null;
        this.validateInvisibleEditors = false;
        
        this.buttonCell = null;
        this.contentDiv = null;
        this.checkedInput = null;
        this.buttonImage = null;
        this.internalButton = null;
        this.textElement = null; 
        this.textControl = null;
        this.textContainer = null;
        this.isTextEmpty = false;
        this.CheckedChanged = new ASPxClientEvent();
        this.GotFocus = new ASPxClientEvent();
        this.LostFocus = new ASPxClientEvent();    
        this.Click = new ASPxClientEvent();
    },
    InlineInitialize: function() {
        this.InitializeEvents();
        this.InitializeEnabled();
        this.InitializeChecked();
        this.PreventButtonImageDragging();
    },
    InitializeEnabled: function(){
        this.SetEnabledInternal(this.clientEnabled, true);
    },
    InitializeChecked: function(){
        this.SetCheckedInternal(this.checked, true);
    },
    InitializeEvents: function(){
        if (!this.isNative) {
            var element = this.GetInternalButton();
            if(_aspxIsExists(element))
                element.onfocus = null;
        
	        var textControl = this.GetTextControl();
	        if (_aspxIsExists(textControl)) {
                if (__aspxIE)
                    _aspxAttachEventToElement(textControl, "onmouseup", _aspxClearSelection);
                _aspxPreventElementDragAndSelect(textControl, false);
            }	         
        }
        
        var name = this.name;
        this.onClick = function() {
            var processOnServer = aspxBClick(name);
            if (!processOnServer) {
                var evt = _aspxGetEvent(arguments[0]);
                if (evt)
                    _aspxPreventEvent(evt);
            }
            return processOnServer;
        };
        this.onGotFocus = function() { aspxBGotFocus(name); };
        this.onLostFocus = function() { aspxBLostFocus(name); };
        this.onKeyUp = function(evt) { aspxBKeyUp(evt, name); };
        this.onKeyDown = function(evt) { aspxBKeyDown(evt, name); };    
        
        if(!this.isNative) {
            this.AttachNativeHandlerToMainElement("focus", "SetFocus");
            this.AttachNativeHandlerToMainElement("click", "DoClick");
        }
    },
    PreventButtonImageDragging: function() {
        var image = this.GetButtonImage();
        if(_aspxIsExists(image)) {
            if(__aspxNS)
                image.onmousedown = function(evt) {
                    evt.cancelBubble = true;
                    return false;
                };
            else
                image.ondragstart = function() {
                    return false;
                };
        }
    },
    
    AttachNativeHandlerToMainElement: function(handlerName, correspondingMethodName) {
        var mainElement = this.GetMainElement();
        if (!_aspxIsExistsElement(mainElement))
            return;
        eval("mainElement." + handlerName + " = function() { _aspxBCallButtonMethod('" + this.name + "', '" + correspondingMethodName + "'); }");
    },
    
    GetContentDiv: function(){
        if(!_aspxIsExistsElement(this.contentDiv))
            this.contentDiv = this.GetChild("_CD");
        return this.contentDiv;
    },                         
    GetButtonCell: function(){
        if(!_aspxIsExistsElement(this.buttonCell))
            this.buttonCell = this.GetChild("_B");
        return this.buttonCell;
    },   
    GetButtonCheckedInput: function(){
        if(!_aspxIsExistsElement(this.checkedInput))
            this.checkedInput = _aspxGetElementById(this.name + "_CH");
        return this.checkedInput;
    },  
    GetButtonImage: function(){
        if(!_aspxIsExistsElement(this.buttonImage))
            this.buttonImage = _aspxGetChildByTagName(this.GetButtonCell(), "IMG", 0);
        return this.buttonImage;
    },
    GetInternalButton: function() {
        if(!_aspxIsExistsElement(this.internalButton))
            this.internalButton = this.isNative ? this.GetMainElement() : _aspxGetChildByTagName(this.GetMainElement(), "INPUT", 0);
        return this.internalButton;
    },
    GetTextContainer: function() {
        if (!_aspxIsExists(this.textContainer)) {
            var textElement = this.GetTextElement();
            this.textContainer = _aspxGetChildByTagName(textElement, "SPAN", 0);
        }
        return this.textContainer;
    },
    GetTextElement: function(){
        if(!_aspxIsExistsElement(this.textElement)){
            var contentDiv = this.GetContentDiv();
            if (this.GetButtonImage() == null) 
                this.textElement = contentDiv;
            else {
                this.textElement = _aspxGetChildByTagName(contentDiv, "TD", 0);
                var img = _aspxGetChildByTagName(this.textElement, "IMG", 0);
                if (_aspxIsExists(img))
                    this.textElement = _aspxGetChildByTagName(contentDiv, "TD", 1);
            }
        }
        return this.textElement;
    },    
    GetTextControl: function(){
        if(!_aspxIsExistsElement(this.textControl))
            this.textControl = _aspxGetParentByTagName(this.GetTextElement(), "table");
        if (!_aspxIsExistsElement(this.textControl) || (this.textControl.id == this.name))
            this.textControl = this.GetTextElement();
        return this.textControl;
    },
    
    GetPostfixes: function(){
        return this.isNative ? [""] : ["_B"];
    },
    IsHovered: function(){
        var hoverElement = this.isNative ? this.GetMainElement() : this.GetButtonCell();
        return aspxGetStateController().currentHoverItemName == hoverElement.id;
    },      
    
    SetEnabledInternal: function(enabled, initialization) {
        if(!this.enabled)
            return;
        if(!initialization || !enabled)
            this.ChangeEnabledStateItems(enabled);
        this.ChangeEnabledAttributes(enabled);
    },
    ChangeEnabledAttributes: function(enabled) {
        if(this.isNative)
            this.GetMainElement().disabled = !enabled;
        else {
            var element = this.GetInternalButton();
            if(_aspxIsExists(element))
                element.disabled = !enabled;
        }
        this.ChangeEnabledEventsAttributes(_aspxChangeEventsMethod(enabled));
    },
    ChangeEnabledEventsAttributes: function(method) {
        var element = this.GetMainElement();
        method(element, "click", this.onClick);
        if (this.allowFocus){
            if (!this.isNative) 
                element = this.GetInternalButton();
            if(_aspxIsExists(element)) {
                method(element, "focus", this.onGotFocus);
                method(element, "blur", this.onLostFocus);
                if (!this.isNative){
                    method(element, "keyup", this.onKeyUp);
                    method(element, "blur", this.onKeyUp);
                    method(element, "keydown", this.onKeyDown);
                }
            }
        }
    },
    ChangeEnabledStateItems: function(enabled){
        if(!this.isNative){
            aspxGetStateController().SetElementEnabled(this.GetButtonCell(), enabled);
            this.UpdateFocusedStyle();
        }
    },
    OnFocus: function() {
        if(!this.allowFocus)
            return false;
        this.focused = true;
        if(this.isInitialized)
            this.RaiseFocus();
        this.UpdateFocusedStyle();
    },        
    OnLostFocus: function() {
        if(!this.allowFocus)
            return false;
        this.focused = false;
        if(this.isInitialized)
            this.RaiseLostFocus();
        this.UpdateFocusedStyle();
    },
    CauseValidation: function() {
        if (this.causesValidation && _aspxIsExistsType(typeof(ASPxClientEdit)))
            return this.validationContainerID != null ?
                ASPxClientEdit.ValidateEditorsInContainerById(this.validationContainerID, this.validationGroup, this.validateInvisibleEditors) :
                ASPxClientEdit.ValidateGroup(this.validationGroup, this.validateInvisibleEditors);
        else
            return true;
    },
    OnClick: function() {
        if(this.clickLocked)
            return true;
        else if(this.checked && this.groupName != "" && this.GetCheckedGroupList().length > 1)
            return;
        this.SetFocus();
        var isValid = this.CauseValidation();
        var processOnServer = this.autoPostBack;
        // Process CheckedChanged
        if (this.groupName != "") {
            if(this.GetCheckedGroupList().length == 1)
                this.SetCheckedInternal(!this.checked, false);
            else {
                this.SetCheckedInternal(true, false);
                this.ClearButtonGroupChecked(true);
            }
            processOnServer = this.RaiseCheckedChanged();
            if (processOnServer && isValid)
                this.SendPostBack("CheckedChanged");
        }
        // Process click
        processOnServer = this.RaiseClick();
        if (processOnServer && isValid){
            this.SendPostBack("Click");
            return true;
        }
        return false;
    },
    OnKeyUp: function(evt) {
       if(this.pressed)
            this.SetUnpressed();
    },
    OnKeyDown: function(evt) {
       if(evt.keyCode == ASPxKey.Enter || evt.keyCode == ASPxKey.Space)
           this.SetPressed();
    },  

    GetChecked: function(){
        return this.groupName != "" ? this.GetButtonCheckedInput().value == "1" : false;
    },  
    GetCheckedGroupList: function(){
        var collection = aspxGetControlCollection();    
        var result = new Array();
        for(var name in collection.elements) {
            var element = collection.elements[name];
            if (element != null && ASPxIdent.IsASPxClientButton(element) &&
                (element.groupName == this.groupName))
                _aspxArrayPush(result, element);
        }
        return result;
    },
    ClearButtonGroupChecked: function(raiseCheckedChanged){
        var list = this.GetCheckedGroupList();
        for(var i = 0; i < list.length; i ++){
            if(list[i] != this && list[i].checked) {
                list[i].SetCheckedInternal(false, false);
                if(raiseCheckedChanged)
                    list[i].RaiseCheckedChanged();
            }
        }
    },
    ApplyCheckedStyle: function(){
        var stateController = aspxGetStateController();
        if(this.IsHovered()) 
            stateController.SetCurrentHoverElement(null);        
        stateController.SelectElementBySrcElement(this.GetButtonCell());
    }, 
    ApplyUncheckedStyle: function(){
        var stateController = aspxGetStateController();
        if(this.IsHovered()) 
            stateController.SetCurrentHoverElement(null);
        stateController.DeselectElementBySrcElement(this.GetButtonCell());
    },     
    SetCheckedInternal: function(checked, initialization){
        if(initialization && checked || (this.checked != checked)){
            this.checked = checked;
            var inputElement = this.GetButtonCheckedInput();
            if(_aspxIsExists(inputElement)) inputElement.value = checked ? "1" : "0";                                    
            if(checked)
                this.ApplyCheckedStyle();
            else
                this.ApplyUncheckedStyle();
        }
    },
    
    ApplyPressedStyle: function(){
        aspxGetStateController().OnMouseDownOnElement(this.GetButtonCell());
    },
    ApplyUnpressedStyle: function(){    
        aspxGetStateController().OnMouseUpOnElement(this.GetButtonCell());
    },
    SetPressed: function(){
        this.pressed = true;
        this.ApplyPressedStyle();
    }, 
    SetUnpressed: function(){
        this.pressed = false;
        this.ApplyUnpressedStyle();
    },
    
    SetFocus: function(){
        if(!this.allowFocus || this.focused)
            return;
        var element = this.GetInternalButton();
        if(_aspxIsExists(element)) {
            if(__aspxSafariFamily) //Q39454
                ASPxClientButton.MakeHiddenElementFocusable(element);
            if(_aspxIsFocusable(element) && _aspxGetActiveElement() != element){
                element.focus();
            }
            if(__aspxSafariFamily) //Q39454
                ASPxClientButton.RestoreHiddenElementAppearance(element);
        }
    },
    ApplyFocusedStyle: function(){
        if(this.focusElementSelected) return;
        aspxGetStateController().SelectElementBySrcElement(this.GetContentDiv());
        this.focusElementSelected = true;
    },
    ApplyUnfocusedStyle: function(){    
        if(!this.focusElementSelected) return;
        aspxGetStateController().DeselectElementBySrcElement(this.GetContentDiv());
        this.focusElementSelected = false;
    },
    UpdateFocusedStyle: function(){
        if(this.isNative)
            return;
        if(this.enabled && this.clientEnabled && this.allowFocus && this.focused)
            this.ApplyFocusedStyle();
        else
            this.ApplyUnfocusedStyle();
    },
    
    SendPostBack: function(postBackArg){
        if(!this.enabled || !this.clientEnabled)
            return;
        
        var arg = _aspxIsExists(postBackArg) ? postBackArg : "";
        if(_aspxIsExists(this.autoPostBackFunction))
            this.autoPostBackFunction(arg);
        else if(!this.useSubmitBehavior)
            ASPxClientControl.prototype.SendPostBack.call(this, arg);
        
        if(this.useSubmitBehavior && !this.isNative)
            this.ClickInternalButton();
    },
    ClickInternalButton: function(){
        var element = this.GetInternalButton();
        if(_aspxIsExists(element)) {
            this.clickLocked = true;
            if (__aspxNS)
                this.CreateUniqueIDCarrier(); // B93118
            element.click();
            if (__aspxNS)
                this.RemoveUniqueIDCarrier(); // B93118
            this.clickLocked = false;
        }
    },
    CreateUniqueIDCarrier: function() {
        var name = this.uniqueID;
        var id = this.GetUniqueIDCarrierID();
        var field = _aspxCreateHiddenField(name, id);
        var form = _aspxGetServerForm();
        form.appendChild(field);
    },
    RemoveUniqueIDCarrier: function() {
        var field = document.getElementById(this.GetUniqueIDCarrierID());
        if (_aspxIsExists(field))
            field.parentNode.removeChild(field);
    },
    GetUniqueIDCarrierID: function() {
        return this.uniqueID + "_UIDC";
    },
    
    // API
    DoClick: function(){
        if(!this.enabled || !this.clientEnabled)
            return;
        var button = this.isNative ? this.GetMainElement() : this.GetInternalButton();
        if(_aspxIsExists(button))
            button.click();
        else 
            this.OnClick();   
    }, 
    GetChecked: function(){
        return this.checked;
    }, 
    SetChecked: function(checked){
        this.SetCheckedInternal(checked, false);
        this.ClearButtonGroupChecked(false);
    },
    GetText: function(){
        if (this.isTextEmpty)
            return "";
        else
            return this.isNative ? this.GetMainElement().value : this.GetTextContainer().innerHTML;
    },
    SetText: function(text){
        this.isTextEmpty = (text == null || text == "");
        if (this.isNative)
            this.GetMainElement().value = (text != null) ? text : "";
        else {
            var textContainer = this.GetTextContainer();
            textContainer.innerHTML = this.isTextEmpty ? "&nbsp;" : text;
        }
    }, 
    SetEnabled: function(enabled){
        if (this.clientEnabled != enabled) {
            if (!enabled && this.focused)
                this.OnLostFocus();
            this.clientEnabled = enabled;
            this.SetEnabledInternal(enabled, false);
        }
    }, 
    GetEnabled: function(){
        return this.enabled && this.clientEnabled;
    },
    Focus: function(){
        this.SetFocus();
    },

    RaiseCheckedChanged: function(){
        var processOnServer = this.autoPostBack || this.IsServerEventAssigned("CheckedChanged");
        if(!this.CheckedChanged.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.CheckedChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    RaiseFocus: function(){
        if(!this.GotFocus.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.GotFocus.FireEvent(this, args);
        }
    },
    RaiseLostFocus: function(){
        if(!this.LostFocus.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.LostFocus.FireEvent(this, args);
        }
    },
    RaiseClick: function(){
        var processOnServer = this.autoPostBack || this.IsServerEventAssigned("Click");
        if(!this.Click.IsEmpty()){
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.Click.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    }
});

ASPxClientButton.MakeHiddenElementFocusable = function(element) {
        element.__dxHiddenElementState = {
            parentDisplay: element.parentNode.style.display,
            height: element.style.height,
            width: element.style.width
        };
        element.parentNode.style.display = "block";
        element.style.height = "1px";
        element.style.width = "1px";
};

ASPxClientButton.RestoreHiddenElementAppearance = function(element) {
    var state = element.__dxHiddenElementState;
    
    element.parentNode.style.display = state.parentDisplay;
    element.style.height = state.height;
    element.style.width = state.width;
    
    delete element.__dxHiddenElementState;
};

ASPxIdent.IsASPxClientButton = function(obj) {
    return _aspxIsExists(obj.isASPxClientButton) && obj.isASPxClientButton;
};

function _aspxBCallButtonMethod(name, methodName) {
    var button = aspxGetControlCollection().Get(name); 
    if (button != null)
        eval("button." + methodName + "()");
}

function aspxBGotFocus(name){
    var button = aspxGetControlCollection().Get(name); 
    if(button != null)
        return button.OnFocus();
}
function aspxBLostFocus(name){
    var button = aspxGetControlCollection().Get(name);
    if(button != null) 
        return button.OnLostFocus();
}
function aspxBClick(name){
    var button = aspxGetControlCollection().Get(name); 
    if(button != null)
        return button.OnClick();
}
function aspxBKeyDown(evt,name){
    var button = aspxGetControlCollection().Get(name); 
    if(button != null)
        button.OnKeyDown(evt);
}
function aspxBKeyUp(evt,name){
    var button = aspxGetControlCollection().Get(name); 
    if(button != null)
        button.OnKeyUp(evt);
}

var __aspxSpindButtonIdPostfix = "_B";

var __aspxNumberDecimalSeparator = ",";
var __aspxPossibleNumberDecimalSeparators = [",", "."];

var __aspxNumberNegativeSymbol = "-";

__aspxSERepeatBtnMinIntervalDelay = 5;
__aspxSERepeatBtnMaxIntervalDelay = 300;
ASPxClientSpinEdit = _aspxCreateClass(ASPxClientButtonEditBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.NumberChanged = new ASPxClientEvent();
        
        this.largeDecButtonIndex = -1;
        this.incButtonIndex = -2;
        this.decButtonIndex = -3;
        this.largeIncButtonIndex = -4;
        
        this.decimalPlaces = 0;
        this.number = 0;        
        this.inc = 1;
        this.largeInc = 10;
        this.minValue = 0;
        this.maxValue = 0;        
        
        this.allowMouseWheel = true;
        this.allowNull = true;
        this.numberType = "f";
        this.maxLength = 0;
        
        this.valueChangedDelay = 0;
        this.valueChangedTimerID = -1;
        this.repeatButtonTimerID = -1;
        
        this.buttonsHeightCorrected = false;
        
        this.inputValueBeforeFocus = null;
        this.valueChangedProcsCalledBeforeLostFocus = false;
        
        // for paste operations        
        this.pasteTimerID = -1;
        this.UpdateLastCorrectValueString();
        this.keyUpProcessing = false;
        this.isChangingCheckProcessed = false;
        
        // Height correction
        this.loadedSpinButtonImageCount = 0;
        this.spinButtonImageCount = 0;
               
        aspxGetSpinEditCollection().Add(this);
    },
    Initialize: function() {
        this.AssignClientAttributes();
        ASPxClientEdit.prototype.Initialize.call(this);
        this.GenerateValidationRegExp();
    },
    AssignClientAttributes: function() {
        var buttons = [ this.GetIncrementButton(), this.GetDecrementButton(), this.GetLargeIncrementButton(), this.GetLargeDecrementButton() ];
        for (var i = 0; i < buttons.length; this.AssignClientAttributesTo(buttons[i++]));
    },
    AssignClientAttributesTo: function(button) {
        if(_aspxIsExistsElement(button)) {
            _aspxPreventElementDragAndSelect(button, true);
            button.needClearSelection = true;
            if (!__aspxNS)
                button.unselectable = "on";
        }
    },
    GenerateValidationRegExp: function() {
        var decimalSeparator = "";
        var allowDecimalSeparatorSymbols = "";
        
        if (this.IsFloatNumber()) {
            decimalSeparator = this.GetDecimalSeparatorRegExpString(__aspxNumberDecimalSeparator);
            for (var i = 0; i < __aspxPossibleNumberDecimalSeparators.length; i++)
                allowDecimalSeparatorSymbols += this.GetDecimalSeparatorRegExpString(__aspxPossibleNumberDecimalSeparators[i]);
        }

        this.allowSymbolRegExp = new RegExp("[-0-9" + allowDecimalSeparatorSymbols + "]");
        
        this.validNumberRegExp = new RegExp("^[-]?[0-9]+[" + decimalSeparator + "]?[0-9]*([0-9]+)?$");
        
        var validFormattedNumberRegExpString = "^[-]?[0-9]+[" + decimalSeparator + "]?[0-9]";
        if (this.decimalPlaces != 0)
            validFormattedNumberRegExpString += "{0," + this.decimalPlaces + "}$";
        else
            validFormattedNumberRegExpString += "*([0-9]+)?$";
            
        this.validFormattedNumberRegExp = new RegExp(validFormattedNumberRegExpString);
    },
    GetDecimalSeparatorRegExpString: function(decimalSeparator) {
        return decimalSeparator == "." ? "\\." : decimalSeparator;    
    },
    GetIncrementButton: function() {
        return this.GetButton(this.incButtonIndex);
    },
    GetDecrementButton: function() {
        return this.GetButton(this.decButtonIndex);
    },
    GetLargeIncrementButton: function() {
        return this.GetButton(this.largeIncButtonIndex);
    },
    GetLargeDecrementButton: function() {
        return this.GetButton(this.largeDecButtonIndex);
    },
    GetSpinButtonCell: function() {
        return this.GetChild("_SPBtnCell");
    },
    GetMainTableCellSpacing: function() {
        var cell = this.GetSpinButtonCell();
        var tableElem = _aspxGetParentByTagName(cell, "table");
        return __aspxNS ? tableElem.attributes["cellspacing"].value : tableElem.cellSpacing;
    },

    // size correction
    CalcMainCellHeight: function() {
        var ret = 0;
        var cell = this.GetSpinButtonCell();
        if (_aspxIsExists(cell)) {
            ret = __aspxSafariFamily ? cell.offsetHeight : cell.clientHeight;
            var cellSpacing = this.GetMainTableCellSpacing();
            if (__aspxOpera) {
                if (cellSpacing == 0) // spacing
                    ret = ret - 1;
            }
        }
        return ret;
    },    
    
    AdjustControlCore: function() {
        ASPxClientEdit.prototype.AdjustControlCore.call(this);
        this.CorrectButtonsHeight();
    },
    CorrectButtonsHeight: function() {
        var cell = this.GetSpinButtonCell();
        if (_aspxIsExists(cell)) {            
            var incButton = this.GetIncrementButton();
            var decButton = this.GetDecrementButton();
            if (_aspxIsExists(incButton) && _aspxIsExists(decButton)) {
            
                var incImg = _aspxGetElementsByTagName(incButton, "img")[0];
                var decImg = _aspxGetElementsByTagName(decButton, "img")[0];
                
                if (__aspxOpera) {
                    if (_aspxIsExists(decImg.isLoaded) || _aspxIsExists(incImg.isLoaded))
                        this.CorrectSpinButtonsHeightWithImage();
                    else {
                        if (_aspxIsExists(incImg) && !_aspxIsExists(incImg.isLoaded)) {
                            this.spinButtonImageCount ++;
                            _aspxAttachEventToElement(incImg, "load", _aspxCreateEventHandlerFunction("aspxSpinImageOnLoad", this.name, false));
                        }                    
                        if (_aspxIsExists(decImg) && !_aspxIsExists(decImg.isLoaded)) {
                            this.spinButtonImageCount ++;
                            _aspxAttachEventToElement(decImg, "load", _aspxCreateEventHandlerFunction("aspxSpinImageOnLoad", this.name, false));
                        }
                    }
                }
                else
                    this.CorrectSpinButtonsHeightWithImage();
            }
            else {
                var largeIncButton = this.GetLargeIncrementButton();
                var largeDecButton = this.GetLargeDecrementButton();
                if (_aspxIsExists(largeIncButton) && _aspxIsExists(largeDecButton)) {
                    var h = largeIncButton.offsetHeight;
                    var mainCellHeight = this.CalcMainCellHeight();
                    if (__aspxOpera || __aspxSafariFamily ||(__aspxIE && (h < mainCellHeight)))
                        this.SetActualElementHeigth(_aspxGetChildByTagName(cell, "table", 0), mainCellHeight);
                }
            }
        }
        this.buttonsHeightCorrected = true;
    },    
    CorrectSpinButtonsHeightWithImage: function() {
        var cell = this.GetSpinButtonCell();
        var mainCellHeight = this.CalcMainCellHeight();
        
        var incButton = this.GetIncrementButton();
        var decButton = this.GetDecrementButton();                        
        
        var h = incButton.offsetHeight + decButton.offsetHeight;
        if (__aspxOpera || __aspxSafariFamily || (__aspxIE && (h < mainCellHeight)))
            this.SetActualElementHeigth(_aspxGetChildByTagName(cell, "table", 0), mainCellHeight);
        if(!__aspxNS)
            this.CorrectIncrementButtonsHeight();
        
        var cell = this.GetSpinButtonCell();
        var cellSpacing = 1;
        if (_aspxIsExists(cell))
            cellSpacing = this.GetMainTableCellSpacing();
        
        if ((cellSpacing == 0) && _aspxIsExists(_aspxGetElementsByTagName(incButton, "img"))) {
            var incImg = _aspxGetElementsByTagName(incButton, "img")[0];                    
            incImg.style.marginTop="1px"; // if cellspacing == 0 then Hack 
        }
    },    
    CorrectIncrementButtonsHeight: function() {
        var incButton = this.GetIncrementButton();
        var decButton = this.GetDecrementButton();
        
        if(_aspxIsExistsElement(incButton) && _aspxIsExistsElement(decButton) && !__aspxIE && !__aspxSafariFamily) {
            var incBtnHeight = incButton.clientHeight;
            var dcBtnHeight = decButton.clientHeight;
            var absDif = Math.abs(incBtnHeight - dcBtnHeight);
            if (absDif > 1) {
                var difHeight = Math.floor(absDif/2);
                if (incBtnHeight > dcBtnHeight) {
                    incBtnHeight -= difHeight;
                    dcBtnHeight += difHeight;
                }
                else {
                    dcBtnHeight -= difHeight;
                    incBtnHeight += difHeight;
                }
            }
            if (__aspxOpera) {
                var d = decButton.offsetHeight - incButton.offsetHeight;
                dcBtnHeight = dcBtnHeight - d;
                incBtnHeight = incBtnHeight + d;
            }
            this.SetElementHeigth(incButton, incBtnHeight);
            this.SetElementHeigth(decButton, dcBtnHeight);
        }
    },
    
    
    ChangeNumber: function(offset) {
        if (!this.readOnly) {
            // Hack: 3.12 + 10 = 13.1200000000000001 - bug is javascript
            var precisionNum = this.GetToPrecisionNumber(this.number + offset);
            var newNumber = this.GetValidNumber(this.ParseNumber((this.number + offset).toPrecision(precisionNum), "Decimal"), this.number);
            if(newNumber != this.number) {
                this.SetNumberInternal(newNumber);                
                this.OnValueChanged();
            }
            this.SelectInputElement();
        }
    },    
    GetToPrecisionNumber: function(number) {
        var ret = 21; // max number
        var numString = number.toPrecision(21);
        var integerDigitsCount = numString.indexOf(__aspxPossibleNumberDecimalSeparators[0]);
        
        if (integerDigitsCount < 0)
            integerDigitsCount = numString.indexOf(__aspxPossibleNumberDecimalSeparators[1]);

        if (integerDigitsCount > 0)
            // 14 was fitted. Elsewise, the testChangeNumberInternal isn't successful
            ret = integerDigitsCount + 14;
        return Math.min(ret, 21);
    },
    ProcessInternalButtonClick: function(number) {
        var ret = false;
        this.ParseValueAfterPaste();
        if(this.largeDecButtonIndex == number) {
          this.ChangeNumber(-this.largeInc);
          ret = true;
        } else if(this.incButtonIndex == number) {
            this.ChangeNumber(this.inc);
            ret = true;
        } else if(this.decButtonIndex == number) {
            this.ChangeNumber(-this.inc);
            ret = true;
        } else if(this.largeIncButtonIndex == number) {
            this.ChangeNumber(this.largeInc);
            ret = true;
        }
        return ret;
    },
    GetButtonNumber: function(id) {
        var pos = id.lastIndexOf(__aspxSpindButtonIdPostfix);
        if(pos > -1)
            return id.substring(pos + __aspxSpindButtonIdPostfix.length, id.length);
        return null;
    },    
    GetCorrectNumberInRange: function(number) {    
        if(this.UseRestrictions() && number > this.maxValue)
            number = this.maxValue;
        if(this.UseRestrictions() && number < this.minValue)
            number = this.minValue;    
        return number;
    },    
    GetValidNumber: function(number, oldNumber) {
        var validNumber = 0;
        if(this.UseRestrictions() && number < this.minValue && (this.number == null || number > this.number))
            validNumber = this.minValue;
        else if(this.UseRestrictions() && number > this.maxValue && (this.number == null || number < this.number))
            validNumber = this.maxValue;
        else if((!this.UseRestrictions() || number <= this.maxValue) && 
            (!this.UseRestrictions() || number >= this.minValue))
            validNumber = number;
        else
            validNumber = this.number;
        if(!this.IsFloatNumber())
            validNumber = Math.round(validNumber);
        if ((this.maxLength > 0) && (validNumber.toString().length > this.maxLength))
            validNumber = oldNumber;
        
        return validNumber;
    },
    GetValueType: function() {
        return this.IsFloatNumber() ? "Decimal" : "Int"; 
    },
    GetFormattedNumber: function(number) {
        if (number == null)
            return "";
        
        var value = String(number);
        if(__aspxNumberDecimalSeparator != ".") {
            if(value.indexOf(".") != -1)
                value = value.replace(".", __aspxNumberDecimalSeparator);
        }
        value = this.GetCorrectFormattedNumberString(value);
        return value;
    },
    GetNextTimerInterval: function(iterationIndex) {
        var coef = 1 / iterationIndex;
        if (coef < 0.13)
            coef = 0.13;
        return coef * __aspxSERepeatBtnMaxIntervalDelay;
    },
    IsFloatNumber: function() {
        return this.numberType == "f";
    },            
    DoRepeatButtonClick: function(num, iterationIndex) {
        this.ProcessInternalButtonClick(num);

        var timerInterval = this.GetNextTimerInterval(iterationIndex);
        
        if (iterationIndex < 50)
            iterationIndex++;            
            
        this.SetButtonRepeatClickTimer(num, timerInterval, iterationIndex);
    },
    ParseValue: function() {
        var inputElement = this.GetInputElement();
        if(_aspxIsExistsElement(inputElement)) {
            var valueString = inputElement.value;            
            
            var newNumber = (valueString != "") ? this.ParseValueInternal(valueString) : null;
            if((newNumber != null) && !isNaN(newNumber)){
                if (newNumber != this.number) {
                    newNumber = this.GetCorrectNumberInRange(newNumber);                
                    var isEqual = newNumber == this.number;
                    this.SetNumberInternal(newNumber);
                    if (!isEqual)
                        this.OnValueChanged();
                }
            }
            else {
                if (this.allowNull)
                    this.SetNumberInternal(null);
                else
                    this.SetNumberInternal(this.GetCorrectNumberInRange(0));                    
                this.OnValueChanged();
            }
        }
    },
    ParseValueAfterPaste: function() {
        if (this.isChangingCheckProcessed) {
            this.ParseValue();
            this.isChangingCheckProcessed = false;
        }
    },    
    ParseValueOnPaste: function() {
        var inputElement = this.GetInputElement();
        if(_aspxIsExistsElement(inputElement)) {
            var valueString = inputElement.value;            
            
            if (valueString != "") {
                if (!this.IsValidNumberString(valueString)) {
                    valueString = this.lastValue;
                    inputElement.value = this.lastValue;
                }
                else
                    this.lastValue = valueString;
            }
            
            var newNumber = (valueString != "") ? this.ParseValueInternal(valueString) : null;
            if (newNumber != null)
                this.SetFormattedNumberInInput(newNumber);
        }
    },
    ParseValueInternal: function(value) {
        if(value == null || value.toString() == "")
            return null;
            
        // correct DecimalSeparator in number string
        if(__aspxNumberDecimalSeparator != ".") {
            if(value.indexOf(__aspxNumberDecimalSeparator) != -1)
                value = value.replace(__aspxNumberDecimalSeparator, ".");
        }
        if(typeof(value) == "number")
            return value;
        return this.ParseNumber(value.toString(), this.GetValueType());
    },
    ParseNumber: function(value, type) {
        if(type == "Decimal")
            return parseFloat(value, 10);
        return parseInt(value, 10);
    },
    RaiseValueChangedEvent: function() {
        return this.OnNumberChanged();
    },    
    SetNumberInternal: function(value) {
        this.number = value;
        this.SetFormattedNumberInInput(this.number);
    },
    SetFormattedNumberInInput: function(number) {
        var inputElement = this.GetInputElement();
        if(inputElement != null) {
            this.UpdateSelectionStartAndEndPosition(inputElement); // for paste
            var formattedNumber = this.GetFormattedNumber(number);
            if (formattedNumber.toString() != inputElement.value)
                inputElement.value = formattedNumber;
            _aspxSetSelectionCore(inputElement, inputElement.selectionStart, inputElement.selectionEnd); // for paste
            this.UpdateLastCorrectValueString();
        }
    },
    UseRestrictions: function() {
        return (this.maxValue != 0 || this.minValue != 0);
    },
    UpdateLastCorrectValueString: function() {
        this.lastValue = this.GetInputElement().value;
    },    
    GetValue: function() {
        var input = this.GetInputElement();
        if (!_aspxIsExistsElement(input))
            return null;
        if (this.allowNull && input.value == "")
            return null;
        else if (!this.allowNull && this.number == null)
            return this.GetCorrectNumberInRange(0);
        else
            return this.number;
    },    
    SetValue: function(number) {
        this.number = number;
        // Update
        ASPxClientButtonEditBase.prototype.SetValue.call(this, this.GetFormattedNumber(this.number));
    },
    ChangeEnabledAttributes: function(enabled){
        ASPxClientButtonEditBase.prototype.ChangeEnabledAttributes.call(this, enabled);
        var btnElement = this.GetIncrementButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        btnElement = this.GetDecrementButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        btnElement = this.GetLargeIncrementButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
        btnElement = this.GetLargeDecrementButton();
        if(_aspxIsExists(btnElement))
            this.ChangeButtonEnabledAttributes(btnElement, _aspxChangeAttributesMethod(enabled));
    },
    ChangeEnabledStateItems: function(enabled){
        this.ClearButtonRepeatClickTimer();

        ASPxClientButtonEditBase.prototype.ChangeEnabledStateItems.call(this, enabled);
        var btnElement = this.GetIncrementButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
        btnElement = this.GetDecrementButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
        btnElement = this.GetLargeIncrementButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
        btnElement = this.GetLargeDecrementButton();
        if(_aspxIsExists(btnElement))
            aspxGetStateController().SetElementEnabled(btnElement, enabled);
    },
        
    // Event handlers    
    OnAfterSetPressed: function(id) {
        var num = this.GetButtonNumber(id);        
        if (num != null)
            this.SetButtonRepeatClickTimer(num, 300, 1);
    },
    OnAfterClearPressed: function(id) {
        this.ClearButtonRepeatClickTimer();
    },
    OnButtonMouseDown: function(evt) { // for NSFamilly browsers hack
        if(!__aspxNS)
            this.LockFocusEvents();
        if(__aspxNS)
            evt.preventDefault();
    },
    
    OnKeyPress: function(evt) {
        ASPxClientTextEdit.prototype.OnKeyPress.call(this, evt);
        var inputElement = this.GetInputElement();
        if (!_aspxIsExists(inputElement)) {
            if (this.pasteTimerID != -1)
                this.ClearTextChangingTimer();
            return;
        }
        
        this.keyUpProcessing = true;
        if (!__aspxIE && this.IsPasteShortcut(evt))
            this.keyUpProcessing = false;
            
        if (evt.altKey || evt.ctrlKey)
            return true;

        var keyCode = _aspxGetKeyCode(evt);
        if (this.IsSpecialKey(evt, false)) {
            this.keyUpProcessing = true;
            return true;
        }
        
        this.UpdateSelectionStartAndEndPosition(inputElement);
        var selectionStart = inputElement.selectionStart;
        var selectionEnd = inputElement.selectionEnd;

        var pressed = String.fromCharCode(keyCode);
        if (!this.IsAllowableSymbol(pressed)) {
            this.keyUpProcessing = false;
            return _aspxPreventEvent(evt); // false
        }

        if (this.IsSignSymbol(pressed)) {
            var isAllowTypeNumberSignSymbol = this.IsAllowTypeNumberSignSymbol(selectionStart, selectionEnd);
            this.keyUpProcessing = isAllowTypeNumberSignSymbol;
            return isAllowTypeNumberSignSymbol ? true : _aspxPreventEvent(evt);  // false
        }
        if (this.IsDecimalSeparatorSymbol(pressed)) {
            var isAllowTypeDecimalSeparator = this.IsAllowTypeDecimalSeparatorSymbol(selectionStart, selectionEnd);
            if (isAllowTypeDecimalSeparator)
                this.TypeDecimalSeparator(selectionStart, selectionEnd);

            this.keyUpProcessing = isAllowTypeDecimalSeparator;
            return _aspxPreventEvent(evt);
        }
        // Digit symbol
        if (!this.IsAllowTypeDigitToCurrentPosition(selectionStart, selectionEnd, pressed)) {
            this.keyUpProcessing = false;
            return _aspxPreventEvent(evt); // false
        }
        return true;
    },
    OnKeyUp: function(evt) {
        ASPxClientTextEdit.prototype.OnKeyUp.call(this, evt);
                
        if (this.keyUpProcessing) {
            this.UpdateLastCorrectValueString();
            this.keyUpProcessing = false;
        }
        if (this.IsPageOrArrowKey(evt))
            this.OnPageOrArrowKeyUp();
    },    
    OnKeyDown: function(evt) {
        if(evt.keyCode == ASPxKey.Enter)
            this.OnTextChanged();
            
        ASPxClientTextEdit.prototype.OnKeyDown.call(this, evt);
        
        if (this.IsPageOrArrowKey(evt))
            this.OnPageOrArrowKeyDown(evt);
        
        // If special key is pressed, event OnKeyPress isnt' fired
        if ((__aspxIE) && this.IsSpecialKey(evt, true)) 
            this.keyUpProcessing = true;
    },
    OnPageOrArrowKeyDown: function(evt) {
        var btnIndex = this.GetButtonIndexByKeyCode(_aspxGetKeyCode(evt), evt.ctrlKey);
        if (__aspxOpera)
            this.SetButtonRepeatClickTimer(btnIndex, 60, 1);                
        else {
            this.ProcessInternalButtonClick(btnIndex);
            _aspxPreventEvent(evt);
        }
    },
    OnPageOrArrowKeyUp: function(evt) {
        if (__aspxOpera)
            this.ClearButtonRepeatClickTimer();
    },        
    OnFocus: function() {
        if (__aspxIE || __aspxFirefox2)
            this.SaveIEOnValueChangedEmulationData();
        ASPxClientEdit.prototype.OnFocus.call(this);
        this.SetTextChangingTimer();
    },
    OnLostFocus: function() {
        //fix B131458
        if (__aspxFirefox2)
            this.EmulateIEOnValueChanged();
        
        ASPxClientEdit.prototype.OnLostFocus.call(this);
        this.ClearTextChangingTimer();
        if (__aspxIE)
            this.EmulateIEOnValueChanged();
    },
    OnNumberChanged: function() {
        var processOnServer = ASPxClientEdit.prototype.RaiseValueChangedEvent.call(this);
        processOnServer = this.RaiseNumberChanged(processOnServer);
            
        if (this.focused)
            this.valueChangedProcsCalledBeforeLostFocus = true;
            
        return processOnServer;
    },
    OnValueChanged: function() {
        if(this.valueChangedDelay == 0)
            this.OnValueChangedTimer();
        else{
            if(this.valueChangedTimerID > -1){
                window.clearTimeout(this.valueChangedTimerID);
                this.valueChangedTimerID = -1;
            }
            this.valueChangedTimerID = window.setTimeout("aspxSEValueChanged(\"" + this.name + "\")", this.valueChangedDelay);
        }
    },
    OnValueChangedTimer: function() {
        if(this.valueChangedTimerID > -1){
            window.clearTimeout(this.valueChangedTimerID);
            this.valueChangedTimerID = -1;
        }

        this.valueChangedCalledBeforeLostFocus = true;
        this.RaisePersonalStandardValidation();    
        ASPxClientEdit.prototype.OnValueChanged.call(this);
    },
    OnMouseOver: function(evt) {
        if (_aspxGetIsLeftButtonPressed(evt))
            this.OnTextChangingCheck();
    },
    OnMouseWheel: function(evt) {
        if (!this.allowMouseWheel)
            return;
            
        var wheelDelta = _aspxGetWheelDelta(evt);
        if(wheelDelta > 0)
            this.ChangeNumber(this.inc);
        else  if(wheelDelta < 0)
            this.ChangeNumber(-this.inc);    
        _aspxPreventEvent(evt);
    },
    OnTextChangingCheck: function(evt) {
        var input = this.GetInputElement();
        if (!_aspxIsExists(input))
            return;
        var curValueString = input.value.toString();
        this.isChangingCheckProcessed = true;
        if ((this.lastValue != curValueString) && !this.keyUpProcessing)
            this.OnPaste();
    },
    OnPaste: function() {
        this.ParseValueOnPaste();
    },
    OnTextChanged: function() {
        this.ParseValue();
    },
    OnSpinButtonImageLoad: function() {
        this.loadedSpinButtonImageCount ++;
        if (this.loadedSpinButtonImageCount == this.spinButtonImageCount)
            this.CorrectSpinButtonsHeightWithImage();
    },
    
    // IE ValueChanged emulation
    SaveIEOnValueChangedEmulationData: function() {
        this.valueChangedCalledBeforeLostFocus = false;
        this.valueChangedProcsCalledBeforeLostFocus = false;
        var input = this.GetInputElement();
        if (_aspxIsExistsElement(input))
            this.inputValueBeforeFocus = input.value;
    },
    EmulateIEOnValueChanged: function() {
        if (!this.valueChangedProcsCalledBeforeLostFocus) {
            var input = this.GetInputElement();
            if (_aspxIsExistsElement(input) && input.value != this.inputValueBeforeFocus) {
                this.OnTextChanged();
                this.RaiseValidationInternal();
                this.RaisePersonalStandardValidation();
            }
        }
    },
    
    // Timer utils
    SetTextChangingTimer: function() {
        var str = "aspxSETextChangingCheck(\"" + this.name + "\")";
        this.pasteTimerID = _aspxSetInterval(str, __aspxPasteCheckInterval);
    },
    ClearTextChangingTimer: function() {
        this.pasteTimerID = _aspxClearInterval(this.pasteTimerID);
    },    
    SetButtonRepeatClickTimer: function(num, timerInterval, iterationIndex) {
        var str = "aspxSRepeatButtonClick(\"" + this.name + "\"," + 
                    num.toString()+ "," + iterationIndex.toString() + ")";
        this.repeatButtonTimerID = _aspxSetTimeout(str, timerInterval);
    },
    ClearButtonRepeatClickTimer: function() {
        this.repeatButtonTimerID = _aspxClearTimer(this.repeatButtonTimerID);
    },
    
    // Input correction utils
    IsAllowableSymbol: function(symbol) {
        return this.allowSymbolRegExp.test(symbol);
    },    
    IsAllowTypeNumberSignSymbol: function(selectionStart, selectionEnd) {    
        var curValueString = this.GetInputElement().value.toString();
        if ((curValueString != null) && this.IsSignSymbol(curValueString.charAt(0)))
            return (selectionStart == 0) &&  (selectionEnd > 0);
        else
            return (selectionStart == 0);
    },
    IsAllowTypeDecimalSeparatorSymbol: function(selectionStart, selectionEnd) {
        var curValueString = this.GetInputElement().value.toString();        
        var decimalSepIndex = curValueString.indexOf(__aspxNumberDecimalSeparator);
        if ((this.decimalPlaces != 0) && (decimalSepIndex == -1)) {
            var possibleValueString = this.GetPossibleValueString(selectionStart, selectionEnd, __aspxNumberDecimalSeparator);
               return this.IsValidFormattedNumberString(possibleValueString);
        }
        return decimalSepIndex == -1;
    },
    IsAllowTypeDigitToCurrentPosition: function(selectionStart, selectionEnd, pressedSymbol) {
        var possibleValueString = this.GetPossibleValueString(selectionStart, selectionEnd, pressedSymbol);
        var decimalSepIndex = possibleValueString.indexOf(__aspxNumberDecimalSeparator);
        
        if ((this.decimalPlaces != 0) && (decimalSepIndex != -1))
            return this.IsValidFormattedNumberString(possibleValueString);
            
        return true;
    },
    IsValidNumberString: function(numString) {
        return this.validNumberRegExp.test(numString);
    },
    IsValidFormattedNumberString: function(numString) {
        return this.validFormattedNumberRegExp.test(numString);
    },
    GetCorrectFormattedNumberString: function(numString) {
        var ret = numString;
        if (!this.IsValidFormattedNumberString(numString)) {
            // number with exponent
            if (numString.toLowerCase().indexOf("e") > -1)
                numString = ASPxClientSpinEdit.RemoveExponentialNotation(numString);
                
            var decimalSepIndex = numString.indexOf(__aspxNumberDecimalSeparator);
            if (decimalSepIndex > -1) {
                ret = numString.substring(0, decimalSepIndex);
                if (this.IsFloatNumber())
                    ret += numString.substr(decimalSepIndex, this.decimalPlaces + 1);                            
            }
            else
                ret = numString;
                
        }
        return ret;
    },
    
    GetPossibleValueString: function(selectionStart, selectionEnd, pressedSymbol) {        
        var curValueString = this.GetInputElement().value.toString();
        
        var newValueString = curValueString.substring(0, selectionStart);
        newValueString += pressedSymbol;
        
        var selectionLength = selectionEnd - selectionStart;
        newValueString += curValueString.substr(selectionEnd, curValueString.length - selectionLength);
        
        return newValueString;
    },
    IsDecimalSeparatorSymbol: function(symbol) {
        for (var i = 0 ; i < __aspxPossibleNumberDecimalSeparators.length; i++)
            if (__aspxPossibleNumberDecimalSeparators[i] == symbol)
                return true;
        return false;
    },
    IsPasteShortcut: function(evt) {
        var keyCode = _aspxGetKeyCode(evt);
        return (evt.ctrlKey && (keyCode == 118 /*v*/ || (keyCode == 86)/*V*/)) || 
               (evt.shiftKey && !evt.ctrlKey && !evt.altKey && 
                (keyCode == ASPxKey.Insert));
    },
    IsValidMinMaxValue: function(minValue, maxValue) {        
        if (typeof(maxValue) != "number")
            maxValue = this.ParseValueInternal(maxValue.toString());
        if (typeof(minValue) != "number")
            minValue = this.ParseValueInternal(minValue.toString());
        return (isNaN(maxValue) || isNaN(minValue)) ? false : (maxValue >= minValue);
    },
    // Keyboard utils
    IsSpecialKey: function(evt, inKeyDown) {
        var keyCode = _aspxGetKeyCode(evt);
        return keyCode == 0 || keyCode == ASPxKey.Backspace || 
            (inKeyDown && keyCode == ASPxKey.Delete) || keyCode > 60000 /*Saffari*/;
    },
    IsPageOrArrowKey: function(evt) {
        var keyCode = _aspxGetKeyCode(evt);
        // hack in opera
        if (__aspxOpera && evt.ctrlKey && (keyCode == ASPxKey.Up || keyCode == ASPxKey.Down))
            return false;
        else        
            return keyCode == ASPxKey.Up || keyCode == ASPxKey.Down || 
                   keyCode == ASPxKey.PageUp || keyCode == ASPxKey.PageDown;
    },
    IsSignSymbol: function(symbol) {
       return _aspxIsExists(symbol) && (symbol == __aspxNumberNegativeSymbol);
    },
    
    GetButtonIndexByKeyCode: function(keyCode, ctrl) {
        var ret = 0;
        switch (keyCode) {
            case ASPxKey.Up:
                ret = ctrl ? this.largeIncButtonIndex : this.incButtonIndex;
                break;
            case ASPxKey.Down:
                ret = ctrl ? this.largeDecButtonIndex : this.decButtonIndex;
                break;
            case ASPxKey.PageUp:
                ret = this.largeIncButtonIndex;
                break;
            case ASPxKey.PageDown:
                ret = this.largeDecButtonIndex;
                break;
        }
        return ret;
    },    
    TypeDecimalSeparator: function(selectionStart, selectionEnd) {
        var possibleValueString = this.GetPossibleValueString(selectionStart, selectionEnd, __aspxNumberDecimalSeparator);
        var inputElement = this.GetInputElement();
        inputElement.value = possibleValueString;
        var decimalSepIndex = possibleValueString.indexOf(__aspxNumberDecimalSeparator);
        _aspxSetCaretPosition(inputElement, decimalSepIndex + 1); // for paste
    },    
    UpdateSelectionStartAndEndPosition: function(inputElement) {    
        if (__aspxIE && _aspxIsExists(document.selection)) {
            inputElement.selectionStart = 0;
            inputElement.selectionEnd = 0;
            
            var curRange = document.selection.createRange();
            var copyRange = curRange.duplicate();
            
            curRange.move('character', - inputElement.value.length);
            curRange.setEndPoint('EndToStart', copyRange);
            inputElement.selectionStart = curRange.text.length;
            inputElement.selectionEnd = inputElement.selectionStart + copyRange.text.length;
        }
    },
    SetActualElementHeigth: function(element, expectedHeight) {
        element.style.height = expectedHeight + "px";    
        if (__aspxIE) {
            var actualHeight = element.clientHeight;
            var d = actualHeight - expectedHeight;
            if (d > 0) {
                expectedHeight = expectedHeight - d;                
                element.style.height = expectedHeight + "px";
            }
        }
    },
    SetElementHeigth: function(element, expectedHeight) {
        element.style.height = expectedHeight + "px";    
    },    
    // API
    RaiseNumberChanged: function(processOnServer) {
        if(!this.NumberChanged.IsEmpty()) {
            var args = new ASPxClientProcessingModeEventArgs(processOnServer);
            this.NumberChanged.FireEvent(this, args);
            processOnServer = args.processOnServer;
        }
        return processOnServer;
    },
    SetNumber: function(number) {
        this.SetValue(number);
    },
    GetNumber: function() {
        return this.number;
    },
    GetText: function() {
        return ASPxClientButtonEditBase.prototype.GetValue.call(this);
    },
    SetMinValue: function(value) {
        if (this.IsValidMinMaxValue(value, this.maxValue)) {
            this.minValue = this.ParseValueInternal(value.toString());
            this.EnsureCurrentNumberInBoundries();
        }
    },
    GetMinValue: function() {
        return this.minValue;
    },
    SetMaxValue: function(value) {
        if (this.IsValidMinMaxValue(this.minValue, value)) {
            this.maxValue = this.ParseValueInternal(value.toString());
            this.EnsureCurrentNumberInBoundries();
        }
    },
    GetMaxValue: function() {
        return this.maxValue;
    },
    EnsureCurrentNumberInBoundries: function() {
        var value = this.GetValue();
        if(value != null)
            this.SetNumber(this.GetCorrectNumberInRange(value));
    },
    // ** Only for ASPxHtmlEditor **
    GetParsedNumber: function() {
        var inputElement = this.GetInputElement();
        var valueString = inputElement.value;
        var newNumber = valueString != "" ? this.ParseValueInternal(valueString) : null;
        if((newNumber != null) && !isNaN(newNumber)){
            if (newNumber != this.number)
                newNumber = this.GetCorrectNumberInRange(newNumber);
        } else
            newNumber = this.GetCorrectNumberInRange(0);
        return newNumber;
    },    
    RestoreSelectionStartAndEndPosition: function() {
        var inputElement = this.GetInputElement();
        _aspxSetSelectionCore(inputElement, inputElement.selectionStart, inputElement.selectionEnd)
    },
    SaveSelectionStartAndEndPosition: function() {
        this.UpdateSelectionStartAndEndPosition(this.GetInputElement());
    }
});

ASPxClientSpinEdit.RemoveExponentialNotation = function(numString) {
    var mantissaPossition = numString.toLowerCase().indexOf("e");
    var ret = numString;
    if (mantissaPossition > -1) {
        var isNegative = numString.indexOf("-") == 0;
        var isNegativeMantissa = numString.indexOf("-") > 0;
        
        var mantissa = numString.replace(new RegExp('^[+-]?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][+-]?)'), "");
        var numberBase = numString.replace(new RegExp('([eE][+-]?[0-9]+)'), "");
        numberBase = numberBase.replace("+", "");
        numberBase = numberBase.replace("-", "");
                
        var decimalDecimalSeparator = ".";
        if (numberBase.indexOf(decimalDecimalSeparator) == -1) {
            numberBase = numberBase.replace(decimalDecimalSeparator, __aspxNumberDecimalSeparator);
            decimalDecimalSeparator = __aspxNumberDecimalSeparator;
        }
        
        var numberParts = numberBase.split(decimalDecimalSeparator);
        var zeroNumbers = parseInt(mantissa, 10) - numberParts[1].length;
        
        if (isNegativeMantissa)
            ret = "0" + __aspxNumberDecimalSeparator +  ASPxClientSpinEdit.GetZeros(zeroNumbers + 1) +
              numberParts[0] + numberParts[1];
        else
            ret = numberParts[0] + numberParts[1] + ASPxClientSpinEdit.GetZeros(zeroNumbers);
        if (isNegative)
            ret += "-";
    }
    return ret;
}
ASPxClientSpinEdit.GetZeros = function(length) {
    var zeros = [];
    for (var i = 0; i < length; i++)
        zeros.push('0');
    return zeros.join("");    
}

function aspxSEMouseOver(name, evt) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.OnMouseOver(evt);
}
function aspxSETextChangingCheck(name) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.OnTextChangingCheck();
}
function aspxSEValueChanged(name) {
    var spinEdit = aspxGetControlCollection().Get(name);
    if (spinEdit != null) spinEdit.OnValueChangedTimer();
}

function aspxBEMouseDown(name, evt) {
    var edit = aspxGetControlCollection().Get(name);
    if(edit != null) edit.OnButtonMouseDown(evt);
}
function aspxSRepeatButtonClick(name, buttonIndex, iterationIndex) {
    var spinEdit = aspxGetControlCollection().Get(name);
    if (spinEdit != null) spinEdit.DoRepeatButtonClick(buttonIndex, iterationIndex);
}
function aspxSpinImageOnLoad(name) {
    var spinEdit = aspxGetControlCollection().Get(name);
    if (spinEdit != null) spinEdit.OnSpinButtonImageLoad();
}

aspxAddAfterSetPressedState(aspxAfterSetPressed);
aspxAddAfterClearPressedState(aspxAfterClearPressed);
function aspxAfterSetPressed(source, args) {
    var item = args.item;
    var spinEdit = aspxGetSpinEditCollection().GetSpinEdit(item.name);
    if (spinEdit != null && item.enabled) spinEdit.OnAfterSetPressed(item.name);
}
function aspxAfterClearPressed(source, args) {
    var spinEdit = aspxGetSpinEditCollection().GetSpinEdit(args.item.name);
    if (spinEdit != null) spinEdit.OnAfterClearPressed(args.item.name);
}

// SpinEdit collection
var __aspxSpinEditCollection = null;
function aspxGetSpinEditCollection(){
    if(__aspxSpinEditCollection == null)
        __aspxSpinEditCollection  = new ASPxClientSpinEditCollection();
    return __aspxSpinEditCollection;
}

ASPxClientSpinEditCollection = _aspxCreateClass(ASPxClientControlCollection, {
    constructor: function(){
        this.constructor.prototype.constructor.call(this);        
    },
    GetSpinEdit: function(id){
        return this.Get(this.GetSpinEditName(id));
    },
    GetSpinEditName: function(id) {
        var pos = id.lastIndexOf(__aspxSpindButtonIdPostfix);
        if(pos > -1)
            return id.substring(0, pos);
            
        pos = id.lastIndexOf(__aspxTEInputSuffix);
        if (pos > -1)
            return id.substring(0, pos);
                
        return id;
    }
});
ASPxClientFilterControl = _aspxCreateClass(ASPxClientControl, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.callBacksEnabled = true;
        this.nodeIndex = -1;
        this.editorIndex = "";
        this.prevEditorValue = null;
        this.imagemouseoversrc = null;
        this.ownerControl = null;
        this.isCallbackInProcess = false;
        this.isApplied = false;
        this.lpTimer = -1;
        this.lpDelay = 500;    
        this.Applied = new ASPxClientEvent();
        
    },
    GetChildElementById: function(childName){
        return _aspxGetElementById(this.name + "_" + childName);
    },
    GetFilterExpression: function() { return this.GetChildElementById("E").value; },
    GetAppliedFilterExpression: function() { return this.GetChildElementById("AE").value; },
    GetEditor: function(editorIndex) { return aspxGetControlCollection().Get(this.name + "_" + "DXEdit" + editorIndex.toString()); },
    GetNodeIndexByEditor: function(editorIndex) { return Math.round(editorIndex / 1000); },
    GetValueIndexByEditor: function(editorIndex) { return editorIndex % 1000; },
	GetRootTable: function() { return _aspxGetElementById(this.name); },
	GetRootTD: function() { 
	    var table = this.GetRootTable();
	    if(!_aspxIsExists(table)) return null;
	    return table.rows[0].cells[0];
	},        
    WakeLoadingPanel: function() {
		if(this.lpTimer > -1) return;
		this.CreateLoadingDiv(this.GetRootTD());			
		var js = "aspxFCShowLoadingPanel('" + this.name + "')";
		this.lpTimer = _aspxSetTimeout(js, this.lpDelay);
    },
    ShowLoadingPanel: function() {
		this.ClearLoadingPanelTimer();		
		var div = this.GetLoadingDiv();
		if(div) div.style.cursor = "wait";
		var cell = this.GetRootTD();
		if(cell) this.CreateLoadingPanelWithAbsolutePosition(cell);
    },
    ClearLoadingPanelTimer: function() {
		this.lpTimer = _aspxClearTimer(this.lpTimer);		
    },
    HideLoadingPanel: function() {
		this.ClearLoadingPanelTimer();
		this.constructor.prototype.HideLoadingPanel.call(this);
    },            
    FilterCallback: function(action, index, params) {
	    if(!_aspxIsExists(index)) {
	        index = this.nodeIndex;
	    }
	    this.nodeIndex = -1;
        var args = action + '|' + index.toString()  + '|' + params;
	    if(!_aspxIsExists(this.callBack) || !this.callBacksEnabled) {
            this.SendPostBack(args);
	        return;
	    } 
	    if(!this.isCallbackInProcess)  {
	        this.isCallbackInProcess = true;
	        this.WakeLoadingPanel();
            this.CreateCallback(args, "");
        }
    },
	OnCallback: function(result){
	    //this.HideLoadingPanel();
        this.ClearEditorsValues();
	    if(this.ownerControl != null) {
	        this.ownerControl.OnCallback(result);	        
	    } else {
	        var rootTD = this.GetRootTD();
	        if(rootTD != null) {
	            _aspxSetInnerHtml(rootTD, result);
	        }
        }
        this.isCallbackInProcess = false;
        if(this.isApplied) {
            this.RaiseFilterApplied();
        }
        this.isApplied = false;
	},
	DoEndCallback: function() {
		if(this.ownerControl != null) {
			this.ownerControl.DoEndCallback();
			this.ownerControl = null;
		} else {
			ASPxClientControl.prototype.DoEndCallback.call(this);
		}
	},
	OnCallbackError: function(result, data){
	    //this.HideLoadingPanel();
	    this.isCallbackInProcess = false;
	    this.isApplied = false;
	    alert(result);
	    if(this.editorIndex > -1) {
	        var editor = this.GetEditor(this.editorIndex);
	        editor.SetFocus();
	    }
    },	
	ShowPopupMenu: function(menuName, evt, index, propertyType) {
	    if(this.CheckEditor()) return;
	    this.nodeIndex = index;
	    var menu = aspxGetControlCollection().Get(this.name + "_" + menuName);
	    if(menu != null) {
	        if(_aspxIsExists(propertyType)) {
	            this.CheckOperationMenuItemVisibility(menu, propertyType);
	        }
	        menu.ShowAtElement(_aspxGetEventSource(evt));
	    }
	},
	ShowFieldNamePopup: function(evt, index) { this.ShowPopupMenu("FieldNamePopup", evt, index); },
	ShowOperationPopup: function(evt, index, propertyType) { this.ShowPopupMenu("OperationPopup", evt, index, propertyType); },
	ShowGroupPopup: function(evt, index) { this.ShowPopupMenu("GroupPopup", evt, index); },
	ChangeFieldName: function(fieldName, index) {  this.FilterCallback("FieldName", index, fieldName); },
	ChangeOperation: function(operation, index) {  this.FilterCallback("Operation", index, this.RemoveDivider(operation)); },
	ChangeGroup: function(group, index) {  
	    if(group.indexOf("|") == 0) {
	        this.FilterCallback(group.substr(1), index, ""); 
	    } else {
	        this.FilterCallback("GroupType", index, group); 
	    }
	},
	Apply: function(ownerControl) { 
        if(this.editorIndex > -1) {
            var editor = this.GetEditor(this.editorIndex);
            if(editor && !editor.GetIsValid())
                return;
        }
	    if(this.isCallbackInProcess) return;
	    this.ownerControl = ownerControl;
	    this.isApplied = true;
    	this.FilterCallback("Apply", -1, ownerControl ? "T" : "F"); 
	},
	Reset: function() { 
	    if(this.isCallbackInProcess) return;
    	this.FilterCallback("Reset", -1, ""); 
	},
    RemoveNode: function(index) { this.FilterCallback("Remove", index, ""); },
    AddConditionNode: function(index) { this.FilterCallback("AddCondition", index, ""); },
	AddValue: function(index) {  this.FilterCallback("AddValue", index, ""); },
    ShowEditor: function(editorIndex) {
        if(this.CheckEditor()) return;
        var editor = this.ChangeEditorVisibility(editorIndex, true);
        if(editor != null) {
            editor.SetIsValid(true);
            editor.Filter = this;
            editor.Focus();
            this.prevEditorValue = editor.GetValue();
            this.editorIndex = editorIndex;
        }
    },
    HideEditor: function() {
        if(this.editorIndex < 0) return;
        this.ChangeEditorVisibility(this.editorIndex, false);
        var editor = this.GetEditor(this.editorIndex);
        if(editor != null) {
            editor.SetValue(this.prevEditorValue);            
        }
        this.ClearEditorsValues();
    },
    ChangeEditorVisibility: function(editorIndex, visible) {
        var link = this.GetChildElementById("DXValue" + editorIndex);
        var editor = this.GetEditor(editorIndex);
        if(link != null && editor != null) {
            link.style.display = visible ? "none" : "";
            editor.SetVisible(visible);
            return editor;
        }
        return null;
    },
    CheckEditor: function() {
        if(this.editorIndex < 0) return false;
        var editor = this.GetEditor(this.editorIndex);
        if(editor == null) return false;
        editor.Validate();
        if(!editor.GetIsValid()) return true;        
        if(editor.GetValue() == this.prevEditorValue) {
            this.HideEditor();
            return false;
        }
        var editorIndex = this.editorIndex;
        var value = editor.GetValueString();
        if(value == null) value = "";
        var params = this.GetValueIndexByEditor(editorIndex).toString() + '|' + value;
        this.FilterCallback("Value", this.GetNodeIndexByEditor(editorIndex), params);
        return true;
    },
    ClearEditorsValues: function() {
        this.prevEditorValue = null;
        this.editorIndex = -1;
    },
    OnImageMouseOver: function(image, hotImageName) {
        var hotImage = this.GetChildElementById(hotImageName);
        if(!_aspxIsExists(hotImage)) return;
        this.imagemouseoversrc = image.src;
        image.src = hotImage.src;
        image.style.cursor = "pointer";
    },
    OnImageMouseOut: function(image) {
        if(this.imagemouseoversrc != null) {
            image.src = this.imagemouseoversrc;
            this.imagemouseoversrc = null;
        }
    },
    CheckOperationMenuItemVisibility: function(menu, propertyType) {
        for(var i = 0; i < menu.GetItemCount(); i ++) {
            var item = menu.GetItem(i);
            var str = this.GetBeforeDivider(item.name);
            item.SetVisible( propertyType.length > 0 && str.indexOf(propertyType) > -1);
        }
    },
    RemoveDivider: function(str) {
        var pos = str.indexOf('|');
        if(pos < 0) return str;
        return str.substr(pos + 1);
    },
    GetBeforeDivider: function(str) {
        var pos = str.indexOf('|');
        if(pos < 0) return "";
        return str.substr(0, pos);
    },
    RaiseFilterApplied: function() {
        if(this.Applied.IsEmpty()) return;
        var args = new ASPxClientFilterAppliedEventArgs(this.GetFilterExpression());
        this.Applied.FireEvent(this, args);
    }
});
ASPxClientFilterAppliedEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(filterExpression){
	    this.constructor.prototype.constructor.call(this);
        this.filterExpression = filterExpression;
    }
});

function aspxFCShowFieldNamePopup(name, evt, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ShowFieldNamePopup(evt, index);
    }
}
function aspxFCShowOperationPopup(name, evt, index, propertyType) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ShowOperationPopup(evt, index, propertyType);
    }
}
function aspxFCShowGroupPopup(name, evt, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ShowGroupPopup(evt, index);
    }
}
function aspxFCChangeFieldName(name, fieldName) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ChangeFieldName(fieldName);
    }
}
function aspxFCChangeOperation(name, operation) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ChangeOperation(operation);
    }
}
function aspxFCAddValue(name, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.AddValue(index);
    }
}
function aspxFCChangeGroup(name, group) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ChangeGroup(group);
    }
}
function aspxFCRemoveNode(name, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.RemoveNode(index);
    }
}
function aspxFCAddConditionNode(name, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.AddConditionNode(index);
    }
}
function aspxFCNodeValueClick(name, index) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.ShowEditor(index);
    }
}
function aspxFCEditorKeyDown(s, e) {
    var keyCode = _aspxGetKeyCode(e.htmlEvent);
	if(keyCode == ASPxKey.Enter)
		_aspxPreventEventAndBubble(e.htmlEvent); // prevent form submission
}
function aspxFCEditorKeyUp(s, e) {
	var filter = s.Filter;
	if(!filter) return;
    var keyCode = _aspxGetKeyCode(e.htmlEvent);
    if(keyCode == ASPxKey.Enter) {
		filter.CheckEditor();
    } else if(keyCode == ASPxKey.Esc) {
        filter.HideEditor();
    }
}
function aspxFCEditorLostFocus(s, e) {
	if(s.Filter)
        s.Filter.CheckEditor();
}
function aspxFCImageMouseOver(name, e, hotImage) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.OnImageMouseOver(_aspxGetEventSource(e), hotImage);
    }
}
function aspxFCImageMouseOut(name, e) {
    var control = aspxGetControlCollection().Get(name); 
    if(control != null) {
        control.OnImageMouseOut(_aspxGetEventSource(e));
    }
}

function aspxFCPopupInit(s, e) { s.Show(); }
function aspxFCPopupShown(s, e) { s.GetWindowContentElement(-1).style.height = 0; } // ask Serge S
function aspxFCShowLoadingPanel(name) {
	var obj = aspxGetControlCollection().Get(name);
	if(obj) obj.ShowLoadingPanel();
}
_aspxMaskPartBase = _aspxCreateClass(null, {
    typeCode: 1,
    constructor: function() {
        this.valueInitialized = false;
        this.dateTimeRole = null;
	},
	Grow: function(text) {
	    throw "Not supported";
	},
	GetSize: function() {
		throw "Not supported";
	},
	GetValue: function() {
		this.EnsureValueInitialized();
		return this.GetValueCore();
	},
	EnsureValueInitialized: function() {
		if(this.valueInitialized) return;
		this.InitValue();
		this.valueInitialized = true;
	},
	InitValue: function() {
		throw "Not supported";
	},	
	GetValueCore: function() {
		throw "Not supported";
	},
	Clear: function(startPos, endPos) {
	},
	HandleKey: function(maskInfo, keyInfo, pos) {
		throw "Not supported";
	},
	HandleControlKey: function(maskInfo, keyInfo, pos) {	     
	    switch(keyInfo.keyCode) {
		    case ASPxKey.Left:
		        if(keyInfo.ctrlState)
		            maskInfo.MoveToPrevNonLiteral();
		        else
		            maskInfo.IncCaretPos(-1);
			    break;
		    case ASPxKey.Right:
		        if(keyInfo.ctrlState)
		            maskInfo.MoveToNextNonLiteral();
		        else
		            maskInfo.IncCaretPos(1);
			    break;
	    }
	},
	HandleMouseWheel: function(maskInfo, delta, pos) {
	},
	AllowIncreaseSize: function() { 
	    return false; 
	},
	SupportsUpDown: function() { 
	    return false; 
	},
	IsValid: function() {
		return true;
	},
	GetHintHtml: function() {
		return "";
	}
});	

_aspxLiteralMaskPart = _aspxCreateClass(_aspxMaskPartBase, {
    typeCode: 2,
    constructor: function() {
        this.constructor.prototype.constructor.call(this);        
        this.literal = "";
	},
	Grow: function(text) {
	    this.literal += text;
	},	
	GetSize: function() {
		return this.literal.length;
	},
	InitValue: function() {
	},	
	GetValueCore: function() {
		return this.literal;
	},
	HandleKey: function(maskInfo, keyInfo, pos) {
		if(keyInfo.keyCode == 32){
		    maskInfo.IncCaretPos();
		    return true;
		}
		var ch = String.fromCharCode(keyInfo.keyCode).toLowerCase();
		var index = this.GetValue().toLowerCase().indexOf(ch, pos);
		if(index > -1){
		    maskInfo.IncCaretPos(index - pos + 1);
		    return true;
		}
		maskInfo.IncCaretPos();
	    return false;
	},
	HandleControlKey: function(maskInfo, keyInfo, pos) {
		switch(keyInfo.keyCode) {
		    case ASPxKey.Right:
			case ASPxKey.Delete:
			    maskInfo.IncCaretPos(this.GetSize() - pos);
			    break;
            case ASPxKey.Left:			    
			case ASPxKey.Backspace:
			    maskInfo.IncCaretPos(-pos);
			    break;
			default:
			    _aspxMaskPartBase.prototype.HandleControlKey.call(this, maskInfo, keyInfo, pos);
	    }
	}
});	

_aspxEnumMaskPart = _aspxCreateClass(_aspxMaskPartBase, {
    typeCode: 3,
    constructor: function(items) {
        this.constructor.prototype.constructor.call(this);
        this.items = [];
        this.itemIndex = 0;
        this.defaultItemIndex = 0;
        this.PrepareItems(items);
	},
	PrepareItems: function(items){	
	    var hash = {};
	    for(var i = 0; i < items.length; i++){
	        var item = String(items[i]);
	        if(item.length > 0 && !_aspxIsExists(hash[item])){
	            if(item.charAt(0) == "*"){
	                this.defaultItemIndex = i;
	                item = item.substr(1);
	            }
	            this.items.push(item);
	            hash[item] = 1;
	        } 
	    }
	},
	GetSize: function() {
		return this.GetValue().length;
	},
	InitValue: function() {	    
		this.itemIndex = this.defaultItemIndex;
	},	
	GetValueCore: function() {
		return this.items[this.itemIndex];
	},
	Clear: function(startPos, endPos) {
	    this.ClearInternal(startPos);
	},
	ClearInternal: function(pos) {
        var prefix = this.GetValue().substr(0, pos);
        if(prefix.length < 1)
			this.itemIndex = this.defaultItemIndex;
		else
			this.itemIndex = this.FindItemIndexByPrefix(prefix);
	},
	HandleKey: function(maskInfo, keyInfo, pos) {
	    var ch = String.fromCharCode(keyInfo.keyCode);
	    var prefix = this.GetValue().substr(0, pos) + ch;
	    var index = this.FindItemIndexByPrefix(prefix);

		if(index < 0 && ch != " ") {
			maskInfo.SetCaret(maskInfo.caretPos, this.GetSize() - pos);
			return false;
		}
		if(index > -1)
			this.itemIndex = index;
		maskInfo.SetCaret(1 + maskInfo.caretPos, this.GetSize() - pos - 1);
	    return true;
	},
	HandleControlKey: function(maskInfo, keyInfo, pos) {
		switch(keyInfo.keyCode) {
			case ASPxKey.Up:
			    this.ChangeItemIndex(maskInfo, this.dateTimeRole != null ? 1 : -1, pos);
				break;
			case ASPxKey.Down:
			    this.ChangeItemIndex(maskInfo, this.dateTimeRole != null ? -1 : 1, pos);
				break;				
			case ASPxKey.Backspace:
			    if(keyInfo.ctrlState){
			        this.itemIndex = this.defaultItemIndex;
			        maskInfo.IncCaretPos(-pos);
			    }
				else {
				    this.ClearInternal(pos - 1);
				    maskInfo.SetCaret(maskInfo.caretPos - 1, 0);
				}
				break;
			case ASPxKey.Delete:
			    if(keyInfo.ctrlState){
			        this.itemIndex = this.defaultItemIndex;
			        maskInfo.IncCaretPos(this.GetSize() - pos);
				}
				else {
				    this.ClearInternal(pos);
				    maskInfo.SetCaret(maskInfo.caretPos + 1, 0);
				}
				break;				
			default:
			    _aspxMaskPartBase.prototype.HandleControlKey.call(this, maskInfo, keyInfo, pos);
	    }
	},
	HandleMouseWheel: function(maskInfo, delta, pos) {
		if(this.dateTimeRole == null)
			delta = -delta;
	    this.ChangeItemIndex(maskInfo, delta, pos);
	},
	ChangeItemIndex: function(maskInfo, delta, pos) {
	    this.itemIndex += delta;
		while(this.itemIndex < 0)
			this.itemIndex += this.items.length;
		while(this.itemIndex > this.items.length - 1)
			this.itemIndex -= this.items.length;
	    maskInfo.SetCaret(maskInfo.caretPos - pos, this.GetSize());
	},
	FindItemIndexByPrefix: function(prefix) {	    
	    prefix = prefix.toLowerCase();
	    for(var i = 0; i < this.items.length; i++) {
	        var item = this.items[i];
	        if(item.toLowerCase().indexOf(prefix) == 0)
	            return i;
	    }
	    return -1;
	},
	SupportsUpDown: function() { 
	    return true; 
	},
	GetHintHtml: function() {
		if(this.dateTimeRole != null)
			return "";
		var list = [];
		for(var i = 0; i < this.items.length; i++) {
			var text = this.items[i];
			if(i == this.itemIndex)
				text = "<strong>" + text + "</strong>";
			list.push(text);
		}
		return list.join(", ");
	}
});

_aspxRangeMaskPart = _aspxCreateClass(_aspxMaskPartBase, {
    typeCode: 4,
    constructor: function(minNumber, maxNumber) {
        this.constructor.prototype.constructor.call(this);        
        if(maxNumber < minNumber)
			maxNumber = minNumber;        
        this.minNumber = minNumber;
        this.maxNumber = maxNumber;
        this.defaultNumber = null;
        this.zeroFill = false;
        this.absNumber = 0;
        this.negative = false;
        this.enableGroups = false;
	},
	GetSize: function() {
		return this.GetValue().length;
	},
	InitValue: function() {
	    var number = 0;
	    if(this.defaultNumber != null)
	        number = this.defaultNumber;
		else {
		    if(this.maxNumber < 0)
			    number = this.maxNumber;
		    else if(this.minNumber < 0)
			    number = 0;
		    else
			    number = this.minNumber;
	    }
	    this.SetNumber(number);
	},	
	GetValueCore: function() {
	    var value = String(this.absNumber);
	    if(this.zeroFill) {
            var size = Math.max(this.minNumber.toString().length, this.maxNumber.toString().length);
            var incSize = size - value.length;
            for(var i = 0; i < incSize; i++)
                value = "0" + value;
        }
        if(this.enableGroups)
			value = this.AddGroupSeparators(value);
        if(this.negative)
            value = "-" + value;
        return value;
	},
	AddGroupSeparators: function(text) {
		if(text.length < 4)
			return text;
		var temp = [ ];
		var count = Math.ceil(text.length / 3);
		for(var i = 1; i < count; i++)
			temp.unshift(text.substr(text.length - i * 3, 3));
		temp.unshift(text.substr(0, text.length % 3 || 3));
		return temp.join(__aspxCultureInfo.numGroupSeparator);
	},
	IsGroupSeparatorPos: function(pos) {
		if(!this.enableGroups)
			return false;
		var reversePos = this.GetSize() - pos;
		return reversePos > 0 && reversePos % 4 == 0;
	},	
	GetNumber: function() {
		var result = this.absNumber;
		if(this.negative)
			result = -result;
		return result;
	},
	SetNumber: function(number) {
	    this.negative = (number < 0);
	    this.absNumber = Math.abs(number);
	},
	SetText: function(text, checkMinNumber) {
		checkMinNumber = checkMinNumber || Math.abs(this.minNumber) < 2;
	    if(text == "") text = "0";
	    if(text == "-") text = "-0";
	    if(this.enableGroups) {
			text = text.split(__aspxCultureInfo.numGroupSeparator).join("");
	    }
	    var number = Number(text);
        if(number > this.maxNumber) {
            this.SetNumber(this.maxNumber);
        } else if(checkMinNumber && number < this.minNumber) {
            this.SetNumber(this.minNumber);
        } else {
            this.absNumber = Math.abs(number);
            this.negative = (text.indexOf("-") > -1);
        }	    
	},
	Clear: function(startPos, endPos){
		var newText = _aspxInsertEx(this.GetValue(), "", startPos, endPos);
		this.SetText(newText, true);
    },
	HandleKey: function(maskInfo, keyInfo, pos) {
	    var keyCode = keyInfo.keyCode;	    	    
	    var ch = String.fromCharCode(keyCode);
	    if((ch == __aspxCultureInfo.numGroupSeparator && this.IsGroupSeparatorPos(pos)
			|| keyCode == 32) && pos < this.GetSize()) {
	        maskInfo.IncCaretPos();
	        return true;
	    }	    
	    var oldNumber = this.GetNumber();
        if(_aspxMaskManager.IsSignumCode(keyCode)) {
            if((ch == "-" && this.minNumber < 0)  || (ch == "+" && oldNumber < 0)) {               
                var newNumber = -oldNumber;
                if(this.CheckRange(newNumber)) {
                    this.negative = !this.negative;
                    maskInfo.SetCaret(maskInfo.caretPos - pos + (this.negative ? 1 : 0), 0);
                    return true;
                }
            }
        }
        if(_aspxMaskManager.IsDigitCode(keyCode)) {
			if(!this.zeroFill && ch == "0" && oldNumber == 0 && pos > 0)
				return false;
            this.TryTypeAtPos(maskInfo, ch, pos, 1);
            return true;
        }
	    return false;
	},
	HandleControlKey: function(maskInfo, keyInfo, pos) {
		switch(keyInfo.keyCode) {
			case ASPxKey.Up:			    
			    this.ChangeNumber(maskInfo, 1, pos);
				break;
			case ASPxKey.Down:
			    this.ChangeNumber(maskInfo, -1, pos);
				break;
			case ASPxKey.Delete:
			    if(keyInfo.ctrlState) {			    
			        var newText = this.GetValue().substr(0, pos);
                    this.SetText(newText, false);
                    maskInfo.IncCaretPos(this.GetSize() - pos);
			    } else {
					if(this.IsGroupSeparatorPos(pos)) {
						maskInfo.IncCaretPos();
					} else {						        
						if(this.zeroFill)
							this.TryTypeAtPos(maskInfo, "0", pos, 1);
						else
							this.TryTypeAtPos(maskInfo, "", pos, 1);
					}
			    }
			    break;
			case ASPxKey.Backspace:
			    if(keyInfo.ctrlState) {			    
			        var newText = this.GetValue().substr(pos);
                    this.SetText(newText, false);
                    maskInfo.IncCaretPos(-pos);
                } else {
					if(this.IsGroupSeparatorPos(pos - 1)) {
						maskInfo.IncCaretPos(-1);
					} else {
						if(this.zeroFill)
							this.TryTypeAtPos(maskInfo, "0", pos, -1);
						else
							this.TryTypeAtPos(maskInfo, "", pos, -1);
					}
			    }
			    break;
			default:
			    _aspxMaskPartBase.prototype.HandleControlKey.call(this, maskInfo, keyInfo, pos);
	    }
	},
    HandleMouseWheel: function(maskInfo, delta, pos) {
        this.ChangeNumber(maskInfo, delta, pos);
    },
    ChangeNumber: function(maskInfo, delta, pos) {
		var number = this.GetNumber();
		if(number < this.minNumber)
			number = this.minNumber;
        var newNumber = number + delta;
		while(newNumber < this.minNumber)
			newNumber += 1 + this.maxNumber - this.minNumber;
		while(newNumber > this.maxNumber)
			newNumber -= 1 + this.maxNumber - this.minNumber;
        this.SetNumber(newNumber);
        maskInfo.SetCaret(maskInfo.caretPos - pos, this.GetSize());    
    },
	CheckRange: function(number){
	    return (this.minNumber <= number && number <= this.maxNumber);
	},
	TryTypeAtPos: function(maskInfo, str, pos, dir) {
		if(dir > 0 && this.IsGroupSeparatorPos(pos)) {
			pos++;
			maskInfo.IncCaretPos();
		}
        var oldSize = this.GetSize();
        var strPos = pos;
        if(dir < 0) strPos -= 1;
        var newText = _aspxInsertEx(this.GetValue(), str, strPos, strPos + 1);
        this.SetText(newText, false);
        var newPos;
        if(dir > 0 && oldSize == pos) {
			newPos = this.GetSize();
        } else  {        
			var diff = 0;
			if(!this.zeroFill) {
				diff = this.GetSize() - oldSize;
				if(dir < 0) diff += 1;
				if(diff > 0) diff = 0;
			}
			newPos = pos + dir + diff;
		}
        if(newPos < 0) newPos = 0;        
        if(newPos > this.GetSize()) newPos = this.GetSize();
        if(this.IsGroupSeparatorPos(newPos))
			newPos++;
        maskInfo.IncCaretPos(newPos - pos);
	},
	AllowIncreaseSize: function() {
	    if(this.zeroFill) return false;
	    	    
		var currentNumber = this.GetNumber();
	    if(currentNumber < 0)
	        return currentNumber * 10 >= this.minNumber;
	    if(currentNumber >= 0)
            return currentNumber * 10 <= this.maxNumber;
        return false;
	},
	SupportsUpDown: function() { 
	    return true; 
	},
	GetHintHtml: function() {
		if(this.dateTimeRole != null)
			return "";	
		return this.minNumber + ".." + this.maxNumber;
	}
});	

_aspxPromptMaskPart = _aspxCreateClass(_aspxMaskPartBase, {
    typeCode: 5,
    constructor: function() {
        this.constructor.prototype.constructor.call(this);
        this.required = false;
        this.size = 0;
        this.text = "";
	},
	Grow: function(text) {
	    this.size += text.length;
	},	
	GetSize: function() {
		return this.size;
	},
	InitValue: function() {		
		var size = this.GetSize();
		for(var i = 0; i < size; i++)
			this.text += " ";
	},	
	GetValueCore: function() {
		return this.text;
	},
	Clear: function(startPos, endPos){
	    this.ClearInternal(startPos, endPos - startPos);
    },
    ClearInternal: function(pos, count){
	    for(var  i = 0; i < count; i++)
            this.SetCharInPos(" ", i + pos);                                    
    },
	HandleKey: function(maskInfo, keyInfo, pos) {
		var keyCode = keyInfo.keyCode;		
		if(maskInfo.IsPromptCode(keyCode))
			keyCode = 32;
		if(keyCode != 32 && !this.IsValidCharCode(keyCode, pos))
			return false;
		this.SetCharInPos(String.fromCharCode(keyCode), pos);
		maskInfo.IncCaretPos();
		return true;
	},
	HandleControlKey: function(maskInfo, keyInfo, pos) {
        switch(keyInfo.keyCode) {
			case ASPxKey.Delete:
			    var count = keyInfo.ctrlState ? this.GetSize() - pos : 1;
			    this.ClearInternal(pos, count);
                maskInfo.IncCaretPos(count);
			    break; 
			case ASPxKey.Backspace:
			    var count = keyInfo.ctrlState ? pos : 1;
			    this.ClearInternal(pos - count, count);
                maskInfo.IncCaretPos(-count);
			    break;
			default:
			    _aspxMaskPartBase.prototype.HandleControlKey.call(this, maskInfo, keyInfo, pos);
	    }
	},	
	SetCharInPos: function(ch, pos) {
	    this.text = _aspxInsertEx(this.GetValue(), ch, pos, pos + 1);
	},	
	IsValidCharCode: function(code, pos) {
		throw "Not supported";
	},
	IsValid: function() {
		if(!this.required)
			return true;
		return this.GetValue().indexOf(" ") < 0;
	}
});
	
_aspxNumericMaskPart = _aspxCreateClass(_aspxPromptMaskPart, {
    typeCode: 6,
    constructor: function() {
        this.constructor.prototype.constructor.call(this);
        this.acceptsSignum = false;
	},
    IsValidCharCode: function(code, pos) {
		if(_aspxMaskManager.IsSignumCode(code)) {
		    if(!this.acceptsSignum) return false;
		    
		    var value = this.GetValue();
		    for(var i = 0; i < pos; i++){
		        var currentCode = value.charCodeAt(i);
		        if(_aspxMaskManager.IsDigitCode(currentCode) || _aspxMaskManager.IsSignumCode(currentCode))
		            return false;
		    }
		    return true;
	    }
		return _aspxMaskManager.IsDigitCode(code);
    }	
});

_aspxCharMaskPart = _aspxCreateClass(_aspxPromptMaskPart, {
    typeCode: 7,
    constructor: function() {
        this.constructor.prototype.constructor.call(this);
        this.caseConvention = 0;
	},
	SetCharInPos: function(ch, pos) {
		if(this.caseConvention < 0)
			ch = ch.toLowerCase();
		if(this.caseConvention > 0)
			ch = ch.toUpperCase();			
		_aspxPromptMaskPart.prototype.SetCharInPos.call(this, ch, pos);
	},	
    IsValidCharCode: function(code, pos) {
		return code > 31;
    }
});
	
_aspxAlphaMaskPart = _aspxCreateClass(_aspxCharMaskPart, {
    typeCode: 8,
    IsValidCharCode: function(code, pos) {
		return _aspxMaskManager.IsAlphaCode(code);
    }
});	

_aspxAlphaNumericMaskPart = _aspxCreateClass(_aspxCharMaskPart, {
    typeCode: 9,
    IsValidCharCode: function(code, pos) {
		return _aspxMaskManager.IsAlphaCode(code) || _aspxMaskManager.IsDigitCode(code);
    }    
});	


_aspxMaskParser = {
    Parse: function(mask, dateTimeOnly) {
        this.result = [ ];
        this.currentCaseConvention = 0;        
        this.quoteMode = null;
        this.dateTimeOnly = (dateTimeOnly === true);
        mask.replace(this.GetMasterRegex(), this.ParseCallback);
        return this.result;
    },

    // Regular expressions
    regex: {       
        ranges: "\\<-?\\d+(\\.\\.-?\\d+){1,2}g?\\>",
        enums: "\\<\\*?[^|*<>]*(\\|\\*?[^|*<>]*)+\\>",
        prompts: "[LlAaCc09#,.:/$<>~]",
        dates: "(y{1,4}|M{1,4}|d{1,4}|hh?|HH?|mm?|ss?|F{1,6}|f{1,6}|tt?)"
    },    
    GetMasterRegex: function() {
		if(this.dateTimeOnly) {
			if(!this.__masterDateTimeOnlyRegex)
				this.__masterDateTimeOnlyRegex = this.CreateMasterRegex(true);
			return this.__masterDateTimeOnlyRegex;						
		}
		if(!this.__masterRegex)
			this.__masterRegex = this.CreateMasterRegex(false);
		return this.__masterRegex;
    }, 
    GetRangesRegex: function() {
        if(!this.__rangesRegex) 
            this.__rangesRegex = this.CreateAnchoredRegex(this.regex.ranges);
        return this.__rangesRegex;
    },
    GetEnumsRegex: function() {
        if(!this.__enumsRegex) 
            this.__enumsRegex = this.CreateAnchoredRegex(this.regex.enums);
        return this.__enumsRegex;
    },
    GetDatesRegex: function() {
		if(!this.__datesRegex)
			this.__datesRegex = this.CreateAnchoredRegex(this.regex.dates);
		return this.__datesRegex;
    },
    CreateAnchoredRegex: function(text) {
		return new RegExp("^" + text + "$");
    },
    CreateMasterRegex: function(dateTimeOnly) {
		var list = [ ];
		this.PushConditional(list, "\\\\\\\\", true);
		this.PushConditional(list, "\\\\[\"']", true);
		this.PushConditional(list, "[\"']", true);
		this.PushConditional(list, this.regex.ranges, !dateTimeOnly);
		this.PushConditional(list, this.regex.enums, !dateTimeOnly);
		this.PushConditional(list, "\\\\" + this.regex.dates, true);
		this.PushConditional(list, "\\\\" + this.regex.prompts, !dateTimeOnly);
		this.PushConditional(list, this.regex.dates, true);
		this.PushConditional(list, this.regex.prompts, !dateTimeOnly);
		this.PushConditional(list, ".", true);
		return new RegExp("(" + list.join("|") + ")", "g");
    },
    PushConditional: function(list, item, allow) {
		if(allow)
			list.push(item);	
    },
        
    ParseCallback: function(text) {        
        _aspxMaskParser.ParseCore(text, null);
    },
    ParseCore: function(text, dateTimeRole) {
		var acceptRangesEnums = (dateTimeRole != null || !this.dateTimeOnly);
		if(text == "'" || text == '"')
			this.ParseQuote(text);
		else if(this.quoteMode != null)
			this.ParseLiteral(text);
        else if(acceptRangesEnums && this.GetRangesRegex().test(text)) 
            this.ParseRange(text, dateTimeRole);
        else if(acceptRangesEnums && this.GetEnumsRegex().test(text)) 
            this.ParseEnum(text, dateTimeRole);
        else if(this.GetDatesRegex().test(text))
			this.ParseDate(text);
        else
            this.ParseSimple(text);
    },
    ParseRange: function(text, dateTimeRole) {
		var enableGroups = false;
        text = this.StripBrockets(text);
        if(text.charAt(text.length - 1) == "g") {
			enableGroups = true;
			text = text.substr(0, text.length - 1);
		}
        var data = text.split("..");
        var minNumber, maxNumber = 0;
        var defaultNumber = null;
        if(data.length == 2){
            minNumber = Number(data[0]);
            maxNumber = Number(data[1]);
        }
        else if(data.length == 3){
            minNumber = Number(data[0]);
            maxNumber = Number(data[2]);
            defaultNumber = Number(data[1]);
        }
        
        var part = new _aspxRangeMaskPart(minNumber, maxNumber);
        part.defaultNumber = defaultNumber;
        part.zeroFill = (data[0] == "00") || (data[0].length > 1 && data[0].charAt(0) == "0");
        part.dateTimeRole = dateTimeRole;
        part.enableGroups = enableGroups;
        this.result.push(part);
    },
    ParseEnum: function(text, dateTimeRole) {
        text = this.StripBrockets(text);
        var part = new _aspxEnumMaskPart(text.split("|"));
        part.dateTimeRole = dateTimeRole;
        this.result.push(part);
    },
    StripBrockets: function(text) {
        return text.substr(1, text.length - 2);
    },   

    ParseSimple: function(text) {  
		switch(text) {
			case ":":
				this.ParseLiteral(__aspxCultureInfo.ts);
				break;
			case "/":
				this.ParseLiteral(__aspxCultureInfo.ds);
				break;
			default:
				if(this.dateTimeOnly) {
					this.ParseLiteral(text);					
				} else {
					switch(text) {
						case "L":
						case "l":
							this.ParseChar(text, _aspxAlphaMaskPart, text == "L");
							break;
						case "A":
						case "a":
							this.ParseChar(text, _aspxAlphaNumericMaskPart, text == "A");   
							break;
						case "C":
						case "c":
							this.ParseChar(text, _aspxCharMaskPart, text == "C");
							break;
						case ">":
							 this.currentCaseConvention = 1;
							 break;							
						case "<":
							 this.currentCaseConvention = -1;
							 break;						
						case "~":
							 this.currentCaseConvention = 0;
							 break;							
						case "0":
						case "9":
						case "#":
							this.ParseNumeric(text);   
							break;
						case ".":
							this.ParseLiteral(__aspxCultureInfo.numDecimalPoint);
							break;
						case ",":
							this.ParseLiteral(__aspxCultureInfo.numGroupSeparator);
							break;
						case "$":
							this.ParseLiteral(__aspxCultureInfo.currency);
							break;
						default:
							this.ParseLiteral(text);
							break;													 								 
					}
				}				
				break;
		}
    },
    ParseChar: function(text, ctor, required) {
        var part = this.GetCurrentPart();
        if(part == null || part.typeCode != ctor.prototype.typeCode || part.required != required || part.caseConvention != this.currentCaseConvention) {
            part = new ctor();
            part.required = required;
            part.caseConvention = this.currentCaseConvention;
            this.result.push(part);
        }
        part.Grow(text);
    },
    ParseNumeric: function(text) {
        var required = text == "0";
        var acceptsSignum = text == "#";
        var part = this.GetCurrentPart();
        if(part == null || part.typeCode != _aspxNumericMaskPart.prototype.typeCode || part.required != required || part.acceptsSignum != acceptsSignum) {
            part = new _aspxNumericMaskPart();
            part.required = required;
            part.acceptsSignum = acceptsSignum;
            this.result.push(part);
        }
        part.Grow(text);
    },
    ParseLiteral: function(text) {
        var part = this.GetCurrentPart();
        if(part == null || part.typeCode != _aspxLiteralMaskPart.prototype.typeCode) {
            part = new _aspxLiteralMaskPart();
            this.result.push(part);
        }
        if(text.length > 0 && text.charAt(0) == "\\")
			text = text.substr(1);
        part.Grow(text);
    },
    GetCurrentPart: function() {
        var len = this.result.length;
        if(len < 1)
            return null;
        return this.result[len - 1];
    },
    
    ParseDate: function(text) {
		this.ParseCore(this.GetDateSpecifierReplacement(text), this.GetDateTimeRole(text));
    },
    GetDateSpecifierReplacement: function(text) {
		var now = this.now || new Date();
        switch(text) {
            case "yyyy":				
                return "<0100.." + now.getFullYear() + "..9999>"; 
            case "yyy":				
                return "<100.." + now.getFullYear() + "..9999>"; 
            case "yy":				
                return "<00.." + (now.getFullYear() % 100) + "..99>";
            case "y":				
                return "<0.." + (now.getFullYear() % 100) + "..99>";
            case "MMMM":				
                return "<" + this.GetEnumItems(__aspxCultureInfo.genMonthNames, now.getMonth()).join("|") + ">";
            case "MMM":				
                return "<" + this.GetEnumItems(__aspxCultureInfo.abbrGenMonthNames, now.getMonth()).join("|") + ">";
            case "MM":				
                return "<01.." + (now.getMonth() + 1) + "..12>";
            case "M":				
                return "<1.." + (now.getMonth() + 1) + "..12>";
            case "dddd":
                return "<" + this.GetEnumItems(__aspxCultureInfo.dayNames, now.getDay()).join("|") + ">";
            case "ddd":
                return "<" + this.GetEnumItems(__aspxCultureInfo.abbrDayNames, now.getDay()).join("|") + ">";
            case "dd":				
                return "<01.." + now.getDate() + "..31>";
            case "d":				
                return "<1.." + now.getDate() + "..31>";
            case "hh":
                return "<01.." + this.GetShortHours(now) + "..12>";
            case "h":
                return "<1.." + this.GetShortHours(now) + "..12>";
            case "HH":
                return "<00.." + now.getHours() + "..23>";
            case "H":
                return "<0.." + now.getHours() + "..23>";
            case "mm":
                return "<00.." + now.getMinutes() + "..59>";
            case "m":
                return "<0.." + now.getMinutes() + "..59>";
            case "ss":
                return "<00.." + now.getSeconds() + "..59>";
            case "s":
                return "<0.." + now.getSeconds() + "..59>";
            case "tt":
            case "t":
				if(__aspxCultureInfo.am.length < 1)
					return "";
                return "<" + this.GetAmPmArray(now.getHours(), text.length == 1).join("|") + ">";
        }
        if(/^f{1,6}$/i.test(text)) {
            if(text.length == 1)
                return "<0..9>";
            if(text.length == 2)
                return "<0..99>";                
            return "<0..999>";
        }
		throw "Not supported";
    },	    
    GetDateTimeRole: function(text) {
		var ch = text.charAt(0);
		if(ch == "y" || ch == "M" || ch =="d"
			|| ch.toLowerCase() == "h" || ch == "m" || ch == "s" 
			|| ch.toLowerCase() == "f" || ch == "t")
			return ch;
		return null;
    },
	GetEnumItems: function(items, defaultIndex){
         var result = [].concat(items);
         result[defaultIndex] = "*" + result[defaultIndex];
         return result;
    },
    GetShortHours: function(date){
        var result = (date.getHours() % 12);
        if(result == 0) result = 12;
        return result;
    },
    GetAmPmArray: function(hours, useFirstChar){
        var result = [__aspxCultureInfo.am, __aspxCultureInfo.pm];
        for(var i = 0; i < result.length; i++){
            if(useFirstChar)
                result[i] = result[i].charAt(0);
        }
        if(hours > 12)
            result[1] = "*" + result[1];
        return result;
    },
    
    ParseQuote: function(text) {		
		if(this.quoteMode == null) {
			this.quoteMode = text;
		} else {
			if(text == this.quoteMode)
				this.quoteMode = null;
			else
				this.ParseLiteral(text);
		}
    }
};

_aspxMaskIncludeLiterals = {
    All: 1,
    None: 2,
    DecimalSymbol: 3	
};

_aspxMaskInfo = _aspxCreateClass(null, {    
    constructor: function() {    		
		this.parts = null;
        this.promptChar = "_";        
        this.includeLiterals = _aspxMaskIncludeLiterals.All;        
        this.errorText = "";

		this.caretPos = 0;
		this.selectionLength = 0;	
		
		this.lastEditedPart = null;
		this.lastEditedPartValue = null;
		this.lastDateBag = null;
    },
    
    GetSize: function() { 
        var size = 0;
        for(var i = 0; i < this.parts.length; i++)
            size += this.parts[i].GetSize();
        return size;
    },
    GetText: function() {
		var result = "";
		for(var i = 0; i < this.parts.length; i++) {
			var part = this.parts[i];
			if(_aspxMaskManager.IsLiteralPart(part) || _aspxMaskManager.IsEnumPart(part))
				result += part.GetValue();
			else
				result += part.GetValue().split(" ").join(this.promptChar);
		}
		return result;
    },
    GetValue: function() {
		var list = [];
		for(var i = 0; i < this.parts.length; i++) {
			var part = this.parts[i];
			var partValue = part.GetValue();
			if(partValue.match(/^\s*$/))
				continue;
			if(_aspxMaskManager.IsLiteralPart(part) && _aspxMaskManager.IsIgnorableLiteral(partValue, this.includeLiterals))
				continue;
			if(_aspxMaskManager.IsRangePart(part) && this.includeLiterals != _aspxMaskIncludeLiterals.All)
				partValue = partValue.split(__aspxCultureInfo.numGroupSeparator).join("");
			list.push(partValue);
		}		
		return list.join("");
    },
    SetText: function(text) {
		this.Clear();
		this.SetCaret(0, 0);
		this.SetValueCore(text, _aspxMaskIncludeLiterals.All);
		this.SetCaret(0, 0);
    },
    SetValue: function(value) {
		this.Clear();
		this.SetCaret(0, 0);
		this.SetValueCore(value, this.includeLiterals);
		this.SetCaret(0, 0);
    },
    SetValueCore: function(value, includeLiterals) {
	    for(var i = 0; i < value.length; i++) {
		    var keyInfo = _aspxMaskManager.CreateKeyInfo(value.charCodeAt(i), false, false);
		    _aspxMaskManager.HandleKey(this, keyInfo, false, includeLiterals);
	    }
    },
    Clear: function() {
		for(var i = 0; i < this.parts.length; i++) {
			var part = this.parts[i];
			part.Clear(0, part.GetSize());
		}
    },

    ProcessPaste: function(rawText, caretPosAfterPaste) {
        var currentText = this.GetText();
        var leadLength = 0;
        for(var i = 0; i < Math.min(rawText.length, currentText.length); i++) {
            if(rawText.charAt(i) != currentText.charAt(i))
                break;
            leadLength++;
        }
        var pastedText = rawText.substr(leadLength, caretPosAfterPaste - leadLength);
        this.SetCaret(caretPosAfterPaste - pastedText.length, 0);
        var padLength = 0;
        for(var i = pastedText.length + rawText.length; i < currentText.length; i++) {
			pastedText += " ";
			padLength++;
		}
        this.SetValueCore(pastedText, _aspxMaskIncludeLiterals.All);
        this.caretPos -= padLength;
    },
    IsValid: function() {
		for(var i = 0; i < this.parts.length; i++) {
			if(!this.parts[i].IsValid())
				return false;
		}
		return true;
    },
            
    SetCaret: function(caretPos, selectionLength) {
        if(selectionLength < 0) throw "Internal Error";
        
		this.caretPos = caretPos;
		this.selectionLength = selectionLength;
    },
    IncCaretPos: function(delta) {
        if(!_aspxIsExists(delta))
            delta = 1;
		this.caretPos += delta;
		this.selectionLength = 0;
    },
    MoveToPrevNonLiteral: function() {
		var partPos = 0;
		var resultPos = 0;
		for(var i = 0; i < this.parts.length; i++) {
			if(partPos >= this.caretPos)
				break;
			var part = this.parts[i];
			var nextPartPos = partPos + part.GetSize();
			if(!_aspxMaskManager.IsLiteralPart(part))
				resultPos = nextPartPos < this.caretPos ? nextPartPos : partPos;
			partPos = nextPartPos;
		}
		this.SetCaret(resultPos, 0);
    },        
    MoveToNextNonLiteral: function() {
		var partPos = 0;
		for(var i = 0; i < this.parts.length; i++) {
			var part = this.parts[i];
			var nextPartPos = partPos + part.GetSize();			
			if(nextPartPos > this.caretPos && !_aspxMaskManager.IsLiteralPart(part)) {
				if(partPos <= this.caretPos)
					partPos = nextPartPos;
				break;
			}
			partPos = nextPartPos;
		}
		this.SetCaret(partPos, 0);
    },    
    
    IsPromptCode: function(code) {
		return (code == 32 || code == this.promptChar.charCodeAt(0));
    },
    
    BeforeChange: function(part) {
		this.FixLastRangePart(part);
		this.lastEditedPart = part;
		this.lastEditedPartValue = part.GetValue();
		this.lastDateBag = _aspxMaskDateTimeHelper.GetDateBag(this);
    },
    AfterChange: function(part) {
		if(this.lastEditedPart.GetValue() != this.lastEditedPartValue) {
			var prefixSize = this.GetSizeBeforeEditedPart();
			_aspxMaskDateTimeHelper.FixDate(this);
			this.caretPos += this.GetSizeBeforeEditedPart() - prefixSize;
		}
    },
    GetSizeBeforeEditedPart: function() {
		var pos = 0;
		for(var i = 0; i < this.parts.length; i++) {
			if(this.parts[i] == this.lastEditedPart)
				break;
			pos += this.parts[i].GetSize();
		}
		return pos;
    },
    FixLastRangePart: function(currentPart) {		
		var part = this.lastEditedPart;
		if(!part || part == currentPart || !_aspxMaskManager.IsRangePart(part))
			return false;
		var number = part.GetNumber();
		if(number >= part.minNumber) 
			return false;
		var prevSize = part.GetSize();
		part.SetNumber(part.minNumber);
		this.SetCaret(this.caretPos + part.GetSize() - prevSize, 0);
		return true;
    }
});
_aspxMaskInfo.Create = function(maskText, dateTimeOnly) {
	var info = new _aspxMaskInfo();
	info.parts = _aspxMaskParser.Parse(maskText, dateTimeOnly);
	return info;
}

// Consider moving this functionality to mask info class
_aspxMaskManager = {
    
	OnKeyPress: function(maskInfo, keyInfo) {
	    if(maskInfo.selectionLength > 0)
	        this.ClearSelection(maskInfo);
		
	    this.HandleKey(maskInfo, keyInfo, true, _aspxMaskIncludeLiterals.All);
	},
	OnKeyDown: function(maskInfo, keyInfo) {
	    if(maskInfo.selectionLength > 0 && (keyInfo.keyCode == ASPxKey.Backspace || keyInfo.keyCode == ASPxKey.Delete))
	        this.ClearSelection(maskInfo);
	    else
	        this.HandleControlKey(maskInfo, keyInfo);
	},
	OnMouseWheel: function(maskInfo, delta) {
		this.HandleMouseWheel(maskInfo, delta);
	},
	
	HandleKey: function(maskInfo, keyInfo, autoSkipLiterals, includeLiterals) {	
		var partStart = 0;
		var caretInfoBeforeSkip = null;		
		for(var i = 0; i < maskInfo.parts.length; i++) {
			var part = maskInfo.parts[i];
		    if(this.IsCaretInPart(maskInfo.caretPos, partStart, part)) {				
				if(!this.IsLiteralPart(part) || !this.IsIgnorableLiteral(part.GetValue(), includeLiterals)) {
					maskInfo.BeforeChange(part);
					if(this.savedKeyDownKeyInfo && this.savedKeyDownKeyInfo.keyCode == ASPxKey.Decimal)
						keyInfo.keyCode = __aspxCultureInfo.numDecimalPoint.charCodeAt(0);
					if(part.HandleKey(maskInfo, keyInfo, maskInfo.caretPos - partStart)) {								
						if(autoSkipLiterals)
							this.TrySkipLiteralOnPartEdge(maskInfo, partStart, i);
						maskInfo.AfterChange(part);
						return;
					}
					if(caretInfoBeforeSkip == null)
						caretInfoBeforeSkip = [ maskInfo.caretPos, maskInfo.selectionLength ];
				}									
				maskInfo.SetCaret(partStart + part.GetSize(), 0);
		    }
			partStart += part.GetSize();
		}
		if(caretInfoBeforeSkip != null)
			maskInfo.SetCaret(caretInfoBeforeSkip[0], caretInfoBeforeSkip[1]);
	},
	HandleControlKey: function(maskInfo, keyInfo) {
		maskInfo.selectionLength = 0;
		var partStart = 0;		
	    for(var i = 0; i < maskInfo.parts.length; i++) {
		    var part = maskInfo.parts[i];
		    if(this.IsCaretInPartOnControlKey(maskInfo.caretPos, partStart, part, keyInfo.keyCode)) {
				maskInfo.BeforeChange(part);
			    part.HandleControlKey(maskInfo, keyInfo, maskInfo.caretPos - partStart);
				if(keyInfo.keyCode == ASPxKey.Delete)
					this.TrySkipLiteralOnPartEdge(maskInfo, partStart, i);
		        maskInfo.AfterChange(part);
			    break;
		    }
		    partStart += part.GetSize();
	    }
	},
	HandleMouseWheel: function(maskInfo, delta) {
		var partStart = 0;		
		for(var i = 0; i < maskInfo.parts.length; i++) {
			var part = maskInfo.parts[i];
			if(this.IsCaretInPartOnMouseWheel(maskInfo.caretPos, partStart, part)) {
				maskInfo.BeforeChange(part);
				part.HandleMouseWheel(maskInfo, delta, maskInfo.caretPos - partStart);
				maskInfo.AfterChange(part);
				break;
			}			
			partStart += part.GetSize();
		}
	},
	
	TrySkipLiteralOnPartEdge: function(maskInfo, partStartPos, partIndex) {
	    var part = maskInfo.parts[partIndex];
	    var posInPart = maskInfo.caretPos - partStartPos;
	    var amount = 0;
	    	    
        if(part.AllowIncreaseSize()) return;
        if(partIndex > maskInfo.parts.length - 3 ||  posInPart < part.GetSize()) return;
        var sibling = maskInfo.parts[partIndex + 1];
        if(this.IsLiteralPart(sibling))
            amount = sibling.GetSize();
	    
	    maskInfo.IncCaretPos(amount);
	},
	
	ClearSelection: function(maskInfo){		
		var partStart = 0;
	    for(var i = 0; i < maskInfo.parts.length; i++) {
		    var part = maskInfo.parts[i];
		    var outerLeft = Math.min(partStart, maskInfo.caretPos);
		    var outerRight = Math.max(partStart + part.GetSize(), maskInfo.caretPos + maskInfo.selectionLength);
		    var size = part.GetSize();
		    if(size + maskInfo.selectionLength > outerRight - outerLeft){
		        var innerLeft = Math.max(partStart, maskInfo.caretPos);
		        var innerRight = Math.min(partStart + size, maskInfo.caretPos + maskInfo.selectionLength);		        
		        part.Clear(innerLeft - partStart, innerRight - partStart);
		        maskInfo.selectionLength += part.GetSize() - size;		        
		    }
		    partStart += part.GetSize();
	    }
	    maskInfo.selectionLength = 0;
	},
	
	IsCaretInPart: function(caretPos, partStartPos, part) {
		if(caretPos < partStartPos)
			return false;
		var nextPartPos = partStartPos + part.GetSize();
		if(caretPos > nextPartPos)
			return false;
		if(caretPos == nextPartPos)
			return part.AllowIncreaseSize();
		return true;
	},
	IsCaretInPartOnControlKey: function(caretPos, partStartPos, part, keyCode) {
		if(caretPos == partStartPos) {
			if(keyCode == ASPxKey.Backspace || keyCode == ASPxKey.Left)
				return false;
			return true;
		}
		var nextPartPos = partStartPos + part.GetSize();
		if(partStartPos < caretPos && caretPos < nextPartPos) 
			return true;
		if(caretPos == nextPartPos) {
		    if(keyCode == ASPxKey.Up || keyCode == ASPxKey.Down)
		        return part.SupportsUpDown();
			if(keyCode == ASPxKey.Backspace || keyCode == ASPxKey.Left)
				return true;
			return false;			
		}
		return false;
	},
	IsCaretInPartOnMouseWheel: function(caretPos, partStartPos, part) {
	    if(!part.SupportsUpDown()) 
	        return false;
	    return caretPos >= partStartPos && caretPos <= partStartPos + part.GetSize();
	},
	
    GetHintHtml: function(maskInfo) {
		var pos = 0;
		for(var i = 0; i < maskInfo.parts.length; i++) {
			var part = maskInfo.parts[i];
			if(this.IsCaretInPartOnMouseWheel(maskInfo.caretPos, pos, part))
				return part.GetHintHtml();
			pos += part.GetSize();
		}
		return "";
    },	
		
	CreateKeyInfo: function(keyCode, shiftState, ctrlState) {
		return {
			keyCode: keyCode,
			shiftState: shiftState,
			ctrlState: ctrlState
		};	
	},
	CreateKeyInfoByEvent: function(evt) {
	    return this.CreateKeyInfo(_aspxGetKeyCode(evt), evt.shiftKey, evt.ctrlKey);
	},
	
    IsLiteralPart: function(part) {
		return (part.typeCode == _aspxLiteralMaskPart.prototype.typeCode);
    },    
    IsEnumPart: function(part) {
		return (part.typeCode == _aspxEnumMaskPart.prototype.typeCode);
    },
    IsRangePart: function(part) {
		return (part.typeCode == _aspxRangeMaskPart.prototype.typeCode);
    },
	IsAlphaCode: function(code) {		
		return (64 < code && code < 91 || 96 < code && code < 123 || code > 127);
	},
	IsDigitCode: function(code) {		
		return (47 < code && code < 58);
	},
    IsSignumCode: function(code) {		
		return (code == 43 || code == 45);
	},
	
	CanHandleControlKey: function(keyInfo) {
	    if(keyInfo.shiftState)
	        return false;
	    return keyInfo.keyCode == ASPxKey.Up || keyInfo.keyCode == ASPxKey.Down
	        || keyInfo.keyCode == ASPxKey.Left || keyInfo.keyCode == ASPxKey.Right
	        || keyInfo.keyCode == ASPxKey.Backspace || keyInfo.keyCode == ASPxKey.Delete;
	},	
	IsPrintableKeyCode: function(keyInfo) {
	    if(keyInfo.ctrlState) 
	        return false;
	    var code = keyInfo.keyCode;	    
	    return code == 32
	        || (code >= 48 && code <= 57)
	        || (code >= 65 && code <= 90)
	        || (code >= 96 && code <= 107)
	        || (code >= 109 && code <= 111)
	        || (code >= 186 && code <= 192)
	        || (code >= 219 && code <= 222);	    
	},
	
	IsIgnorableLiteral: function(text, mode) {
		if(mode == _aspxMaskIncludeLiterals.None)
			return true;
		if(mode == _aspxMaskIncludeLiterals.All)
			return false;
		return text != __aspxCultureInfo.numDecimalPoint;
	}
};
_aspxMaskManager.keyCancelled = false;
_aspxMaskManager.keyDownHandled = false;
_aspxMaskManager.savedKeyDownKeyInfo = null;


_aspxMaskDateTimeHelper = {

	GetDate: function(maskInfo) {
		return this.CreateDateFromBag(this.GetDateBag(maskInfo));
	},
	
	GetDateBag: function(maskInfo) {
		var now = new Date();
		var bag = {
			year: now.getFullYear(), 
			month: now.getMonth(), 
			day: now.getDate(),
			hours: 0,
			min: 0,
			sec: 0,
			msec: 0,
			pm: false,
			hasAmPm: false,
			hasDate: false
		};
		for(var i = 0; i < maskInfo.parts.length; i++) {
			var part = maskInfo.parts[i];
			switch(part.dateTimeRole) {
				case "y":					
					bag.year = Number(part.GetValue());
					if(bag.year < 100)
						bag.year = _aspxExpandTwoDigitYear(bag.year);					
					bag.hasDate = true;
					break;
				case "M":
					bag.month = _aspxMaskManager.IsEnumPart(part) ? part.itemIndex : Number(part.GetValue()) - 1;
					bag.hasDate = true;
					break;
				case "d":
					if(!_aspxMaskManager.IsEnumPart(part)) {
						bag.day = Number(part.GetValue());
						bag.hasDate = true;
					}
					break;
				case "H":
				case "h":
					bag.hours = Number(part.GetValue());
					break;
				case "m":
					bag.min = Number(part.GetValue());
					break;
				case "s":
					bag.sec = Number(part.GetValue());
					break;
				case "f":
				case "F":
					bag.msec = Number(part.GetValue());
					break;
				case "t":
					bag.hasAmPm = true;
					bag.pm = _aspxMaskManager.IsEnumPart(part) && part.itemIndex > 0;
					break;
			}
		}
		if(bag.hasAmPm) {
			if(!bag.pm && bag.hours == 12)
				bag.hours = 0;
			if(bag.pm && bag.hours < 12)
				bag.hours += 12;
		}
		return bag;
	},
	CreateDateFromBag: function(bag) {
		if(bag.year == 100 && bag.month == 0 && bag.day == 1 
			&& bag.hours + bag.min + bag.sec + bag.msec == 0)
			return null;
		return new Date(bag.year, bag.month, bag.day, bag.hours, bag.min, bag.sec, bag.msec);
	},
	
	FixDate: function(maskInfo) {
		var bag = this.GetDateBag(maskInfo);
		if(!bag.hasDate)
			return;
		var maxDay = this.GetMaxDayInMonth(bag.month, bag.year);
		
		if(bag.day > maxDay) {
			var lockDay = false, lockMonth = false, lockYear = false;
			for(var i = 0; i < maskInfo.parts.length; i++) {
				var part = maskInfo.parts[i];
				if(part == maskInfo.lastEditedPart) break;
				switch(part.dateTimeRole) {
					case "d":
						lockDay = true;
						break;
					case "M":
						lockMonth = true;
						break;
					case "y":
						lockYear = true;
						break;
				}
				if(lockDay && lockMonth && lockYear)
					break;
			}

			switch(maskInfo.lastEditedPart.dateTimeRole) {
				case "d":
					if(bag.day == 29 && bag.month == 1 && !lockYear)
						bag.year = this.GetNextLeapYear(bag.year);
					else if(!lockMonth)
						bag.month--;
					else
						bag.day = maxDay;
					break;
				case "M":
					if(bag.day == 29 && bag.month == 1 && !lockYear)
						bag.year = this.GetNextLeapYear(bag.year);
					else if(!lockDay)
						bag.day = maxDay;
					else
						bag = maskInfo.lastDateBag;
					break;
				case "y":
					if(bag.day == 29 && bag.month == 1)
						bag.day = 28;
					break;
			}
		}

		this.SetDate(maskInfo, this.CreateDateFromBag(bag));
	},
    GetMaxDayInMonth: function(month, year) {
		if(month == 1)
			return this.IsLeapYear(year) ? 29 : 28;
		if(month == 3 || month == 5 || month == 8 || month == 10)
			return 30;
		return 31;
    },
    IsLeapYear: function(year) {
		if(year % 4 != 0)
			return false;
		if(year % 100 == 0)		
			return year % 400 == 0;
		return true;
    },
    GetNextLeapYear: function(year) {
		var result = 4 * (1 + Math.floor(year / 4));
		if(!this.IsLeapYear(result))
			result += 4;
		return result;
    },	

    SetDate: function(maskInfo, date) {
		if(date == null)
			date = new Date(100, 0, 1);
		for(var i = 0; i < maskInfo.parts.length; i++) {
			var part = maskInfo.parts[i];
			part.EnsureValueInitialized();			
			switch(part.dateTimeRole) {
				case "y":
					this.SetYear(part, date);
					break;
				case "M":
					this.SetMonth(part, date);
					break;
				case "d":
					this.SetDay(part, date);
					break;
				case "h":
					this.SetHours(part, date, false);
					break;
				case "H":
					this.SetHours(part, date, true);
					break;	
				case "m":
					this.SetMinutes(part, date);
					break;
				case "s":
					this.SetSeconds(part, date);						
					break;
				case "f":
				case "F":
					this.SetMilliseconds(part, date);
					break;
				case "t":
					this.SetAmPm(part, date);						
					break;						
			}			
		}
    },
    
    
    SetYear: function(part, date) {
		if(!_aspxMaskManager.IsRangePart(part))
			return;
		var value = date.getFullYear();
		if(part.maxNumber < 100)
			value = value % 100;
		part.SetNumber(value);
    },
    SetMonth: function(part, date) {	
		if(_aspxMaskManager.IsRangePart(part))
			part.SetNumber(1 + date.getMonth());
		else if(_aspxMaskManager.IsEnumPart(part))
			part.itemIndex = date.getMonth();
    },
    SetDay: function(part, date) {
		if(_aspxMaskManager.IsRangePart(part))
			part.SetNumber(date.getDate());
		else if(_aspxMaskManager.IsEnumPart(part))
			part.itemIndex = date.getDay();
    },
    SetHours: function(part, date, full) {
		if(!_aspxMaskManager.IsRangePart(part))
			return;    
		var value = date.getHours();
		if(!full) {
			value = value % 12;
		    if(value == 0)
			    value = 12;
	    }
		part.SetNumber(value);
    },
    SetMinutes: function(part, date) {
		if(!_aspxMaskManager.IsRangePart(part))
			return;    
		part.SetNumber(date.getMinutes());
    },
    SetSeconds: function(part, date) {
		if(!_aspxMaskManager.IsRangePart(part))
			return;
		part.SetNumber(date.getSeconds());
    },
    SetMilliseconds: function(part, date) {
		if(!_aspxMaskManager.IsRangePart(part))
			return;
		var value = date.getMilliseconds();
		while(value > part.maxNumber)
			value = value / 10;
		part.SetNumber(Math.round(value));
    },
    SetAmPm: function(part, date) {
		if(!_aspxMaskManager.IsEnumPart(part))
			return;    
		part.itemIndex = date.getHours() < 12 ? 0 : 1;
    }  
};
ASPxClientProgressBar = _aspxCreateClass(ASPxClientEditBase, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    InlineInitialize: function() {
        ASPxClientEditBase.prototype.InlineInitialize.call(this);
        this.GetProgressBar().mainElement = this.GetMainElement(); // hack
    },
    AdjustControlCore: function() {
        ASPxClientEditBase.prototype.AdjustControlCore.call(this);    
        this.GetProgressBar().AdjustControlCore();
    },
    
    GetProgressBar: function() {
        return  window[this.name + "_MC"];
    },
    
    ChangeEnabledStateItems: function(enabled){
        aspxGetStateController().SetElementEnabled(this.GetMainElement(), enabled);
        var indicatorTextCell = this.GetProgressBar().GetValueIndicatorCell();
        if (_aspxIsExists(indicatorTextCell))
            aspxGetStateController().SetElementEnabled(indicatorTextCell, enabled);
    },
        
    // API    
    SetPosition: function(position) {
        this.GetProgressBar().SetPosition(position);
    },    
    GetPosition: function() {
        return this.GetProgressBar().GetPosition();
    },        
    GetPercent: function() {
        this.GetProgressBar().GetPercent();
    },
    
    SetValue: function(value) {
        this.SetPosition(value);
    },
    GetValue: function() {
        return this.GetPosition();
    }
 });