Click here to Skip to main content
Click here to Skip to main content

A Web-based Rich Content Editor that works on Opera

, 18 Oct 2005
Rate this:
Please Sign up or sign in to vote.
The Editor used DOM to alter node properties. This enables edition of rich content in any DOM-supporting browser.

Sample Image - Editor/Editor.gif

Introduction

There are plenty of web-based rich text editors around. To list some:

#http://xstandard.com/ - XStandard's WYSIWYG Editor which creates strict XHTML markup.

#http://www.fckeditor.net/ - FCKEditor which works in IE and Mozilla-based browsers.

#http://www.kevinroth.com/rte/demo.htm - Kevin's Cross-browser rich text editor.

#CodeProject's Article Editor.

Before I begin, I'd like to state that I do not want to be understood incorrectly. All the above rich content editors do an excellent job: They convert the boring textarea to a fully editable item. And their development team put great effort to improve/enhance/support them. I fully respect their work. This article is by no means a complementary method. I just wanted to look at things from a different aspect. That's it.

There are many many other rich content editors, which offer different features. All these editors utilize ContentEditable mode of the browsers to create the so called "what you see is what you get" effect.

However, enabling edition of rich text content is not a standard. So each vendor has their own implementation.

Moreover, the support for ContentEditable mode is limited: Only mozilla-based browsers and Internet Explorer currently support it. So say when a third browser comes out and says "hey you guys out there! I have my own way of implementing ContentEditable mode as well!" there is nobody to enforce them do in a certain way. Because there isn't any declared standard for rich content creation.

What happens then? We will triple our effort to fork our code to support both of those browsers. Say all of a sudden ten browsers pop out with ten different API implemantation. You will have two chocies then: Either you will spend ten times more effort and create a code spagethi to handle all possible cases or you will ignore some (or most) of the minority browsers.

But this should not be the case:

Actually there is a standard way of editing HTML documents which World Wide Web Consortium recommends:

a la Document Object Model!

If we somehow achive rich content editability via manipulating DOM, we can add rich content support to any DOM-supporting browser; not only Mozilla and IE.

I know that we cannot support every single browser on earth. But, imho, at least equal opportunuties should be given to all the browsers that fully support DOM.

But, is this really necessary?

The necessity of such a product is another issue. As mentioned above, the vast majority of the web use IE and Mozilla-family browsers which implement content-editability to an excellent extent.

Another point of discussion may be the suitability and necessity of wysiwyg on a web page. Or to state in other words, will it be better to use textareas along with BBCode and leave the template-management and wysiwyg stuff to application packages like Macromedia Contribute.

According to my web stats Opera users constitute less than 1% of my traffic. (8-10 percent Mozilla, which is good Smile | :) ; around 90 percent IE and 1-2 percent all the rest).

So people will be happy with the WYSIWYG editors that work in mozilla-based browsers and IE (which make up about 96-99% of my incoming web traffic)

Will I be happy? Certainly not. But that's me and just me Smile | :)

Anyway one goal of the code project is manifesing crazy-looking thoughts, isn't it?

May be an idea will pull it from a techy-minded perspective to a more reasonable ground where average web user may utilize.

I hear you saying "enough philosopy, let's see the work done". So here we go:

Implementation

The best way to test an article editor is to write an article with it; so that I may get an idea of which features I may add, what can be done to ease use of the editor, kill bugs etc. So I'm writing this article from the Editor (sorry I couldn't find a more imaginative name for it Smile | :) )

Believe me or not, I'm writing this article from within Opeara: my second favorite browser. My first is Mozilla; I've never considered Internet Exploder at all, shame on me Smile | :)

Editor uses several js classes to function:

DraggableLayer:
The class is used to add drag-drop support and DOM integration.

ToolTip:
This class is used to pop tiny tooltip on hovering over the node anchors 
(the images with a white plus on them).

DOMManager:
The object is used to seek parent-child relations as well as remove
empty text nodes which cause problem in Opera and Mozilla.

WindowObject:
This class is used to get the inner dimensions and scroll ofsets of the
browser window.

TextFormatter:
The object is used to remove extra markup that is used
for editing purposes only. Moreover, it makes the tags lowercase,
so the markup will be more or less standards-compatible.

Editor:
Editor is the model for the article editing application GUI (the view).

Using the Editor, you may do a lot of formatting to your article, add images, add links and still create valid XHTML-strict code. I tried to make the GUI simple, but I am not a usability expert. I'm open to any ideas/sketches/mock-ups etc that may enhance the usability further.

The Editor Object

For those who are new to the MVC (Model View Controller) paradigm: The Editor object is a Model for the editor. By seperating the Model from the view ; we can change the view as much as we like, change the layout, change the style etc. without the need of changing the js code behind.

Here is a highly truncated prototype of the Editor.

_this=_Editor.prototype;

function _Editor() {
	/*initialize members*/
	/**/
}
/*public methods*/
_this.setActiveNodeName=function(strValue){...};
_this.setActiveNode=function(obj){...};
_this.getActiveNode=function(){...};
_this.setCurrentAction=function(intValue){...};		
_this.isBlockLevel=function(strName){...}
_this.isInline=function(strName){...}
_this.isActiveNodeBlockLevel=function(){...}
_this.getActiveNodeName=function(){...};
_this.getCurrentAction=function(){...};
_this.init=function(){
	this.controlPane={
		/* 
		  * Bind GUI elements to the Editor model
		  * using this associative array.
		  */
	};

	/*
	  * create span elements that contain
	  * icons which will initiate the editor when
	  * clicking on them.
	  */
	
	 /*register events*/
};

_this.getExtendedNodeDescription=function(strNode){...};
_this._RadH1_click=function(evt){...};
_this._RadH2_click=function(evt){...};
_this._RadH3_click=function(evt){...};
_this._RadH4_click=function(evt){...};
_this._RadH5_click=function(evt){...};
_this._RadH6_click=function(evt){...};
_this._RadP_click=function(evt){...};
_this._RadPre_click=function(evt){...};
_this._RadStrong_click=function(evt){...};
_this._RadEm_click=function(evt){...};
_this._RadNormal_click=function(evt){...};
_this._btnChangeType_click=function(evt){...};
_this.toggleCommitAction=function(){...};
_this._btnEdit_click=function(evt) {...};
_this._blnPreview_click=function(evt){...};
_this._btnAddAfter_click=function(evt) {...};
_this._btnAddBefore_click=function(evt){...};
_this._btnMoveAfter_click=function(evt){...};
_this._btnMoveBefore_click=function(evt){...};
_this._btnCopyHere_click=function(evt){...};
_this._btnCopyBefore_click=function(evt){...};
_this._btnCopyAfter_click=function(evt){...};
_this._btnDelete_click=function(evt){...}
_this._btnCancel_click=function(evt){...}
_this.cancelAction=function(){...};
_this._resetGUI=function(){...};
_this.createProperNode=function(strName,strText){...};
_this.getProperNodeValue=function(objNode){...};
_this.getProperNode=function(objNode){...};
_this._btnCommit_click=function(evt){
	switch(Editor.getCurrentAction()){
		case Constant.Editor.Action.ADD_AFTER:
			Editor.addAfter();		
			break;
		case Constant.Editor.Action.ADD_BEFORE:
			Editor.addBefore();
			break;
		case Constant.Editor.Action.MOVE_AFTER:
			Editor.moveAfter();
			break;
		case Constant.Editor.Action.MOVE_BEFORE:
			Editor.moveBefore();
			break;
		case Constant.Editor.Action.DUPLICATE:
			Editor.duplicate();
			break;
		case Constant.Editor.Action.DELETE_NODE:
			Editor.deleteNode();
			break;
		case Constant.Editor.Action.EDIT_TEXT:
			Editor.editText();
			break;		
		case Constant.Editor.Action.CHANGE_TYPE:
			Editor.changeType();
			break;
		default:
			break;
	}
	/*set GUI back to initial state*/
	Editor.cancelAction();
};

_this.addAfter=function(){...};
_this.addBefore=function(){...};
_this.moveAfter=function(){...};
_this.moveBefore=function(){...};
_this.duplicate=function(){...};
_this.deleteNode=function(){...};
_this.editText=function(){...};
_this.changeType=function(){...};
_this._appendControlsToFamily=function(nodeToInsert){...};
_this.organizeEditPane=function(){...};
_this.appendControls=function(theNode){...};
_this._edit_click=function(evt){...};

The HTML

The HTML for the Editor page is as follows:

<div id="EditorArea" class="""textEditor">""  
<h2>Introduction</h2>
</div>

<div id="EditorControls">
<h3 id="EditorInfo">Informational Heading</h3>
<div style="padding:10px;" id="EditorControlButtons">
<h4>transform</h4>
<div><input type="button" id="btnChangeType" value="change type" /></div>

<h4>alter</h4>
<div>
<input type="button" id="btnEdit" value="edit text" />
<input type="button" id="btnAddBefore" value="add before" />
<input type="button" id="btnAddAfter" value="add after" />
</div>
<h4>move</h4>
<div>
<input type="button" id="btnMoveBefore" value="move before / move up" />
<input type="button" id="btnMoveAfter" value="move after / move down" />
</div>
<h4>copy</h4>
<div>
<input type="button" id="btnCopyHere" value="duplicate element" />
</div>
<h4>remove</h4>
<div><input type="button" id="btnDelete" value="remove element" /></div>
</div>
<div style="padding:10px;">

<ul id="ListTagsBlockLevel">
... radio buttons ...
</ul>

<ul id="ListTagsInline">
.... radio buttons ...
</ul>


<textarea rows="10" cols="30" id="TxtContent"></textarea>
</div>

<div style="padding:10px;text-align:right;">
<input type="button" value="cancel" id="btnCancel" />
<input type="button" value="commit" id="btnCommit" />
</div>
</div>

The EditorArealayer includes editable content, wheras EditorControls layer includes GUI controls.

To initialize the editor we call its init() method on page load.

window.onload=function() {
	Editor.init();
	document.getElementById("BtnArticleSource"
	).onclick=BtnArticleSource_click;
};

function BtnArticleSource_click(evt){
	alert("This action may take some time and your browser may_
	 hang for a few seconds.\nPlease be patient.");

	var src=new EventObject(evt).getSource();

	document.getElementById("ArticleSource").value=
	TextFormatter.properHTML(
		Editor.controlPane.panel.EditorArea.getObject().innerHTML);
}

BtnArticleSource_click method is triggered when clicking the "retrieve article's html" button. This will fill the textbox at the bottom of the page with the article's HTML.

You may further investigate the code. I tried to write the code and the markup as clean as possible. Feel free to express any queries and comments.

Tips and Usage

tipCreation and modification of the article is done on node basis. If you are working in the edit mode, you will recognize the node anchors (inline edit icon and block level edit icon). inline edit icon is used to add/edit/modify inline elements (strong text, emphasized text, normal text, links and images) whereas block level edit icon is used to add/edit/modify block-level elements (heading level 1-6, paragraphs and preformatted text).

tipClicking on a node anchor pops up available actions for that particular node. The pop-up is draggable DHTML layer (my following article will be on creating a simple drag&drop layer I suppose).

tipDouble-clicking anywhere on the article toggles between edit and preview modes.

tipAs far as my experience is concerned: getting used to the Editor takes some time. Because, you need to look the entire document as a whole instead of bunches of paragraphs and text to copy, paste and drag around. But you become more creative as you get used to it. In fact the "duplicate element" functionality happens to be a good helper at times. In addition bubbling block level elements up and down is fun.

tipActions available for all nodes (inline and block-level):

# change type: Changes the type of the node, for instance you may convert a text elemen to a link or to an image.

# add before: Adds an element before that node.

# add after: Adds an element after that node.

# move before/move up: Moves the node upwards in the node hierarchy.

# move after/move down: Moves the node downwards in the node hierarchy.

# duplicate: Creates an identical node and positions it just after the selected node.

# remove: Removes the selected node entirely. caution! this operation cannot be undone.

# cut node: removes the node and copies the node contents to a temporary variance.

# copy node: copies the node content to a temporary variance; it does not remove the original node.

# paste before: pastes the copied or cut node just before the selected node.

# paste after: pastes the copied or cut node just after the selected node.

tipActions only available for inline nodes only:

# edit: changes the contents of the node (the text and (source, link, width etc if available)).

tipAvailable types for inline nodes:

# strong text: bold text.

# emphasized text: italic text.

# normal text: normal text.

# link:A link.

# image:lovely image.

tipAvailable types for block-level nodes:

# heading level 1: First level heading.

# heading level 2: Second level heading.

# heading level 3: Third level heading.

# heading level 4: Fourth level heading.

# heading level 5: Fifth level heading.

# heading level 6: Sixth level heading.

# paragraph: A paragraph.

# preformatted text: preformatted text (which is generally used for formatting code).

Things that can be Done to Improve the Editor

From now on, I will post new versions to the Editor to sardalya (http://www.sarmal.com/sardalya/). The interested may follow the changes there. Here is a list of things to do that come mind at a first glance. Though I would like to remind you that I am open to any suggestions positive&negative criticisims so that I may enhance the Editor further.

# Ability to insert lists and nested lists.

# Add support for other tags (like <q>, <blockquote>, <hr />)

# Add some descriptive icons to the pop-up interface.

# Add tab-support for preformatted text, which is a great aid when indenting code (Opera will not support it but it will transform gracefully - may be add a button to copy tab character (from a hidden field may be) for Opera and other non-supporting browsers).

# Some more mouse interaction (such as dragging an ancor and dropping it to another anchor will move the node befoore the second one, shift-dragging will copy it etc).

# Allow selection and modification of multiple nodes (i.e making two paragraps bold, converting three headings to paragraphs etc).

# Allow creation and modification of nested elements (such as <strong><em>strong and emphasized text</em></strong>).

# Assign keyboard shortcuts to certain opearations.

# Ability to add predefined smiley images, bullet images, avatars etc.

Note to the Reader

I've included only the necessary parts of s@rdalya API that I used in this project not to deviate from the subject and to keep code small. You may find the entire api at: http://www.sarmal.com/sardalya/.

However, the API on web currently does not include the Editor. Editor will be added to the next stable relase.

Also note that this is a development release and it is not optimized for web. An optimized version along with usage examples and documentation will be available soon.

History

#03-10-2005 - Article created.

#18-10-2005 - version 1.1.0 is released. (former version was 1.0.0). Article updated accordingly.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

volkan.ozcelik
Web Developer
Turkey Turkey
Volkan is a java enterprise architect who left his full-time senior developer position to venture his ideas and dreams. He codes C# as a hobby, trying to combine the .Net concept with his Java and J2EE know-how. He also works as a freelance web application developer/designer.
 
Volkan is especially interested in database oriented content management systems, web design and development, web standards, usability and accessibility.
 
He was born on May '79. He has graduated from one of the most reputable universities of his country (i.e. Bogazici University) in 2003 as a Communication Engineer. He also has earned his Master of Business Administration degree from a second university in 2006.

Comments and Discussions

 
Newsopera now supports wysiwyg mode Pinmembervolkan.ozcelik17-Apr-06 20:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 18 Oct 2005
Article Copyright 2005 by volkan.ozcelik
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid