Index: /CKEditor/branches/prototype/_source/core/config.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/config.js	(revision 2793)
+++ /CKEditor/branches/prototype/_source/core/config.js	(revision 2794)
@@ -159,5 +159,5 @@
 	 * config.plugins = 'elementspath,toolbar,wysiwygarea';
 	 */
-	plugins : 'basicstyles,button,dialog,elementspath,horizontalrule,htmldataprocessor,keystrokes,removeformat,smiley,link,sourcearea,tab,toolbar,wysiwygarea,forms,image,find,table,specialchar',
+	plugins : 'basicstyles,button,dialog,elementspath,horizontalrule,htmldataprocessor,keystrokes,removeformat,domwalker,smiley,link,sourcearea,tab,toolbar,wysiwygarea,forms,image,find,table,specialchar',
 
 	/**
Index: /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js	(revision 2794)
+++ /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js	(revision 2794)
@@ -0,0 +1,181 @@
+/*
+ * CKEditor - The text editor for Internet - http://ckeditor.com
+ * Copyright (C) 2003-2008 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ */
+
+CKEDITOR.plugins.add( 'domwalker' );
+
+(function()
+{
+	var fireDomWalkerEvent = function( transistionType, fromNode, toNode )
+		{
+			var eventData = { from : fromNode, to : toNode, type : transistionType };
+			this.fire( transistionType, eventData );
+			this._.actionEvents.push( eventData );
+		};
+
+	CKEDITOR.domWalker = function( node )
+	{
+		if ( arguments.length < 1 )
+			return;
+
+		this._ = { currentNode : node, actionEvents : [], stopFlag : false };
+		CKEDITOR.event.implementOn( this );
+	};
+
+	CKEDITOR.domWalker.prototype = {
+		next : (function()
+		{
+			var dfsStepForward = function()
+			{
+				var current = this._.currentNode, next;
+
+				if ( !current )
+					return null;
+
+				if ( current.getChildCount() > 0 )
+				{
+					next = current.getChild( 0 );
+					fireDomWalkerEvent.call( this, 'down', current, next );
+					return next;
+				}
+				else if ( current.getNext() )
+				{
+					next = current.getNext();
+					fireDomWalkerEvent.call( this, 'sibling', current, next );
+					return next;
+				}
+				else
+				{
+					var ancestor = current.getParent();
+					fireDomWalkerEvent.call( this, 'up', current, ancestor );
+
+					while ( ancestor )
+					{
+						if ( ancestor.getNext() )
+						{
+							next = ancestor.getNext();
+							fireDomWalkerEvent.call( this, 'sibling', ancestor, next );
+							return next;
+						}
+						else
+						{
+							next = ancestor.getParent();
+							fireDomWalkerEvent.call( this, 'up', ancestor, next );
+							ancestor = next;
+						}
+					}
+				}
+				return null;
+			};
+
+			return function()
+			{
+				this._.actionEvents = [];
+				return {
+					node : ( this._.currentNode = dfsStepForward.apply( this ) ),
+					events : this._.actionEvents
+				};
+			};
+		})(),
+
+		back : (function()
+		{
+			var dfsStepBackward = function()
+			{
+				var current = this._.currentNode, next;
+
+				if ( !current )
+					return null;
+
+				if ( current.getPrevious() )
+				{
+					var lastChild = current.getPrevious();
+					fireDomWalkerEvent.call( this, 'sibling', current, lastChild );
+					while ( lastChild.getChildCount() > 0 )
+					{
+						next = lastChild.getChild( lastChild.getChildCount() - 1 );
+						fireDomWalkerEvent.call( this, 'down', lastChild, next );
+						lastChild = next;
+					}
+					return lastChild;
+				}
+				else
+				{
+					next = current.getParent();
+					fireDomWalkerEvent.call( this, 'up', current, next );
+					return next;
+				}
+				return null;
+			};
+
+			return function()
+			{
+				this._.actionEvents = [];
+				return {
+					node : ( this._.currentNode = dfsStepBackward.apply( this ) ),
+					events : this._.actionEvents
+				};
+			};
+		})(),
+
+		forward : function( guardFunc )
+		{
+			var retval;
+			this._.stopFlag = false;
+
+			// The default behavior is top stop once the end of document is reached.
+			guardFunc = guardFunc || function( evt ) { if ( !evt.data.to ) this.stop(); };
+
+			this.on( 'sibling', guardFunc );
+			this.on( 'up', guardFunc );
+			this.on( 'down', guardFunc );
+			while( !this._.stopFlag )
+				retval = this.next();
+			this.removeListener( 'sibling', guardFunc );
+			this.removeListener( 'up', guardFunc );
+			this.removeListener( 'down', guardFunc );
+			return retval;
+		},
+
+		reverse : function( guardFunc )
+		{
+			var retval;
+			this._.stopFlag = false;
+
+			// The default behavior is top stop once the start of document is reached.
+			guardFunc = guardFunc || function( evt ) { if ( !evt.data.to ) this.stop(); };
+
+			this.on( 'sibling', guardFunc );
+			this.on( 'up', guardFunc );
+			this.on( 'down', guardFunc );
+			while( !this._.stopFlag )
+				retval = this.back();
+			this.removeListener( 'sibling', guardFunc );
+			this.removeListener( 'up', guardFunc );
+			this.removeListener( 'down', guardFunc );
+			return retval;
+		},
+
+		stop : function()
+		{
+			this._.stopFlag = true;
+		}
+	};
+})();
