ASPxClientToolbarComboBox = _aspxCreateClass(ASPxClientComboBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.defaultCaption = "";
    },
    SetValue: function(value){
        if (!value && this.defaultCaption)
            value = this.defaultCaption;
        
        ASPxClientComboBox.prototype.SetValue.call(this, value);
        if(this.GetSelectedIndex() == -1)
            ASPxClientComboBox.prototype.SetText.call(this, value);
    }
});
ASPxClientNativeToolbarComboBox = _aspxCreateClass(ASPxClientNativeComboBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        this.defaultCaption = "";
    },
    SetValue: function(value){
        if (!value && this.defaultCaption)
            value = this.defaultCaption;
        
        ASPxClientNativeComboBox.prototype.SetValue.call(this, value);
        if(this.GetSelectedIndex() == -1)
            ASPxClientNativeComboBox.prototype.SetText.call(this, value);
    }
});

ASPxClientToolbarCustomCssComboBox = _aspxCreateClass(ASPxClientToolbarComboBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.cssClasses = [];
        this.tagNames = [];
        
        this.cssClassesValueHashTable = {};
        this.tagNameCssClassesValueHashTable = {};
    },
    Initialize: function(){
        ASPxClientComboBox.prototype.Initialize.call(this);
        this.CreateTagNamesAndCssClassesHashTable();
    },
    GetValue: function() {
        var ret = ASPxClientToolbarComboBox.prototype.GetValue.call(this);        
        if (ret && this.AreCustomTagsExist())
            ret = this.GetExtValueByIndex(ret);
        return ret;
    },
    SetValue: function(value) {
        var newValue = value;
        if (newValue && this.AreCustomTagsExist())
            newValue = this.GetIndexByTagNameAndCssClass(value.tagName, value.cssClass);
        ASPxClientToolbarComboBox.prototype.SetValue.call(this, newValue);
    }
});
ASPxClientNativeToolbarCustomCssComboBox = _aspxCreateClass(ASPxClientNativeToolbarComboBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.cssClasses = [];
        this.tagNames = [];
        
        this.cssClassesValueHashTable = {};
        this.tagNameCssClassesValueHashTable = {};
    },
    Initialize: function(){
        ASPxClientNativeToolbarComboBox.prototype.Initialize.call(this);
        this.CreateTagNamesAndCssClassesHashTable();
    },
    GetValue: function() {
        var ret = ASPxClientNativeToolbarComboBox.prototype.GetValue.call(this);        
        if (ret && this.AreCustomTagsExist())
            ret = this.GetExtValueByIndex(ret);
        return ret;
    },
    SetValue: function(value) {
        var newValue = value;
        if (newValue && this.AreCustomTagsExist())
            newValue = this.GetIndexByTagNameAndCssClass(value.tagName, value.cssClass);
        ASPxClientNativeToolbarComboBox.prototype.SetValue.call(this, newValue);
    }
});
ASPxClientToolbarCustomCssComboBox.prototype.CreateTagNamesAndCssClassesHashTable=
    ASPxClientNativeToolbarCustomCssComboBox.prototype.CreateTagNamesAndCssClassesHashTable= function() {
        for (var i = 0; i < this.tagNames.length; i++) {
            var tagName = this.tagNames[i];
            var cssClass = this.cssClasses[i];
            if (tagName) {
                var key = ASPxClientToolbarCustomCssComboBox.GetKeyByTagNameAndCssClass(tagName, cssClass);
                if (!_aspxIsExists(this.tagNameCssClassesValueHashTable[key]))
                    this.tagNameCssClassesValueHashTable[key] = this.GetItem(i).value;
            }
            else if (cssClass) {
                if (!_aspxIsExists(this.cssClassesValueHashTable[cssClass]))
                    this.cssClassesValueHashTable[cssClass] = this.GetItem(i).value;
            }
        }
}
ASPxClientToolbarCustomCssComboBox.prototype.GetIndexByTagNameAndCssClass =
    ASPxClientNativeToolbarCustomCssComboBox.prototype.GetIndexByTagNameAndCssClass = function(tagName, cssClass) {
        var ret = null;
        if (tagName) {
            var key = ASPxClientToolbarCustomCssComboBox.GetKeyByTagNameAndCssClass(tagName, cssClass);
            ret = _aspxIsExists(this.tagNameCssClassesValueHashTable[key]) ? this.tagNameCssClassesValueHashTable[key] : null;
        }
        if (cssClass && (ret == null))
            ret = _aspxIsExists(this.cssClassesValueHashTable[cssClass]) ? this.cssClassesValueHashTable[cssClass] : null;
        return ret;
    }
ASPxClientToolbarCustomCssComboBox.prototype.GetExtValueByIndex =
    ASPxClientNativeToolbarCustomCssComboBox.prototype.GetExtValueByIndex = function(index) {
    return { tagName: this.tagNames[index], cssClass: this.cssClasses[index] };
}
ASPxClientToolbarCustomCssComboBox.prototype.AreCustomTagsExist =
    ASPxClientNativeToolbarCustomCssComboBox.prototype.AreCustomTagsExist = function() {
    return (this.tagNames.length > 0) && (this.cssClasses.length > 0);
}
ASPxClientToolbarCustomCssComboBox.GetKeyByTagNameAndCssClass = 
    ASPxClientNativeToolbarCustomCssComboBox.GetKeyByTagNameAndCssClass = function(tagName, cssClass) {
    return tagName + "|" + cssClass;
}

/*region* * * * * * * * * * * * * * *  ASPxClientToolbarListBox  * * * * * * * * * * * * * * * */
ASPxClientToolbarListBox = _aspxCreateClass(ASPxClientListBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    GetItem: function(index){
        var item = ASPxClientListBox.prototype.GetItem.call(this, index);
        if(item){
            item.text = _aspxTrim(item.text);
            return item;
        }
        return null;
    }
});
ASPxClientNativeToolbarListBox = _aspxCreateClass(ASPxClientNativeListBox, {
    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
    },
    GetItem: function(index){
        var item = ASPxClientNativeListBox.prototype.GetItem.call(this, index);
        if(item){
            item.text = _aspxTrim(item.text);
            return item;
        }
        return null;
    }
});
var __aspxCPColorTableIDSuffix = "_CT";
var __aspxCPColorCellIDSuffixPart = "_C";

ASPxClientColorPicker = _aspxCreateClass(ASPxClientControl, {
	constructor: function(name){
		this.constructor.prototype.constructor.call(this, name);
        this.colorColCount = 8; // sync with server!
		this.colorValues = [
		      "#000000", "#993300", "#333300", "#003300", "#003366", "#000080", "#333399", "#333333",
              "#800000", "#ff6600", "#808000", "#008000", "#008080", "#0000ff", "#666699", "#808080",
              "#ff0000", "#ff9900", "#99cc00", "#339966", "#33cccc", "#3366ff", "#800080", "#999999",
              "#ff00ff", "#ffcc00", "#ffff00", "#00ff00", "#00ffff", "#00ccff", "#993366", "#c0c0c0",
              "#ff99cc", "#ffcc99", "#ffff99", "#ccffcc", "#ccffff", "#99ccff", "#cc99ff", "#ffffff" ];
        this.selectedIndex = -1;

		this.ColorChanged = new ASPxClientEvent();
    },
    Initialize: function(){
        this.constructor.prototype.Initialize.call(this);
        
        var mainElement = this.GetMainElement();
        mainElement.unselectable = "on";
        mainElement.rows[0].cells[0].unselectable = "on";
        this.InitializeColorsTable();
    },
    InitializeColorsTable: function() {
        var colorsTable = this.GetColorsTableElement();
        colorsTable.unselectable = "on";
        var colorIndex = 0;
        for(var i = 0; i < colorsTable.rows.length; i++){
            for(var j = 0; j < colorsTable.rows[i].cells.length; j++){
                var colorCell = colorsTable.rows[i].cells[j];
                colorCell.id = this.GetColorCellElementID(colorIndex);
                colorCell.unselectable = "on";
                if (__aspxSafariFamily)
                    colorCell.cellIndex_Safari = j;
                    
                var colorDiv = colorCell.firstChild;
                if(_aspxIsExists(colorDiv) && colorDiv.tagName == "DIV"){
                    colorDiv.style.backgroundColor = this.colorValues[colorIndex];
                    colorDiv.unselectable = "on";
                    colorIndex++;
                }
            } 
        } 
    },
    FindColorIndexByColor: function(colorValue){
        for(var i = 0; i < this.colorValues.length; i++) {
            if(this.colorValues[i] == colorValue)
                return i;
        }
        return -1;
    },
    GetColorsTableElement: function(){
        return _aspxGetElementById(this.name + __aspxCPColorTableIDSuffix);
    },
    GetColorCellElementID: function(colorIndex){
        return this.name + __aspxCPColorCellIDSuffixPart + colorIndex.toString();
    },
    GetColorCellElementByIndex: function(colorIndex){
        return _aspxGetElementById(this.GetColorCellElementID(colorIndex));
    },
    SelectColorByIndex: function(colorIndex, fireEvent){
        if(this.selectedIndex != colorIndex) {
            var stateController = aspxGetStateController();
            
            var element = this.GetColorCellElementByIndex(this.selectedIndex);
            if(element != null)
                stateController.DeselectElementBySrcElement(element);
            
            this.selectedIndex = colorIndex;
            
            element = this.GetColorCellElementByIndex(this.selectedIndex);
            if(element != null)
                stateController.SelectElementBySrcElement(element);
            
            if(fireEvent)
                this.RaiseColorChanged();
        }
    },
    OnControlClick: function(clickedElement, htmlEvent) {
        if (_aspxIsExists(clickedElement)) {
            if (clickedElement.tagName == "DIV")
                clickedElement = clickedElement.parentNode;
            if (clickedElement.tagName == "TD") {
                var cellIndex = !__aspxSafariFamily ? clickedElement.cellIndex : clickedElement.cellIndex_Safari;
                var rowIndex = clickedElement.parentNode.rowIndex;
                var colorIndex = rowIndex * this.colorColCount + cellIndex;
                if(0 <= colorIndex && colorIndex < this.colorValues.length)
                    this.SelectColorByIndex(colorIndex, true);
            }
        }
    },
    // TODO API
    RaiseColorChanged: function(){
        if(!this.ColorChanged.IsEmpty()){
            var args = new ASPxClientEventArgs(false);// [Seleznev] No argument
            this.ColorChanged.FireEvent(this, args);
        }
    },
    GetColor: function(){
        if(0 <= this.selectedIndex && this.selectedIndex < this.colorValues.length)
            return this.colorValues[this.selectedIndex];
        return "";
    },
    SetColor: function(value){
        var colorIndex = this.FindColorIndexByColor(value);
        this.SelectColorByIndex(colorIndex, false);
    }
});
var __aspxBarDockToolbarNameSuffix = "_T";

ASPxClientBarDockControl = _aspxCreateClass(ASPxClientControl, {
	constructor: function(name){
		this.constructor.prototype.constructor.call(this, name);
		
		this.toolbarsCount = -1;
		this.toolbarName = "";
		this.updateItemsTimeout = 200;
		this.updateItemsTimerId = -1;
		this.updateItemsTimerString = "";
		
		this.Command = new ASPxClientEvent();
		this.ItemUpdate = new ASPxClientEvent();
    },
    Initialize: function(){
        this.constructor.prototype.Initialize.call(this);
        this.toolbarName = this.name + __aspxBarDockToolbarNameSuffix;
        this.updateItemsTimerString = "aspxBDUpdateItemsByTimer('" + this.name + "')";
    },
    GetToolbarControl: function(index){
        return aspxGetControlCollection().Get(this.toolbarName + index);
    },
    Focus: function(){
        if(this.toolbarsCount > 0)
            this.GetToolbarControl(0).Focus();
    },
    DoUpdateItems: function(){    
        if(!_aspxIsExists(this.GetMainElement())) return;
        for(var i = 0 ; i < this.toolbarsCount; i ++) {
            var toolBar = this.GetToolbarControl(i);
            if (_aspxIsExists(toolBar)) 
                toolBar.UpdateItems();
        }
    },
    OnCommand: function(toolbar, item, value){
        this.RaiseCommand(toolbar, item, value);
    },
    OnItemUpdate: function(toolbar, item){
        this.RaiseItemUpdate(toolbar, item);
    },
    OnPopup: function(toolbar, e){
        this.HideAllPopups(toolbar, e.item);
    },
    // API
    RaiseCommand: function(toolbar, item, value){
        if(!this.Command.IsEmpty()){
            var args = new ASPxClientBarDockControlEventArgs(false, toolbar, item, value);
            this.Command.FireEvent(this, args);
        }
    },
    RaiseItemUpdate: function(toolbar, item){
        if(!this.ItemUpdate.IsEmpty()){
            var args = new ASPxClientBarDockControlEventArgs(false, toolbar, item, null);
            this.ItemUpdate.FireEvent(this, args);
        }
    },
    UpdateItems: function(){
        _aspxClearTimer(this.updateItemsTimerId);
        this.updateItemsTimerId = window.setTimeout(this.updateItemsTimerString, this.updateItemsTimeout);
    },
    HideAllPopups: function(exeptToolbar, exeptItem){
        var exeptId = _aspxIsExists(exeptToolbar) && _aspxIsExists(exeptItem) ? 
            exeptToolbar.GetMenuElement(exeptItem.GetIndexPath()).id : "";
        aspxGetMenuCollection().DoHidePopupMenus(null, 0, "", false, exeptId);
        for(var i = 0 ; i < this.toolbarsCount; i ++) {
            var toolBar = this.GetToolbarControl(i);
            if (_aspxIsExists(toolBar))
                toolBar.HideTemplatesPopup();
        }
    }
});
   
ASPxClientBarDockControlEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(processOnServer, toolbar, item, value){
        this.constructor.prototype.constructor.call(this);
        this.item = item;
        this.toolbar = toolbar;
        this.value = value;
    }
});

ASPxClientToolbarItem = _aspxCreateClass(ASPxClientMenuItem, {
	constructor: function(menu, parent, index, name){
	    this.constructor.prototype.constructor.call(this, menu, parent, index, name);
        this.itemTemplateControl = null;
        this.itemTextTemplateControl = null;
    },
    
	SetValue: function(value){
	    var templateControl = this.GetTemplateControl();
	    if(_aspxIsExists(templateControl)){
	        templateControl.SetValue(value);
	    }
	},
	GetTemplateControl: function(){
	    var itemTemplate = this.GetItemTemplateControl();
	    if(_aspxIsExists(itemTemplate))
	        return itemTemplate;
        return this.GetItemTextTemplateControl();
    },
    GetItemTemplateControl: function(){
    	if(this.itemTemplateControl === null){
	        var templateCell = this.menu.GetItemTextCell(this.GetIndexPath());
	        this.itemTemplateControl = this.GetControlFromParentElement(templateCell);
	    }
    	return this.itemTemplateControl;
    },
    GetItemTextTemplateControl: function(){
    	if(this.itemTextTemplateControl === null){
	        var templateCell = this.menu.GetItemTemplateCell(this.GetIndexPath());
	        this.itemTextTemplateControl = this.GetControlFromParentElement(templateCell);
	    }
    	return this.itemTextTemplateControl;
    },
    GetControlFromParentElement: function(element){
        if (_aspxIsExists(element)) {
            for(var i = 0; i < element.childNodes.length; i ++) {
                var childNode = element.childNodes[i];
                if(!_aspxIsExists(childNode.id)) continue;
                
                var control = aspxGetControlCollection().Get(childNode.id);
                if (_aspxIsExists(control))
                    return control;
            }
        }
        return null;
    }
});

ASPxClientToolbar = _aspxCreateClass(ASPxClientMenu, {
	constructor: function(name){
		this.constructor.prototype.constructor.call(this, name);
		this.barDockControlName = "";
		
		// Cache
		this.barDockControl = null;
		
		this.ItemUpdate = new ASPxClientEvent();
		this.Command = new ASPxClientEvent();
    },
    Initialize: function(){
        this.constructor.prototype.Initialize.call(this);
        this.SetMenuUnselectable();
        this.InitializeItemState();        
    },
    InitializeItemState: function() {
        var element = this.GetItemTextCell(this.GetItem(0).GetIndexPath());
        if (element != null)
            element = this.GetItemImageCell(this.GetItem(0).GetIndexPath());
            
        this.SetHoverElement(element);
        this.SetHoverElement(null);
        if (_aspxIsExists(this.itemCheckedGroups[0])) {
            this.SetItemChecked(this.itemCheckedGroups[0][0].toString(), true);
            this.SetItemChecked(this.itemCheckedGroups[0][0].toString(), false);
        }
    },
    
    SetMenuUnselectable: function() {
        for (var i = 0 ; i < this.GetItemCount(); i++) {
            var indexPath = this.GetItem(i).GetIndexPath();  
            _aspxSetElementAsUnselectable(this.GetItemTextCell(indexPath), true);
            _aspxSetElementAsUnselectable(this.GetItemImageCell(indexPath));
            _aspxSetElementAsUnselectable(this.GetItemIndentCell(indexPath));
            _aspxSetElementAsUnselectable(this.GetItemTemplateCell(indexPath));
            _aspxSetElementAsUnselectable(this.GetItemPopOutImageCell(indexPath));
            _aspxSetElementAsUnselectable(this.GetMenuBorderCorrectorElement(indexPath));
			_aspxSetElementAsUnselectable(this.GetItemIndentElement(indexPath), true);
			_aspxSetElementAsUnselectable(this.GetItemSeparatorElement(indexPath), true);
			_aspxSetElementAsUnselectable(this.GetItemSeparatorIndentElement(indexPath), true);
        }
    },
    GetToolbarDockControl: function(){
        if(this.barDockControl == null)
            this.barDockControl = aspxGetControlCollection().Get(this.barDockControlName);
        return this.barDockControl;
    },
    GetClientItemType: function(){
        return ASPxClientToolbarItem;
    },
    GetToolbatItemTemplateID: function(indexPath){
        return this.GetItemByIndexPath(indexPath).name;
    },
    GetToolbarItemTemplateId: function(indexPath){
        var templateName = this.GetToolbatItemTemplateID(indexPath);
        return this.name + "_ITTCNT" + indexPath+ "_" + templateName;
    },
    GetToolbarItemTemplateElement: function(indexPath){
        return _aspxGetElementById(this.GetToolbarItemTemplateId(indexPath));
    },
    GetToolbarItemTemplateControl: function(indexPath){
        var templateElement = this.GetToolbarItemTemplateElement(indexPath);
        return _aspxIsExists(templateElement) ? aspxGetControlCollection().Get(this.GetToolbarItemTemplateId(indexPath)) : null;
    },
    SetItemEnabled: function(indexPath, enabled){
        ASPxClientMenu.prototype.SetItemEnabled.call(this, indexPath, enabled);
        var item = this.GetItemByIndexPath(indexPath);
        var templateControl = item.GetTemplateControl();
	    if(_aspxIsExists(templateControl)){
	        var templateElement = templateControl.GetMainElement();
	        if(enabled)
	            aspxGetStateController().EnableElement(templateElement);
	        else
	            aspxGetStateController().DisableElement(templateElement);
	    }
    },
    
    HideTemplatesPopup: function(){
        for (var i = 0 ; i < this.GetItemCount(); i++) {
            var item = this.GetItem(i);
            var templateControl = item.GetTemplateControl();
	        if(_aspxIsExists(templateControl)){
	            if(_aspxIsExists(templateControl.HideDropDown))
	                templateControl.HideDropDown();
	        }
        }
    },
    // Utils
    DoUpdateItems: function(){
        for (var i = 0 ; i < this.GetItemCount(); i++)
            this.RaiseItemUpdate(this.GetItem(i));
    },

    OnTemplateItemValueChanged: function(itemName, itemValue){
        var item = this.GetItemByName(itemName);
        this.DoRaiseCommand(item.GetIndexPath(), itemValue);
    },
    DoRaiseCommand: function(indexPath, value){
        var item = this.GetItemByIndexPath(indexPath);
        this.RaiseCommand(item, value);
    },
    RaiseItemClick: function(indexPath, htmlEvent){
        if(_aspxIsExists(this.GetToolbarItemTemplateControl(indexPath))) return;
        
        this.DoRaiseCommand(indexPath, null);
        if(_aspxIsExists(ASPxClientMenu.prototype.RaiseItemClick))
            ASPxClientMenu.prototype.RaiseItemClick.call(this, indexPath, htmlEvent);
    },
    // API
    RaiseCommand: function(item, value){
        if(!this.Command.IsEmpty()){
            var args = new ASPxClientToolbarEventArgs(item, value);
            this.Command.FireEvent(this, args);
        }
    },
    RaiseItemUpdate: function(item){
        if(!this.ItemUpdate.IsEmpty()){
            var args = new ASPxClientToolbarEventArgs(item, null);
            this.ItemUpdate.FireEvent(this, args);
        }
    },
    UpdateItems: function(){
        this.DoUpdateItems();
    }
});

ASPxClientToolbarEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(item, value){
        this.constructor.prototype.constructor.call(this);
        this.item = item;
        this.value = value;
    }
});

ASPxClientToolbarColorButton = _aspxCreateClass(ASPxClientControl, {
    constructor: function(name){
		this.constructor.prototype.constructor.call(this, name);
		
		this.colorDiv = null;
		this.colorPicker = null;
		this.colorPickerName = "";
		this.colorPickerLockCount = 0;
		this.defaultColor = "";
		this.ColorChanged = new ASPxClientEvent();
	},
	Initialize: function(){
        this.constructor.prototype.Initialize.call(this);
        _aspxSetElementAsUnselectable(this.GetColorDiv());
    },
    
    GetColorDiv: function(){
        if(this.colorDiv == null)
            this.colorDiv = _aspxGetElementById(this.name + "_CD");
        return this.colorDiv;
    },
    ColorDegToHex: function(color){
        var str = color.toString(16);
        var length = str.length;
        for (var i = str.length; i < 6; i ++ )
            str = "0" + str;
        return "#" + str;
    },
    GetColor: function(){
        var colorDiv = this.GetColorDiv();
        var color = _aspxGetCurrentStyle(colorDiv).backgroundColor;
        return _aspxColorToHexadecimal(color);
    },
    SetColor: function(color){
        if(color == null)
            color = this.defaultColor;
        var colorDiv = this.GetColorDiv();
        colorDiv.style.backgroundColor = color;
        this.SetColorPickerColor(color);
    },
    SetColorPickerColor: function(color){
        if(this.colorPickerLockCount == 0){
            var colorPicker = this.GetColorPicker();
            if(_aspxIsExists(colorPicker))
                colorPicker.SetColor(this.GetColor());
        }
    },
    GetValue: function(){
        return this.GetColor();
    },
    SetValue: function(color){
        this.SetColor(color);
    },
    GetColorPicker: function(){
        if(!_aspxIsExists(this.colorPicker))
            this.colorPicker = aspxGetControlCollection().Get(this.colorPickerName);
        return this.colorPicker;
    },
    
    OnColorPickerInit: function (colorPicker){
        this.colorPicker = colorPicker;
        this.colorPickerName = colorPicker.name;
        colorPicker.SetColor(this.GetColor());
    },
    OnColorPickerColorChenged: function (color){
        this.colorPickerLockCount ++;
        this.SetValue(color);
        this.colorPickerLockCount --;
        aspxGetMenuCollection().HideAll();
        this.RaiseColorChanged();
    },
    
    RaiseColorChanged: function(){
        if(!this.ColorChanged.IsEmpty()){
            var args = new ASPxClientEventArgs();
            this.ColorChanged.FireEvent(this, args);
        }
    }
});

function aspxBDUpdateItemsByTimer(name){
    var barDocCotrol = aspxGetControlCollection().Get(name);
    if(_aspxIsExists(barDocCotrol))
        barDocCotrol.DoUpdateItems();
}
function aspxToolbarCommand(toolbar, item, value){
    var barDocCotrol = toolbar.GetToolbarDockControl();
    if(_aspxIsExists(barDocCotrol))
        barDocCotrol.OnCommand(toolbar, item, value);
}
function aspxToolbarItemUpdate(toolbar, item){
    var barDocCotrol = toolbar.GetToolbarDockControl();
    if(_aspxIsExists(barDocCotrol))
        barDocCotrol.OnItemUpdate(toolbar, item);
}
function aspxToolbarPopup(toolbar, e){
    var barDocCotrol = toolbar.GetToolbarDockControl();
    if(_aspxIsExists(barDocCotrol))
        barDocCotrol.OnPopup(toolbar, e);
}   

function aspxTBCPInit(name, s){
    var control = aspxGetControlCollection().Get(name);
    if(control != null) control.OnColorPickerInit(s);
}
function aspxTBCPColorChanged(name, color){
    var control = aspxGetControlCollection().Get(name);
    if(control != null) control.OnColorPickerColorChenged(color);
}

function aspxTBCBValueChanged(toolbarName, itemName, itemValue){
    var tbControl = aspxGetControlCollection().Get(toolbarName);
    if(_aspxIsExists(tbControl))
        tbControl.OnTemplateItemValueChanged(itemName, itemValue);
}
function aspxTBColorButtonValueChanged(toolbarName, itemName, itemValue){
    var tbControl = aspxGetControlCollection().Get(toolbarName);
    if(_aspxIsExists(tbControl))
        tbControl.OnTemplateItemValueChanged(itemName, itemValue);
}
// TODO This is custom part therefore it need to be replaced into HTMLEditor script
function aspxHEToolbarCommand(heControlName, item, value){
    var heControl = aspxGetControlCollection().Get(heControlName);
    if(_aspxIsExists(heControl))
        heControl.ExecuteCommand(item.name, value);
}
function aspxHEToolbarItemUpdate(heControlName, toolbar, item){
    if(item == "") return;    
    var heControl = aspxGetControlCollection().Get(heControlName);
    if(_aspxIsExists(heControl)){
        var commandName = item.name;
        var command = ASPxHtmlEditorCommandList[commandName];
        if(_aspxIsExists(command)){
            var isLocked = command.IsLocked(heControl);
            var isChecked = command.GetState(heControl);
            item.SetEnabled(!isLocked);            
            aspxProcessToolbarItemTemplate(item, !isLocked);
            item.SetChecked(isLocked ? false : isChecked);
            if(!isLocked)
                item.SetValue(command.GetValue(heControl));        
        }
    }
}

function aspxProcessToolbarItemTemplate(item, enabled) {
    var clientInstance = item.GetTemplateControl();
    if(_aspxIsExists(clientInstance) && _aspxIsExists(clientInstance.SetEnabled))
        clientInstance.SetEnabled(enabled);
}
Selection =_aspxCreateClass(null, {
	constructor: function(contentWindow) {
		this.contentWindow = contentWindow;
		this.contentDocument = contentWindow.document;		
    },
    GetHtmlText: function() {
    },
    Save: function() {
    },
	Restore: function() {
	},
	
	GetText: function() {
	},
	GetHtmlText: function() {
	},
	
	IsControl: function() {
	    return false;
	},
	IsTextOnly: function() {
	    return false;
	}
});

Selection.IsHtml = function(text) {
	if (!text || !text.match) return text;
	return text.match(/</);
}

SelectionIE = _aspxCreateClass(Selection, {
	constructor: function(contentWindow) {	
        this.constructor.prototype.constructor.call(this, contentWindow);
    },
    Save: function() {
		this.contentWindow.focus();		
		var curSelection = this.contentDocument.selection;
	    var selRange = curSelection.createRange();
	    if (selRange.length)
		    this.sourceIndex = selRange.item(0).sourceIndex;
        else
	        this.startBookmark = this.GetBookmark(selRange);
    },
	Restore: function() {
		if (_aspxIsExists(this.sourceIndex)) {
			var selRange = this.contentDocument.body.createControlRange();
			selRange.addElement(this.contentDocument.all(this.sourceIndex));
			selRange.select();
		}
		else if (_aspxIsExists(this.startBookmark)) {
			var selRange = this.contentDocument.body.createTextRange();
			selRange.moveToBookmark(this.startBookmark);
	        selRange.select();
	        selRange.collapse();
		}
		this.contentWindow.focus();
	},
	GetBookmark: function(range) {
	    return range.getBookmark();
	},
	GetRange: function() {
        return this.contentDocument.selection.createRange();
	},
	GetParentElement: function() {
	    var ret = null;
		var rng = this.GetRange();
		if (!rng) return ret;
	
		if (rng.length)
			ret = rng.item(0);
		else if (rng.parentElement)
			ret = rng.parentElement();
	    return ret;
	},
	GetText : function() {
	    var ret = "";
		var rng = this.contentDocument.selection.createRange();
        if (_aspxIsExists(rng.text))
			ret = rng.text;
	    return ret;
	},
	GetHtmlText: function() {
	    var ret = "";
		var rng = this.GetRange();
		if (rng.length)
			ret = rng.item(0).outerHTML;
		else if (rng.htmlText)
			ret = rng.htmlText;
	    return ret;
	},
	IsControl : function ()  {				
		return this.contentDocument.selection.type == 'Control';
	},
	IsTextOnly: function() {
		var rng = this.contentDocument.selection.createRange();
		if (_aspxIsExists(rng.text) && _aspxIsExists(rng.htmlText))
		    return (rng.text == rng.htmlText);
		return false;
	}
});

DialogSelectionIE = _aspxCreateClass(SelectionIE, {
    Save: function() {
		this.contentWindow.focus();		
		var curSelection = this.contentDocument.selection;
	    if (curSelection) {
            this.selectionRange = curSelection.createRange();
            if (curSelection.type.toLowerCase() == "control")
		        this.sourceIndex = this.selectionRange.item(0).sourceIndex;
	    }
    },
	Restore: function() {
		if (_aspxIsExists(this.sourceIndex)) {
			var selRange = this.contentDocument.body.createControlRange();
			selRange.addElement(this.contentDocument.all(this.sourceIndex));
			selRange.select();
		}
		else if (_aspxIsExists(this.selectionRange))
            this.selectionRange.select();
            		
		this.contentWindow.focus();
	}
});

SelectionNSOpera = _aspxCreateClass(Selection, {
	constructor: function(contentWindow) {
        this.constructor.prototype.constructor.call(this, contentWindow);
    },
	Save: function() {
	 	var curSelection = this.contentWindow.getSelection();
	 	if (_aspxIsExists(curSelection)) {
	 	    this.isCollapsedSelection = curSelection.isCollapsed;	 	
	 	    this.startNodePath = this.GetNodePath(this.contentDocument.documentElement, curSelection.anchorNode);	 	
	        this.startNodeOffset = curSelection.anchorOffset;
    	    	
	 	    this.endNodePath = this.GetNodePath(this.contentDocument.documentElement, curSelection.focusNode);
	        this.endNodeOffset = curSelection.focusOffset;
    	    
	        if (__aspxOpera) {
	            if (this.NeedExchangeStartAndEndNode(this.startNodePath, this.endNodePath, this.startNodeOffset, this.endNodeOffset)) {
	                var tmp = this.startNodePath;
	                this.startNodePath = this.endNodePath;
	                this.endNodePath = tmp;
        	        
	                tmp = this.startNodeOffset;
	                this.startNodeOffset = this.endNodeOffset;
	                this.endNodeOffset = tmp;
	            }
	        }
	    }
	    else {
	 	    this.isCollapsedSelection = null;
	 	    this.startNodePath = null;
	        this.startNodeOffset = null;
	 	    this.endNodePath = null;
	        this.endNodeOffset = null;
	    }
	},
	Restore: function() {
		var curSelection = this.contentWindow.getSelection();
		var startNode = this.GetNodePathByPath(this.contentDocument.documentElement, this.startNodePath);
		var endNode = this.GetNodePathByPath(this.contentDocument.documentElement, this.endNodePath);		
		
		this.SelectElement(startNode);
		this.SelectElement(endNode);
		
	    if (_aspxIsExists(startNode))
		    curSelection.collapse(startNode, this.startNodeOffset);
				    
	    if (_aspxIsExists(endNode) && !this.isCollapsedSelection) {
	        if (__aspxSafariFamily)
				curSelection.setBaseAndExtent(startNode, this.startNodeOffset, endNode, this.endNodeOffset);
		    else
		        curSelection.extend(endNode, this.endNodeOffset);
		}
	},
	GetParentElement: function() {
	    var ret = null;
		var rng = this.GetRange();
		if (!rng) return ret;
		
		var selection = this.contentWindow.getSelection();
		
		var startContainer = this.GetStartContainer(rng, selection);
		var endContainer = this.GetEndContainer(rng, selection);
		var startOffset = this.GetStartOffset(rng, selection);
		var endOffset = this.GetEndOffset(rng, selection);

		if (startContainer == endContainer && (endOffset - startOffset) == 1 && 
		        (selection.anchorNode.childNodes.length > 0))
			ret = selection.anchorNode.childNodes[selection.anchorOffset];
		else {				
			if (!rng.commonAncestorContainer.tagName) {							
				if (this.contentWindow.document == rng.commonAncestorContainer && 
				    selection.baseNode) //SAFARI
                    ret = selection.baseNode.parentNode;
                else
				    ret = rng.commonAncestorContainer.parentNode;
			}
			else
				ret = rng.commonAncestorContainer;
		}
		return ret;
	},	
	GetRange: function() {
		var selection = this.contentWindow.getSelection();	
		if (!selection || selection.rangeCount < 1)
			return null;
					
		return selection.getRangeAt ? selection.getRangeAt(0) : this.contentWindow.createRange();//SAFARI
	},
	GetText : function() {
	    var ret = "";
		if (this.contentWindow.getSelection)
			ret = this.contentWindow.getSelection().toString();
	    return ret;
	},
	GetHtmlText: function() {
	    var ret = "";
		var selection = this.contentWindow.getSelection();			
		var rng = null;
		if (selection.getRangeAt) {
			rng = selection.getRangeAt(0);
			var tempDiv = this.contentWindow.document.createElement("div");
			var clonedFragment = rng.cloneContents();			
			if (_aspxIsExists(clonedFragment)) {
			    tempDiv.appendChild(clonedFragment);
			    ret = tempDiv.innerHTML;
			}
		}
		return ret;
	},
	
	GetStartContainer: function(rng, selection) {
	    return rng.startContainer ? rng.startContainer : selection.baseNode;
	},
	GetEndContainer: function(rng, selection) {
		return rng.endContainer ? rng.endContainer : selection.extentNode;
	},
	GetStartOffset: function(rng, selection) {
		return rng.startOffset != null ? rng.startOffset : selection.baseOffset;
	},
	GetEndOffset: function(rng, selection) {
		return rng.endOffset != null ? rng.endOffset : selection.extentOffset;
	},	
	IsControl : function ()  {				
		var selection = this.contentWindow.getSelection();
		if(selection.toString() != "") 
			return false;
		
		var focusNode = selection.focusNode;
		if(focusNode.nodeType == 1) { //element node 
    		var rng = this.GetRange();
		    var startContainer = this.GetStartContainer(rng, selection);
		    var endContainer = this.GetEndContainer(rng, selection);
		    var startOffset = this.GetStartOffset(rng, selection);
		    var endOffset = this.GetEndOffset(rng, selection);
		    
		    if (startContainer == endContainer && (endOffset - startOffset) == 1 && 
		            (selection.anchorNode.childNodes.length > 0))
			    focusNode = selection.anchorNode.childNodes[selection.anchorOffset];
	    }
		return (focusNode.tagName == 'IMG');
	},
	IsTextOnly: function() {
		var selection = this.contentWindow.getSelection();
		return (selection.focusNode == selection.anchorNode) || 
		        ((selection.focusNode.nodeType == 3) && (selection.anchorNode.nodeType == 3)); // text node
	},
	
	SelectElementInDocument: function(elem, docObj, windowObj) {
	    if (_aspxIsExists(elem)) {
		    var range = docObj.createRange();
		    range.selectNode(elem);
		    
		    if (__aspxOpera)
   				range.selectNodeContents(elem);
		    
		    var selection = windowObj.getSelection();		    
	        if (__aspxSafariFamily)
    		    selection.setBaseAndExtent(range.startContainer,range.startOffset,range.endContainer,range.endOffset);
    		else {
	            selection.removeAllRanges();	        
		        selection.addRange(range);
		    }
		}	
	},

	SelectElement: function(elem) {
	    this.SelectElementInDocument(elem, this.contentDocument, this.contentWindow);
	},	
	NeedExchangeStartAndEndNode: function(startNodePath, endNodePath, startNodeOffset, endNodeOffset) {
	    if (startNodePath != endNodePath) {
	        var startNodePathArray = startNodePath.split("-");
	        var endNodePathArray = endNodePath.split("-");

	        var length = Math.min(startNodePathArray.length, endNodePathArray.length);
	        for (var i = 0; i < length; i++) {
	            var i1 = parseInt(startNodePathArray[i], 10);
	            var i2 = parseInt(endNodePathArray[i], 10);
	            if (i1 > i2)
	                return true;
	        }
	    }
        return startNodeOffset > endNodeOffset;	    
	},	
	GetNodePath: function(rootNode, node) {
	    var path = "";
	    var curParentNode = node;
	    var curNode = node;
	    if (_aspxIsExists(curParentNode)){
	        while (rootNode != curParentNode) {
	            curParentNode = curParentNode.parentNode;
    	        
	            if (_aspxIsExists(curParentNode.childNodes)) {
	                var index = _aspxArrayIndexOf(curParentNode.childNodes, curNode);
	                path = index.toString() + "-" + path;
	            }
	            curNode = curParentNode;
	        }
	    }
	    return path.substr(0, path.length - 1);
	},
	GetNodePathByPath: function(rootNode, path) {
	    if (path != "") {
	        var pathNodeArray = path.split("-");
	        var curNode = rootNode;
		    for(var i = 0; i < pathNodeArray.length; i++) {
		        var index = parseInt(pathNodeArray[i], 10);
		        curNode = curNode.childNodes[index];
		    }
		    return curNode;
	    }
	    else
	        return null;
	}    
});
var ASPxClientCommandConsts = {
	BOLD_COMMAND : "bold",
    ITALIC_COMMAND: "italic",
    UNDERLINE_COMMAND: "underline",
    STRIKETHROUGH_COMMAND: "strikethrough",
    SUPERSCRIPT_COMMAND: "superscript",
    SUBSCRIPT_COMMAND: "subscript",
    JUSTIFYCENTER_COMMAND: "justifycenter",
    JUSTIFYLEFT_COMMAND: "justifyleft",
    INDENT_COMMAND: "indent",
    OUTDENT_COMMAND: "outdent",    
    JUSTIFYRIGHT_COMMAND: "justifyright",
    JUSTIFYFULL_COMMAND: "justifyfull",    
	FONTSIZE_COMMAND : "fontsize",
	FONTNAME_COMMAND : "fontname",
	FONTCOLOR_COMMAND: "forecolor",
	BACKCOLOR_COMMAND: "backcolor",	
	FORMATBLOCK_COMMAND: "formatblock",		
	APPLYCSS_COMMAND: "applycss",	
	REMOVEFORMAT_COMMAND: "removeformat",
	UNDO_COMMAND: "undo",
	REDO_COMMAND: "redo",
	COPY_COMMAND: "copy",
	KBCOPY_COMMAND: "kbcopy",
	PASTE_COMMAND: "paste",
	KBPASTE_COMMAND: "kbpaste",
	PASTEFROMWORD_COMMAND: "pastefromword",
	PASTEFROMWORDDIALOG_COMMAND: "pastefromworddialog",
	CUT_COMMAND: "cut",
	KBCUT_COMMAND: "kbcut",
	SELECT_ALL: "selectall",
	DELETE_COMMAND: "delete",	
	KBDELETE_COMMAND: "kbdelete",
	
	TEXTTYPE_COMMAND: "texttype",
	NEWPARAGRAPHTYPE_COMMAND: "newparagraphtype",
	LINEBREAKETYPE_COMMAND: "linebreaktype",
	PASTEHTML_COMMAND: "pastehtml",
    RESIZEOBJECT_COMMAND: "resizeobject",
    DRAGDROPOBJECT_COMMAND: "dragdropobject",
    DROPOBJECTFROMEXTERNAL_COMMAND: "dropobjectfromexternal",
	INSERTORDEREDLIST_COMMAND: "insertorderedlist",
	INSERTUNORDEREDLIST_COMMAND: "insertunorderedlist",
	UNLINK_COMMAND: "unlink",
	INSERTLINK_COMMAND: "insertlink",
	INSERTIMAGE_COMMAND: "insertimage",
    CHANGEIMAGE_COMMAND: "changeimage",
    CHECKSPELLING_COMMAND: "checkspelling",
	INSERTIMAGE_DIALOG_COMMAND: "insertimagedialog",
    CHANGEIMAGE_DIALOG_COMMAND: "changeimagedialog",
	INSERTLINK_DIALOG_COMMAND: "insertlinkdialog",
	CHANGELINK_DIALOG_COMMAND: "changelinkdialog",
    PRINT_COMMAND: "print",
	
	CheckSpellingCore_COMMAND: "checkspellingcore",
	Start_COMMAND: "start"
};

// Messages
var ASPxCommandMessageConsts = new Object();
ASPxCommandMessageConsts[ASPxClientCommandConsts.CUT_COMMAND] = "Please use Ctrl+X to Cut";
ASPxCommandMessageConsts[ASPxClientCommandConsts.PASTE_COMMAND] = "Please use Ctrl+V to Paste";
ASPxCommandMessageConsts[ASPxClientCommandConsts.COPY_COMMAND] = "Please use Ctrl+C to Copy";

/*region* * * * * * * * * * * * * * *  Command classes  * * * * * * * * * * * * * * * */
Command = _aspxCreateClass(null, {
	constructor: function(cmdID) {
		this.commandID = cmdID;
    },
	Execute: function(cmdValue, editor) {
	    return true;
	},
	GetCommandID: function() {
	    return this.commandID;
	},	
	GetState: function(editor) {
	    return true;
	},
	GetValue: function(editor) {
	    return null;
	},
    IsDefaultAction: function() {
        return false; 
        // true for all command, that don't require additional processing
    },
	IsImmediateExecution: function() {
	    return false;
	},
	IsHtmlChangeable: function() {
	    return true;
	},
    IsLocked: function(editor) {
        return false;
    },
	IsReversable: function() {
	    return true;
	}
});
HtmlProcessingCommand = _aspxCreateClass(Command, {

	// delete the new added font elements
    DeleteNewAddedFontElements: function(selInfo, editor) {
		var newElements = selInfo.newElements;
		for (var i=0; i < newElements.length; i++) {
			if (__aspxIE || __aspxOpera)
				newElements[i].removeNode(false);
			else {
				var range = editor.GetDesignViewIFrameDocument().createRange();
				
				range.selectNodeContents(newElements[i]);
				var selectedContent = range.extractContents();
				range.selectNode(newElements[i]);
				range.deleteContents();
				range.insertNode(selectedContent);
			}
		}    
    },
    SaveSelectionRangeInfo: function(selectionInfo, editor) {
		this.startR = null;
		this.endR = null;
		if (__aspxIE && !selectionInfo.IsControl()) {
			var range = editor.GetDesignViewIFrameDocument().selection.createRange();
			this.startR = range.duplicate();
			this.endR = range.duplicate();
			this.startR.collapse();
			this.endR.collapse(false);
		}
    },
    RestoreSelectionRange: function(editor) {
		if (__aspxIE) {
			try {
				var newRange = editor.GetDesignViewIFrameDocument().selection.createRange();
				newRange.setEndPoint("StartToStart",this.startR);
				newRange.setEndPoint("EndToEnd",this.endR);
				newRange.select();
			}
			catch (e) {
				//control selection
			}
		} else {
			var selection = editor.GetDesignViewIFrameWindow().getSelection();
			if (!__aspxSafariFamily) {
				var rng = selection.getRangeAt(0);
				rng.collapse(true);
			}
		}
    }
});

var __aspxDefaultFontSizes = ["8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt"];
BrowserCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
        Command.prototype.Execute.call(this);
		var contentAreaDoc = editor.GetDesignViewIFrameDocument();
		if (!this.NeedUseCss())
       	    contentAreaDoc.execCommand("useCSS", false, false);
       	var isSuccessfully = contentAreaDoc.execCommand(this.GetCommandName(), false, this.GetCorrectedValue(cmdValue));
		if (!this.NeedUseCss())
       	    contentAreaDoc.execCommand("useCSS", false, true);
       	       	
	    editor.SetFocus();
		return isSuccessfully;
	},
	GetCommandName: function() {
	    return this.commandID;
	},	
	GetCorrectedValue: function(value) {
		var retValue = value;
		if ((this.commandID == ASPxClientCommandConsts.FONTSIZE_COMMAND) && (__aspxSafariFamily))
			retValue =__aspxDefaultFontSizes[parseInt(value)];
		return retValue;
	},
	GetState: function(editor) {
	    var ret = true;
	    if (!this.IsAlwaysEnabledCommand(this.commandID)) {	        	        
            try {
	            ret = this.TryGetState(editor);
	        }
	        catch(ex) { ret = false; }
	    }
        return ret;
	},
	GetValue: function(editor) {	    
	    var ret = null;
        try {
	        ret = this.TryGetValue(editor);
	    }
	    catch(e) {}
	    return ret;
	},
	IsLocked: function(editor) {
	    var ret = this.TryGetIsLocked(editor);
	    if (__aspxOpera && ret) {// Opera bug. When text underline (bold and etc.), then queryCommandEnabled == false
            try { 
                ret = !this.TryGetState(editor); 
            }
	        catch(ex) { ret = false; }
	    }
	    return ret;
	},
	TryGetState: function(editor) {
        return editor.GetDesignViewIFrameDocument().queryCommandState(this.GetCommandName());	
	},
	TryGetValue: function(editor) {
	    return editor.GetDesignViewIFrameDocument().queryCommandValue(this.GetCommandName());
	},
	TryGetIsLocked: function(editor) {
	    return !editor.GetDesignViewIFrameDocument().queryCommandEnabled(this.GetCommandName());
	},
	
	IsAlwaysEnabledCommand: function(commandID) {
	    return this.commandID == ASPxClientCommandConsts.FONTSIZE_COMMAND || this.commandID == ASPxClientCommandConsts.FONTNAME_COMMAND;
	},
	NeedUseCss: function() {
	    return true;
	}
});


var __aspxHEFakeElementClassName = "dxHClass";
ApplyCssCommand = _aspxCreateClass(HtmlProcessingCommand, {
     // cmdValue = { tagName, cssClass };
    Execute: function(cmdValue, editor) {
	    cmdValue.tagName = _aspxTrim(cmdValue.tagName);
	    cmdValue.cssClass = _aspxTrim(cmdValue.cssClass);
    
        if (this.IsExclusionTagName(cmdValue.tagName)) return false;
                
	    var curSelection = editor.CreateRestoreSelection();
        this.SaveSelectionRangeInfo(curSelection, editor);
        var parentTagName = curSelection.GetParentElement().tagName.toUpperCase();
        
	    if (this.IsExclusionTagName(parentTagName) && (cmdValue.tagName == "")) {
	        this.SetClassNameAttribute(curSelection.GetParentElement(), cmdValue.cssClass);
	    }
	    else if (curSelection.GetText()!= "") {
	        var selInfo = editor.GetEditorSelectionInfo(true);
		    for (var i = 0; i < selInfo.allElements.length; i++) {
		        var curElem = selInfo.allElements[i];		        
		        var targetParentElem = this.GetEqualParentElement(curElem, cmdValue.tagName, cmdValue.cssClass);
		        
				if (!_aspxIsExists(targetParentElem)) {
				    var elemInnerHtml = curElem.innerHTML;
				    var newElement = null;
				    
				    if (cmdValue.tagName) {
				        var newElement = null;
		                targetParentElem = this.GetParentElementWithTagName(curElem, cmdValue.tagName);
		                
		                if (_aspxIsExists(targetParentElem)) // is exists tag with cmdValue.tagName
	                        this.SetClassNameAttribute(targetParentElem, cmdValue.cssClass);
		                else {
		                    newElement = editor.GetDesignViewIFrameDocument().createElement(cmdValue.tagName);
	                        this.SetClassNameAttribute(newElement, cmdValue.cssClass);
	                     }
				    }
				    else { // only if tagName == ""
				        // fake element
				        newElement = editor.GetDesignViewIFrameDocument().createElement("span");
	                    this.SetClassNameAttribute(newElement, __aspxHEFakeElementClassName);
				        newElement.className += " " + cmdValue.cssClass;
				    }
				    if (newElement) {
				        newElement.innerHTML = elemInnerHtml;
				        curElem.innerHTML = '';
				        curElem.appendChild(newElement);
				    }
			    }
		    }
		    this.DeleteNewAddedFontElements(selInfo, editor);
		    
		    if (cmdValue.tagName == "")
		        this.OptimizeChildCssClass(editor.GetDesignViewIFrameDocument().body, cmdValue.cssClass);
	    }
        this.RestoreSelectionRange(editor);	    
	    return true;
    },   
	GetValue: function(editor) {
	    return (__aspxIE) ? this.GetValueIE(editor) : this.GetValueMoz(editor);
	},
	GetValueIE : function(editor) {	    	    
        var contentAreaDoc = editor.GetDesignViewIFrameDocument();
		var curSelection = contentAreaDoc.selection;
		
		if (!curSelection) return "";
		
		var selRange = curSelection.createRange();
		// multiple selection if selRange.length > 0
		var parentElement = (selRange.length > 0 ? selRange(0) : selRange.parentElement());
		
		return this.GetValueByElement(parentElement);
	},
	GetValueMoz: function(editor) {
        var contentWindow = editor.GetDesignViewIFrameWindow();	
		var curSelection = contentWindow.getSelection();
        
		if (!curSelection || curSelection.rangeCount != 1/*multiple selection*/) return "";
		
		/*anchorNode -  beginning point of the selection*/
		var startNode = (curSelection.anchorNode.nodeType != 3
							? curSelection.anchorNode
							: curSelection.anchorNode.parentNode);
		
		/*focusNode - end point of the selection*/
		var endNode = (curSelection.focusNode.nodeType != 3
							? curSelection.focusNode
							: curSelection.focusNode.parentNode);							
							
		if (startNode != endNode)
			return "";
			
	    var restoreSelection = editor.CreateRestoreSelection();
	    return this.GetValueByElement(restoreSelection.GetParentElement());
	},
	GetValueByElement: function(element) {
	    var tagName = "";
	    var cssClass = "";
	
		if (element && element.tagName.toLowerCase() != "body") {
			tagName = element.tagName.toLowerCase();
			cssClass = element.className;
		}
		return { tagName: tagName, cssClass: cssClass };
	},
	IsExclusionTagName: function(tagName) {
	    tagName = tagName.toUpperCase();
	    return tagName == "IMG" || tagName == "TABLE" || tagName == "OBJECT" || tagName == "EMBED";
	},
	OptimizeChildCssClass: function(parentElement, targetCssClass) {
	    var fakeElementCollection = _aspxGetChildsByClassName(parentElement, __aspxHEFakeElementClassName);
	    var newElementArray = new Array();
	    
	    for (var i = 0; i < fakeElementCollection.length; i++) {
	        var newElement = this.TryRemoveFakeElement(fakeElementCollection[i], targetCssClass);
	        if (!newElement)
	            this.RemoveFakeCssClass(fakeElementCollection[i]);
	        else
	            _aspxArrayPush(newElementArray, newElement);
	    }
        this.TryMoveCssClassToParent(newElementArray, targetCssClass);
	},
	TryRemoveFakeElement: function(fakeElement, targetCssClass) {
	    var parentElement = fakeElement.parentNode;
	    var ret = null;
	    if ((parentElement.tagName.toUpperCase() != "BODY") && 
	            (parentElement.childNodes.length == 1)) {
	            
	        var elemInnerHtml = fakeElement.innerHTML;
	        _aspxRemoveElement(fakeElement);
	        parentElement.innerHTML = elemInnerHtml;
            this.SetClassNameAttribute(parentElement, targetCssClass);
	        ret = parentElement;
	        
	    } else if(targetCssClass == "")
	        _aspxRemoveOuterTags(fakeElement);
	    return ret;
	},
	TryMoveCssClassToParent: function(elements, targetCssClass) {
	    for (var i = 0; i < elements.length; i++) {
	        var parentElement = elements[i].parentNode;
	        var parentElementTagName = parentElement.tagName.toUpperCase();
	        
	        // List
	        if ((parentElementTagName == "OL" || parentElementTagName == "UL") && 
	            (elements[i].className == targetCssClass)) {
	            this.ProcessListElements(parentElement, targetCssClass);
	        }
	    }
	},
	ProcessListElements: function(mainListElement, targetCssClass) {
        var listItemElements = _aspxGetElementsByTagName(mainListElement, "li");
        var isAllItemNeedClear = true;
        for (var i = 0 ; i< listItemElements.length; i++) {
            if (listItemElements[i].className != targetCssClass) {
                isAllItemNeedClear = false;
                return;
            }
        }
        if (isAllItemNeedClear) {
            this.SetClassNameAttribute(mainListElement, targetCssClass);
            for (var i = 0 ; i< listItemElements.length; i++)
                this.SetClassNameAttribute(listItemElements[i], "");
        }
	},
		
	RemoveFakeCssClass: function(sourceElement) {
	    var sourceCssClass = sourceElement.className;
	    sourceCssClass = sourceCssClass.replace(__aspxHEFakeElementClassName, "");
	    sourceCssClass = _aspxTrim(sourceCssClass);
	    sourceElement.className = sourceCssClass;
	},
	
	// Utils
	GetParentElementWithTagName: function(element, tagName) {
	    var parentElement = element.parentNode;
	    return 	parentElement.tagName.toUpperCase() == tagName.toUpperCase() ? parentElement : null;
	},
	GetEqualParentElement: function(elem, tagName, cssClass) {
	    var ret = null;
        if (tagName && cssClass)
		    ret = _aspxGetParentByTagNameAndAttributeValue(elem, tagName, "class", cssClass);
		else if (cssClass)
		    ret = _aspxGetParentByClassName(elem);
		return ret;
	},	
	SetClassNameAttribute: function(element, className) {
	    if (className)
	        element.className = className;
	    else
	        _aspxRemoveAttribute(element, __aspxNS ? "class" : "className");
	}
});

FontColorBrowserCommand  = _aspxCreateClass(BrowserCommand, {
    GetValue: function(editor) {
        return _aspxColorToHexadecimal(BrowserCommand.prototype.GetValue.call(this, editor));
    }
});
BgColorBrowserCommand = _aspxCreateClass(FontColorBrowserCommand, {
    GetCommandName: function() {
        return __aspxIE ? BrowserCommand.prototype.GetCommandName.call(this) : "HiliteColor" ;
    }
});
InsertListCommand = _aspxCreateClass(BrowserCommand, {
    GetState: function(editor) {
        var curSelection = editor.CreateRestoreSelection();
        var tagName = "";
        switch (this.GetCommandName()) {
            case ASPxClientCommandConsts.INSERTORDEREDLIST_COMMAND:
                tagName = "OL";break;
            case ASPxClientCommandConsts.INSERTUNORDEREDLIST_COMMAND:
                tagName = "UL";break;
        }
        return _aspxIsExists(_aspxGetParentByTagName(curSelection.GetParentElement(), tagName));
    }
});

FormatBlockCommand = _aspxCreateClass(BrowserCommand, {
	GetCorrectedValue: function(value) {
	    return __aspxIE ? "<" + value + ">" : value;
	},
	GetValue: function(editor) {
	    var value = BrowserCommand.prototype.GetValue.call(this, editor);
	    if (_aspxIsExists(value)) {
	        value =  value.toString();
	        switch (value.toLowerCase()) {
			    case "x":
			    case "":
			    case "normal":
				    value = "Normal";
				    break;
		    }
		}
		return value;	    
	}
});
RemoveFormatCommand = _aspxCreateClass(BrowserCommand, {
	Execute: function(cmdValue, editor) {
        BrowserCommand.prototype.Execute.call(this, cmdValue, editor);
        var curSelection = editor.CreateRestoreSelection();
        var parentElem = curSelection.GetParentElement();
        if (!curSelection.IsTextOnly() && !curSelection.IsControl() && 
            _aspxIsExists(parentElem))
            this.CleanElement(parentElem);
		return true;
	},
	CleanElement: function(element) {
        for (var i =  0; i < element.childNodes.length ; i++)
            this.CleanElement(element.childNodes[i]);
        if (element.nodeType == 1 && element.tagName.toUpperCase() != "BODY") {
            _aspxRemoveAllAttributes(element, ["href", "src", "alt", "target", "id", "title", "value"]);
            _aspxRemoveAllStyles(element);
        }
	}
});

PasteFromWord = _aspxCreateClass(Command, {
    // cmdValue = { html, stripFontFamily };
	Execute: function(cmdValue, htmlEditor) {
        Command.prototype.Execute.call(this);        
        
	    return ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTEHTML_COMMAND].Execute(
	        PasteFromWord.ClearWordFormatting(cmdValue.html, cmdValue.stripFontFamily), htmlEditor, false);	
	}
});

PasteFromWord.ClearWordFormatting = function(html, stripFontFamily) {
	// remove word attributes
    html = PasteFromWord.ClearWordAttributes(html);
    // remove special Word tags
	html = html.replace(/<\/?\w+:[^>]*>/gi, '');
	html = html.replace(/<STYLE[^>]*>[\s\S]*?<\/STYLE[^>]*>/gi, '');
	html = html.replace(/<(?:META|LINK)[^>]*>\s*/gi, '');
	html = html.replace(/<\\?\?xml[^>]*>/gi, '');
	html = html.replace(/<o:[pP][^>]*>\s*<\/o:[pP]>/gi, '');
	html = html.replace(/<o:[pP][^>]*>.*?<\/o:[pP]>/gi, '&nbsp;');
	html = html.replace(/<st1:.*?>/gi, '');
	html = html.replace(/<\!--[\s\S]*?-->/g, '');
	html = html.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3") ;
		
	// remove empty attributes
	html =  html.replace(/\s*style="\s*"/gi, '');
	html = html.replace(/style=""/ig, "");
	html = html.replace(/style=''/ig, "");
	
	// replace ugly Word markup
	html = html.replace(/^\s/i, '');
	html = html.replace(/\s$/i, '');
	html = html.replace(/<p>&nbsp;<\/p>/gi, '<br /><br />');
	
	html = html.replace(/<font\s*>([^<>]+)<\/font>/gi, '$1');
	html = html.replace(/<span\s*><span\s*>([^<>]+)<\/span><\/span>/ig, '$1');
	html = html.replace(/<span>([^<>]+)<\/span>/gi, '$1');
	
	if (stripFontFamily) {
		html = html.replace(/\s*face="[^"]*"/gi, '');
		html = html.replace(/\s*face=[^ >]*/gi, '');
		html = html.replace(/\s*FONT-FAMILY:[^;"]*;?/gi, '');
	}
	// P->DIV
	var replacePwithDIV = new RegExp('(<P)([^>]*>[\\s\\S]*?)(<\/P>)', 'gi');
	html = html.replace(replacePwithDIV, '<div$2<\/div>');
	
	// Remove nested empty tags
	html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' );
	html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' );
	html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' );
	
    return html;
}
PasteFromWord.ClearWordAttributes = function(html) {
	html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3") ;
	html = html.replace(/\s*mso-[^:]+:[^;"]+;?/gi, '');
	html = html.replace(/\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '');
	html = html.replace(/\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"");

	html = html.replace(/\s*TEXT-INDENT: 0cm\s*;/gi, '');
	html = html.replace(/\s*TEXT-INDENT: 0cm\s*"/gi, "\"");

	html = html.replace(/\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"");

	html = html.replace(/\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");

	html = html.replace(/\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"") ;

	html = html.replace(/\s*tab-stops:[^;"]*;?/gi, '') ;
	html = html.replace(/\s*tab-stops:[^"]*/gi, '') ;
	return html;
}

PasteHtmlCommand = _aspxCreateClass(Command, {
	Execute: function(htmlText, htmlEditor, needSelect) {
        Command.prototype.Execute.call(this);
        
        this.needSelect = _aspxIsExists(needSelect) ? needSelect : true;
        this.htmlText = htmlText;
        this.htmlEditor = htmlEditor;
        
        if (__aspxIE)
            return this.ExecuteIE();
        if (__aspxOpera)
	       return this.ExecuteOpera();
        if (__aspxNS)
		   return this.ExecuteNS();
		return false;
	},
	ExecuteIE: function() {
		var doc = this.htmlEditor.GetDesignViewIFrameDocument();	
		
		if (doc.selection.type.toLowerCase() != "none")
			doc.selection.createRange().execCommand("Delete");
									
		doc.body.setActive();
				
		var selRange = doc.selection.createRange();
		if (selRange && selRange.length) {
			var elem = selRange.item(0);
			if (elem && elem.tagName.toUpperCase() == "BODY") {
				var formElement = elem.getElementsByTagName("FORM")[0];
				if (formElement)
				    _aspxSetInnerHtml(formElement, formElement.innerHTML + this.htmlText);
			}
		}
		else {		
			var rngStart = selRange.duplicate();		
			rngStart.collapse(true);							
			selRange.pasteHTML(this.htmlText);							
			
			//select inserted html TODO Optionally
			if (this.needSelect) {
			    rngStart.setEndPoint("EndToEnd", selRange);
			    rngStart.select();
			}
		}
		return true;
	},
	ExecuteNS: function() {
	    return this.ExecuteOpera();
	},
	ExecuteOpera: function() {
		var doc = this.htmlEditor.GetDesignViewIFrameDocument();		
		
		var tmpNode = doc.createElement("SPAN");
		tmpNode.innerHTML = this.htmlText;
		
		this.InsertNodeAtSelection(tmpNode);
				
		var range = doc.createRange();									
		range.selectNodeContents(tmpNode);
		var documentFragment = range.extractContents();
		
		//Delete new node
		range.selectNode(tmpNode);
		range.deleteContents();	
	
		this.InsertNodeAtSelection(documentFragment);
		
		return true;	
	},
	InsertNodeAtSelection : function(insertNode) {			
		var selection = this.htmlEditor.GetDesignViewIFrameWindow().getSelection();		
		var range = selection.getRangeAt(0);
		
		selection.removeAllRanges();
		range.deleteContents();
						
		var container = range.startContainer;
		var startPosition = range.startOffset;

		range = this.htmlEditor.GetDesignViewIFrameDocument().createRange();

		if ((container.nodeType == 3) && (insertNode.nodeType == 3)) {
			container.insertData(startPosition, insertNode.nodeValue);
			range.setEnd(container, startPosition + insertNode.length);
            range.setStart(container, startPosition);
		}
		else {
			var afterNode;
			if (container.nodeType == 3) {
				var textNode = container;
				container = textNode.parentNode;
				var nodeText = textNode.nodeValue;

				var textBefore = nodeText.substr(0, startPosition);
				var textAfter = nodeText.substr(startPosition);

				var beforeNode = this.htmlEditor.GetDesignViewIFrameDocument().createTextNode(textBefore);
				var afterNode = this.htmlEditor.GetDesignViewIFrameDocument().createTextNode(textAfter);

				container.insertBefore(afterNode, textNode);
				container.insertBefore(insertNode, afterNode);
				try {
					container.insertBefore(beforeNode, insertNode);
				}
				catch (exc) {}
				container.removeChild(textNode);
			}
			else {
				if(container.childNodes.length > 0) {
					afterNode = container.childNodes[startPosition];
					container.insertBefore(insertNode, afterNode);
				}
				else {
					if (container.tagName != "BODY")
						container = container.parentNode;
					container.appendChild(insertNode);
				}
			}
			try {
				range.setStart(insertNode, 0);
				if (!_aspxIsExists(afterNode)) // bug in demo: Paste html template
				    afterNode = __aspxNS ? this.htmlEditor.GetDesignViewIFrameDocument().body : insertNode;
				range.setEnd(_aspxIsExists(afterNode) ? afterNode : this.htmlEditor.GetDesignViewIFrameDocument().body, 0);
			}
			catch (exc) { }
		}
		try {
			selection.addRange(range);
		}
		catch (exc) {}
	}
});
CopyPastCommand  = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
	    var isSuccessfully = true;
        Command.prototype.Execute.call(this);
        if (__aspxIE)
            isSuccessfully = this.ExecuteIE(editor);
        else {
            var message = "Not supported in this browser";
            if (_aspxIsExists(ASPxCommandMessageConsts[this.commandID]))
                message = ASPxCommandMessageConsts[this.commandID];
            alert(message);
            return false;
        }
        return isSuccessfully;
	},
	ExecuteIE: function(editor) {
	    var isSuccessfully = false;
	    
		var isEnabled = !__aspxOpera;
		if (isEnabled) {
		    try {
			    editor.GetDesignViewIFrameDocument().queryCommandEnabled(this.commandID); 
	        }
		    catch (e) { 
		        isEnabled = false;
		    }
		}
		if (isEnabled)
            isSuccessfully = editor.GetDesignViewIFrameDocument().execCommand(this.commandID, false, null);
            
        return isSuccessfully;
	},
	IsHtmlChangeable: function() {
	    return this.GetCommandID() != ASPxClientCommandConsts.COPY_COMMAND;
	},
	IsLocked: function(editor) {
	    return !editor.GetDesignViewIFrameDocument().queryCommandEnabled(this.commandID);
	}
});

SelectAllBrowserCommand  = _aspxCreateClass(BrowserCommand, {
	IsHtmlChangeable: function() {
	    return false;
	}
});

// ** Text typing **
TextTypeCommand = _aspxCreateClass(Command, {
    IsDefaultAction: function() {
        return true;
    },
    IsImmediateExecution: function() {
        return true;
    }
});
KbCopyCommand = _aspxCreateClass(TextTypeCommand, {
	IsHtmlChangeable: function() {
	    return false;
	}	
});
KbCutCommand = _aspxCreateClass(TextTypeCommand, {
});
KbPasteCommand = _aspxCreateClass(TextTypeCommand, {
});
LineBreakTypeCommand = _aspxCreateClass(TextTypeCommand, {
});
NewParagraphTypeCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
        return editor.GetDesignViewIFrameDocument().execCommand("InsertParagraph", false, null);
        // todo process if selection in LI
	}
});
// Delete with selection
DeleteCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
      	var isSuccessfully = true;
        var designViewIFrameDocument = editor.GetDesignViewIFrameDocument();
  	    isSuccessfully = designViewIFrameDocument.execCommand(this.commandID, false, null);
  	    if (__aspxOpera) { // hack Opera, because when SelectAll is executed, Delete doesn't work
		    designViewIFrameDocument.contentEditable = true;
		    designViewIFrameDocument.body.focus();
		}
        return isSuccessfully;
	}
});

// Delete without selection. Needn't do anything. Browser must delete content himself.
DeleteWithoutSelectionCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
	    return true;
	},
    IsImmediateExecution: function() {
        return true;
    }
});

// Undo,Redo
UndoCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
		return editor.Undo();
	},
	IsReversable: function() {
	    return false;
	},
	IsLocked: function(editor) {
	    return !editor.IsUndoAvailable();
	}
});
RedoCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
		return editor.Redo();
	},
	IsReversable: function() {
	    return false;
	},
	IsLocked: function(editor) {
	    return !editor.IsRedoAvailable();
	}
});
// Check Spelling
CheckSpellingCommand  = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
		return editor.CheckSpelling();
	},
	IsLocked: function(editor) {
	    return aspxIsEmptyHtml(editor.GetHtmlInternal());
	}
});
CheckSpellingCoreCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
		editor.SetHtmlInternal(cmdValue);
		return true;
	}
});
// Print
PrintCommand  = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
		return editor.Print();
	},
	IsLocked: function(editor) {
	    return aspxIsEmptyHtml(editor.GetHtmlInternal());
	},
	IsReversable: function() {
	    return false;
	}
});

function aspxIsEmptyHtml(html){
    var html = _aspxTrim(html);
    return html == "" || html == "&nbsp;" || html == "<P>&nbsp;</P>";
}
// ** Image **
InsertImageCommand = _aspxCreateClass(BrowserCommand, {
     // cmdValue = { url, width, height, align, alt };
	Execute: function(cmdValue, editor) {
        BrowserCommand.prototype.Execute.call(this, cmdValue.src, editor);	
	    var curSelection = editor.CreateRestoreSelection();
	    var newImageElement = curSelection.GetParentElement();
    	ChangeImageCommand.prototype.SetImageProperties(newImageElement, "", cmdValue.width, 
    	                                                cmdValue.height, cmdValue.align, cmdValue.alt);
    	return true;
    }
});
ChangeImageCommand = _aspxCreateClass(Command, {
     // cmdValue = { imageElement, width, height, align, alt };
	Execute: function(cmdValue, editor) {
    	ChangeImageCommand.prototype.SetImageProperties(cmdValue.imageElement, cmdValue.src, cmdValue.width, 
    	                                                   cmdValue.height, cmdValue.align, cmdValue.alt);
    	return true;
    }
});
ChangeImageCommand.GetImageProperties = function(imageElement) {
    var imageInfoObject = {
        isCustomSize: false,
        src: imageElement.src,
        width: imageElement.width,
        height: imageElement.height,
        align: "",
        alt: ""
    };
        
    imageInfoObject.isCustomSize = ChangeImageCommand.IsExistImageAttribute(imageElement, "width") || 
                                   ChangeImageCommand.IsExistImageAttribute(imageElement, "height");
                                   
    var parentNode = imageElement.parentNode;
    if (parentNode.childNodes.length == 1 && parentNode.tagName != "BODY") {
        imageInfoObject.align = parentNode.style.textAlign;
        if (!imageInfoObject.align)
            imageInfoObject.align = parentNode.align;
    }
        
    if (!imageInfoObject.align)
        imageInfoObject.align = _aspxGetAttribute(imageElement, "align");
        
    imageInfoObject.alt = _aspxGetAttribute(imageElement, "alt");
    return imageInfoObject;
}
ChangeImageCommand.IsExistImageAttribute = function(image, attrName) {
    var styleAttr = _aspxGetAttribute(image.style, attrName);
    return ((styleAttr != "") && (styleAttr != null)) ||
           (!__aspxNS && (image.outerHTML.toLowerCase().indexOf(attrName + "=") > -1));
}

ChangeImageCommand.prototype.SetImageProperties = function(imageElement, src, width, height, align, alt) {
    if (src && src != "")
        _aspxSetAttribute(imageElement, "src", src);
    if (width && width != "")
        _aspxSetAttribute(imageElement.style, "width", width);
    else {
        _aspxRemoveAttribute(imageElement, "width");
        _aspxRemoveAttribute(imageElement.style, "width");
    }
    
    if (height && height != "")
        _aspxSetAttribute(imageElement.style, "height", height);
    else {
        _aspxRemoveAttribute(imageElement, "height");
        _aspxRemoveAttribute(imageElement.style, "height");
    }
        
    if (alt && alt != "")
        _aspxSetAttribute(imageElement, "alt", alt);
        
    // horizontal align
    if (align) {
        var parentNode = imageElement.parentNode;
        var wrapElem = null;    
        if (parentNode.childNodes.length == 1 && parentNode.tagName != "BODY" && parentNode.tagName != "TD")
            wrapElem = parentNode;
            
        _aspxRemoveAttribute(imageElement, "align");
        if (align.toLowerCase() == "left" || align.toLowerCase() == "right") {
            if (wrapElem != null)
                _aspxRemoveOuterTags(wrapElem);
            if (align.toLowerCase() == "right")
                _aspxSetAttribute(imageElement, "align", align);
        }
        else { // align center
            if (wrapElem == null)
                wrapElem = _aspxWrapElementInNewElement(imageElement, "DIV");
            wrapElem.style.textAlign = "center";
        }
    }
}

// ** Link **
InsertLinkCommand = _aspxCreateClass(HtmlProcessingCommand, {
     // cmdValue = { url, text, target, title };
	Execute: function(cmdValue, editor) {
	    var curSelection = editor.CreateRestoreSelection();	    
	    var selContainerElement = curSelection.GetParentElement();
	    	    
	    // for restore selection
        this.SaveSelectionRangeInfo(curSelection, editor);
	    var link = _aspxGetParentByTagName(selContainerElement, "A");
	    // link was selected
	    if (_aspxIsExists(link)) { 
            InsertLinkCommand.prototype.SetLinkProperties(link, cmdValue.url, 
                                                            InsertLinkCommand.PrepareLinkText(cmdValue.text), 
                                                            cmdValue.target, cmdValue.title);
        }        
        // content was selected
	    else if (curSelection.GetText()!= "" || curSelection.GetParentElement().tagName == "IMG" ||
	            curSelection.GetParentElement().tagName == "TABLE") { 
	        ASPxHtmlEditorCommandList[ASPxClientCommandConsts.UNLINK_COMMAND].Execute(null, editor);
	        var selInfo = editor.GetEditorSelectionInfo();
	        // create new links
		    for (var i = 0; i < selInfo.allElements.length; i++) {
		        var curElem = selInfo.allElements[i];
				var parentLink = _aspxGetParentByTagName(curElem, "A");
				
				if (_aspxIsExists(parentLink)) {
					if (parentLink.href != cmdValue.url)					
					    InsertLinkCommand.prototype.SetLinkProperties(parentLink, cmdValue.url, "", 
					                                                    cmdValue.target, cmdValue.title);
				}
				else {
				    var elemInnerHtml = curElem.innerHTML;
				    var newLinkElement = editor.GetDesignViewIFrameDocument().createElement('A');
				    newLinkElement.innerHTML = elemInnerHtml;
				    curElem.innerHTML = '';
                    //SAFARI
				    if (__aspxSafariFamily) newLinkElement.href = "#";
    	
				    curElem.appendChild(newLinkElement);
				    InsertLinkCommand.prototype.SetLinkProperties(newLinkElement, cmdValue.url, InsertLinkCommand.PrepareLinkText(cmdValue.text),
				                                                    cmdValue.target, cmdValue.title);
			    }
		    }
			this.DeleteNewAddedFontElements(selInfo, editor);
	    }
	    // no selection
	    else { 
	        var htmlText = "<a href='" + cmdValue.url + "'";
	        if (cmdValue.title)
	            htmlText = this.AddAttributeStringToHtml(htmlText, "title", cmdValue.title);
	        if (cmdValue.target)
	            htmlText = this.AddAttributeStringToHtml(htmlText, "target", cmdValue.target);
	            
	        htmlText += ">" + InsertLinkCommand.PrepareLinkText(cmdValue.text) + "</a>";
	        return ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTEHTML_COMMAND].Execute(htmlText, editor);
	    }
	    
        this.RestoreSelectionRange(editor);
	    return true;
    },
    // Utils
    AddAttributeStringToHtml: function(html, attrName, attrValue) {
        return html + " " + attrName + "='" + attrValue + "'";
    }
});
InsertLinkCommand.prototype.SetLinkProperties = function(linkElement, url, text, target, title) {
    _aspxSetOrRemoveAttribute(linkElement, "href", url);
    _aspxSetOrRemoveAttribute(linkElement, "target", target);
    _aspxSetOrRemoveAttribute(linkElement, "title", title);
    	        
    if (text != "")
        linkElement.innerHTML = text;
}
InsertLinkCommand.PrepareLinkText = function(text) {
    return text.replace("<", "&lt;").replace(">", "&gt;");
}

// ** Dialogs **
DialogCommand = _aspxCreateClass(Command, {
	Execute: function(cmdValue, editor) {
	    var dialog = ASPxHtmlEditorDialogList[this.commandID];
  		if (dialog != null)
	        dialog.Execute(editor);
	    else
   			alert('Dialog is not found');
   	    return true;
	},
	IsHtmlChangeable: function() {
	    return false;
	},
	GetState: function(editor) {
	    return false;
	},
	IsLocked: function(editor) {
	    return false;
	}
});

PasteFromWordDialogCommand = _aspxCreateClass(DialogCommand, {
    IsLocked: function(editor) {
	    return !editor.GetDesignViewIFrameDocument().queryCommandEnabled(ASPxClientCommandConsts.PASTE_COMMAND);
    }
});

ChangeImageDialogCommand = _aspxCreateClass(DialogCommand, {
    IsLocked: function(editor) {
        return !_aspxIsExists(InsertImageDialog.prototype.GetSelectedImage(editor));
    }
});
ChangeLinkDialogCommand = _aspxCreateClass(DialogCommand, {
    IsLocked: function(editor) {
        return !InsertLinkDialog.prototype.IsLinkSelected(editor);
    }
});

/*region* * * * * * * * * * * * * * *  Commands static list  * * * * * * * * * * * * * * * */

var ASPxHtmlEditorCommandList = {};
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.BOLD_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.BOLD_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.ITALIC_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.ITALIC_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.UNDERLINE_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.UNDERLINE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.STRIKETHROUGH_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.STRIKETHROUGH_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.JUSTIFYCENTER_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.JUSTIFYCENTER_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.JUSTIFYLEFT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.JUSTIFYLEFT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.JUSTIFYRIGHT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.JUSTIFYRIGHT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.JUSTIFYFULL_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.JUSTIFYFULL_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.SUPERSCRIPT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.SUPERSCRIPT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.SUBSCRIPT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.SUBSCRIPT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INDENT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.INDENT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTORDEREDLIST_COMMAND] = new InsertListCommand(ASPxClientCommandConsts.INSERTORDEREDLIST_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTUNORDEREDLIST_COMMAND] = new InsertListCommand(ASPxClientCommandConsts.INSERTUNORDEREDLIST_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.OUTDENT_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.OUTDENT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.SELECT_ALL] = new SelectAllBrowserCommand(ASPxClientCommandConsts.SELECT_ALL);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTE_COMMAND] = new CopyPastCommand(ASPxClientCommandConsts.PASTE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTEFROMWORD_COMMAND] = new PasteFromWord(ASPxClientCommandConsts.PASTEFROMWORD_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CUT_COMMAND] = new CopyPastCommand(ASPxClientCommandConsts.CUT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.COPY_COMMAND] = new CopyPastCommand(ASPxClientCommandConsts.COPY_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.KBPASTE_COMMAND] = new KbPasteCommand(ASPxClientCommandConsts.KBPASTE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.KBCUT_COMMAND] = new KbCutCommand(ASPxClientCommandConsts.KBCUT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.KBCOPY_COMMAND] = new KbCopyCommand(ASPxClientCommandConsts.KBCOPY_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.FONTSIZE_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.FONTSIZE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.FONTNAME_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.FONTNAME_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.FONTCOLOR_COMMAND] = new FontColorBrowserCommand(ASPxClientCommandConsts.FONTCOLOR_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.BACKCOLOR_COMMAND] = new BgColorBrowserCommand(ASPxClientCommandConsts.BACKCOLOR_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.APPLYCSS_COMMAND] = new ApplyCssCommand(ASPxClientCommandConsts.APPLYCSS_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.FORMATBLOCK_COMMAND] = new FormatBlockCommand(ASPxClientCommandConsts.FORMATBLOCK_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.REMOVEFORMAT_COMMAND] = new RemoveFormatCommand(ASPxClientCommandConsts.REMOVEFORMAT_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.UNDO_COMMAND] = new UndoCommand(ASPxClientCommandConsts.UNDO_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.REDO_COMMAND] = new RedoCommand(ASPxClientCommandConsts.REDO_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.LINEBREAKETYPE_COMMAND] = new LineBreakTypeCommand(ASPxClientCommandConsts.LINEBREAKETYPE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.NEWPARAGRAPHTYPE_COMMAND] = new NewParagraphTypeCommand(ASPxClientCommandConsts.NEWPARAGRAPHTYPE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.TEXTTYPE_COMMAND] = new TextTypeCommand(ASPxClientCommandConsts.TextType_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.RESIZEOBJECT_COMMAND] = new TextTypeCommand(ASPxClientCommandConsts.RESIZEOBJECT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.DRAGDROPOBJECT_COMMAND] = new TextTypeCommand(ASPxClientCommandConsts.DRAGDROPOBJECT_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.DROPOBJECTFROMEXTERNAL_COMMAND] = new TextTypeCommand(ASPxClientCommandConsts.DROPOBJECTFROMEXTERNAL_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.DELETE_COMMAND] = new DeleteCommand(ASPxClientCommandConsts.DELETE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.KBDELETE_COMMAND] = new DeleteWithoutSelectionCommand(ASPxClientCommandConsts.KBDELETE_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTEHTML_COMMAND] = new PasteHtmlCommand(ASPxClientCommandConsts.PASTEHTML_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTLINK_COMMAND] = new InsertLinkCommand(ASPxClientCommandConsts.INSERTLINK_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.UNLINK_COMMAND] = new BrowserCommand(ASPxClientCommandConsts.UNLINK_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CHANGEIMAGE_COMMAND] = new ChangeImageCommand(ASPxClientCommandConsts.CHANGEIMAGE_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTIMAGE_COMMAND] = new InsertImageCommand(ASPxClientCommandConsts.INSERTIMAGE_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CHECKSPELLING_COMMAND] = new CheckSpellingCommand(ASPxClientCommandConsts.CHECKSPELLING_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CheckSpellingCore_COMMAND] = new CheckSpellingCoreCommand(ASPxClientCommandConsts.CHECKSPELLING_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PRINT_COMMAND] = new PrintCommand(ASPxClientCommandConsts.PRINT_COMMAND);

// Dialog Commands
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.PASTEFROMWORDDIALOG_COMMAND] = new PasteFromWordDialogCommand(ASPxClientCommandConsts.PASTEFROMWORDDIALOG_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND] = new DialogCommand(ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CHANGEIMAGE_DIALOG_COMMAND] = new ChangeImageDialogCommand(ASPxClientCommandConsts.CHANGEIMAGE_DIALOG_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.CHANGELINK_DIALOG_COMMAND] = new ChangeLinkDialogCommand(ASPxClientCommandConsts.CHANGELINK_DIALOG_COMMAND);
ASPxHtmlEditorCommandList[ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND] = new DialogCommand(ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND);

ASPxHtmlEditorCommandList[ASPxClientCommandConsts.Start_COMMAND] = new Command(ASPxClientCommandConsts.TEXTTYPE_COMMAND);

/*region* * * * * * * * * * * * * * *  CommandManager  * * * * * * * * * * * * * * * */
var __aspxEmptySelection = "empty";
CommandManager = _aspxCreateClass(null, {
	constructor: function(htmlEditor) {
		this.htmlEditor = htmlEditor;
		
		this.commandIdArray = new Array();
		this.currentCmdIDIndex = -1;
				
		// For Undo/Redo
		this.lastRestoreSelection = null;
		this.restoreHtmlArray = new Array();
		this.undoSelectionArray = new Array();
		this.redoSelectionArray = new Array();
				
		this.ExecuteCommand(ASPxClientCommandConsts.Start_COMMAND, "null", true);
    },
	ExecuteCommand: function(cmdID, cmdValue, addToUndoStack) {
	    var isSuccessfully = false;
	    if (!ASPxHtmlEditorCommandList[cmdID].IsHtmlChangeable() || !addToUndoStack)
	        isSuccessfully = ASPxHtmlEditorCommandList[cmdID].Execute(cmdValue, this.htmlEditor);
	    else {
	        this.OnCommandExecuting(cmdID);
		    isSuccessfully = ASPxHtmlEditorCommandList[cmdID].Execute(cmdValue, this.htmlEditor);
            // IsReversable == false only for Undo/Redo
	        var needAddToStack = isSuccessfully && ASPxHtmlEditorCommandList[cmdID].IsReversable();
		    if (needAddToStack) {
		        this.ClearActionsToRedo();
		        this.currentCmdIDIndex = this.commandIdArray.length;
		        _aspxArrayPush(this.commandIdArray, cmdID);
		    }
            this.OnCommandExecuted(needAddToStack);
        }
		return isSuccessfully;
	},
	Undo: function(depth) {
		depth = Math.min(depth, this.commandIdArray.length);
		var actionCount = this.commandIdArray.length;
		depth = Math.min(depth, this.commandIdArray.length);
		while ((depth > 0) && (this.currentCmdIDIndex > 0) && (this.currentCmdIDIndex < actionCount)) {
			this.htmlEditor.SetHtmlInternal(this.GetRestoreText(this.currentCmdIDIndex - 1));
			this.GetUndoSelection(this.currentCmdIDIndex - 1).Restore();
			this.currentCmdIDIndex --;
			depth--;
		}
		return true;
	},
	Redo: function(depth) {
		depth = Math.min(depth, this.commandIdArray.length);
		var actionIndex = this.currentCmdIDIndex + 1;
		while (depth > 0  && this.commandIdArray.length >= actionIndex) {
			this.htmlEditor.SetHtmlInternal(this.GetRestoreText(actionIndex));			
			this.GetRedoSelection(actionIndex).Restore();
			this.currentCmdIDIndex = actionIndex;
			actionIndex ++;
			depth--;
		}
        return true;
	},
	
	// Execute operation for previous command
    OnCommandExecuting: function(cmdID) {
        if (this.IsUndoRedoCommand(cmdID) && 
            _aspxIsExists(this.commandIdArray[this.currentCmdIDIndex + 1]))
            return;
        if (_aspxIsExists(this.commandIdArray[this.currentCmdIDIndex])) {
            var prevActionUndoSelection = this.htmlEditor.SaveLastSelection();
            if (this.IsLastCommandImmediateExecute()) {//prev command
                this.AddNewItemToArray(this.restoreHtmlArray, this.currentCmdIDIndex, this.lastHTML);
                this.UpdateOrAddNewItemToArray(this.undoSelectionArray, this.currentCmdIDIndex, prevActionUndoSelection);
                this.AddNewItemToArray(this.redoSelectionArray, this.currentCmdIDIndex, 
                        _aspxCloneObject(this.lastRestoreSelection));
            }
            else
                // undo selection for previous command
                this.undoSelectionArray[this.currentCmdIDIndex] = prevActionUndoSelection;
        }
    },
    OnCommandExecuted: function(needAddToStack) {
        var lastCmdID = this.GetLastCommandID();                
        if (needAddToStack && !ASPxHtmlEditorCommandList[lastCmdID].IsImmediateExecution()) {
            this.AddNewItemToArray(this.restoreHtmlArray, this.currentCmdIDIndex, this.GetEditorHtml());
            this.AddNewItemToArray(this.undoSelectionArray, this.currentCmdIDIndex, __aspxEmptySelection);
            this.AddNewItemToArray(this.redoSelectionArray, this.currentCmdIDIndex, this.htmlEditor.SaveLastSelection());
        }
    },
    
    // Undo/redo opearations
	ClearActionsToRedo: function() {
	    if (this.IsRedoAvailable()) {
	        this.lastRestoreSelection = null;
	        
	        var startIndex = this.currentCmdIDIndex + 1;
	        var length = this.commandIdArray.length - this.currentCmdIDIndex;
	        			
			this.commandIdArray.splice(startIndex, length);
            this.restoreHtmlArray.splice(startIndex, length);
            this.undoSelectionArray.splice(startIndex, length);
            this.redoSelectionArray.splice(startIndex, length);
		}
	},
	ClearUndoHistory: function() {
        this.lastRestoreSelection = null;
        
        this.currentCmdIDIndex = -1;
        this.commandIdArray.length = 0;
		this.commandIdArray.length = 0;
        this.restoreHtmlArray.length = 0;
        this.undoSelectionArray.length = 0;
        this.redoSelectionArray.length = 0;
	    this.ExecuteCommand(ASPxClientCommandConsts.Start_COMMAND, "null", true);
	},
    CleanEmptyRestoreHtml: function() {
        if (this.GetEditorHtml() == this.lastHTML) {
            _aspxArrayRemoveAt(this.commandIdArray, this.currentCmdIDIndex);
            this.currentCmdIDIndex = this.commandIdArray.length - 1;
        }
    },
	GetEditorHtml: function() {
        return this.htmlEditor.GetHtmlInternal();
	},
    GetRestoreText: function(index) {
        return this.restoreHtmlArray[index];
    },
    GetRedoSelection: function(index) {
        return this.redoSelectionArray[index];
    },
    GetUndoSelection: function(index) {
        return this.undoSelectionArray[index];
    },
    
	IsRedoAvailable: function() {
	    return (this.commandIdArray.length - 1 > this.currentCmdIDIndex);
	},
	IsUndoAvailable: function() {
	    return this.currentCmdIDIndex > 0;
	},
	UpdateLastRestoreSelectionAndHTML: function() {
	    if (this.lastRestoreSelection == null)
	        this.lastRestoreSelection = this.htmlEditor.CreateRestoreSelection();
	    this.lastRestoreSelection.Save();
	    this.UpdateLastRestoreHtml();
	},
	UpdateLastRestoreHtml: function() {
	    this.lastHTML = this.GetEditorHtml();
	},
	// Utils
	IsLastCommandImmediateExecute: function() {
	    var lastCmdID = this.GetLastCommandID();
	    var isImmediateExecution = ASPxHtmlEditorCommandList[lastCmdID].IsImmediateExecution();
	    return (lastCmdID != null) && isImmediateExecution && 
	            (this.currentCmdIDIndex == this.commandIdArray.length - 1);
	},
	
	IsDeleting: function() {
	    var lastCmdID = this.GetLastCommandID();	
	    return (lastCmdID != null) &&  (lastCmdID == ASPxClientCommandConsts.KBDELETE_COMMAND) && 
	            (this.currentCmdIDIndex == this.commandIdArray.length - 1);
	},
    IsUndoRedoCommand: function(cmdID) {
	    return (cmdID == ASPxClientCommandConsts.REDO_COMMAND) || 
	            (cmdID == ASPxClientCommandConsts.UNDO_COMMAND);
    },
	GetLastCommandID: function() {
	    var curAction = this.commandIdArray[this.currentCmdIDIndex];
	    return 	_aspxIsExists(curAction) ? curAction : null;
	},
	AddNewItemToArray: function(array, index, value) {
        if (!_aspxIsExists(array[index]))
            _aspxArrayPush(array, value);
	},
	UpdateOrAddNewItemToArray: function(array, index, value) {
        if (!_aspxIsExists(array[index]))
            _aspxArrayPush(array, value);
        else if (array[index] == __aspxEmptySelection)
            array[index] = value;	
	}
});

CommandManager.prototype.IsDefaultActionCommand = function(cmdID) {
    return  (cmdID == ASPxClientCommandConsts.KBPASTE_COMMAND) || 
            (cmdID == ASPxClientCommandConsts.KBCUT_COMMAND) || 
            (cmdID == ASPxClientCommandConsts.LINEBREAKETYPE_COMMAND);
}

/*region* * * * * * * * * * * * * * *  KeyboardManager  * * * * * * * * * * * * * * * */
var ASPxShortcuts = [
    [ASPxClientCommandConsts.BOLD_COMMAND, "CTRL+B"],
    [ASPxClientCommandConsts.ITALIC_COMMAND, "CTRL+I"],
    [ASPxClientCommandConsts.UNDERLINE_COMMAND, "CTRL+U"],
    
    [ASPxClientCommandConsts.JUSTIFYLEFT_COMMAND, "CTRL+L"],
    [ASPxClientCommandConsts.JUSTIFYCENTER_COMMAND, "CTRL+E"],
    [ASPxClientCommandConsts.JUSTIFYRIGHT_COMMAND, "CTRL+R"],
    [ASPxClientCommandConsts.JUSTIFYFULL_COMMAND, "CTRL+J"],
    
    [ASPxClientCommandConsts.NEWPARAGRAPHTYPE_COMMAND, "CTRL+ENTER"],
    [ASPxClientCommandConsts.LINEBREAKETYPE_COMMAND, "SHIFT+ENTER"],
    
    [ASPxClientCommandConsts.UNDO_COMMAND, "CTRL+Z"],
    [ASPxClientCommandConsts.REDO_COMMAND, "CTRL+Y"],
    
    [ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND, "CTRL+K"],    
    [ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND, "CTRL+G"],    
    [ASPxClientCommandConsts.UNLINK_COMMAND, "CTRL+SHIFT+K"],    
    
    [ASPxClientCommandConsts.KBPASTE_COMMAND, "CTRL+V"],
    [ASPxClientCommandConsts.KBPASTE_COMMAND, "SHIFT+INSERT"],
            
    [ASPxClientCommandConsts.KBCUT_COMMAND, "CTRL+X"],
    [ASPxClientCommandConsts.KBCUT_COMMAND, "SHIFT+DELETE"],
    [ASPxClientCommandConsts.KBCOPY_COMMAND, "CTRL+C"],
    [ASPxClientCommandConsts.KBCOPY_COMMAND, "CTRL+INSERT"],
    
    [ASPxClientCommandConsts.PRINT_COMMAND, "CTRL+P"],
    
    [ASPxClientCommandConsts.SELECT_ALL, "CTRL+A"]
];

KeyboardManager = _aspxCreateClass(null, {
	constructor: function() {
	    this.shortcutCommands = new Array();
    },
    AddShortcut: function(shortcutString, commandID) {
         var shortcutCode = _aspxParseShortcutString(shortcutString);
         this.shortcutCommands[shortcutCode] = commandID;
    }
});

KeyboardManager.prototype.IsSystemKey = function(keyCode) {
    return keyCode == 0 ||
        keyCode >= ASPxKey.F1 && keyCode <= ASPxKey.F12 ||
        keyCode >= ASPxKey.Backspace && keyCode <= ASPxKey.Esc ||
        keyCode >= ASPxKey.Space && keyCode <= ASPxKey.Delete ||
        keyCode == ASPxKey.ContextMenu;
}
KeyboardManager.prototype.IsDeleteOrBackSpaceKey = function(keyCode) {
    return keyCode == ASPxKey.Delete || keyCode == ASPxKey.Backspace;
}
KeyboardManager.prototype.IsCursorMovingKey = function(keyCode) {
    return keyCode >= ASPxKey.PageUp && keyCode <= ASPxKey.Down;
}
KeyboardManager.prototype.GetShortcutCommand = function(evt) {    
    var shortcutCode = _aspxGetShortcutCode(evt.keyCode, evt.ctrlKey, evt.shiftKey, evt.altKey);
    return this.shortcutCommands[shortcutCode];
}
ASPxHtmlEditorDialogSR = { };

ASPxHtmlEditorDialog = _aspxCreateClass(ASPxDialog, {               
    Execute: function(ownerControl) {
        this.htmlEditor = ownerControl;
        this.CreateSelectionInfo();
        if (__aspxIE)
            this.SaveScrollPosition();
        this.htmlEditor.RemoveFocus();
        this.SetDialogNameInput();
        this.isOnCallbackError = false;
        ASPxDialog.prototype.Execute.call(this, ownerControl);        
    },
    OnCallbackError: function(result, data) {
        this.isOnCallbackError = true;
        this.ClearDialogNameInput();
        this.HideDialog();
        ASPxDialog.prototype.OnCallbackError.call(this, result);    
    },    
    OnClose: function() {
        ASPxDialog.prototype.OnClose.call(this);
        this.ClearDialogNameInput();
        if (!this.isOnCallbackError)
            this.SaveEditorsState();
    },
    OnCloseButtonClick: function() {    
  	    this.htmlEditor.RestoreLastSelection(this.selectionInfo.selection);
        this.htmlEditor.UpdateToolbar();
    },
  	OnComplete: function(result, params) {
  	    this.GetDialogPopup().Hide();
  	    this.htmlEditor.RestoreLastSelection(this.selectionInfo.selection);
	    this.DoCustomAction(result, params);
        this.ClearEditorValue();
	    if (!result)
	        this.htmlEditor.UpdateToolbar();
  	},
    OnInitComplete: function() {
        ASPxDialog.prototype.OnInitComplete.call(this);
        if (__aspxIE)
            this.RestoreScrollPositionAndSelection();
        // clear fake input
        this.htmlEditor.ClearFocusInput(); 
        this.RestoreEditorsState();
    },
    
    // Editors state
    SaveEditorsState: function() {
    },
    RestoreEditorsState: function() {
    },
    
    ClearEditorValue: function() {
    },
    CreateSelectionInfo: function() {
        var curSelection = this.htmlEditor.SaveRestoreSelectionForDialog();
        this.selectionInfo = {
            selection: curSelection,
            isControl: curSelection.IsControl(),
            isTextOnly: curSelection.IsTextOnly(),
            text: curSelection.GetText(),
            htmlText: curSelection.GetHtmlText(),
            selectedElement: curSelection.GetParentElement()
        };
    },
    // Utils
    ClearDialogNameInput: function() {
        this.htmlEditor.GetCurrentDialogHiddenInput().value = "";
    },
    SendCallback: function(callbackArgs) {
        this.ownerControl.callbackOwner = this;
        this.ownerControl.CreateCallback(callbackArgs, "", false);
        this.ShowLoadingPanelOverDialogPopup();
    },
    SetDialogNameInput: function() {
        this.htmlEditor.GetCurrentDialogHiddenInput().value = this.name;
    },
    RestoreScrollPositionAndSelection: function() {
  	    this.htmlEditor.RestoreLastSelection(this.selectionInfo.selection);
  	    if (this.savedScrollLeft > 0)
  	        this.htmlEditor.GetDesignViewIFrameDocument().body.scrollLeft = this.savedScrollLeft;
  	    if (this.savedScrollTop > 0)  	        
  	        this.htmlEditor.GetDesignViewIFrameDocument().body.scrollTop = this.savedScrollTop;
    },
    SaveScrollPosition: function() {
        this.savedScrollLeft = this.htmlEditor.GetDesignViewIFrameDocument().body.scrollLeft;
        this.savedScrollTop = this.htmlEditor.GetDesignViewIFrameDocument().body.scrollTop;
    },    
    IsEnabledEditor: function(editor) {
        return _aspxGetElementVisibility(editor.GetMainElement());
    }
});


// ** InsertLinkDialog ** 
__aspxDefaultLinkHref = "http://"; 
__aspxMailtToPrefix = "mailto:";
__aspxMailtToSubjectPrefix = "?subject=";
__aspxOpenInNewWindowTarget = "_blank";

InsertLinkDialog = _aspxCreateClass(ASPxHtmlEditorDialog , {
    // virtual
    DoCustomAction: function(result, params) {
	    if (result) {            
	        var target = "";
	        var title = "";
	        var text = "";
	        var url = "";
	        
	        title = params.title;
	        text = params.text;
	        
            switch (_dxeRblLinkType.GetValue()) {
                case "Email":
                        url = __aspxMailtToPrefix + params.url;
                        if (_aspxIsExists(params.subject))
                            url += __aspxMailtToSubjectPrefix + params.subject;
                        break;
                case "URL":
                        url = params.url;
                        target = params.isCheckedOpenInNewWindow ? __aspxOpenInNewWindowTarget : "";
                        break;
            }
            
	        if (params.isTextOnlySelected && (text == ""))
	            text = params.url;
            
	        this.htmlEditor.InsertLink(url, text, target, title);
	    }
    },
    GetDialogCaptionText: function() {
        return InsertLinkDialog.prototype.IsLink(this.selectionInfo.selectedElement) 
			? ASPxHtmlEditorDialogSR.ChangeLink
			: ASPxHtmlEditorDialogSR.InsertLink;
    },
    GetInitInfoObject: function() {
        var linkInfoObject = {
            href: "",
            text: "",
            title: "",
            target: ""
        };
               
	    var link = _aspxGetParentByTagName(this.selectionInfo.selectedElement, "A");
	    var isTextEditorVisible =true;
	    if (_aspxIsExists(link)) {
	        if (!InsertLinkDialog.prototype.ContaintTextNodeOnly(link) || this.selectionInfo.isControl)
	            isTextEditorVisible = false;	    	        
	    }
	    else {
            var img = _aspxGetParentByTagName(this.selectionInfo.selectedElement, "img");
	        if (_aspxIsExists(img))
	            isTextEditorVisible = false;
	        else {
	            var text = this.selectionInfo.text;
	            if (text) text = this.selectionInfo.htmlText;
	            isTextEditorVisible = !Selection.IsHtml(_aspxTrim(text));
	        }
	    }
	        
	    linkInfoObject.isTextEditorVisible = isTextEditorVisible;
        linkInfoObject.text = this.selectionInfo.text;

	    if (_aspxIsExists(link)) {
	        linkInfoObject.href = _aspxGetAttribute(link, "href");
	        linkInfoObject.text = link.innerHTML;
	        linkInfoObject.title = _aspxGetAttribute(link, "title");
	        linkInfoObject.target = _aspxGetAttribute(link, "target");
	    }
	    
	    if (linkInfoObject.href == "")
	        linkInfoObject.href = __aspxDefaultLinkHref;
	    return linkInfoObject;
    },
    InitializeDialogFields: function(linkInfo) {
        var isLinkSelected = InsertLinkDialog.prototype.IsLink(this.selectionInfo.selectedElement);
        this.GetChangeButton().SetVisible(isLinkSelected);
        this.GetInsertButton().SetVisible(!isLinkSelected);
    
        if ((linkInfo.href != "") && (linkInfo.href.indexOf(__aspxMailtToPrefix) > -1)) {            
            var mailtoIndex = linkInfo.href.indexOf(__aspxMailtToPrefix);
            var subjectIndex = linkInfo.href.indexOf(__aspxMailtToSubjectPrefix);
            
            var index = subjectIndex > -1 ? subjectIndex : linkInfo.href.length;
            this.GetEmailToTextBox().SetValue(linkInfo.href.substring(mailtoIndex + __aspxMailtToPrefix.length, index));
            var subject = "";
            if (subjectIndex > -1)
                subject = linkInfo.href.substring(subjectIndex + __aspxMailtToSubjectPrefix.length);
            
            this.GetSubjectTextBox().SetValue(subject);
            this.GetLinkTypeRadioButtonList().SetValue("Email");
        }
        else {
            this.GetLinkTypeRadioButtonList().SetValue("URL");
            this.GetUrlTextBox().SetValue(linkInfo.href);
            this.GetOpenInNewWindowCheckBox().SetValue(linkInfo.target == __aspxOpenInNewWindowTarget);
        }
        this.GetToolTipTextBox().SetValue(linkInfo.title);
        
        if (linkInfo.isTextEditorVisible)
            this.GetTextTextBox().SetValue(linkInfo.text);
        else {
            this.GetTextTextBox().SetEnabled(false);
            this.GetLabelText().SetEnabled(false);
        }
        this.GetLinkTypeRadioButtonList().RaiseSelectedIndexChanged();        
    },
    ClearEditorValue: function() {
        this.GetEmailToTextBox().SetValue(null);
        this.GetSubjectTextBox().SetValue(null);
        this.GetUrlTextBox().SetValue(null);
        this.GetToolTipTextBox().SetValue(null);
        this.GetTextTextBox().SetValue(null);
    },
    
    SetFocusInField: function() {
        if (this.GetLinkTypeRadioButtonList().GetValue() == "URL")
            _aspxSetFocusToTextEditWithDelay(_dxeTxbURL.name);
        else
            _aspxSetFocusToTextEditWithDelay(_dxeTxbEmailTo.name);
    },
  	// Utils
    GetChangeButton: function() {
        return _aspxIsExists(window._dxeBtnChange) ? _dxeBtnChange : null;
    },
    GetEmailToTextBox: function() {
        return _dxeTxbEmailTo;
    },
    GetInsertButton: function() {
        return _dxeBtnOk;
    },  	
    GetLabelText: function() {
        return _dxeLblText;
    },
    GetLinkTypeRadioButtonList: function() {
        return _dxeRblLinkType;
    },
    GetOpenInNewWindowCheckBox: function() {
        return _dxeCkbOpenInNewWindow;
    },
    GetTextTextBox: function() {
        return _dxeTxbText;
    },
    GetToolTipTextBox: function() {
        return _dxeTxbToolTip;
    },
    GetSubjectTextBox: function() {
        return _dxeTxbSubject;
    },    
    GetUrlTextBox: function() {
        return _dxeTxbURL;
    }
});
InsertLinkDialog.prototype.ContaintTextNodeOnly = function(linkElement) {
    if (linkElement.childNodes.length > 0) {
        for (var i = 0; i < linkElement.childNodes.length; i++) {
            if (linkElement.childNodes[i].nodeType != 3)
                return false;
        }
    }
    return true;
}
InsertLinkDialog.prototype.IsLinkSelected = function(htmlEditor) {
    var curSelection = htmlEditor.CreateRestoreSelection();
    return InsertLinkDialog.prototype.IsLink(curSelection.GetParentElement());
}
InsertLinkDialog.prototype.IsLink = function(element) {
    return _aspxIsExists(_aspxGetParentByTagName(element, "A"));
}

// ** InsertImageDialog ** 
var __aspxPreviewTextElementID = "dxInsertImagePreviewText";
var __aspxPreviewImageElementID = "dxInsertImagePreviewImage";

InsertImageDialog = _aspxCreateClass(ASPxHtmlEditorDialog , {

    // virtual
    DoCustomAction: function(result, params) {
	    if (result) {
	        if (_aspxIsExists(this.selectedImageElement))
	            this.htmlEditor.ChangeImage(this.selectedImageElement, params.src, 
	                                        params.width, params.height,
	                                        params.align, params.alt);
	        else
	            this.htmlEditor.InsertImage(params.src, params.width, params.height, 
	                                                    params.align, params.alt);
	    }
    },
    GetDialogCaptionText: function() {
        var img = InsertImageDialog.prototype.GetImage(this.selectionInfo.selectedElement);
        return _aspxIsExists(img) 
			? ASPxHtmlEditorDialogSR.ChangeImage
			: ASPxHtmlEditorDialogSR.InsertImage;
    },
    GetInitInfoObject: function() {
        var imageInfoObject = {
            isCustomSize: false,
            src: "",
            width: "",
            height: "",
            align: "",
            alt: ""
        };
        this.constrainProportions = true;
        this.selectedImageElement = InsertImageDialog.prototype.GetImage(this.selectionInfo.selectedElement);
        if (_aspxIsExists(this.selectedImageElement))
            imageInfoObject = ChangeImageCommand.GetImageProperties(this.selectedImageElement);
	    return imageInfoObject;
    },
    InitializeDialogFields: function(imageInfo) {
        var isImage = _aspxIsExists(this.selectedImageElement);
        
        this.GetChangeButton().SetVisible(isImage);
        this.GetInsertButton().SetVisible(!isImage);
        
        if (imageInfo.src) {
            this.fieldInitializing = true;
            _dxeTbxInsertImageUrl.SetValue(imageInfo.src);
            this.OnImageSrcChanged(imageInfo.src);
        }
        // don't change the order
        this.UpdateSizeFields(imageInfo.width ? imageInfo.width : 0, imageInfo.height ? imageInfo.height : 0);
        
        if (imageInfo.isCustomSize) {
            _dxeCmbSize.SetValue("custom");
            _dxeCmbSize.OnSelectChanged();
        }
        
        if (imageInfo.align)
            _dxeCmbImagePosition.SetValue(imageInfo.align);
        if (imageInfo.alt)
            _dxeTxbDescription.SetValue(imageInfo.alt);
    },
    ClearEditorValue: function() {
        _dxeTbxInsertImageUrl.SetValue(null);
        _dxeTxbDescription.SetValue(null);
        this.GetThumbnailFileNameTextBox().SetValue(null);
    },
    
    SaveEditorsState: function() {
        var checkBox = this.GetCheckBoxMoreImageOptions();
        if (_aspxIsExists(checkBox)) {
            this.htmlEditor.moreImageOptions = checkBox.GetChecked();
        }
    },
    RestoreEditorsState: function() {        
        if (_aspxIsExists(this.htmlEditor.moreImageOptions)) {
            this.GetCheckBoxMoreImageOptions().SetChecked(this.htmlEditor.moreImageOptions);
            this.GetCheckBoxMoreImageOptions().RaiseCheckedChanged();
        }
    },

    GetChangeButton: function() {
        return _dxeBtnChangeImage;
    },
    GetCheckBoxMoreImageOptions: function() {
        return _aspxIsExists(window._dxeCkbMoreImageOptions) ? _dxeCkbMoreImageOptions : null;
    },
    GetClientUploadDirectory: function() {
        return this.htmlEditor.uploadImageFileDirectoryPath;
    },
    GetThumbnailCheckBox: function() {
        return _dxeCkbCreateThumbnail;
    },    
    GetErrorLabel: function() {
        return _dxeLblError;
    },
    GetInsertButton: function() {
        return _dxeBtnInsertImage;
    },   
    GetImageUploader: function() {
        return _dxeUplImage;
    },
    GetThumbnailFileNameTextBox: function() {
        return _dxeThumbnailFileName;
    },
        
    GetInsertImageUrlTextBox: function() {
        return _dxeTbxInsertImageUrl;
    },
    IsFieldInitializing: function() {
        if (_aspxIsExists(this.fieldInitializing))
            return this.fieldInitializing;
        return false;
    },
    SetFocusInField: function() {
        _aspxSetFocusToTextEditWithDelay(_dxeTbxInsertImageUrl.name);
    },    
    // events
    OnCallback: function(result) {
        if (result.indexOf(__aspxHESaveImageToServerCallbackPrefix) == 0) {
            this.OnImageSavedToServer(result.substring(__aspxHESaveImageToServerCallbackPrefix.length + 1, result.length));
            this.ownerControl.callbackOwner = null;
        }
        else
            ASPxDialog.prototype.OnCallback.call(this, result);
    },
    OnComplete: function(result, params) {
        this.insertImageParams = params;
        if (result) {
            if(_dxeRblImageFromThisComputer.GetChecked())
                return this.GetImageUploader().UploadFile();
            else if (this.IsCreateThumbnail())
                return this.SaveThumbnailImageToServerViaCallback(this.GetInsertImageUrlTextBox().GetText(),
                                                                  this.GetThumbnailFileNameTextBox().GetText(),
                                                                  params.width, params.height);
            else if (this.IsSaveImageToServer() && _dxeRblImageFromTheWeb.GetChecked())
                return this.SaveImageToServerViaCallback(this.GetInsertImageUrlTextBox().GetText());
        }
        ASPxHtmlEditorDialog.prototype.OnComplete.call(this, result, params);
    },
    OnImageSavedToServer: function(result) {
        if (result.indexOf(__aspxSaveImageToServerErrorCallbackPrefix) > -1)
            this.ShowErrorMessage(result.substring(__aspxSaveImageToServerErrorCallbackPrefix.length + 1, result.length));
        else {
            this.insertImageParams.src = result.substr(__aspxSaveImageToServerNewUrlCallbackPrefix.length + 1, result.length);
            ASPxHtmlEditorDialog.prototype.OnComplete.call(this, 1, this.insertImageParams);
        }
    },
    OnConstrainProportionsSwicthClick: function(evt, invisibleSwitchImgID) {
        var visibleSwitchImg = _aspxGetEventSource(evt);
        var invisibleSwitchImg = _aspxGetElementById(invisibleSwitchImgID);
        _aspxSetElementDisplay(visibleSwitchImg, false);
        _aspxSetElementDisplay(invisibleSwitchImg, true);    
        this.constrainProportions = !this.constrainProportions;
        
        if (this.constrainProportions)
            this.UpdateSizeFields(this.initialWidth, this.initialHeight);
    },
    // Image Uploader
    OnImageUploadStart: function() {
        this.ShowLoadingPanelOverDialogPopup();
    },
    OnImageUploadComplete: function(args) {
        delete this.htmlEditor.syncLocked;
        this.HideLoadingPanelOverDialogPopup();
        if (args.isValid) {
            var fileName = this.GetClientUploadDirectory() + args.callbackData;
            this.insertImageParams.src = fileName;
            if (this.IsCreateThumbnail())
                this.SaveThumbnailImageToServerViaCallback(ASPxClientPath.GetBaseUrlPath() + fileName,
                                                           this.GetThumbnailFileNameTextBox().GetText(),
                                                           this.insertImageParams.width, this.insertImageParams.height);
            else
                ASPxHtmlEditorDialog.prototype.OnComplete.call(this, 1, this.insertImageParams);
        }
    },
    OnImageUploadTextChanged: function() {
        this.UpdateThumbnailFileName(this.GetImageUploader().GetText());
    },
    OnImageSrcChanged: function(src) {
        this.CheckImageExisting(src);
        this.UpdateThumbnailFileName(src);
    },
    OnImageFromTypeChanged: function() {
        var src = _dxeRblImageFromTheWeb.GetChecked() ? this.GetInsertImageUrlTextBox().GetText() :
                                                        this.GetImageUploader().GetText();
        this.UpdateThumbnailFileName(src);
    },
    OnLoadTestExistingImage: function() {
        _aspxGetElementById(__aspxPreviewImageElementID).src = this.testImage.src;
        var previewAreaTD = _aspxGetParentByTagName(_aspxGetElementById(__aspxPreviewImageElementID), "td");
                
        var maxWidth = previewAreaTD.clientWidth;
        var maxHeight = previewAreaTD.clientHeight;
        this.SetPreviewImageSize(this.testImage.width, this.testImage.height, maxWidth, maxHeight);
        
        _aspxSetElementDisplay(_aspxGetElementById(__aspxPreviewTextElementID), false);
        _aspxSetElementDisplay(_aspxGetElementById(__aspxPreviewImageElementID), true);
        previewAreaTD.style.borderStyle = "none";
        
        this.UpdateSizeFieldsAfterLoad(this.testImage.width, this.testImage.height);
    },
    OnErrorTestExistingImage: function() {
        _aspxSetElementDisplay(_aspxGetElementById(__aspxPreviewTextElementID), true);
        _aspxSetElementDisplay(_aspxGetElementById(__aspxPreviewImageElementID), false);
        
        var previewAreaTD = _aspxGetParentByTagName(_aspxGetElementById(__aspxPreviewImageElementID), "td");
        previewAreaTD.style.borderStyle = "";
        
        this.UpdateSizeFieldsAfterLoad();
    },
    OnSizeSpinNumberChanged: function(sizeType) {
        if (this.constrainProportions)
            this.UpdateSizeSpinEditsWithConstrainProportions(sizeType);
    },
    OnSizeSpinKeyUp: function(sizeType, htmlEvent) {
        if (this.constrainProportions) {
            var keyCode = _aspxGetKeyCode(htmlEvent);        
            if (keyCode != ASPxKey.Tab && keyCode != ASPxKey.Shift)
                this.UpdateSizeSpinEditsWithConstrainProportions(sizeType);
        }
    },
    
    // internal
    CheckImageExisting: function(checkingSrc) {
        if (document.images) {
            if (this.testImage == null) {
                this.testImage = new Image();
                _aspxAttachEventToElement(this.testImage, "load",
                                            new Function("aspxTestExistingImageOnLoad" + "('" + this.htmlEditor.name + "');"));
                _aspxAttachEventToElement(this.testImage, "error",
                                            new Function("aspxTestExistingImageOnError" + "('" + this.htmlEditor.name + "');"));
            }
            this.testImage.src = checkingSrc;
        }
    },
    IsCreateThumbnail: function() {        
        return this.GetThumbnailCheckBox().GetChecked() && _dxeCmbSize.GetValue() == "custom";
    },
    IsSaveImageToServer: function() {
        return _dxeCkbSaveToServer.GetValue() || !this.htmlEditor.allowInsertDirectImageUrls;
    },
    SetPreviewImageSize: function(sourceWidth, sourceHeight, maxWidth, maxHeight) {
        var newWidth = sourceWidth;
        var newHeight = sourceHeight;
        if ((sourceWidth > maxWidth) || 
            (sourceHeight > maxHeight)) {
            
            var cw = sourceWidth/maxWidth;            
            var ch = sourceHeight/maxHeight;
                        
            if (cw > ch) {
                var cw = sourceWidth/maxWidth;            
                newWidth = Math.floor(sourceWidth/cw);
                newHeight = Math.floor(sourceHeight/cw);
            }
            else {
                newWidth = Math.floor(sourceWidth/ch);
                newHeight = Math.floor(sourceHeight/ch);
            }
        }        
        var previewImage = _aspxGetElementById(__aspxPreviewImageElementID);
        previewImage.style.width = newWidth + "px";
        previewImage.style.height = newHeight + "px";
    },
    SaveImageToServerViaCallback: function(src) {
        this.SendCallback(_aspxFormatCallbackArg(__aspxHESaveImageToServerCallbackPrefix, src));
    },
    SaveThumbnailImageToServerViaCallback: function(src, newImageFileName, thumbnailWidth, thumbnailHeight) {
        this.SendCallback(_aspxFormatCallbackArgs([
            [ __aspxHESaveImageToServerCallbackPrefix, src ],
            [ __aspxHEThumbnailImageWidthCallbackPrefix, thumbnailWidth ],
            [ __aspxHEThumbnailImageHeightCallbackPrefix, thumbnailHeight ],
            [ __aspxHEThumbnailImageFileNameCallbackPrefix, newImageFileName ]
        ]));
    },
    HideErrorMessage: function() {
        this.GetErrorLabel().SetVisible(false);
    },
    ShowErrorMessage: function(message) {
    // todo for show error message when file was uploaded and thumbnail is being created
        var textBox = this.GetInsertImageUrlTextBox();
        textBox.isValid = false;
        textBox.errorText = message;
        textBox.UpdateErrorFrameAndFocus(false, true);
    },
    
    UpdateConstrainProportionsCoef: function(width, height) {
        if ((width == height) || (height == 0))
            this.constrainProportionsCoef_WH = 1;
        else
            this.constrainProportionsCoef_WH = width/height;
                    
        this.initialWidth = width;
        this.initialHeight = height;
    },
    UpdateSizeSpinEditsWithConstrainProportions: function(sizeType) {
        var newWidth = _dxeSpnWidth.GetNumber();
        var newHeight = _dxeSpnHeight.GetNumber();
        switch(sizeType) {
            case "width":
                _dxeSpnWidth.SaveSelectionStartAndEndPosition();
                newHeight = _dxeSpnWidth.GetParsedNumber()/this.constrainProportionsCoef_WH;
                _dxeSpnHeight.SetValue(Math.floor(newHeight));
                _dxeSpnWidth.RestoreSelectionStartAndEndPosition();
                break;
            case "height":
                _dxeSpnHeight.SaveSelectionStartAndEndPosition();
                newWidth = _dxeSpnHeight.GetParsedNumber()*this.constrainProportionsCoef_WH;
                _dxeSpnWidth.SetValue(Math.floor(newWidth));
                _dxeSpnHeight.RestoreSelectionStartAndEndPosition();
                break;
        }
    },    
    UpdateSizeFields: function(imageWidth, imageHeight) {
        _dxeSpnWidth.SetValue(imageWidth >= 0 ? imageWidth : 0);
        _dxeSpnHeight.SetValue(imageHeight >=0 ? imageHeight: 0);
        this.UpdateConstrainProportionsCoef(_dxeSpnWidth.GetNumber(), _dxeSpnHeight.GetNumber());
    },
    UpdateSizeFieldsAfterLoad: function(imageWidth, imageHeight) {
        if (!this.IsFieldInitializing())
            this.UpdateSizeFields(imageWidth, imageHeight);
        else
            this.fieldInitializing = false;
    },
    UpdateThumbnailFileName: function(src) {
        var fileName = ASPxClientPath.GetFileNameWithoutExtension(src);
        if (fileName)
            fileName += "Thumbnail.jpg";
        this.GetThumbnailFileNameTextBox().SetText(fileName);
    }
});

// Utils
InsertImageDialog.prototype.GetSelectedImage = function(htmlEditor) {
    var curSelection = htmlEditor.CreateRestoreSelection();
    var containerElement = curSelection.GetParentElement();	    
    return InsertImageDialog.prototype.GetImage(containerElement);
}
InsertImageDialog.prototype.GetImage = function(element) {
    return _aspxGetParentByTagName(element, "img");
}

function aspxInsertImageSrcValueChanged(src) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null) curDialog.OnImageSrcChanged(src);
}
// Constrain Proportions
function aspxConstrainProportionsSwicthClick(evt, invisibleSwitchImgID) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null) 
	    return curDialog.OnConstrainProportionsSwicthClick(evt, invisibleSwitchImgID);
}
// Image Uploader
function aspxImageUploadStart() {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnImageUploadStart();
}
function aspxImageUploadComplete(args) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnImageUploadComplete(args);
}
function aspxImageUploadTextChanged() {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnImageUploadTextChanged();
}
function aspxOnImageFromTypeChanged() {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnImageFromTypeChanged();
}

// Test image size
function aspxTestExistingImageOnLoad(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null) curDialog.OnLoadTestExistingImage();
}
function aspxTestExistingImageOnError(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null) curDialog.OnErrorTestExistingImage();
}
// Width and Height SpinEdits
function aspxSizeSpinNumberChanged(sizeType) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null) 
	    return curDialog.OnSizeSpinNumberChanged(sizeType);
}
function aspxSizeSpinKeyUp(sizeType, htmlEvent) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnSizeSpinKeyUp(sizeType, htmlEvent);
}

// ** PasteFromWordDialog **
PasteFromWordDialog = _aspxCreateClass(ASPxHtmlEditorDialog, {
    // cmdValue = { html, stripFontFamily };
    DoCustomAction: function(result, cmdValue) {
        _aspxRemoveElement(this.GetPasteContainerIFrame());
        if (result && cmdValue.html)
            this.htmlEditor.ExecuteCommand(ASPxClientCommandConsts.PASTEFROMWORD_COMMAND, cmdValue, true);
    },
    InitializeDialogFields: function() {
        // iframe
        var contentDocument = this.GetPasteContainerIFrame().contentWindow.document;
        
		contentDocument.open();
		contentDocument.write("<head><style></style></head><body></body>");
		contentDocument.close();
		
		if (__aspxIE)
		    contentDocument.body.contentEditable = true;
		else {
		    contentDocument.body.spellcheck = false;
		    contentDocument["designMode"] = "on";
		}
		
        _aspxAttachEventToElement(contentDocument, "keypress", aspxHEPasteFromWordContainerKeyPress);
		contentDocument.body.style.margin = "0px";
		contentDocument.body.style.padding = "2px";
		contentDocument.body.style.border = "Solid 0px";
    },
    SetFocusInField: function() {
        _aspxSetTimeout("_aspxIFrameWindow('" + this.GetPasteContainerIFrameName() + "').focus();", 500);
    },
    GetDialogCaptionText: function() {
        return ASPxHtmlEditorDialogSR.PasteFromWord;
    },
    // Event Handlers
    OnPasteContainerKeyPress: function(evt) {
        if (evt.keyCode == ASPxKey.Esc)
            this.HideDialog();
    },
        
    GetPasteContainerIFrame: function() {
        return document.getElementById(this.GetPasteContainerIFrameName());
    },
    GetPasteContainerIFrameName: function() {
        return "_dxePasteFromWordContainer";
    }
});

function aspxHEPasteFromWordContainerKeyPress(evt) {
    var htmlEdit = aspxGetControlCollection().Get(__aspxCurrentControlNameInDialog);
    var curDialog = htmlEdit != null ? ASPxDialog.GetLastDialog(htmlEdit) : null;
	if (curDialog != null)
	    return curDialog.OnPasteContainerKeyPress(evt);
}

// ** DialogList **
var ASPxHtmlEditorDialogList = {};
ASPxHtmlEditorDialogList[ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND] = new InsertLinkDialog(ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND);
ASPxHtmlEditorDialogList[ASPxClientCommandConsts.CHANGELINK_DIALOG_COMMAND] = new InsertLinkDialog(ASPxClientCommandConsts.INSERTLINK_DIALOG_COMMAND);
ASPxHtmlEditorDialogList[ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND] = new InsertImageDialog(ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND);
ASPxHtmlEditorDialogList[ASPxClientCommandConsts.CHANGEIMAGE_DIALOG_COMMAND] = new InsertImageDialog(ASPxClientCommandConsts.INSERTIMAGE_DIALOG_COMMAND);

ASPxHtmlEditorDialogList[ASPxClientCommandConsts.PASTEFROMWORDDIALOG_COMMAND] = new PasteFromWordDialog(ASPxClientCommandConsts.PASTEFROMWORDDIALOG_COMMAND);

var __aspxHEIsDocumentDragOver = false;
var __aspxHEDragDropDelay = 100;

var __aspxHEMarkClassName = "dxMClassName";
var __aspxHEMarkFontAttr = "dxattr";
var __aspxHEMarkFontFace = "ABC";
var __aspxHEDesignViewDocumentCssClassName = "dxheDesignViewDoc";
var __aspxHEPreviewDocumentCssClassName = "dxhePreviewDoc";
var __aspxHEPreservedTagNamePrefix = "ASPxPreservedTag_";

ASPxClientHtmlEditorIDSuffix = {
    ContentHtmlHiddenField: "_Html",
    CurrentDialogHiddenField: "_CurDialog",
    DesignViewIFrame: "_DesignIFrame",
    DesignViewCell: "_DesignViewCell",
    EditAreaCell: "_EdtCell",
    FakeFocusInput: "_FFI",
    HtmlViewEdit: "_HtmlViewEdit",
    MainCell: "_MainCell",
    PopupDialogControl: "_DPP",
    PopupMenu: "_PPM",
    PreviewIFrame: "_PreviewIFrame",
    PreviewCell: "_PreviewCell",
    
    TabControl: "_TC",
    Toolbar: "_TD",
    ToolbarRow: "_TBRow",
    StatusBarCell: "_SBarCell",
    SpellChecker : "_SC"
};

/* Callback prefixes */
var __aspxHESaveImageToServerCallbackPrefix = "ImageToServer";
var __aspxHEThumbnailImageWidthCallbackPrefix = "TNIW";
var __aspxHEThumbnailImageHeightCallbackPrefix = "TNIH";
var __aspxHEThumbnailImageFileNameCallbackPrefix = "TNIF";
var __aspxHESwitchToDesignViewCallbackPrefix = "ProcessHtml_Design";
var __aspxHESwitchToHtmlViewCallbackPrefix = "ProcessHtml_Html";
var __aspxHESwitchToPreviewCallbackPrefix = "ProcessHtml_Preview";
var __aspxHESpellCheckingCallbackPrefix = "SpellCheck";

// from server
var __aspxSaveImageToServerErrorCallbackPrefix = "ISE";
var __aspxSaveImageToServerNewUrlCallbackPrefix = "ISU";

/* RegExp */
var __aspxJSEventHadlersRegExpPattern = "(\\s(\\bon[a-zA-Z][a-z]+)\\s?\=\\s?[\'\"]?(javascript\:)?[\\w\(\\),\\' ]*;?[\\'\\\"]?)+";
var __aspxJSTagsRegExpPattern = "<(script)([^>]*)>[\\s\\S]*?</(script)([^>]*)>";

ASPxClientHtmlEditorView = {
    Design: "D",
    Html: "H",
    Preview: "P"
};
ASPxClientHtmlEditorCommandEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
    constructor: function(commandName, parameter, isSuccessful) {
        this.constructor.prototype.constructor.call(this);
        this.commandName = commandName;
        this.parameter = parameter;
        this.isSuccessful = isSuccessful;
    }
});

ASPxHtmlEditorsCollection = _aspxCreateClass(null, {
    constructor: function() {
        this.htmlEditors = [ ];

        var postHandler = aspxGetPostHandler();
        postHandler.Post.AddHandler(function(s, e) {
            aspxGetHtmlEditorsCollection().Synchronize();
        });
        
        var controlCollection = aspxGetControlCollection();
        controlCollection.BeforeInitCallback.AddHandler(function(s, e) {
            aspxGetHtmlEditorsCollection().Synchronize();
        });
        controlCollection.ControlsInitialized.AddHandler(function(s, e) {
            aspxGetHtmlEditorsCollection().RemoveSyncLockedAttribute();
        });
    },
    Push: function(htmlEditor) {
        for(var i = 0; i < this.htmlEditors.length; i++) {
            if (this.htmlEditors[i].name == htmlEditor.name) {
                this.htmlEditors[i] = htmlEditor;
                return;
            }
        }
        this.htmlEditors.push(htmlEditor);
    },
    RemoveSyncLockedAttribute: function() {
        for (var i = 0; i < this.htmlEditors.length; i++)
            delete this.htmlEditors[i].syncLocked;
    },
    Synchronize: function() {
        for (var i = 0; i < this.htmlEditors.length; i++) {
            var htmlEditor = this.htmlEditors[i];
            if(!htmlEditor.syncLocked) {
                htmlEditor.syncLocked = "syncLocked";
                htmlEditor.Synchronize();
            }
        }
    },
    FocusActiveEditorToolbar: function() {
        for (var i = 0; i < this.htmlEditors.length; i++)
            if(this.htmlEditors[i].isInFocus)
                this.htmlEditors[i].FocusToolbar();
    }
});

function aspxGetHtmlEditorsCollection() {
    if (!_aspxIsExistsType(typeof(window.__aspxHEHtmlEditorsCollection))) {
        window.__aspxHEHtmlEditorsCollection = new ASPxHtmlEditorsCollection();
    }
    return window.__aspxHEHtmlEditorsCollection;
}

// Layout Calculator

ASPxHtmlEditorLayoutCalculator = _aspxCreateClass(null, {
    
    UpdateLayout: function(htmlEditor, activeView, isInitializing) {
        if (!htmlEditor.IsVisible())
            return;
        if (isInitializing)
            this.FixHtmlEditMainElementWidth(htmlEditor);
        if (__aspxIE && isInitializing)
            this.CalculateTableCellsHeight(htmlEditor, activeView);
        this.HideViewAreas(htmlEditor);
        if (__aspxIE) {
            var contentCellHeight = this.CalculateContentCellHeight(htmlEditor, activeView);
            this.CollapseContentCell(htmlEditor);
        }
        this.UpdateToolbarRowDisplay(htmlEditor, activeView);
        this.CollapseViewAreas(htmlEditor);
        this.ShowHideViewAreasDependingOnActiveView(htmlEditor, activeView);
        if (__aspxIE)
            this.SetContentCellCalculatedHeight(htmlEditor, contentCellHeight);
        this.CorrectActiveViewAreaHeight(htmlEditor, activeView);
        if (__aspxNS && !htmlEditor.IsHtmlView(activeView))
            this.FixToolbarTableBorder_NS(htmlEditor);
    },
    
    CalculateTableCellsHeight: function(htmlEditor, activeView) {
        // Default: toolbars and status bar doesn't exist
        htmlEditor.calculatedToolbarCellHeight = 0;
        htmlEditor.calculatedStatusBarCellHeigth = 0;
        
        var toolbarCell = htmlEditor.GetToolbarCell();
        if (_aspxIsExists(toolbarCell)) {
            var savedToolbarIsVisible = this.IsToolbarRowVisible(htmlEditor, activeView);
            this.ShowHideToolbarRow(htmlEditor, true);
            
            htmlEditor.calculatedToolbarCellHeight = _aspxGetClearClientHeight(toolbarCell);
            this.ShowHideToolbarRow(htmlEditor, savedToolbarIsVisible);
        }
        var statusBarCell = htmlEditor.GetStatusBarCell();
        if (_aspxIsExists(statusBarCell))
            htmlEditor.calculatedStatusBarCellHeigth = _aspxGetClearClientHeight(statusBarCell);
    },
    FixHtmlEditMainElementWidth: function(htmlEditor) {
        var mainElement = htmlEditor.GetMainElement();
        mainElement.style.width = (mainElement.offsetWidth > 0) ? (mainElement.offsetWidth + "px") : "100%";
    },
    ShowHideViewAreas: function(htmlEditor, showDesignView, showHtmlView, showPreview) {
        var designViewTable = htmlEditor.GetDesignViewTable();
        var htmlViewEdit = htmlEditor.GetHtmlViewEdit();
        var previewTable = htmlEditor.GetPreviewTable();
        
        if (_aspxIsExists(designViewTable))
            _aspxSetElementDisplay(designViewTable, showDesignView);
        if (_aspxIsExists(htmlViewEdit))
            htmlViewEdit.SetVisible(showHtmlView);
        if (_aspxIsExists(previewTable))
            _aspxSetElementDisplay(previewTable, showPreview);
    },
    HideViewAreas: function(htmlEditor) {
        this.ShowHideViewAreas(htmlEditor, false, false, false);
    },
    UpdateToolbarRowDisplay: function(htmlEditor, activeView) {
        this.ShowHideToolbarRow(htmlEditor, this.IsToolbarRowVisible(htmlEditor, activeView));
    },
    IsToolbarRowVisible: function(htmlEditor, activeView) {
        return htmlEditor.IsDesignView(activeView);
    },
    ShowHideToolbarRow: function(htmlEditor, isDisplayed) {
        var toolbarRow = htmlEditor.GetToolbarRow();
        if (_aspxIsExists(toolbarRow)) {
            _aspxSetElementDisplay(toolbarRow, isDisplayed);
        }
    },
    CalculateContentCellHeight: function(htmlEditor, activeView) {
        var mainCell = htmlEditor.GetMainCell();
        var mainCellHeight = _aspxGetClearClientHeight(mainCell);
        var toolbarCellHeight = htmlEditor.IsDesignView(activeView) ? htmlEditor.calculatedToolbarCellHeight : 0;
        return (mainCellHeight - toolbarCellHeight - htmlEditor.calculatedStatusBarCellHeigth);
    },
    CollapseContentCell: function(htmlEditor) {
        this.SetContentCellCalculatedHeight(htmlEditor, 0);
    },
    SetContentCellCalculatedHeight: function(htmlEditor, height) {
        var contentCell = htmlEditor.GetEditAreaCell();
        if (height != null)
            _aspxSetOffsetHeight(contentCell, height);
        else
            contentCell.style.height = "";
    },
    CollapseViewAreas: function(htmlEditor) {
        var designViewCell = htmlEditor.GetDesignViewCell();
        var htmlEdit = htmlEditor.GetHtmlViewEdit();
        var previewCell = htmlEditor.GetPreviewCell();
        
        if (_aspxIsExists(designViewCell))
            designViewCell.style.height = "0";
        if (_aspxIsExists(htmlEdit))
            htmlEdit.GetInputElement().style.height = "0";
        if (_aspxIsExists(previewCell))
            previewCell.style.height = "0";
    },
    ShowHideViewAreasDependingOnActiveView: function(htmlEditor, activeView) {
        this.ShowHideViewAreas(htmlEditor, htmlEditor.IsDesignView(activeView), htmlEditor.IsHtmlView(activeView), htmlEditor.IsPreview(activeView));
    },
    CorrectActiveViewAreaHeight: function(htmlEditor, activeView) {
        if (htmlEditor.IsDesignView(activeView))
            this.CorrectDesignViewAreaHeight(htmlEditor);
        else if (htmlEditor.IsHtmlView(activeView))
            this.CorrectHtmlViewAreaHeight(htmlEditor);
        else if (htmlEditor.IsPreview(activeView))
            this.CorrectPreviewAreaHeight(htmlEditor);
    },
    CorrectDesignViewAreaHeight: function(htmlEditor) {
        this.CorrectIFrameContainingAreaHeight(htmlEditor, htmlEditor.GetDesignViewCell(), htmlEditor.GetDesignViewIFrameElement());
    },
    CorrectHtmlViewAreaHeight: function(htmlEditor) {
        var htmlViewEdit = htmlEditor.GetHtmlViewEdit();
        if (_aspxIsExists(htmlViewEdit))
            htmlViewEdit.AdjustControl(false);
    },
    CorrectPreviewAreaHeight: function(htmlEditor) {
        this.CorrectIFrameContainingAreaHeight(htmlEditor, htmlEditor.GetPreviewCell(), htmlEditor.GetPreviewIFrameElement());
    },
    CorrectIFrameContainingAreaHeight: function(htmlEditor, viewCell, viewIFrameElement) {
        var contentCell = htmlEditor.GetEditAreaCell();
        _aspxSetElementDisplay(viewIFrameElement, false);
        
        if (__aspxNS || __aspxOpera) {
            var height = _aspxGetClearClientHeight(contentCell);
            var viewCellCalculatedStyle = _aspxGetCurrentStyle(viewCell);
            height -= Math.round((_aspxPxToInt(viewCellCalculatedStyle.borderTopWidth) + _aspxPxToInt(viewCellCalculatedStyle.borderBottomWidth)) / 2.0);
            _aspxSetOffsetHeight(viewCell, height);
        } else {
            _aspxSetOffsetHeight(viewCell, _aspxGetClearClientHeight(contentCell));
        }
      
        viewIFrameElement.style.height = "100%";
        _aspxSetElementDisplay(viewIFrameElement, true);
    },
    FixToolbarTableBorder_NS: function(htmlEditor) {
        var table = htmlEditor.GetToolbarTable();
        if (_aspxIsExists(table)) {
            var borderCollapse = table.style.borderCollapse;
            table.style.borderCollapse = "";
            table.style.borderCollapse = borderCollapse;
        }
    }
});
__aspxHtmlEditorLayoutCalculator = null;
function aspxGetHtmlEditorLayoutCalculator() {
    if (__aspxHtmlEditorLayoutCalculator == null)
        __aspxHtmlEditorLayoutCalculator = new ASPxHtmlEditorLayoutCalculator();
    return __aspxHtmlEditorLayoutCalculator;
}

// Client State 

var __aspxHEClientStateInputIDSuffix = "_ClientState";
var __aspxHEClientStateRecordSeparator = '|';
ASPxClientHtmlEditorClientState = _aspxCreateClass(null, {
    constructor: function(htmlEditor) {
        this.htmlEditor = htmlEditor;
        this.clientStateInput = null;
        this.fieldsNameValueCollection = this.ParseStateStr();
    },
    GetClientStateInput: function() {
        if (this.clientStateInput == null)
            this.clientStateInput = this.FindClientStateInput();
        return this.clientStateInput;
    },
    FindClientStateInput: function() {
        return _aspxGetElementById(this.htmlEditor.name + __aspxHEClientStateInputIDSuffix);
    },
    SetFieldValue: function(name, value) {
        this.fieldsNameValueCollection[name] = value;
        this.Save(this.fieldsNameValueCollection);
    },
    ParseStateStr: function() {
        var state = this.GetClientStateInput().value;
        collection = {};
        var startIndex = 0;
        while(startIndex < state.length)
            startIndex = this.ParseFieldRecord(state, startIndex, collection);
        return collection;
    },
    ParseFieldRecord: function(state, startIndex, collection) {
        var indexOfFirstSeparator = state.indexOf(__aspxHEClientStateRecordSeparator, startIndex);
        var fieldName = state.substr(startIndex, indexOfFirstSeparator - startIndex);
        startIndex += fieldName.length + 1;
        var indexOfSecondSeparator = state.indexOf(__aspxHEClientStateRecordSeparator, startIndex);
        var fieldValueLengthStr = state.substr(startIndex, indexOfSecondSeparator - startIndex);
        startIndex += fieldValueLengthStr.length + 1;
        var fieldValueLength = parseInt(fieldValueLengthStr);
        var fieldValue = state.substr(startIndex, fieldValueLength);
        startIndex += fieldValueLength;
        collection[fieldName] = fieldValue;
        return startIndex;
    },
    Save: function(collection) {
        var result = [];
        for(var fieldName in collection) {
            var value = collection[fieldName];
            if (typeof(value) == "string")
                result.push(fieldName + __aspxHEClientStateRecordSeparator + value.length + __aspxHEClientStateRecordSeparator + value);
        }
        this.GetClientStateInput().value = result.join('');
    }
});
ASPxClientHtmlEditor = _aspxCreateClass(ASPxClientControl, {
    constructor: function(name){
        this.constructor.prototype.constructor.call(this, name);
        
        //set from server
        this.allowDesignView = true;
        this.allowHtmlView = true;
        this.allowPreview = true;
        this.allowContextMenu = true;
        this.allowScripts = false;
        this.allowInsertDirectImageUrls = true;
        this.cssFileLinkArray = [];
        this.designViewIFrameDocumentStyleCssText = "";
        this.previewIFrameDocumentStyleCssText = "";
        this.html = "";
        this.activeView = ASPxClientHtmlEditorView.Design;
        this.uploadImageFileDirectoryPath = "";
        
        this.areDictionariesAssigned = true; // for spellchecker
        
        // client state
        this.clientState = new ASPxClientHtmlEditorClientState(this);
        
        // callback
        this.callbackOwner = null;
        this.spellCheckerCallback = false;
        
        // for dragdrop operations
        this.isDraggingInsideEditor = false;
        this.isDropExternalContentExecuted = false;
        this.dragDropTimerID = null;
        
        this.beforeShowContextMenuSelection = null;
		this.CustomCommand = new ASPxClientEvent();
		this.CommandExecuted = new ASPxClientEvent();
        this.GotFocus = new ASPxClientEvent();
        this.LostFocus = new ASPxClientEvent();
        this.SelectionChanged = new ASPxClientEvent();
        this.HtmlChanged = new ASPxClientEvent();
        
        // internal
        this.layoutCalculator = aspxGetHtmlEditorLayoutCalculator();
        this.eventListener = null;
        this.commandManager = null;
        this.isErrorOnCallback = false;
        this.isMouseDown = false;
        this.isInFocus = false;
        this.keyboardManager = null;
        this.toolbar = null;
        this.shortcutCmdID = null;
        this.executingCommandName = null;
        this.pasteContainer = null;
    },
    
    /*region* * * * * * * * * * * * * * *  Initialization  * * * * * * * * * * * * * * * */
    
    InlineInitialize: function() {
        this.InitAreas();
        // TODO in base remove, if AdjustControlCore will be in InlineInitialize
        this.UpdateLayout(this.activeView, false);
        this.InitializeSpellChecker();
    },    
    Initialize: function() {
        ASPxClientControl.prototype.Initialize.call(this);

        aspxGetHtmlEditorsCollection().Push(this);
        
        if (this.allowDesignView) {
            this.commandManager = new CommandManager(this);
            this.keyboardManager = new KeyboardManager();
            this.toolbar = this.GetToolbar();
            
            this.InitializeEventListner();
            this.InitializeShortcuts();
        }
    },
    InitAreas: function() {
        if (this.allowDesignView)
            this.InitDesignViewIFrame();
        if (this.allowPreview)
            this.InitPreviewIFrame();
        if (this.allowHtmlView)
            this.InitHtmlViewArea();
        if (this.html != "")
            this.SetHtmlInternal(this.html);
    },
    InitializeSpellChecker: function(){
        var spellChecker = this.GetSpellCheckerClientInstance();
        if(_aspxIsExists(spellChecker)){
            var method = new Function("sender", "args", "aspxHESpellCheckerWordChanged(\"" + this.name + "\", sender, args);");
            spellChecker.WordChanged.AddHandler(method);
        }
    },
    InitDesignViewIFrame: function() {
        this.InitIFrame(this.GetDesignViewIFrameName(), this.html, this.cssFileLinkArray);
        this.InitDesignViewIFrameStyle();
    },
    InitPreviewIFrame: function() {
        this.InitIFrame(this.GetPreviewIFrameName(), this.html, this.cssFileLinkArray);
        this.InitPreviewIFrameStyle();
    },
    InitHtmlViewArea: function() {    
        this.DisableSpellCheckerAttribute(this.GetHtmlViewEdit().GetInputElement());
    },
    InitializeEventListner: function() {
        this.eventListener = new EventListener(this);
    },
    InitializeShortcuts: function() {
        this.shortcutCmdID = null;
        for (var i = 0; i < ASPxShortcuts.length; i++)
            this.keyboardManager.AddShortcut(ASPxShortcuts[i][1], ASPxShortcuts[i][0]);
    },
    InitIFrame: function(iframeName, bodyContentHtml, cssFileLinkArray) {
        var doc = _aspxIFrameDocument(iframeName);
        var element = _aspxIFrameElement(iframeName);
        if(doc != null && element != null){
            this.InitIFrameDocument(doc, bodyContentHtml, cssFileLinkArray);
            // Netscape version < 9 bug. Don't change order!
            element.style.height = "100%";
            doc.body.className = element.className;
        }
    },
    InitIFrameDocument: function(doc, bodyContentHtml, cssFileLinkArray) {
        doc.open();
        doc.write(this.GetIFrameDocumentHtml(bodyContentHtml));
        doc.close();
      	doc.body.style.borderWidth = 0;
      	
      	this.DisableSpellCheckerAttribute(doc.body);
        this.AddCssFileLinksToDocument(doc, cssFileLinkArray);      	
    },
    InitDesignViewIFrameStyle: function() {
        // add custom style
        if (this.designViewIFrameDocumentStyleCssText != "") {
            var designViewIFrameDocumentStyleSheet = _aspxCreateStyleSheetInDocument(this.GetDesignViewIFrameDocument());
            _aspxAddStyleSheetRule(designViewIFrameDocumentStyleSheet,
                        "." + __aspxHEDesignViewDocumentCssClassName, this.designViewIFrameDocumentStyleCssText);
            this.GetDesignViewIFrameDocumentBody().className += " " + __aspxHEDesignViewDocumentCssClassName;
        }
    },
    InitPreviewIFrameStyle: function() {
        // add custom style
        if (this.previewIFrameDocumentStyleCssText != "") {
            var previewIFrameDocumentStyleSheet = _aspxCreateStyleSheetInDocument(this.GetPreviewIFrameDocument());
            _aspxAddStyleSheetRule(previewIFrameDocumentStyleSheet, 
                        "." + __aspxHEPreviewDocumentCssClassName, this.previewIFrameDocumentStyleCssText);
            this.GetPreviewIFrameDocumentBody().className += " " + __aspxHEPreviewDocumentCssClassName;
        }    
    },
    AddCssFileLinksToDocument: function(doc, linkArray) {
        for (var i = 0 ; i < linkArray.length; i++) {
            _aspxAddStyleSheetLinkToDocument(doc, linkArray[i]);
        }
    },
    
    /*region* * * * * * * * * * * * * * * * * *  Client State  * * * * * * * * * * * * * * * * * * */
    
    SetClientStateFieldValue: function(name, value) {
        this.clientState.SetFieldValue(name, value);
    },
    
    /*region* * * * * * * * * * * * * * *  Elements/Subcontrols access  * * * * * * * * * * * * * * * */
    
    GetCurrentDialogHiddenInput: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.CurrentDialogHiddenField);
    },
    GetDesignViewCell: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.DesignViewCell);
    },
    GetDesignViewTable: function() {
        return _aspxGetParentByTagName(this.GetDesignViewCell(), "table");
    },
    GetPreviewTable: function() {
        return _aspxGetParentByTagName(this.GetPreviewCell(), "table");
    },
    GetPreviewCell: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.PreviewCell);    
    },
    GetDesignViewIFrameDocument: function() {
        return _aspxIFrameDocument(this.GetDesignViewIFrameName());
    },
    GetDesignViewIFrameDocumentBody: function() {
        return _aspxIFrameDocumentBody(this.GetDesignViewIFrameName());
    },
    GetDesignViewIFrameWindow: function() {
        return _aspxIFrameWindow(this.GetDesignViewIFrameName());
    },
    GetEditAreaCell: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.EditAreaCell);
    },
    GetDialogPopupControl: function() {
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.PopupDialogControl);
    },
    GetDesignViewIFrameElement: function() {
        return _aspxIFrameElement(this.GetDesignViewIFrameName());
    },
    GetDesignViewIFrameName: function() {
        return this.name + ASPxClientHtmlEditorIDSuffix.DesignViewIFrame;
    },
    GetPasteContainer: function() {
        if (this.pasteContainer == null)
            this.pasteContainer = this.CreatePasteContainer();
        return this.pasteContainer;
    },
    GetPreviewIFrameName: function() {
        return this.name + ASPxClientHtmlEditorIDSuffix.PreviewIFrame;
    },
    GetPreviewIFrameElement: function() {
        return _aspxIFrameElement(this.GetPreviewIFrameName());
    },
    GetPreviewIFrameDocument: function() {
        return _aspxIFrameDocument(this.GetPreviewIFrameName());
    },
    GetPreviewIFrameDocumentBody: function() {
        return _aspxIFrameDocumentBody(this.GetPreviewIFrameName());
    },
    GetElementDocument: function(element) {
        return element.ownerDocument || element.document;
    },    
    GetFakeFocusInputElement: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.FakeFocusInput);
    },
    GetHtmlViewEdit: function() {
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.HtmlViewEdit);
    },
    GetMainCell: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.MainCell);
    },
    GetContextMenu: function() {
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.PopupMenu);
    },
    GetToolbar: function() { // TODO [Seleznyov] rename to GetBarDock
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.Toolbar);
    },
    GetTabControl: function() {
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.TabControl);
    },
    GetToolbarRow: function() {
        return this.GetChild(ASPxClientHtmlEditorIDSuffix.ToolbarRow);
    },
    GetToolbarTable: function() {
        return _aspxGetParentByTagName(this.GetToolbarRow(), "table");
    },
    GetToolbarCell: function() {    
        return _aspxGetChildByTagName(this.GetToolbarRow(), "td", 0);
    },
    GetStatusBarCell: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.StatusBarCell);
    },
    GetSpellCheckerClientInstance: function() {
        return aspxGetControlCollection().Get(this.name + ASPxClientHtmlEditorIDSuffix.SpellChecker);
    },
    GetHtmlSyncHiddenField: function() {
        return _aspxGetElementById(this.name + ASPxClientHtmlEditorIDSuffix.ContentHtmlHiddenField);
    },
    
    /*region* * * * * * * * * * * * * * *  Hierarchy elements processing  * * * * * * * * * * * * * * * */
    
    UpdateLayout: function(activeView, isInitializing) {
        if (!_aspxIsExists(isInitializing))
            isInitializing = false;
        this.layoutCalculator.UpdateLayout(this, activeView, isInitializing);
    },
    UpdateToolbarAndMenu: function(){
        if (this.allowDesignView && this.IsDesignView()) {
            this.UpdateToolbarImmediately();
            this.UpdateContextMenu();
        }
    },
                
    AdjustControlCore: function() {
        if (this.IsDesignView()) {
            this.SetDesignModeAttribute();
            this.SetFocus();
        }
        
        this.UpdateToolbarAndMenu();
        this.UpdateLayout(this.activeView, true);
        
        if (this.IsDesignView())
            this.RemoveFocus();
    },
    IsStatusBarShown: function() {
        return (this.allowDesignView ? 1 : 0) + (this.allowHtmlView ? 1 : 0) + (this.allowPreview ? 1 : 0) > 1;
    },
    SetDesignModeAttribute: function() {
        var doc = _aspxIFrameDocument(this.GetDesignViewIFrameName());
        if (__aspxNS)
            doc.designMode = "on";
        else
            doc.body.contentEditable = true;
    },
    // only for FireFox2
    DisableSpellCheckerAttribute: function(element) {
        if (__aspxFirefox)
            element.spellcheck = false;
    },
    
    ShowLoadingPanel: function() {
        var container = this.GetMainElement();
        // _aspxGetIEDocumentClientOffset
        this.CreateLoadingDiv(document.body, container);
        this.CreateLoadingPanelWithAbsolutePosition(document.body, container);
    },
    
    /*region* * * * * * * * * * * * * * *  Focus  * * * * * * * * * * * * * * * */
    Focus: function(){
        this.SetFocus();
    },
    ClearFocusInput: function() {
        var focusInputElement = this.GetFakeFocusInputElement();
        if (_aspxIsExists(focusInputElement))
            focusInputElement.value = "";
    },
    SetActive: function() { //IE ONLY
        if (__aspxIE) {
            var iFrame = this.GetDesignViewIFrameElement();
            if (_aspxIsExists(iFrame))
                iFrame.setActive();
        }
    },
    SetFocus: function() {
        if (this.IsDesignView())
            this.GetDesignViewIFrameWindow().focus();
        else if (this.IsHtmlView())
            this.GetHtmlViewEdit().SetFocus();
    },
    RemoveFocus: function() {
        var inputElement = this.GetFakeFocusInputElement();
        _aspxRemoveAttribute(inputElement, "disabled");
        inputElement.focus();
        if (__aspxIE) {
            try {
                window.focus();
            }
            catch (e) {}
        }
        inputElement.disabled = "disabled";
    },
    
    /*region* * * * * * * * * * * * * * *  Content HTML works  * * * * * * * * * * * * * * * */
    GetHtml: function() {
        return this.ProcessHtmlToGetHtml(this.GetHtmlInternal());
    },
    GetHtmlInternal: function() {
        var html = this.html;
        if (this.IsDesignView() && this.IsExistsWindowInIFrame(this.GetDesignViewIFrameName()))
            html = this.GetDesignViewIFrameDocumentBody().innerHTML;
        else if (this.IsHtmlView() && _aspxIsExistsElement(this.GetHtmlViewEdit().GetMainElement()))
            html = this.GetHtmlViewEdit().GetText();
        else if (this.IsExistsWindowInIFrame(this.GetPreviewIFrameName()))
            html = this.GetPreviewIFrameDocumentBody().innerHTML;
        return html;
    },
    SetHtml: function(html) {
        this.ClearUndoHistory();
        this.SetHtmlInternal(html);
        this.OnHtmlChanged(false);
    },
    SetHtmlInternal: function(html, newActiveView) {
        if (!_aspxIsExists(newActiveView))
            newActiveView = this.activeView;
        
        if (!this.allowScripts && this.IsDesignView()) // TODO: do all html processing in server
            html = ASPxClientHtmlEditor.CleanHtmlScripts(html);
            
        html = this.ProcessHtmlToSetHtml(html, newActiveView);
                    
        if (this.allowDesignView && this.IsDesignView(newActiveView))
            this.SetInnerHtmlToBody(this.GetDesignViewIFrameDocumentBody(), html);
        if (this.allowHtmlView && this.IsHtmlView(newActiveView))
            this.GetHtmlViewEdit().SetText(html);
        if (this.allowPreview && this.IsPreview(newActiveView))
            _aspxSetInnerHtml(this.GetPreviewIFrameDocumentBody(), html);            
    },
        
    ProcessHtmlToSetHtml: function(html, newActiveView) {
        if (this.IsPreview(newActiveView) || this.IsDesignView(newActiveView)) {
            html = this.PreserveTags(html);
            if (this.IsDesignView(newActiveView)) {
                if (__aspxIE)
                    html = ASPxClientHtmlProcessingUtils.ReplaceUnderlineSpanWithUTag(html);
                html = ASPxClientHtmlProcessingUtils.ReplaceSpanWithFontTag(html);
            }
            html = this.DeconvertEmptyHtml(html);
        } else if (this.IsHtmlView(newActiveView))
            html = this.ConvertToEmptyHtml(html);
        return html;
    },
    ProcessHtmlToGetHtml: function(html) {
        if (this.IsPreview() || this.IsDesignView()) {
            html = this.ConvertToEmptyHtml(this.DepreserveTags(html));
            if (this.IsDesignView())
                html = ASPxClientHtmlProcessingUtils.ReplaceFontWithSpanTag(html);
        }
        return html;
    },
        
    PreserveTags: function(html) {
        return _aspxApplyReplacement(html, [
            [ "<noscript", "<" + "!--" + __aspxHEPreservedTagNamePrefix + "noscript" ],
            [ "<" + "/noscript>", "</" + __aspxHEPreservedTagNamePrefix + "noscript--" + ">" ]
        ]);
    },
    DepreserveTags: function(html) {
        return _aspxApplyReplacement(html, [
            [ "<" + "!--" + __aspxHEPreservedTagNamePrefix + "noscript", "<noscript" ],
            [ "</" + __aspxHEPreservedTagNamePrefix + "noscript--" + ">", "<" + "/noscript" + ">" ]
        ]);
    },
    RestoreHtmlViewEditText: function() {
        var htmlSyncInput = this.GetHtmlSyncHiddenField();
        if (this.allowHtmlView && _aspxIsExistsElement(htmlSyncInput)) {
            var html = _aspxDecodeHtml(htmlSyncInput.value);
            this.GetHtmlViewEdit().SetText(html);
        }
    },
    SetInnerHtmlToBody: function(bodyElement, html) {
        _aspxSetInnerHtml(bodyElement, html);
        if (__aspxIE) // hack. InIE if set markup with font element, then attribute size=+0 appears
            ASPxClientHtmlProcessingUtils.CleanWrongSizeAttribute(bodyElement);
    },    
    Synchronize: function() {
        this.SynchronizeContentHtml();
        this.ClearHtmlViewMarkup();
        this.ClearFocusInput();
    },
    SynchronizeContentHtml: function() {
        var htmlSyncInput = this.GetHtmlSyncHiddenField();
        if (_aspxIsExistsElement(htmlSyncInput)) {
            var html = this.GetHtml();
            htmlSyncInput.value = _aspxEncodeHtml(html);
        }
    },
    ClearHtmlViewMarkup: function() {
        var htmlViewArea = this.GetHtmlViewEdit();
        if(htmlViewArea != null)
            htmlViewArea.SetText("");
    },

    /*region* * * * * * * * * * * * * * * * * * View * * * * * * * * * * * * * * * * * */
    
    IsDesignView: function(view) {
        if (!_aspxIsExists(view))
            view = this.activeView;
        return view == ASPxClientHtmlEditorView.Design;
    },
    IsHtmlView: function(view) {
        if (!_aspxIsExists(view))
            view = this.activeView;
        return view == ASPxClientHtmlEditorView.Html;
    },
    IsPreview: function(view) {
        if (!_aspxIsExists(view))
            view = this.activeView;
        return view == ASPxClientHtmlEditorView.Preview;
    },
    ChangeActiveView: function(activeView) {
        if (activeView == this.activeView)
            return;
        switch(activeView) {
            case ASPxClientHtmlEditorView.Design:
                this.SetDesignActiveView();
                break;
            case ASPxClientHtmlEditorView.Html:
                this.SetHtmlActiveView();
                break;
            case ASPxClientHtmlEditorView.Preview:
                this.SetPreviewActiveView();
                break;
        }
        this.activeView = activeView;
    },
    SetDesignActiveView: function() {
        this.SetClientStateFieldValue("ActiveView", "Design");
        if(this.IsHtmlView())
            this.ClearUndoHistory();
        this.SendCallback(__aspxHESwitchToDesignViewCallbackPrefix, "", true);
    },
    SetHtmlActiveView: function() {
        this.SetClientStateFieldValue("ActiveView", "Html");
        if(this.IsDesignView())
            this.ClearUndoHistory();
        this.SendCallback(__aspxHESwitchToHtmlViewCallbackPrefix, "", true);
    },
    SetPreviewActiveView: function() {
        this.SetClientStateFieldValue("ActiveView", "Preview");
        this.ClearUndoHistory();
        this.SendCallback(__aspxHESwitchToPreviewCallbackPrefix, "", true);
    },
    // Called directly with this.GetHtml() or through a callback with purged HTML
    SwitchToDesignViewCore: function(html) {
        this.SwitchToViewCore(ASPxClientHtmlEditorView.Design, html);
        this.SetDesignModeAttribute();
        this.OnHtmlChanged(false);
    },
    SwitchToHtmlViewCore: function(html) {
        this.SwitchToViewCore(ASPxClientHtmlEditorView.Html, html);
    },
    SwitchToPreviewCore: function(html) {
        this.SwitchToViewCore(ASPxClientHtmlEditorView.Preview, html);
    },
    SwitchToViewCore: function(view, html) {
        this.UpdateLayout(view);
        this.SetHtmlInternal(html, view);
        if(this.IsPreview(view))
            this.RemoveFocus();
        else
            this.SetFocus();
    },
    
    /*region* * * * * * * * * * * * * * * * * *  Commands  * * * * * * * * * * * * * * * * * */    
    ExecuteCommand: function(commandName, parameter, addToHistory) {
        var isSuccessfully = false;
        if (this.IsDesignView()) {
            addToHistory = _aspxIsExists(addToHistory) ? addToHistory : true;
            var cmd = ASPxHtmlEditorCommandList[commandName];
            if (cmd != null) {
                this.executingCommandName = commandName;
                
                var contentHtml = this.GetHtmlInternal();
                var isSuccessfully = this.commandManager.ExecuteCommand(commandName, parameter, addToHistory);
                
                if (!cmd.IsImmediateExecution())
                    this.executingCommandName = null;
                            
                if (contentHtml != this.GetHtmlInternal())
                    this.OnHtmlChanged(false);
                else
                    if (isSuccessfully)
                        this.OnSelectionChanged();
                this.RaiseCommandExecuted(commandName, parameter, isSuccessfully);
            }
            else{
               if(!this.CustomCommand.IsEmpty()) {
                    isSuccessfully = this.RaiseCustomCommand(commandName, parameter);
                    this.RaiseCommandExecuted(commandName, parameter, isSuccessfully);
               }
               else
                    alert('Command not found');
            }
        }
        return isSuccessfully;
    },
    PasteHtml: function(htmlText) {
        return this.ExecuteCommand(ASPxClientCommandConsts.PASTEHTML_COMMAND, htmlText);
    },
    InsertLink: function(url, text, target, title) {
        var value = {
            url: url,
            text: text,
            target: target,
            title: title
        };
        this.ExecuteCommand(ASPxClientCommandConsts.INSERTLINK_COMMAND, value);
    },
    InsertImage: function(src, width, height, align, alt) {
        var value = {
            src: src,
            width: width,
            height: height,
            align: align,
            alt: alt
        };
        this.ExecuteCommand(ASPxClientCommandConsts.INSERTIMAGE_COMMAND, value);
    },
    ChangeImage: function(imageElement, src, width, height, align, alt) {        
        var value = {
            imageElement: imageElement,
            src: src,
            width: width,
            height: height,
            align: align,
            alt: alt
        };
        this.ExecuteCommand(ASPxClientCommandConsts.CHANGEIMAGE_COMMAND, value);
    },
    CheckSpelling: function() {
        var spellChecker = this.GetSpellCheckerClientInstance();
        if(_aspxIsExists(spellChecker)) {
            if (this.areDictionariesAssigned)
                this.SendCallback(__aspxHESpellCheckingCallbackPrefix, spellChecker.CreateCallbackArgumentToCheckText(""), true);
            else
                alert("The built-in spell checker doesn't have dictionaries by default. Use the SettingsSpellChecker.Dictionaries property to define the spell checker's dictionaries.");
        }
    },    
    OnSpellCheckerWordChanged: function(sender, args){
        // similar to SetHtml
        this.ExecuteCommand(ASPxClientCommandConsts.CheckSpellingCore_COMMAND, args.checkedText);
    },
    
    Print: function() {
        var window = this.GetDesignViewIFrameWindow();
        if(_aspxIsExists(window)) window.print();
    },
    Undo: function() {
        if (this.IsUndoAvailable())
            return this.commandManager.Undo(1);
        return false;            
    },
    Redo: function() {
        if (this.IsRedoAvailable())
            return this.commandManager.Redo(1);
        return false;
    },
    
    /*region* * * * * * * * * * * * * * * * * *  Selection  * * * * * * * * * * * * * * * * * */
    
    GetEditorSelectionInfo: function(needNestingFontElementCorrection) {
        needNestingFontElementCorrection  = _aspxIsExists(needNestingFontElementCorrection) ? 
                                                needNestingFontElementCorrection && __aspxNS : false;
        var ret = { allElements: [], newElements: [] };
        // todo for Control
        var fontTagName = __aspxSafariFamily ? "span" : "font";
        var curSelection = this.CreateRestoreSelection();
        
        if (curSelection.IsControl()) {
            var controlElement = curSelection.GetParentElement();
            var fontElement = this.GetDesignViewIFrameDocument().createElement('FONT');
            fontElement.appendChild(controlElement.cloneNode(true));
            controlElement.parentNode.replaceChild(fontElement, controlElement);
            
            ret.allElements.push(fontElement);
            ret.newElements.push(fontElement);
        }
        else {
            // 1. mark existing fontElements
            var savedExistingFontElems = _aspxGetElementsByTagName(this.GetDesignViewIFrameDocument(), fontTagName);
            for (var i = 0; i < savedExistingFontElems.length; i++) {

                // save the original attr
                if (savedExistingFontElems[i].face) {
                    savedExistingFontElems[i].setAttribute('_dxface', savedExistingFontElems[i].face);
                }
                savedExistingFontElems[i].setAttribute(__aspxHEMarkFontAttr, 1);
            }
            // 2. mark all selection
            if (__aspxNS)
        		this.GetDesignViewIFrameDocument().execCommand("useCSS", false, true);
                      
            ASPxHtmlEditorCommandList[ASPxClientCommandConsts.FONTNAME_COMMAND].Execute(__aspxHEMarkFontFace, this, false);
            var allFontElems = _aspxGetElementsByTagName(this.GetDesignViewIFrameDocument(), fontTagName);
            for (var i = 0; i < allFontElems.length; i++) {
            
                if (this.IsMarkedFontElement(allFontElems[i])) {
                    ret.allElements.push(allFontElems[i]);
                    
                    var childElems = _aspxGetElementsByTagName(allFontElems[i], fontTagName);
                    var count = childElems.length;
                    for (var j = 0; j < count; j ++) {
                        if (this.IsMarkedFontElement(childElems[i]))
                            ret.allElements.push(childElems[i]);
                    }
                    
                    // new font element
                    if (!allFontElems[i].getAttribute(__aspxHEMarkFontAttr)) {
                        var elem = allFontElems[i];
                        
        		        if (needNestingFontElementCorrection) {
        		            elem = this.CorrectNestedFontElement(elem);
        		            // update existing item
                            ret.allElements[i] = elem;
                            allFontElems[i] = elem;
        		        }
        		        
                        ret.newElements.push(elem);
                    }
                }
                allFontElems[i].removeAttribute(__aspxHEMarkFontAttr);
            }
            // 3. Restore font element attributes
            for (var i = 0; i < savedExistingFontElems.length; i++) {
                savedExistingFontElems[i].face = savedExistingFontElems[i].getAttribute('_dxface');
                savedExistingFontElems[i].removeAttribute('_dxface');        
            }
            savedExistingFontElems = [];
            
            if (__aspxNS)
        		this.GetDesignViewIFrameDocument().execCommand("useCSS", false, false);            
        }
        return ret;
    },
    CorrectNestedFontElement: function(fontElement) {
        var ret = fontElement;
        if ((fontElement.childNodes.length == 1) && (fontElement.childNodes[0].nodeType != 3)) {
            // mark child by className
            var childNode = fontElement.childNodes[0];
            childNode.className += " " + __aspxHEMarkClassName;            
            var html = childNode.innerHTML;
            
            _aspxRemoveOuterTags(fontElement);
            
            // save child
            childNode = _aspxGetChildsByClassName(this.GetDesignViewIFrameDocumentBody(), __aspxHEMarkClassName)[0];
            childNode.className.replace(__aspxHEMarkClassName, "");
            childNode.innerHTML = "";
            
            var newFontElement = this.GetDesignViewIFrameDocument().createElement('FONT');
            newFontElement.face = __aspxHEMarkFontFace;
            _aspxSetInnerHtml(newFontElement, html);
            
            childNode.appendChild(newFontElement);
            ret = newFontElement;
        }
        return ret;
    },
    IsMarkedFontElement: function(elem, font) {
        return elem.getAttribute('face') == __aspxHEMarkFontFace || elem.style.fontFamily == __aspxHEMarkFontFace;
    },
    CreateRestoreSelection: function() {
        return (__aspxNS || __aspxOpera || __aspxSafariFamily) ?
            new SelectionNSOpera(this.GetDesignViewIFrameWindow()) : new SelectionIE(this.GetDesignViewIFrameWindow());
    },
    RestoreLastSelection: function(selectionObj) {
        if (selectionObj != null) {
              this.SetFocus();
            selectionObj.Restore();
        }
    },
    SaveLastSelection: function() {
        var selectionObj = this.CreateRestoreSelection();
        selectionObj.Save();
        return selectionObj;
    },
   
    CreateRestoreSelectionForDialog: function() {
        return __aspxIE ? new DialogSelectionIE(this.GetDesignViewIFrameWindow()) :
                            new SelectionNSOpera(this.GetDesignViewIFrameWindow());
    },    
    SaveRestoreSelectionForDialog: function() {
        var selectionObj = this.CreateRestoreSelectionForDialog();
        selectionObj.Save();
        return selectionObj;
    },
   
    /*region* * * * * * * * * * * * * * * * * *  Context menu  * * * * * * * * * * * * * * * * * */
    
    UpdateContextMenu: function() {
        if (this.allowContextMenu == true && this.GetContextMenu().GetVisible())
            this.GetContextMenu().SetVisible(false);
    },
    SaveBeforeShowContextMenuSelection: function() {
        this.beforeShowContextMenuSelection = this.CreateRestoreSelection();
        this.beforeShowContextMenuSelection.Save();
    },
    ShowContextMenu: function(evt) {
        this.SaveBeforeShowContextMenuSelection();
        if (!this.GetContextMenu().GetVisible()) {
            this.UpdateContextMenuItemsState();
            var scrollX = __aspxSafariFamily ? 0 : _aspxGetDocumentScrollLeft();
            var scrollY = __aspxSafariFamily ? 0 : _aspxGetDocumentScrollTop();
            var x = _aspxGetEventX(evt) + _aspxGetAbsoluteX(this.GetDesignViewIFrameElement()) - scrollX;
            var y = _aspxGetEventY(evt) + _aspxGetAbsoluteY(this.GetDesignViewIFrameElement()) - scrollY;
            this.GetContextMenu().ShowAtPos(x + 8, y + 8);
        }
    },
    UpdateContextMenuItemsState: function() {
        var popupMenu = this.GetContextMenu();
        for (var i = 0 ; i < popupMenu.GetItemCount(); i++) {
            var item = popupMenu.GetItem(i);
            if (_aspxIsExists(ASPxHtmlEditorCommandList[item.name]))
                item.SetVisible(!ASPxHtmlEditorCommandList[item.name].IsLocked(this));            
        }
    },
        
    /*region* * * * * * * * * * * * * * * * * *  Toolbars  * * * * * * * * * * * * * * * * * */
        
    UpdateToolbar: function() {    
        if (_aspxIsExists(this.toolbar))
            this.toolbar.UpdateItems();
    },
    UpdateToolbarImmediately: function() {
        if (_aspxIsExists(this.toolbar))
            this.toolbar.DoUpdateItems();
    },    
        
    /*region* * * * * * * * * * * * * * * * * *  Callback  * * * * * * * * * * * * * * * * * */
    
    SendCallback: function(prefix, arg, showLoadingPanel) {
        if(showLoadingPanel)
            this.ShowLoadingPanel();
        this.CreateCallback(_aspxFormatCallbackArg(prefix, arg));
    },
    OnCallback: function(result) {
        if (this.callbackOwner != null)
            this.callbackOwner.OnCallback(result);
        else
            this.OnHtmlEditorCallback(result.action, result.html, result.spellcheck);
    },
    OnCallbackError: function(result, data){
        if (this.callbackOwner != null)
            this.callbackOwner.OnCallbackError(result);
        else
            this.RollbackActiveView();

        this.isErrorOnCallback = true;
        this.constructor.prototype.OnCallbackError.call(this, result, data);
    },
    DoEndCallback: function(){
        this.constructor.prototype.DoEndCallback.call(this);
        if (this.callbackOwner != null && !this.isErrorOnCallback)
            this.callbackOwner.OnEndCallback();
        else if(this.spellCheckerCallback){
            var spellChecker = this.GetSpellCheckerClientInstance();
            if(_aspxIsExists(spellChecker)) 
                spellChecker.DoEndCallback();
        }
        this.isErrorOnCallback = false;
    },
    DoFinalizeCallback: function() {
        this.RestoreHtmlViewEditText();
    },
    OnHtmlEditorCallback: function(prefix, result, spellCheckResult) {
        this.spellCheckerCallback = false;
        switch(prefix) {
            case __aspxHESwitchToDesignViewCallbackPrefix:
                this.SwitchToDesignViewCore(result);
                break;
            case __aspxHESwitchToHtmlViewCallbackPrefix:
                this.SwitchToHtmlViewCore(result);
                break;
            case __aspxHESwitchToPreviewCallbackPrefix:
                this.SwitchToPreviewCore(result);
                break;
            case __aspxHESpellCheckingCallbackPrefix:
                // todo
                var spellChecker = this.GetSpellCheckerClientInstance();
                if(_aspxIsExists(spellChecker)) {
                    this.ClearUndoHistory();
                    this.SetHtmlInternal(result);
                    spellChecker.CheckByCallbackResult(result, spellCheckResult, this.GetMainElement());
                    this.RemoveFocus();
                }
                this.spellCheckerCallback = true;
                break;
            default:
                throw "Unexpected callback prefix.";
                break;
        }
        
    },
    RollbackActiveView: function(){
        if (this.allowDesignView){
            this.activeView = ASPxClientHtmlEditorView.Design;
            this.SwitchToDesignViewCore(this.GetHtml());
        }
        else if (this.allowHtmlView){
            this.activeView = ASPxClientHtmlEditorView.Html;
            this.SwitchToHtmlViewCore(this.GetHtml());
        }
        else if (this.allowPreview){
            this.activeView = ASPxClientHtmlEditorView.Preview;
            this.SwitchToPreviewCore(this.GetHtml());
        }
        var tabControl = this.GetTabControl();
        if(tabControl != null)
            tabControl.SetActiveTab(tabControl.GetTab(0));
    },

    /*region* * * * * * * * * * * * * * * * * *  Utils  * * * * * * * * * * * * * * * * * */    
    ConvertToEmptyHtml: function(html) {
        if (_aspxTrim(html) == "&nbsp;" && (__aspxSafariFamily || __aspxNS))
            html = "";
        return html;
    },
    DeconvertEmptyHtml: function(html) {
        if (html == "" && (__aspxSafariFamily || __aspxNS))
            html = "&nbsp;";
        return html;
    },
    
    CreatePasteContainer: function() {
        var divElem = document.createElement("DIV");
        divElem.style.overflow = "hidden";
        divElem.style.width = "1px";
        divElem.style.height = "1px";
        divElem.contentEditable = true;
            
        document.body.appendChild(divElem);
        return divElem;
    },
    IsExistsWindowInIFrame: function(iframeName) {
        try {
            return _aspxIsExists(_aspxIFrameWindow(iframeName));
        }
        catch(e) {
            return false;
        }
    },
    GetClipboardHtml: function() {
        var pasteContainer = this.GetPasteContainer();        
		pasteContainer.innerHTML = "";
		pasteContainer.setActive();
		
		var newRange = document.selection.createRange();
		newRange.execCommand('paste', null);

        var innerHTML = pasteContainer.innerHTML;
        pasteContainer.innerHTML = "";
        return innerHTML;
    },
    GetIFrameDocumentHtml: function(bodyContentHtml) {
        var html = "<head>";
        if(__aspxIE)
            html += "  <scr" + "ipt type=\"text/javascript\">" +
                "    window.document.onkeydown = function() {" + 
                "       if(event.keyCode == 121) {" + 
                "           window.parent.FocusActiveEditorToolbar();" + 
                "           event.keyCode = 0;" + 
                "           return false;" + 
                "       }" +
                "   }" + 
                "  </scr" + "ipt>";  
        html += "<style></style></head><body>";
        if (_aspxIsExists(bodyContentHtml) && bodyContentHtml != "")
            html += bodyContentHtml;
        else if (__aspxSafariFamily || __aspxNS)
            html += "&nbsp;";
        html += "</body>";
        return html;
    },
    ClearUndoHistory: function() {
        if (this.allowDesignView)
        this.commandManager.ClearUndoHistory();
    },
    CreateKeyDownInfo: function(evt) { // call only on keydown
        this.keyDownInfo = {
            isSystemKey: this.keyboardManager.IsSystemKey(evt.keyCode),
            isDeleteOrBackSpaceKey: this.keyboardManager.IsDeleteOrBackSpaceKey(evt.keyCode),
            isCursorMovingKey: this.keyboardManager.IsCursorMovingKey(evt.keyCode)
        };
    },
    ClearKeyDownInfo: function() { // call only on keyup
        this.keyDownInfo = null;
    },
    IsDefaultActionCommand: function(cmdID) {
        return _aspxIsExists(ASPxHtmlEditorCommandList[cmdID]) && 
                ASPxHtmlEditorCommandList[cmdID].IsDefaultAction();
    },
    IsDeleting: function() {
        return this.commandManager.IsDeleting() && 
            _aspxIsExists(this.keyDownInfo) && this.keyDownInfo.isDeleteOrBackSpaceKey;
    },
    IsHtmlChangeableCommand: function(cmdID) {
        return _aspxIsExists(ASPxHtmlEditorCommandList[cmdID]) && 
            ASPxHtmlEditorCommandList[cmdID].IsHtmlChangeable();        
    },
    IsShortcut: function() {
        return _aspxIsExists(this.shortcutCmdID);
    },
    IsTextTyping: function() {        
        return this.executingCommandName && 
                (this.executingCommandName == ASPxClientCommandConsts.TEXTTYPE_COMMAND) && 
                _aspxIsExists(this.keyDownInfo) && !this.keyDownInfo.isSystemKey;
    },
    IsRedoAvailable: function() {
        return this.commandManager.IsRedoAvailable();
    },
    IsUndoAvailable: function() {
        return this.commandManager.IsUndoAvailable();
    },

    /*region* * * * * * * * * * * * * * * * * *  Hight Level Event Handlers  * * * * * * * * * * * * * * * * * */
    
    OnSelectionChanged: function() {
        this.UpdateToolbar();
        this.UpdateContextMenu();
        this.RaiseSelectionChanged();
    },
    OnHtmlChanged: function(saveSelectionAndHtml) {
        if (saveSelectionAndHtml && this.IsDesignView())
            this.commandManager.UpdateLastRestoreSelectionAndHTML();
        this.RaiseHtmlChanged();
        if (this.IsDesignView())
        this.OnSelectionChanged();
    },
            
    /*region* * * * * * * * * * * * * * * * * *  Low Level Event Handlers  * * * * * * * * * * * * * * * * * */
    FocusToolbar: function() {
        this.GetToolbar().Focus();
    },
    OnKeyDown: function(evt) {
        this.shortcutCmdID = this.keyboardManager.GetShortcutCommand(evt);
        this.isAllowToPreventShortcut = false;
        if(!__aspxIE && evt.keyCode == ASPxKey.F10){
            aspxGetHtmlEditorsCollection().FocusActiveEditorToolbar();
            return _aspxPreventEvent(evt);
        }
        else if (this.IsShortcut()) {
            this.ExecuteCommand(this.shortcutCmdID, null);
            this.isAllowToPreventShortcut = !this.IsDefaultActionCommand(this.shortcutCmdID);
        }
        else {
            this.CreateKeyDownInfo(evt);
            if (this.keyDownInfo.isSystemKey) {
                if (this.keyDownInfo.isDeleteOrBackSpaceKey) {
                    var curSelection = this.CreateRestoreSelection();
                    if (curSelection.GetHtmlText() != "" && (__aspxIE || __aspxOpera)) {
                        this.ExecuteCommand(ASPxClientCommandConsts.DELETE_COMMAND, null);                    
                        _aspxPreventEvent(evt);
                    }
                    else {
                        this.ExecuteCommand(ASPxClientCommandConsts.KBDELETE_COMMAND, null);
                        this.commandManager.UpdateLastRestoreHtml();                    
                    }
                }
            }
            else
                if (!this.keyDownInfo.isCursorMovingKey) {
                    if (!this.IsTextTyping())
                        this.ExecuteCommand(ASPxClientCommandConsts.TEXTTYPE_COMMAND, null);
                }
        }
        // Cancel event in IE
        if (this.IsShortcut() && __aspxIE && this.isAllowToPreventShortcut) {
            evt.keyCode = 123;
            evt.returnValue = false;
        }
    },
    OnKeyUp: function(evt) {
        if (!this.IsShortcut()) {
            if (this.IsDeleting())
                this.commandManager.CleanEmptyRestoreHtml();
            if (this.IsTextTyping() || this.IsDeleting())
                this.OnHtmlChanged(true);
            this.OnSelectionChanged();
            this.ClearKeyDownInfo();
        } else 
            if (this.IsDefaultActionCommand(this.shortcutCmdID) && 
                this.IsHtmlChangeableCommand(this.shortcutCmdID)) {                
                // Don't change order !!!
                this.OnHtmlChanged(true);
            }
            else
                this.OnSelectionChanged();
    },
    OnKeyPress: function(evt) {
        if (this.IsShortcut(evt) && this.isAllowToPreventShortcut ) {
            this.shortcutCmdID = null;
            return _aspxPreventEvent(evt);
        }
    },
    OnDblClick: function(evt) {
        var source = _aspxGetEventSource(evt);
        if (!_aspxIsExists(source))
            return;
        
        if (source.tagName == "IMG")
            this.ExecuteCommand(ASPxClientCommandConsts.CHANGEIMAGE_DIALOG_COMMAND);
    },
    OnMouseDown: function(evt) {
        var toolBar = this.GetToolbar();
        if (_aspxIsExists(toolBar))
            toolBar.HideAllPopups();
        this.isMouseDown = true;
    },
    OnMouseUp: function(evt) {
        this.OnSelectionChanged();
        this.isMouseDown = false;
    },
    OnFocus: function() {
        if (!this.isInitialized || (!__aspxIE && this.isMouseDown && this.isInFocus))
            return;
        this.isInFocus = true;
        this.RaiseFocus();
    },
    OnLostFocus: function() {
        if (!this.isInitialized || (__aspxSafariFamily && !this.isInFocus))
            return;
        this.isInFocus = false;
        this.RaiseLostFocus();
    },
    
    /*region* * * * * * * * * * * * * * * * * *  Dragging  * * * * * * * * * * * * * * * * * */
    
    ClearDragDropTimer: function() {
        if (this.dragDropTimerID)
            _aspxClearTimer(this.dragDropTimerID);
    },
    OnAfterDocumentObjectDragEnd: function() {
        this.isDropExternalContentExecuted = false;
        //this.OnPaste();
        this.OnHtmlChanged(true);
        this.ClearDragDropTimer();
    },
    OnAfterDocumentObjectDragEndCallWithDelay: function() {
        var callStr = "aspxHEAfterDocumentObjectDragEnd(\"" + this.name + "\")";
        this.dragDropTimerID = _aspxSetTimeout(callStr, __aspxHEDragDropDelay);
    },
    OnAfterObjectDragEnd: function() {
        this.OnHtmlChanged(true);
        this.ClearDragDropTimer();
    },
    OnAfterObjectDragEndWithDelay: function() {
        var callStr = "aspxHEAfterObjectDragEnd(\"" + this.name + "\")";
        this.dragDropTimerID = _aspxSetTimeout(callStr, __aspxHEDragDropDelay);
    },    
    OnDocumentObjectDragEnd: function() {
        if (this.isDropExternalContentExecuted)
            this.OnAfterDocumentObjectDragEnd();
    },
    OnObjectDragStart: function(evt) {
        if (!this.isDraggingInsideEditor && !__aspxHEIsDocumentDragOver /*firefox only*/) {
            this.isDraggingInsideEditor = true;
            this.ExecuteCommand(ASPxClientCommandConsts.DRAGDROPOBJECT_COMMAND, null);
        }
    },
    OnObjectDrop: function(evt) {
        if (!this.isDraggingInsideEditor) {
            this.isDropExternalContentExecuted = true;
            this.ExecuteCommand(ASPxClientCommandConsts.DROPOBJECTFROMEXTERNAL_COMMAND, null);
            if (__aspxHEIsDocumentDragOver) { // for FireFox Only
                __aspxHEIsDocumentDragOver = false;
                this.OnAfterDocumentObjectDragEndCallWithDelay();
            }
        }
        if (__aspxNS)
            this.OnObjectDragEnd();
    },
    OnObjectDragEnd: function() {
        if (this.isDraggingInsideEditor) {
            this.isDraggingInsideEditor = false;
            this.OnAfterObjectDragEndWithDelay();
        }
    },
    
    /*region* * * * * * * * * * * * * * * * * *  Resizing  * * * * * * * * * * * * * * * * * */
    
    OnObjectResizeStart: function(evt) {
        this.ExecuteCommand(ASPxClientCommandConsts.RESIZEOBJECT_COMMAND, null);
    },
    OnObjectResizeEnd: function(evt) {
        this.OnHtmlChanged(true);
    },
    OnContextMenuItemClick: function(item) {
        if (_aspxIsExists(this.beforeShowContextMenuSelection)) {
            this.beforeShowContextMenuSelection.Restore();
            this.beforeShowContextMenuSelection = null;
        }
        this.ExecuteCommand(item.name, null);
    },
    OnContextMenu: function(evt) {
        if (this.allowContextMenu == true)
            this.ShowContextMenu(evt);
        if (__aspxIE)
            evt.returnValue = false;
        else
            evt.preventDefault();
    },
        
    /*region* * * * * * * * * * * * * * * * * *  API  * * * * * * * * * * * * * * * * * */
    
    RaiseCommandExecuted: function(commandName, parameter, isSuccessful) {
        if(!this.CommandExecuted.IsEmpty()){
            var args = new ASPxClientHtmlEditorCommandEventArgs(commandName, parameter, isSuccessful, this);
            this.CommandExecuted.FireEvent(this, args);
        }
    },
    RaiseCustomCommand: function(commandName, parameter){
        if(!this.CustomCommand.IsEmpty()){
            var args = new ASPxClientHtmlEditorCommandEventArgs(commandName, parameter, true, this);
            this.CustomCommand.FireEvent(this, args);
            return args.isSuccessful;
        }
        return true;
    },
    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);
        }
    },    
    RaiseHtmlChanged: function() {
        if(!this.HtmlChanged.IsEmpty()){
            var args = new ASPxClientEventArgs();    
            this.HtmlChanged.FireEvent(this, args);
        }
    },
    RaiseSelectionChanged: function(){
        if(!this.SelectionChanged.IsEmpty()){
            var args = new ASPxClientEventArgs();    
            this.SelectionChanged.FireEvent(this, args);
        }
    }
});

ASPxClientHtmlEditor.CleanHtmlScripts = function(html) {
    var ret = ASPxClientHtmlEditor.RemoveScriptTags(html);
    return ASPxClientHtmlEditor.RemoveScriptEventHadlers(ret);
};
ASPxClientHtmlEditor.RemoveScriptTags = function(html) {
    var ret = html;
	var rx = new RegExp(__aspxJSTagsRegExpPattern, "gi");
	if (ret != "")
	    ret = ret.replace(rx, "");
	return ret;
};
ASPxClientHtmlEditor.RemoveScriptEventHadlers = function(html) {
   var ret = html;
	var rx = new RegExp(__aspxJSEventHadlersRegExpPattern, "g");
	if (ret != "")
	    ret = ret.replace(rx, "");
	return ret;
};

EventListener = _aspxCreateClass(null, {
    constructor: function(htmlEditor) {
        this.htmlEditor = htmlEditor;
        this.AttachEventsToEditor();
    },
    AttachEventsToEditor: function() {
        var doc = this.htmlEditor.GetDesignViewIFrameDocument();

        _aspxAttachEventToElement(doc, "keydown", this.CreateEventHandlerFunction("aspxHEEditAreaKeyDown", true));
        _aspxAttachEventToElement(doc, "keyup", this.CreateEventHandlerFunction("aspxHEEditAreaKeyUp", true));
        if (!__aspxIE) // prevent built-in command shortcuts in Moz, Safari, Opera
            _aspxAttachEventToElement(doc, "keypress", this.CreateEventHandlerFunction("aspxHEEditAreaKeyPress", true));
        _aspxAttachEventToElement(doc, "mousedown", this.CreateEventHandlerFunction("aspxHEEditAreaMouseDown", true));
        _aspxAttachEventToElement(doc, "mouseup", this.CreateEventHandlerFunction("aspxHEEditAreaMouseUp", true));
        
        
        var designViewIFrame = this.htmlEditor.GetDesignViewIFrameWindow();
        _aspxAttachEventToElement(designViewIFrame, "focus", this.CreateEventHandlerFunction("aspxHEEditAreaOnFocus", false));
        _aspxAttachEventToElement(designViewIFrame, "blur", this.CreateEventHandlerFunction("aspxHEEditAreaOnLostFocus", false));
        
        if (!__aspxOpera)
            _aspxAttachEventToElement(doc, "dblclick", this.CreateEventHandlerFunction("aspxHEEditAreaDblClick", true));
        if (__aspxNS) {
            this.htmlEditor.GetDesignViewIFrameWindow().captureEvents(Event.DRAGDROP | Event.DBLCLICK);
            _aspxAttachEventToElement(this.htmlEditor.GetDesignViewIFrameWindow(), "dragdrop", 
                this.CreateEventHandlerFunction("aspxHEEditObjectDrop", true));
            _aspxAttachEventToElement(this.htmlEditor.GetDesignViewIFrameWindow(), "dragover",
                this.CreateEventHandlerFunction("aspxHEEditObjectDragStart", true));
        }
        else {
            _aspxAttachEventToElement(doc.body, "resizestart",
                this.CreateEventHandlerFunction("aspxHEEditObjectResizeStart", true));
            _aspxAttachEventToElement(doc.body, "resizeend",
                this.CreateEventHandlerFunction("aspxHEEditObjectResizeEnd", true));

            _aspxAttachEventToElement(document.body, "dragend", /* global document */
                this.CreateEventHandlerFunction("aspxHEDocumentObjectDragEnd", true));
            _aspxAttachEventToElement(doc.body, "dragstart",
                this.CreateEventHandlerFunction("aspxHEEditObjectDragStart", true));
            _aspxAttachEventToElement(doc.body, "drop",
                this.CreateEventHandlerFunction("aspxHEEditObjectDrop", true));

            _aspxAttachEventToElement(doc.body, "dragend",
                this.CreateEventHandlerFunction("aspxHEEditObjectDragEnd", true));
        }
        if (this.htmlEditor.allowContextMenu != "default")
            _aspxAttachEventToElement(doc, "contextmenu", this.CreateEventHandlerFunction("aspxHEEditAreaContextMenu", true));
    },
    
    CreateEventHandlerFunction: function(funcName, withHTMLEventArg) {
        return withHTMLEventArg ? new Function("event", funcName + "('" + this.htmlEditor.name + "', event);") :
            new Function(funcName + "('" + this.htmlEditor.name + "');");
    }
});

// TODO: Replace to classes.js
// PathUtils
ASPxClientPath = {
    DirectorySeparatorChar: "\\",
    AltDirectorySeparatorChar: "/",
    VolumeSeparatorChar: ":",
    
    GetBaseUrlPath: function() {
		var tempLocation = window.location;
		return tempLocation.protocol + "//" +  tempLocation.host + (tempLocation.port ? ":" + tempLocation.port : "");
    },
    GetFileName: function(path) {
        if (path) {
            var length = path.length;
            var index = length;
            while (--index >= 0) {
                var ch = path.charAt(index);
                if (((ch == ASPxClientPath.DirectorySeparatorChar) || (ch == ASPxClientPath.AltDirectorySeparatorChar)) || 
                        (ch == ASPxClientPath.VolumeSeparatorChar))
                    return path.substr(index + 1, (length - index) - 1);
            }
        }
        return "";
    },
    GetFileNameWithoutExtension: function(path) {
        path = ASPxClientPath.GetFileName(path);
        if (!path)
            return "";
        var length = path.lastIndexOf('.');
        if (length == -1)
            return path;
        return path.substr(0, length);
    }
};
// HtmlProcessing Utils
ASPxClientHtmlProcessingUtils = {
    // Font -> Span
    ReplaceFontWithSpanTag: function(html) {
        html = _aspxApplyReplacement(html, [
            [ /<font/ig, "<span" ],
            [ /<\/font>/ig, "</span>" ]
        ]);
        var containerElement = document.createElement("DIV");
        containerElement.innerHTML = html;
        var spans = _aspxGetElementsByTagName(containerElement, "SPAN");
        for (var i = 0; i < spans.length; i++) {
            var curSpan = spans[i];
            // face            
            var curSpanFace = _aspxGetAttribute(curSpan, "face");
			if (_aspxIsExists(curSpanFace)) { // hack
			    if (curSpanFace != "null")
				    curSpan.style.fontFamily = curSpanFace;
				_aspxRemoveAttribute(curSpan, "face");
		    }
		    				
	        // size
			var size = 0;
			if (!isNaN(size = parseInt(_aspxGetAttribute(curSpan, "size")))) {
				try {
					curSpan.style.fontSize = __aspxDefaultFontSizes[size - 1];
				}
				catch (ex) { }
				_aspxRemoveAttribute(curSpan, "size");
			}
			
			// color
			if (this.IsExistAttribute(curSpan, "color")) {
				curSpan.style.color = _aspxGetAttribute(curSpan, "color");
				_aspxRemoveAttribute(curSpan, "color");
		    }
        }
        return containerElement.innerHTML;
    },
    // Span -> Font
    ReplaceSpanWithFontTag: function(html) {
        html = _aspxApplyReplacement(html, [
            [ /<span/ig, "<font" ],
            [ /<\/span>/ig, "</font>" ]
        ]);
        var defaultFontSizeReverseArray = [];
	    for (var i = 0; i < __aspxDefaultFontSizes.length; i ++)
		    defaultFontSizeReverseArray[parseInt(__aspxDefaultFontSizes[i])] = i;
		    
        var containerElement = document.createElement("DIV");
        containerElement.innerHTML = html;
        var fonts = _aspxGetElementsByTagName(containerElement, "FONT");
        
        for (var i = 0; i < fonts.length; i++) {
            var curFont = fonts[i];
            
            // fontFamily            
			if (curFont.style.fontFamily) {
				curFont.face = curFont.style.fontFamily;				
                _aspxRemoveStyleAttribute(curFont, "fontFamily");
			}
			
            // size
			if (curFont.style.fontSize) {
			    _aspxRemoveAttribute(curFont, "size");
				var size = defaultFontSizeReverseArray[parseInt(curFont.style.fontSize)];
				if (typeof(size) != 'undefined') {				
					curFont.size = size + 1;
                    _aspxRemoveStyleAttribute(curFont, "fontSize");
			    }
			}
			
			// color
			if (curFont.style.color) {
				curFont.color = curFont.style.color;
                _aspxRemoveStyleAttribute(curFont, "color");
			}
        }
        html =  containerElement.innerHTML;
        
        // remove empty STYLE attribute
	    html =  html.replace(/\s*style="\s*"/gi, '');
	    html = html.replace(/style=""/ig, "");
	    html = html.replace(/style=''/ig, "");
        return html;
    },
    // <SPAN style="text-decoration: underline"> -> <U>
    ReplaceUnderlineSpanWithUTag: function(html) {
        var containerElement = document.createElement("DIV");
        containerElement.innerHTML = html;
        
	    var spans = this.GetUnderlineSpanElements(containerElement);
		var UElement = document.createElement("U");
                
        while (spans.length > 0) {
            var curSpan = spans[0];
            
            if (this.IsUnderlineSpan(curSpan)) {
			    var newUELement = UElement.cloneNode(false);
                var parentNode = curSpan.parentNode;
			    
			    newUELement.id = curSpan.id;
			    newUELement.style.cssText = curSpan.style.cssText;
			    newUELement.style.textDecoration = "";
			    
			    if (curSpan.className)
				    newUELement.className = curSpan.className;
				
				_aspxSetInnerHtml(newUELement, curSpan.innerHTML);
			    parentNode.replaceChild(newUELement, curSpan);
			    spans = this.GetUnderlineSpanElements(containerElement);
            }
        }
        return containerElement.innerHTML;
    },
    CleanWrongSizeAttribute: function(element) {
        var fonts = _aspxGetElementsByTagName(element, "font");
        for (var i = 0; i < fonts.length; i++)
            if (fonts[i].size == "+0")
                _aspxRemoveAttribute(fonts[i], "size");
    },
    IsExistAttribute: function(element, attrName) {
        var attrObj = _aspxGetAttribute(element, attrName);
        return _aspxIsExists(attrObj) && attrObj != "null";
    },
    IsUnderlineSpan: function(element) {
        return (element.tagName.toLowerCase() == "span") && 
                (element.style.textDecoration.toLowerCase() == "underline") &&
                (element.style.fontFamily == "") &&
                (element.style.fontSize == "");
    },
    GetUnderlineSpanElements: function(element) {
        var spans = element.getElementsByTagName("SPAN");
        var ret = new Array();
        for (var i = spans.length - 1; i >= 0; i--) {
            if (this.IsUnderlineSpan(spans[i]))
                _aspxArrayPush(ret, spans[i]);
        }
        return ret;
    }
};

function aspxHEChangeActiveView(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        htmlEdit.ChangeActiveView(evt.tab.name);
}
// Editing Area event handlers
window.FocusActiveEditorToolbar = function() {
    aspxGetHtmlEditorsCollection().FocusActiveEditorToolbar();
}
function aspxHEEditAreaKeyDown(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnKeyDown(evt);
}
function aspxHEEditAreaKeyUp(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnKeyUp(evt);
}
function aspxHEEditAreaKeyPress(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnKeyPress(evt);
}
function aspxHEEditAreaMouseDown(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        htmlEdit.OnMouseDown(evt);
}
function aspxHEEditAreaMouseUp(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        htmlEdit.OnMouseUp(evt);
}
function aspxHEEditAreaContextMenu(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        htmlEdit.OnContextMenu(evt);
}
function aspxHEEditAreaDblClick(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        htmlEdit.OnDblClick(evt);
}
function aspxHEEditAreaOnFocus(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnFocus();
}
function aspxHEEditAreaOnLostFocus(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnLostFocus();
}
// Resizing Objects
function aspxHEEditObjectResizeStart(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnObjectResizeStart(evt);
}
function aspxHEEditObjectResizeEnd(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnObjectResizeEnd(evt);
}
// DragDrop objects
function aspxHEAfterDocumentObjectDragEnd(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnAfterDocumentObjectDragEnd();
}
function aspxHEAfterObjectDragEnd(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnAfterObjectDragEnd();
}
function aspxHEEditObjectDragStart(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnObjectDragStart(evt);
}
function aspxHEEditObjectDragEnd(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnObjectDragEnd();
}
function aspxHEEditObjectDrop(name, evt) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnObjectDrop(evt);
}
function aspxHEDocumentObjectDragEnd(name) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnDocumentObjectDragEnd();
}
function aspxHESpellCheckerWordChanged(name, sender, args) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null)
        return htmlEdit.OnSpellCheckerWordChanged(sender, args);
}

function aspxHEDocumentDragOver(evt) {
    __aspxHEIsDocumentDragOver = true;
}
function aspxHEDocumentDragDrop() {
    __aspxHEIsDocumentDragOver = false;
}

if (__aspxNS)
    _aspxAttachEventToDocument("dragover", aspxHEDocumentDragOver);
if (__aspxNS)
    _aspxAttachEventToDocument("dragdrop", aspxHEDocumentDragDrop);

// PopupMenu
function aspxHEContextMenuItemClick(name, args) {
    var htmlEdit = aspxGetControlCollection().Get(name);
    if (htmlEdit != null) return htmlEdit.OnContextMenuItemClick(args.item);
}