Index: /CKEditor/branches/prototype/_source/core/config.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/config.js	(revision 2411)
+++ /CKEditor/branches/prototype/_source/core/config.js	(revision 2412)
@@ -159,5 +159,5 @@
 	 * config.plugins = 'elementspath,toolbar,wysiwygarea';
 	 */
-	plugins : 'basicstyles,button,elementspath,htmldataprocessor,keystrokes,sourcearea,toolbar,wysiwygarea,dialog',
+	plugins : 'basicstyles,button,dialog,elementspath,htmldataprocessor,keystrokes,sourcearea,tab,toolbar,wysiwygarea',
 
 	/**
Index: /CKEditor/branches/prototype/_source/core/dom/element.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/element.js	(revision 2411)
+++ /CKEditor/branches/prototype/_source/core/dom/element.js	(revision 2412)
@@ -254,4 +254,72 @@
 
 		/**
+		 * Gets the value of an element attribute.
+		 * @param {String} name The attribute name.
+		 * @returns {String} The attribute value or null if not defined.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input type="text" /&gt;' );
+		 * alert( <b>element.getAttribute( 'type' )</b> );  // "text"
+		 */
+		getAttribute : (function()
+		{
+			var standard = function( name )
+			{
+				return this.$.getAttribute( name, 2 );
+			};
+
+			if ( CKEDITOR.env.ie )
+			{
+				return function( name )
+				{
+					switch ( name )
+					{
+						case 'class':
+							name = 'className';
+							break;
+
+						case 'tabindex':
+							var tabIndex = standard.call( this, name );
+
+							// IE returns tabIndex=0 by default for all
+							// elements. For those elements,
+							// getAtrribute( 'tabindex', 2 ) returns 32768
+							// instead. So, we must make this check to give a
+							// uniform result among all browsers.
+							if ( tabIndex !== 0 && this.$.tabIndex === 0 )
+								tabIndex = null;
+
+							return tabIndex;
+							break;
+					}
+
+					return standard.call( this, name );
+				};
+			}
+			else
+				return standard;
+		})(),
+
+		/**
+		 * Gets the current computed value of one of the element CSS style
+		 * properties.
+		 * @param {String} propertyName The style property name.
+		 * @returns {String} The property value.
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'span' );
+		 * alert( <b>element.getComputedStyle( 'display' )</b> );  // "inline"
+		 */
+		getComputedStyle :
+			CKEDITOR.env.ie ?
+				function( propertyName )
+				{
+					return this.$.currentStyle[ propertyName ];
+				}
+			:
+				function( propertyName )
+				{
+					return this.getWindow().$.getComputedStyle( this.$, '' ).getPropertyValue( propertyName );
+				},
+
+		/**
 		 * Gets the document containing this element.
 		 * @returns {CKEDITOR.dom.document} The document.
@@ -271,4 +339,53 @@
 				})();
 		},
+
+		/**
+		 * Gets the computed tabindex for this element.
+		 * @returns {Number} The tabindex value.
+		 * @example
+		 * var element = CKEDITOR.document.getById( 'myDiv' );
+		 * alert( <b>element.getTabIndex()</b> );  // e.g. "-1"
+		 */
+		getTabIndex :
+			CKEDITOR.env.ie ?
+				function()
+				{
+					var tabIndex = this.$.tabIndex;
+
+					// IE returns tabIndex=0 by default for all elements. In
+					// those cases we must check that the element really has
+					// the tabindex attribute set to zero, or it is one of
+					// those element that should have zero by default.
+					if ( tabIndex === 0 && !CKEDITOR.dtd.$tabIndex[ this.getName() ] && parseInt( this.getAttribute( 'tabindex' ), 10 ) !== 0 )
+						tabIndex = -1;
+
+						return tabIndex;
+				}
+			: CKEDITOR.env.webkit ?
+				function()
+				{
+					var tabIndex = this.$.tabIndex;
+
+					// Safari returns "undefined" for elements that should not
+					// have tabindex (like a div). So, we must try to get it
+					// from the attribute.
+					// https://bugs.webkit.org/show_bug.cgi?id=20596
+					if ( tabIndex == undefined )
+					{
+						tabIndex = parseInt( this.getAttribute( 'tabindex' ), 10 );
+
+						// If the element don't have the tabindex attribute,
+						// then we should return -1.
+						if ( isNaN( tabIndex ) )
+							tabIndex = -1;
+					}
+
+					return tabIndex;
+				}
+			:
+				function()
+				{
+					return this.$.tabIndex;
+				},
 
 		/**
Index: /CKEditor/branches/prototype/_source/core/dtd.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dtd.js	(revision 2411)
+++ /CKEditor/branches/prototype/_source/core/dtd.js	(revision 2412)
@@ -101,4 +101,11 @@
 		 */
 		$removeEmpty : {abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,dfn:1,em:1,font:1,i:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1},
+
+		/**
+		 * List of elements that have tabindex set to zero by default.
+		 * @type Object
+		 * @example
+		 */
+		$tabIndex : {a:1,area:1,button:1,input:1,object:1,select:1,textarea:1},
 
 		/**
Index: /CKEditor/branches/prototype/_source/plugins/keystrokes/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/keystrokes/plugin.js	(revision 2411)
+++ /CKEditor/branches/prototype/_source/plugins/keystrokes/plugin.js	(revision 2412)
@@ -86,5 +86,5 @@
 		editor : editor
 	};
-	
+
 	return this;
 };
Index: /CKEditor/branches/prototype/_source/plugins/tab/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/tab/plugin.js	(revision 2412)
+++ /CKEditor/branches/prototype/_source/plugins/tab/plugin.js	(revision 2412)
@@ -0,0 +1,296 @@
+﻿/*
+ * 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 ==
+ */
+
+(function()
+{
+	var blurInternal = function( editor, previous )
+	{
+		var hasContainer = editor.container;
+
+		if ( hasContainer )
+		{
+			// We need an empty element after the container, so the focus don't go to a container child.
+			var tempSpan = new CKEDITOR.dom.element( 'span' );
+			tempSpan.setAttribute( 'tabindex', editor.container.getTabIndex() );
+			tempSpan.hide();
+
+			// Insert the temp element and set the focus.
+			if ( previous )
+			{
+				tempSpan.insertBefore( editor.container );
+				tempSpan.focusPrevious();
+			}
+			else
+			{
+				tempSpan.insertAfter( editor.container );
+				tempSpan.focusNext();
+			}
+
+			// Remove the temporary node.
+			tempSpan.remove();
+		}
+
+		return hasContainer;
+	};
+
+	var blurCommand =
+		{
+			exec : function( editor )
+			{
+				return blurInternal( editor );
+			}
+		};
+
+	var blurBackCommand =
+		{
+			exec : function( editor )
+			{
+				return blurInternal( editor, true );
+			}
+		};
+
+	CKEDITOR.plugins.add( 'tab',
+	{
+		requires : [ 'keystrokes' ],
+
+		init : function( editor, pluginPath )
+		{
+			// Register the keystrokes.
+			var keystrokes = editor.keystrokeHandler.keystrokes;
+			keystrokes[ 9 /* TAB */ ] = 'tab';
+			keystrokes[ CKEDITOR.SHIFT + 9 /* TAB */ ] = 'shiftTab';
+
+			var tabSpaces = editor.config.tabSpaces,
+				tabText = '';
+
+			while ( tabSpaces-- )
+				tabText += '\xa0';
+
+			// Register the "tab" and "shiftTab" commands.
+			editor.addCommand( 'tab',
+				{
+					exec : function( editor )
+					{
+						// Fire the "tab" event, making it possible to
+						// customize the TAB key behavior on specific cases.
+						if ( !editor.fire( 'tab' ) )
+						{
+							if ( tabText.length > 0 )
+							{
+								// TODO
+								/*jsl:pass*/
+							}
+							else
+							{
+								// All browsers jump to the next field on TAB,
+								// except Safari, so we have to do that manually
+								// here.
+								/// https://bugs.webkit.org/show_bug.cgi?id=20597
+								return editor.execCommand( 'blur' );
+							}
+						}
+
+						return true;
+					}
+				});
+
+			editor.addCommand( 'shiftTab',
+				{
+					exec : function( editor )
+					{
+						// Fire the "tab" event, making it possible to
+						// customize the TAB key behavior on specific cases.
+						if ( !editor.fire( 'shiftTab' ) )
+							return editor.execCommand( 'blurBack' );
+
+						return true;
+					}
+				});
+
+			editor.addCommand( 'blur', blurCommand );
+			editor.addCommand( 'blurBack', blurBackCommand );
+		}
+	});
+})();
+
+/**
+ * Moves the UI focus to the element following this element in the tabindex
+ * order.
+ * @example
+ * var element = CKEDITOR.document.getById( 'example' );
+ * element.focusNext();
+ */
+CKEDITOR.dom.element.prototype.focusNext = function()
+{
+	var $ = this.$,
+		curTabIndex = this.getTabIndex(),
+		passedCurrent = false,
+		elected,
+		electedTabIndex;
+
+	var all = document.body.all || document.body.getElementsByTagName( '*' );
+
+	if ( curTabIndex <= 0 )
+	{
+		for ( var i = 0, element ; element = all[ i ] ; i++ )
+		{
+			if ( !passedCurrent )
+			{
+				if ( element == $ )
+					passedCurrent = true;
+				continue;
+			}
+
+			element = new CKEDITOR.dom.element( element );
+
+			if ( element.getTabIndex() === 0 )
+			{
+				elected = element;
+				break;
+			}
+		}
+	}
+	else
+	{
+		for ( i = 0, element ; element = all[ i ] ; i++ )
+		{
+			if ( !passedCurrent && element == $ )
+			{
+				passedCurrent = true;
+				continue;
+			}
+
+			element = new CKEDITOR.dom.element( element );
+
+			if ( element.getComputedStyle( 'display' ) == 'none' || element.getComputedStyle( 'visibility' ) == 'hidden' )
+				continue;
+
+			var elementTabIndex = element.getTabIndex();
+
+			if ( passedCurrent && elementTabIndex == curTabIndex )
+			{
+				elected = element;
+				break;
+			}
+			else if ( elementTabIndex > curTabIndex && ( !elected || electedTabIndex > elementTabIndex || electedTabIndex === 0 ) )
+			{
+				elected = element;
+				electedTabIndex = elementTabIndex;
+			}
+			else if ( !elected && elementTabIndex === 0 )
+			{
+				elected = element;
+				electedTabIndex = elementTabIndex;
+			}
+		}
+	}
+
+	if ( elected )
+		elected.focus();
+};
+
+/**
+ * Moves the UI focus to the element before this element in the tabindex order.
+ * @example
+ * var element = CKEDITOR.document.getById( 'example' );
+ * element.focusPrevious();
+ */
+CKEDITOR.dom.element.prototype.focusPrevious = function()
+{
+	var $ = this.$,
+		curTabIndex = this.getTabIndex(),
+		passedCurrent = false,
+		elected,
+		electedTabIndex;
+
+	var all = document.body.all || document.body.getElementsByTagName( '*' );
+
+	if ( curTabIndex <= 0 )
+	{
+		for ( var i = 0, element ; element = all[ i ] ; i++ )
+		{
+			if ( !passedCurrent && element == $ )
+			{
+				if ( elected && electedTabIndex === 0 )
+					break;
+
+				passedCurrent = true;
+				continue;
+			}
+
+			element = new CKEDITOR.dom.element( element );
+
+			if ( element.getComputedStyle( 'display' ) == 'none' || element.getComputedStyle( 'visibility' ) == 'hidden' )
+				continue;
+
+			var elementTabIndex = element.getTabIndex();
+
+			if ( ( !passedCurrent && elementTabIndex === 0 )
+				|| ( elementTabIndex > 0 && ( !elected || ( electedTabIndex > 0 && electedTabIndex <= elementTabIndex ) ) ) )
+			{
+				elected = element;
+				electedTabIndex = elementTabIndex;
+			}
+		}
+	}
+	else
+	{
+		for ( i = 0, element ; element = all[ i ] ; i++ )
+		{
+			if ( !passedCurrent && element == $ )
+			{
+				if ( elected && electedTabIndex == curTabIndex )
+					break;
+
+				passedCurrent = true;
+				continue;
+			}
+
+			element = new CKEDITOR.dom.element( element );
+
+			elementTabIndex = element.getTabIndex();
+
+			if ( elementTabIndex > 0 )
+			{
+				if ( ( !passedCurrent && elementTabIndex == curTabIndex )
+					|| ( elementTabIndex < curTabIndex && ( !elected || electedTabIndex <= elementTabIndex ) ) )
+				{
+					elected = element;
+					electedTabIndex = elementTabIndex;
+				}
+			}
+		}
+	}
+
+	if ( elected )
+		elected.focus();
+};
+
+/**
+ * Intructs the editor to add a number of spaces (&amp;nbsp;) to the text when
+ * hitting the TAB key. If set to zero, the TAB key will have its default
+ * behavior instead (like moving out of the editor).
+ * @type {Number}
+ * @default 0
+ * @example
+ * config.tabSpaces = 4;
+ */
+CKEDITOR.config.tabSpaces = 0 ;
Index: /CKEditor/branches/prototype/_source/tests/core/dom/element.html
===================================================================
--- /CKEditor/branches/prototype/_source/tests/core/dom/element.html	(revision 2411)
+++ /CKEditor/branches/prototype/_source/tests/core/dom/element.html	(revision 2412)
@@ -367,4 +367,64 @@
 
 			assert.areSame( '', document.getElementById( 'removeOuter' ).innerHTML );
+		},
+
+		test_getAttribute_tabindex1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndex10' ) );
+			assert.areEqual( 10, element.getAttribute( 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexDef' ) );
+			assert.isNull( element.getAttribute( 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInputDef' ) );
+			assert.isNull( element.getAttribute( 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex4 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInput20' ) );
+			assert.areEqual( 20, element.getAttribute( 'tabindex' ) );
+		},
+
+		test_getAttribute_tabindex5 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexScriptDef' ) );
+			assert.areEqual( null, element.getAttribute( 'tabindex' ) );
+		},
+
+		test_getTabIndex1 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndex10' ) );
+			assert.areSame( 10, element.getTabIndex() );
+		},
+
+		test_getTabIndex2 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexDef' ) );
+			assert.areSame( -1, element.getTabIndex() );
+		},
+
+		test_getTabIndex3 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInputDef' ) );
+			assert.areSame( 0, element.getTabIndex() );
+		},
+
+		test_getTabIndex4 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexInput20' ) );
+			assert.areSame( 20, element.getTabIndex() );
+		},
+
+		test_getTabIndex5 : function()
+		{
+			var element = new CKEDITOR.dom.element( document.getElementById( 'tabIndexScriptDef' ) );
+			assert.areSame( -1, element.getTabIndex() );
 		},
 
@@ -386,4 +446,9 @@
 	<div id="removeClass"></div>
 	<div id="removeOuter"><b id="removeInner"></b></div>
+	<div id="tabIndex10" tabindex="10"></div>
+	<div id="tabIndexDef"></div>
+	<input id="tabIndexInputDef" />
+	<input id="tabIndexInput20" tabindex="20" />
+	<script id="tabIndexScriptDef" type="text/javascript"></script>
 </body>
 </html>
Index: /CKEditor/branches/prototype/_source/themes/default/theme.js
===================================================================
--- /CKEditor/branches/prototype/_source/themes/default/theme.js	(revision 2411)
+++ /CKEditor/branches/prototype/_source/themes/default/theme.js	(revision 2412)
@@ -76,5 +76,13 @@
 			element.append( container );
 
-		editor._.container = container;
+		/**
+		 * The DOM element that holds the main editor interface.
+		 * @name CKEDITOR.editor.prototype.container
+		 * @type CKEDITOR.dom.element
+		 * @example
+		 * var editor = CKEDITOR.instances.editor1;
+		 * alert( <b>editor.container</b>.getName() );  "span"
+		 */
+		editor.container = container;
 
 		editor.fireOnce( 'themeLoaded' );
@@ -136,5 +144,5 @@
 	destroy : function( editor )
 	{
-		var container = editor._.container;
+		var container = editor.container;
 
 		if ( container )
Index: /CKEditor/branches/prototype/ckeditor.pack
===================================================================
--- /CKEditor/branches/prototype/ckeditor.pack	(revision 2411)
+++ /CKEditor/branches/prototype/ckeditor.pack	(revision 2412)
@@ -90,4 +90,5 @@
 					'_source/plugins/keystrokes/plugin.js',
 					'_source/plugins/sourcearea/plugin.js',
+					'_source/plugins/tab/plugin.js',
 					'_source/plugins/toolbar/plugin.js',
 					'_source/plugins/wysiwygarea/plugin.js',
