Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

ASP.NET HTML Editor Control

4.91/5 (68 votes)
30 May 2013CPOL13 min read 198.3K   11.2K  
Creating a useful ASP.NET HTML Editor custom control.

Introduction

Nowadays lots of web applications such as forums and blogs use HTML editors as a primary tool for users publications. Web based HTML editor is a kind of control that allows online users to create and edit their html documents in a way that user can write text and set font, color, size, and hyperlink for it and also insert image,swf, files in that. Furthermore the user can view the HTML code of his document and edit it and view the result in design mode and vice versa.

This article discusses how can create an HTML editor server control with some security considerations.

Background 

Four years ago I was working in a company which was so cautious about developing secure products, thus our technical manager asked me about developing an HTML text editor as a module in our CMS Portal for our important customers which security was their first priority. So I developed an HTML editor which considered some important security issues such as XSS. Generally it is not more secure than Current new versions of text editors like FCKEditor but it was a good opportunity for me to create a useful, user friendly and above all a unique experience for my mind necessity for programming some creative web-based software. I hope this article helps you too.

System Requirements 

  • ASP.NET 2+

UI

Component appearance:

  • Three toolbar rows across the top
  • Dialog bars for adding elements like table,button, etcetera.
  • An editor area that displays and/or edits the document in either mode

Asp.net HTML Editor component

Component Parts

There are two part for this custom control, server side and client side.server side undertakes some tasks such as security stuffs using AntiXSS, providing the html source of html editor,setting leveles of security,providing some methods like setting value for editor and getting secure value from editor,script registration,storing uploaded files, providing decoded data which has been sent from html editor and providing WebResources for control.

The client side guarantees eliminating some dangerous stuffs(XSS aspects) from html editor source which will send to the server side and encoding them, sending data to the serve side , etc.

In client side there are six JavaScript source files

  • RichText.js is the main javascript source , all things which is related with editor abilities is done here.
  • Encoder.js provides all methods for encoding data
  • Loading.js provides optional loader for editor.(some part of it has been commented, if you want to have loader,uncomment them)
  • Slider.js provides a slider for resizing the editor text area
  • jscolor.js is an open source javascript which i just used it

In server side there are five classes included:

  • HtmlSourceInitializer.cs : Initializes the editor HTML
  • Registrar.cs : Registers a list of scripts into our control
  • Security.cs : Responsibles for security using AntiXSS AND HtmlSanitizationLibrary
  • SourceActions.cs : Get the data which has been sent from cleint
  • RichTextBox.cs : Establishes accessible methods and properties of HtmlEditor control

EditorStyles , RichTextBoxIcons , Colorpicker are another parts.

HtmlSourceInitializer.cs

The component has some its own HTML sources which creates HTML editor view.This class uses StringBuilder to append HTML Editor view parts which has been added in_HtmlSource with InitializeHtmlSource method and then places the _HtmlSource into the Static RichTextHtmlSource property.

C#
internal static class HtmlSourceInitializer
{
#region fields
private static StringBuilder _HtmlSource = null;
#endregion

#region getHtmlSource
public static StringBuilder RichTextHtmlSource
{
    get
    {
        if (_HtmlSource != null)
        {
            return _HtmlSource;
        }
    return null;
    }
}
#endregion

#region Initialize html source
/// <summary>
/// Initializes the Html source of editor
/// </summary>
/// <param name="CurrentPage"></param>
public static void InitializeHtmlSource(Page CurrentPage)
{
    StringBuilder HtmlSource = new StringBuilder();
        HtmlSource.Append("<center id=\"centerElement\" style=\"display:none\">");
...............

Security.cs

For preventing cross-site scripting,i decided to use Microsoft AntiXSS library. As you can read in its overview :

"The Microsoft Anti-Cross Site Scripting Library V4.2 (AntiXSS V4.2) is an encoding library designed to help developers protect their ASP.NET web-based applications from XSS attacks. It differs from most encoding libraries in that it uses the white-listing technique -- sometimes referred to as the principle of inclusions -- to provide protection against XSS attacks.

This approach works by first defining a valid or allowable set of characters, and encodes anything outside this set (invalid characters or potential attacks). The white-listing approach provides several advantages over other encoding schemes."

The _SetHighLevelSecurityForHtmlTags field, has been setted as True which means the html document which has been sent from client side,will decode in two levels and it will change to only text codes. For instance the "<b>hello</b>" will be something like this ";&lt;b&gt;hello&lt;/b&gt;".It is useful in some cases which you want to get much more secured html code and after checking it,you can decode it to get a pure HTML source again.

The EncodeHtml method encodes the string HTML source of created document AntiXSS HtmlEncode

method. Also the GetSafeHtmlFragment method returns HTML fragments with tags intact.

C#
public static class Security
{
    /// <summary>
    /// if this properties is true, in this case will set two steps security over exchanged data
    /// The default value of this property is true
    /// </summary>
    internal static bool _SetHighLevelSecurityForHtmlTags = true;
    internal static string EncodeHtml(string html)
    {
        return AntiXss.HtmlEncode(Sanitizer.GetSafeHtmlFragment(html));
    }
}

SourceActions.cs

The SourceActions class provides the HTML source code which has been sent from client with SourceProvider.

C#
public class SourceActions : System.Web.UI.Page
{
#region fields
  internal string _SourceCode = string.Empty;
  #endregion

#region Cunstructor
public SourceActions()
{
//
// TODO: Add constructor if it is requiered
//
}
#endregion Cunstructor
#region Source provider
/// <summary>
/// provide editor html source code
/// </summary>
/// <summary>
/// Sets the Current page.
/// </summary>
/// <param name="CurrentPage">The current page.</param>
/// <returns>void</returns>
internal void SourceProvider(Page CurrentPage)
{
    CurrentPage.ClientScript.GetPostBackEventReference(CurrentPage, string.Empty);
    if (CurrentPage.IsPostBack)
    {
        string eventTarget = (CurrentPage.Request["__EVENTTARGET"] == null ? 
               string.Empty : CurrentPage.Request["__EVENTTARGET"]);
        string eventArgument = (CurrentPage.Request["__EVENTARGUMENT"] == null ? 
               string.Empty : CurrentPage.Request["__EVENTARGUMENT"]);
        if (eventTarget == "getHtmlData")
        {
            if (SigmaToolBox.TextEditor.Security._SetHighLevelSecurityForHtmlTags)
            {
                _SourceCode = SigmaToolBox.TextEditor.Security.EncodeHtml(eventArgument);
            }
        else
        {
            _SourceCode = eventArgument;
             }
    }
}
}
#endregion
}

RichTextBox.cs

SetValue method

This method will set some values like HTML document for editor. For instant if you want to use some documents which have been stored in database or other resources, you can use this method to insert your documents into the editor for edition.

C#
/// <summary>
/// This function sets a value to the text editor like some text
/// or html data from database or other resources for edition activities
/// </summary>
/// <param name="Value"></param>
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public void SetValue(string Value)
{
    string script = "disableElement(document.getElementById('textToolsContainer'));" + 
       "document.getElementById('textEditor').style.display='none';isOnSourceMode" + 
       "=true;isOndesignMode=false;document.getElementById('sourceTxt')." + 
       "style.display='block';document.getElementById('sourceTxt').value='" + Value + "'";
    Page.ClientScript.RegisterStartupScript(Page.GetType(), 
      "valueSetterScript", script, true);
} 

GetValue method

This function gets the data which has been provided in text editor for some purposes such as : demonstration in a specified page,storing in database,etc.

C#
public string GetValue()
{
    return this.Page.Server.HtmlDecode(AntiXss.HtmlAttributeEncode(GetDecodedValue()));
}

OnPreRender must be modified to call RegisterStartupScript, as follows:

C#
protected override void OnPreRender(EventArgs e)
{

    base.OnInit(e);
    Registrar.RegisterScripts(new List<string> { "SigmaToolBox.js.loading.js", 
      "SigmaToolBox.js.testJS.js", "SigmaToolBox.js.Encoder.js", 
      "SigmaToolBox.js.slider.js" }, this.Page, this.GetType());
    string InitializerJS = Page.ClientScript.GetWebResourceUrl(this.GetType(), 
              "SigmaToolBox.js.Initializer.js");
          this.Page.ClientScript.RegisterStartupScript(this.GetType(), 
          "RichText", "<script language="\""javascript\" src='" + 
          InitializerJS + "'></script>");
}

RichText.js

Because the client-side behaviors for an HTML editor are fairly extensive, there is a fairly extensive amount of JavaScript required in the client control. Most of this is typical designMode client-side programming.

This includes some significant functions which undertake some tasks such as :

  • Commands execution on editor
  • Elements creation
  • All other design mode activities
  • Making handlers for handeling events

The document is created and design with these javascript functions and then if someone wants to use it on server side (for example: storing/decoding)should send it to the server.This action happens with sendValue().

sendValue() function send created document to the server,as well as it eliminates some of the risky tags over XSS attacks like script,iframe,javascript and encode the document using Encoder.js<code>(with htmlEncode() function) before sending it to the server. Actually there will be two step of encoding process,first step in the client and the second one is in the server side.

JavaScript
//Sends all elements of textEditor to the server

function sendValue(){

    var innerHtmlData= usedFrame.innerHTML;

    var clearData=innerHtmlData.toLowerCase().replace(/<script[^>]*?>/g,"");

    var clearData = clearData.replace(/<\/script>/g, "");

    clearData = clearData.replace(/javascript/g, "");

    clearData = clearData.replace(/script/g, "");

    clearData = clearData.replace(/<iframe[^>]*?>/g, "");

    clearData = clearData.replace(/<\/iframe>/g, "");

    clearData = Encoder.htmlEncode(clearData);

    __doPostBack('getHtmlData', clearData);

}

The most vital function which in another word is the hurt of this editor,is  textEdit(x,y) function which responses for all requests over text editing issues.The essential idea behind of it is using a fundamental function of the browsers which enables you to execute a command on the current document, current selection, or the given range such as bold,italic,underline,copy,delete,reformat and so on ,named execCommand  .

The code generated by the execCommand method is different in browsers. Internet Explorer uses HTML tags, Firefox, Google Chrome and Safari generate inline styles and Opera sometimes uses HTML tags, sometimes styles.

For example, if the 'bold' command is executed on a non-bold text,

•Internet Explorer and Opera generate a strong element around it, 

•Firefox, Google Chrome and Safari generate a span element around it and set the fontWeight style property of the span element to 'bold'. If an element exists around the non-bold text, then Firefox, Google Chrome and Safari set the fontWeight style property of this element to 'bold'. 

If the 'bold' command is executed on a bold text, browsers remove the specified style property and/or the element including the text.

This caused that i encountered with problems in recognizing bold or underline and italic text to demonstrate it in the shape of bold,italic and underline button.Anyway i resolved it for firefox and IE but it has some problems in other browsers such Chrome,opera and firefox. getActiveButtons() method do that as follows :

JavaScript
function getActiveButtons(){
    isColorPickerInAction=isAdvanceColorPickerInAction=false;
    var isBold=false;var isItalic=false;var isUnderline=false;
    try{
       if(document.all){
           var xmlDocument=StringtoXML(getRangeNode(window.frames["textEditor"]));
           var richTextElementsnodes=xmlDocument.documentElement.getElementsByTagName("nodeName");
           var NodesCount=richTextElementsnodes.length;
           for(var i=0;i<NodesCount;i++){
                var innerNodeValue=richTextElementsnodes[i].firstChild.nodeValue;
                //alert(innerNodeValue)
                switch(innerNodeValue){
                    case "STRONG":
                        isBold=true;
                        break;
                    case "EM":
                        isItalic=true;
                        break;
                    case "U":
                        isUnderline=true;
                        break;
                }
           }
       }
       else{
            var retrivedData=getRangeNode(textEditorElement.contentWindow);
            for(var i=0;i<retrivedData.length;i++){
                switch(removeSpaces(retrivedData[i])){
                    case "font-style:italic":
                        isItalic=true;
                        break;
                    
                    case "text-decoration:underline":
                        isUnderline=true;
                        break;
                    
                    case "font-weight:bold":
                        isBold=true;
                        break;
                }
            }
       }
   }
   catch(e){
        
   }
   setActivationStatus(isBold,isItalic,isUnderline);
} 

The StringtoXML()  Method converts input string (which is generated from getRangeNode() method) to XML : 

JavaScript
function StringtoXML(text){
    var doc;
    if (window.ActiveXObject){
      doc=new ActiveXObject('Microsoft.XMLDOM');
      doc.async='false';
      doc.loadXML(text);
    } 
    else{
      parser=new DOMParser();
      doc=parser.parseFromString(text,"text/xml");
    }
    return doc;
} </span> 

getRangeNode() method gets the selected text parent node and its styles for recognizing the selected text style : 

JavaScript
 function getRangeNode(win){
      var retrivedString="";
      checkCursor(usedFrame);
      if (window.getSelection){
        node = win.getSelection().anchorNode;
        var nodeStyleAttribute=node.parentNode.getAttributeNode("style").nodeValue.toString();
        var nodeStyleAttributeChildes=nodeStyleAttribute.split(";");
        retrivedString=nodeStyleAttributeChildes;
      }
      else if (win.document.selection){
        var range = win.document.selection.createRange();
        if (range){
            node = range.parentElement();
            nodesList="";
            retrivedString= "<nodesList>"+getParentNodesList(node)+
              "<nodeName>"+node.nodeName+
              "</nodeName>"+"</nodesList>";
        }
      }
  return retrivedString;
} 

The syntax is :  object.execCommand(cmdID, showUI, value). The execCommand function abilities and compatibilities are different in various browsers,hence in this component there are some abilities like "document saving" or "background color" which are not executed in other browsers except IE. As you can see below there are lots of incompatibilities between browsers which makes our job more difficult and it seems it goes to be more improved than before. 

Method or property Explorer 6/7  Firefox 2 Safari 3 Opera 9
backcolor yes yes yes yes

Moz/Saf require the #. Moz/Op give bgcolor to block-level element selection is part of, IE/Saf to selection itself.
To get the IE/Saf effect in Moz, use hilitecolor.

bold yes yes yes yes
Notes
contentReadOnly no yes ? no
IE gives error
copy yes protected yes protected
Ctrl+C always works
createbookmark ? ? ? ?
Notes
createlink yes yes yes yes
Notes
cut yes protected yes protected
Ctrl+X always works
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
decreasefontsize no yes ? incorrect
Op allows only 1 decrease; the 2nd reverts text to original font size.
delete yes yes yes yes
Notes
fontname yes yes yes yes
Notes
fontsize no horrid yes horrid

Moz/Op generate a (gulp!) <font> tag with size equal to parseInt(value). Saf creates a normal font-size CSS declaration.

forecolor yes yes yes yes
Moz/Saf require #
formatblock

(put selection in header or paragraph)

no yes buggy incomplete
Opera changes only the first block-level element in the selection to the desired block.
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
heading no yes no no
Notes
hilitecolor no yes no yes

Does the same as bgcolor in IE/Saf: it gives only the selection (and not the containing block) the defined bgcolor.

increasefontsize no yes ? incorrect
Op allows only 1 increase; the 2nd reverts text to original font size.
indent incorrect yes buggy more incorrect

Moz adds 40px of margin-left per indent. IE/Op add a (gulp!) blockquote for every indent.

When applied to an <li>, Moz/IE generate a nested <ol/ul>, but Op again inserts a <blockquote>.

inserthorizontalrule yes yes yes yes
Notes
inserthtml no yes ? no
Notes
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
insertimage yes yes yes yes
IE allows resizing of image
insertorderedlist almost yes almost yes
If the newly created ordered list borders an existing list, IE and Safari merge the two.
insertunorderedlist almost yes yes yes
If the newly created unordered list borders an existing list, IE merges the two.
insertparagraph yes alternative yes yes
Mozilla adds a paragraph around the selected block. The other browsers delete the selected block and insert an empty paragraph which the user can fill.
italic yes yes yes yes
Notes
justifycenter yes yes yes yes
Notes
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
justifyfull yes yes yes yes
Notes
justifyleft yes yes yes yes
Notes
justifyright yes yes yes yes
Notes
multipleselection ? ? ? ?
Notes
outdent yes yes buggy yes

When applied to an <li> that's a child of a single <ol/ul>, Moz/IE move the <li> to outside the <ol/ul>, while Op doesn't react.

Unfortunately IE has an extra bug in my test page: it moves the <li> to entirely outside my test element.

overwrite ? ? ? ?
Notes
paste yes protected yes protected
Ctrl+V always works
print ? ? ? ?
Notes
redo yes yes yes yes
Redo works in Safari, but if you do Undo/Redo too often, it crashes. Solved in 3. 
If you make your own changes in the editable area, Undo/Redo continues to work in Mozilla and Safari (though it ignores your custom changes), but in IE and Opera it stops working.
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
refresh ? ? ? ?
Notes
removeformat ? ? ? ?
Notes
saveas ? ? ? ?
Notes
selectall ? ? ? ?
Notes
strikethrough yes yes yes yes
Notes
styleWithCSS no yes ? no
Gives a generic command that styles should be applied with CSS (true; default) or with tags (false). When doing execCommand("bold")the first would generate a <span style="font-weight: bold";>, the second a <b> tag.
subscript yes yes yes yes
IE/Moz/Op: using the same command again removes the subscript. Using subscript and superscript together gives odd effects.
superscript yes yes yes yes
IE/Moz/Op: using the same command again removes the superscript. Using subscript and superscript together gives odd effects.
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9
unbookmark ? ? ? ?
Notes
underline yes yes yes yes
Notes
undo yes yes yes yes
Undo works in Safari, but if you do Undo/Redo too often, it crashes. Solved in 3. 
If you make your own changes in the editable area, Undo/Redo continues to work in Mozilla and Safari (though it ignores your custom changes), but in IE and Opera it stops working.
unlink yes yes yes yes
Notes
Method or property Explorer 6/7 Firefox 2 Safari 3 Opera 9 

Another crucial  issue is finding the last point of cursor which has been there. this will cause the editor knows which text element should be effected by current execCommand. This function mostly is used in IE because Firefox and others by default have this ability by theme self. This tasks is being responsible by checkCursor() Method As you observe : 

JavaScript
//checks the cursor and return the carret Position
function checkCursor(where){
    try{
        Current=where;
        if (!isToolBoxContainerDivDisabled)
        {
            where.focus();
            if(document.all){
                CarretPosition=document.selection.createRange();
                if(CarretPosition.text==""){
                    where.focus();
                }
            }
        }
    }
    catch(error){
        alert(error.name + ": " + error.message);
    }
}  

CreateEventForGeneratedButton()   is the other vital method which allocates the specified  event handlers to their own buttons which dynamically created like all buttons in dialogs such as table creator, link adder,ETC.  

JavaScript
function CreateEventForGeneratedButton(ActionType){
    try{
        switch(ActionType){
            case "Link":
               (document.all)?SetCommandButton.attachEvent ("onclick",
                 SetLinkCommandEventHandler):SetCommandButton.addEventListener (
                 "click",SetLinkCommandEventHandler,false);
               break;
            case "Image":
               (document.all)?SetCommandImageButton.attachEvent ("onclick",
                  SetImageCommandEventHandler):SetCommandImageButton.addEventListener (
                  "click",SetImageCommandEventHandler,false);
                break;
            case "insertTable":
               (document.all)?SetCommandInsertTableButton.attachEvent ("onclick",
                   SetInsertTableCommandEventHandler):SetCommandInsertTableButton.addEventListener (
                   "click",SetInsertTableCommandEventHandler,false);
                break;
            case "insertButton":
               (document.all)?SetCommandInsertButton_Button.attachEvent ("onclick",
                  SetInsertButtonCommandEventHandler): SetCommandInsertButton_Button.addEventListener (
                  "click",SetInsertButtonCommandEventHandler,false);
                break;
            case "insertSWF":
                isToolBoxContainerDivDisabled=false;
               (document.all)?SetSWFCommandButton.attachEvent ("onclick",
                 SetInsertSWFButtonCommandEventHandler): SetSWFCommandButton.addEventListener (
                 "click",SetInsertSWFButtonCommandEventHandler,false);
                break;
            case "uploadSWF":
               (document.all)?SetSWFUploaderCommandButton.attachEvent ("onclick",
                  SetSWFUploaderButtonCommandEventHandler): 
                  SetSWFUploaderCommandButton.addEventListener (
                  "click",SetSWFUploaderButtonCommandEventHandler,false);
                break;
            case "CancelingInsertButtton":
               (document.all)?SetCommandCancelInsertingButton_Button.attachEvent (
                  "onclick",SetCancelingInsertButtonCommandEventHandler):
                  SetCommandCancelInsertingButton_Button.addEventListener (
                  "click",SetCancelingInsertButtonCommandEventHandler,false);
                break;
        }
    }
    catch(error){
        alert(error.name + ": " + error.message);
    }
}

The Setposition() method set the created object position. When an object is created  it needs to located on the page. This method set objects position based on mouse and page.For instance when you drag a dialog bar, it uses this method with set the related argument to "mouse" and when an object maker is created, it uses this function with "page" as related argument. 

JavaScript
//Sets the position of object maker
function SetPosition(e,elementName,Related){
    try{
        var ContainerElement=document.getElementById(elementName);
        switch(Related){
            case "Page":
                if(ContainerElement!=null){
                    ContainerElement.style.left=(document.body.clientWidth)/2-100 +"px";
                    ContainerElement.style.top=(document.body.clientHeight)/2-100 +"px";
                }
            break;
            case "Mouse":
                    e=e||window.event;
                    document.getElementById(elementName).style.display="block";
                    document.getElementById(elementName).style.left=e.clientX+10+"px";
                    document.getElementById(elementName).style.top=e.clientY +"px";
            break;
        }
    }
    catch(error){
        alert(error.name + ": " + error.message);
    } } 

RichText.js is not based on object oriented programming but i tried to make it clear and useable. 

Using this Code

For using this control you should add the control's DLL from RichTextBox\Control_dll folder in your Visual Studio Toolbox and then just drag it on your ASPX page. you will see something like below:

ASP.NET
<%@ Page Language="C#" AutoEventWireup="true" 
   CodeBehind="Default.aspx.cs" Inherits="testCustom._Default" %>
<%@ Register Assembly="SigmaToolBox" 
   Namespace="SigmaToolBox.TextEditor" TagPrefix="sigma" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body id="body1">
<%=htmlCode %>
<form id="form1" runat="server">
<div>
<sigma:RichTextBox ID="RichTextBox1" runat="server" />
</div>
<div id="div1">
<asp:Button Style="border-style: groove" 
ID="Button1" runat="server" OnClientClick="sendValue()"
Text="Get Data" OnClick="Button1_Click" />
</div>
</form>
</body>
</html>

if you want to get the created document, first you should send it with OnClientClick="sendValue()" to the server and then get safe data with setting OnClick event on your event raiser control like button and write your code like this:

C#
public partial class _Default : System.Web.UI.Page
{
    public string htmlCode = string.Empty;
    protected void Page_Load(object sender, EventArgs e)
    {
        //To DO
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        RichTextBox1.SetHighLevelSecurityForHtmlTags = false;
        htmlCode =RichTextBox1.GetValue();
    }
} 

Browser Testing 

The control discussed in this article was tested on Firefox 5+, IE 8 , Opera , Safari and Chrome.

IE 9 : There are no error in IE 9 but exist some lacks in its appearance :

  • Two excessive  scroll bar
  • Dialog bars disappear at the behind of tables or texts 

How to open this project with Visual studio 2010 or lower versions 

Since last time i opened this project with Visual Studio 2012 for checking its reliability, maybe you have problem with opening this project by lower versions than VS2012. For fixing this problem just follow below steps:

  • Open project SLN file with a text editor like notepad. In the second line you'll see this line : Microsoft Visual Studio Solution File, Format Version 12.00.  Change the version to 11.00 or lower
  • Save text and open the project with your VS. Enjoy it ! 

Conclusion 

I tried that this control be user friendly and altogether I believe that working with it is so simple. Entirely it is not fault-free but it was good experience for me during all 6 months which I developed it and I hope this source code learns you some valuable points about creating an asp.net custom control and web-based HTML Editor.

There will be lots of tiny points for your learning like : addressing images from CSS files into a custom control project and Assemblyinfo and lots of other stuffs in Javascript sources. Hope to be useful.

History

November 5, 2012

  • The  onclick javascript error for firefox  over color picker actions was fixed 
  • Created table appearance on Chrome and Opera  was improved 

October 26, 2012

  • Fixing for Safari Browser

November 23 , 2009

  • GetValue and SetValue methods was Added

April 10, 2009

Added features :

  • SWF support
  • Special characters support
  • Preview support

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)