Index: /CKEditor/trunk/_source/core/_bootstrap.js
===================================================================
--- /CKEditor/trunk/_source/core/_bootstrap.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/_bootstrap.js	(revision 2948)
@@ -0,0 +1,43 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview API initialization code.
+ */
+
+// Load core plugins.
+CKEDITOR.plugins.load( CKEDITOR.config.corePlugins.split( ',' ), function()
+	{
+		CKEDITOR.status = 'loaded';
+		CKEDITOR.fire( 'loaded' );
+
+		// Process all instances created by the "basic" implementation.
+		var pending = CKEDITOR._.pending;
+		if ( pending )
+		{
+			delete CKEDITOR._.pending;
+
+			for ( var i = 0 ; i < pending.length ; i++ )
+				CKEDITOR.add( pending[ i ] );
+		}
+	});
+
+/*
+TODO: Enable the following and check if effective.
+
+if ( CKEDITOR.env.ie )
+{
+	// Remove IE mouse flickering on IE6 because of background images.
+	try
+	{
+		document.execCommand( 'BackgroundImageCache', false, true );
+	}
+	catch (e)
+	{
+		// We have been reported about loading problems caused by the above
+		// line. For safety, let's just ignore errors.
+	}
+}
+*/
Index: /CKEditor/trunk/_source/core/ajax.js
===================================================================
--- /CKEditor/trunk/_source/core/ajax.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/ajax.js	(revision 2948)
@@ -0,0 +1,143 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.ajax} object, which holds ajax methods for
+ *		data loading.
+ */
+
+/**
+ * Ajax methods for data loading.
+ * @namespace
+ * @example
+ */
+CKEDITOR.ajax = (function()
+{
+	var createXMLHttpRequest = function()
+	{
+		// In IE, using the native XMLHttpRequest for local files may throw
+		// "Access is Denied" errors.
+		if ( !CKEDITOR.env.ie || location.protocol != 'file:' )
+			try { return new XMLHttpRequest(); } catch(e) {}
+
+		try { return new ActiveXObject( 'Msxml2.XMLHTTP' ); } catch (e) {}
+		try { return new ActiveXObject( 'Microsoft.XMLHTTP' ); } catch (e) {}
+
+		return null;
+	};
+
+	var checkStatus = function( xhr )
+	{
+		// HTTP Status Codes:
+		//	 2xx : Success
+		//	 304 : Not Modified
+		//	   0 : Returned when running locally (file://)
+		//	1223 : IE may change 204 to 1223 (see http://dev.jquery.com/ticket/1450)
+
+		return ( xhr.readyState == 4 &&
+				(	( xhr.status >= 200 && xhr.status < 300 ) ||
+					xhr.status == 304 ||
+					xhr.status === 0 ||
+					xhr.status == 1223 ) );
+	};
+
+	var getResponseText = function( xhr )
+	{
+		if ( checkStatus( xhr ) )
+			return xhr.responseText;
+		return null;
+	};
+
+	var getResponseXml = function( xhr )
+	{
+		if ( checkStatus( xhr ) )
+		{
+			var xml = xhr.responseXML;
+			return new CKEDITOR.xml( xml && xml.firstChild ? xml : xhr.responseText );
+		}
+		return null;
+	};
+
+	var load = function( url, callback, getResponseFn )
+	{
+		var async = !!callback;
+
+		var xhr = createXMLHttpRequest();
+
+		if ( !xhr )
+			return null;
+
+		xhr.open( 'GET', url, async );
+
+		if ( async )
+		{
+			// TODO: perform leak checks on this closure.
+			/** @ignore */
+			xhr.onreadystatechange = function()
+			{
+				if ( xhr.readyState == 4 )
+				{
+					callback( getResponseFn( xhr ) );
+					xhr = null;
+				}
+			};
+		}
+
+		xhr.send(null);
+
+		return async ? '' : getResponseFn( xhr );
+	};
+
+	return 	/** @lends CKEDITOR.ajax */ {
+
+		/**
+		 * Loads data from an URL as plain text.
+		 * @param {String} url The URL from which load data.
+		 * @param {Function} [callback] A callback function to be called on
+		 *		data load. If not provided, the data will be loaded
+		 *		asynchronously, passing the data value the function on load.
+		 * @returns {String} The loaded data. For asynchronous requests, an
+		 *		empty string. For invalid requests, null.
+		 * @example
+		 * // Load data synchronously.
+		 * var data = CKEDITOR.ajax.load( 'somedata.txt' );
+		 * alert( data );
+		 * @example
+		 * // Load data asynchronously.
+		 * var data = CKEDITOR.ajax.load( 'somedata.txt', function( data )
+		 *     {
+		 *         alert( data );
+		 *     } );
+		 */
+		load : function( url, callback )
+		{
+			return load( url, callback, getResponseText );
+		},
+
+		/**
+		 * Loads data from an URL as XML.
+		 * @param {String} url The URL from which load data.
+		 * @param {Function} [callback] A callback function to be called on
+		 *		data load. If not provided, the data will be loaded
+		 *		asynchronously, passing the data value the function on load.
+		 * @returns {CKEDITOR.xml} An XML object holding the loaded data. For asynchronous requests, an
+		 *		empty string. For invalid requests, null.
+		 * @example
+		 * // Load XML synchronously.
+		 * var xml = CKEDITOR.ajax.loadXml( 'somedata.xml' );
+		 * alert( xml.getInnerXml( '//' ) );
+		 * @example
+		 * // Load XML asynchronously.
+		 * var data = CKEDITOR.ajax.loadXml( 'somedata.xml', function( xml )
+		 *     {
+		 *         alert( xml.getInnerXml( '//' ) );
+		 *     } );
+		 */
+		loadXml : function( url, callback )
+		{
+			return load( url, callback, getResponseXml );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/ckeditor.js
===================================================================
--- /CKEditor/trunk/_source/core/ckeditor.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/ckeditor.js	(revision 2948)
@@ -0,0 +1,96 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Contains the third and last part of the {@link CKEDITOR} object
+ *		definition.
+ */
+
+// Remove the CKEDITOR.loadFullCore reference defined on ckeditor_basic.
+delete CKEDITOR.loadFullCore;
+
+/**
+ * Holds references to all editor instances created. The name of the properties
+ * in this object correspond to instance names, and their values contains the
+ * {@link CKEDITOR.editor} object representing them.
+ * @type {Object}
+ * @example
+ * alert( <b>CKEDITOR.instances</b>.editor1.name );  // "editor1"
+ */
+CKEDITOR.instances = {};
+
+/**
+ * The document of the window holding the CKEDITOR object.
+ * @type {CKEDITOR.dom.document}
+ * @example
+ * alert( <b>CKEDITOR.document</b>.getBody().getName() );  // "body"
+ */
+CKEDITOR.document = new CKEDITOR.dom.document( document );
+
+/**
+ * Adds an editor instance to the global {@link CKEDITOR} object. This function
+ * is available for internal use mainly.
+ * @param {CKEDITOR.editor} editor The editor instance to be added.
+ * @example
+ */
+CKEDITOR.add = function( editor )
+{
+	CKEDITOR.instances[ editor.name ] = editor;
+
+	editor.on( 'focus', function()
+		{
+			if ( CKEDITOR.currentInstance != editor )
+			{
+				CKEDITOR.currentInstance = editor;
+				CKEDITOR.fire( 'currentInstance' );
+			}
+		});
+
+	editor.on( 'blur', function()
+		{
+			if ( CKEDITOR.currentInstance == editor )
+			{
+				CKEDITOR.currentInstance = null;
+				CKEDITOR.fire( 'currentInstance' );
+			}
+		});
+};
+
+/**
+ * Removes and editor instance from the global {@link CKEDITOR} object. his function
+ * is available for internal use mainly.
+ * @param {CKEDITOR.editor} editor The editor instance to be added.
+ * @example
+ */
+CKEDITOR.remove = function( editor )
+{
+	delete CKEDITOR.instances[ editor.name ];
+};
+
+// Load the bootstrap script.
+CKEDITOR.loader.load( 'core/_bootstrap' );		// %REMOVE_LINE%
+
+// Tri-state constants.
+
+/**
+ * Used to indicate the ON or ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_ON = 1;
+
+/**
+ * Used to indicate the OFF or NON ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_OFF = 2;
+
+/**
+ * Used to indicate DISABLED state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_DISABLED = 0;
Index: /CKEditor/trunk/_source/core/ckeditor_base.js
===================================================================
--- /CKEditor/trunk/_source/core/ckeditor_base.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/ckeditor_base.js	(revision 2948)
@@ -0,0 +1,146 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Contains the first and essential part of the {@link CKEDITOR}
+ *		object definition.
+ */
+
+// #### Compressed Code
+// Must be updated on changes in the script, as well as updated in the
+// ckeditor_source.js and ckeditor_basic_source.js files.
+
+// if (!window.CKEDITOR){window.CKEDITOR=(function(){return/**@lends CKEDITOR*/{_:{},status:'unloaded',timestamp:'',basePath:(function(){var A='';var B=document.getElementsByTagName('script');for (var i=0;i<B.length;i++){var C=B[i].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if (C){A=C[1];break;}};if (A.indexOf('://')==-1){if (A.indexOf('/')===0) A=location.href.match(/^.*?:\/\/[^\/]*/)[0]+A;else A=location.href.match(/^[^\?]*\//)[0]+A;};return A;})(),getUrl:function(resource){if (resource.indexOf('://')==-1&&resource.indexOf('/')!==0) resource=this.basePath+resource;if (this.timestamp) resource+=(resource.indexOf('?')>=0?'&':'?')+'t='+this.timestamp;return resource;}};})();};
+
+// #### Raw code
+// ATTENTION: read the above "Compressed Code" notes when changing this code.
+
+if ( !window.CKEDITOR )
+{
+	/**
+	 * This is the API entry point. The entire CKEditor code runs under this object.
+	 * @name CKEDITOR
+	 * @namespace
+	 * @example
+	 */
+	window.CKEDITOR = (function()
+	{
+		return /** @lends CKEDITOR */ {
+
+			/**
+			 * A constant string unique for each release of CKEditor. Its value
+			 * is used, by default, to build the URL for all resources loaded
+			 * by the editor code, guaranteing clean cache results when
+			 * upgrading.
+			 * @type String
+			 * @example
+			 * alert( CKEDITOR.timestamp );  // e.g. '87dm'
+			 */
+			timestamp : '',				// %REMOVE_LINE%
+			/*							// %REMOVE_LINE%
+			// The production implementation contains a fixed timestamp, unique
+			// for each release, generated by the releaser.
+			// (Base 36 value of each component of YYMMDDHH - 4 chars total - e.g. 87bm == 08071122)
+			timestamp : '%TIMESTAMP%',
+			 */							// %REMOVE_LINE%
+
+			/**
+			 * Private object used to hold core stuff. It should not be used out of
+			 * the API code as properties defined here may change at any time
+			 * without notice.
+			 * @private
+			 */
+			_ : {},
+
+			/**
+			 * Indicates the API loading status. The following status are available:
+			 *		<ul>
+			 *			<li><b>unloaded</b>: the API is not yet loaded.</li>
+			 *			<li><b>basic_loaded</b>: the basic API features are available.</li>
+			 *			<li><b>basic_ready</b>: the basic API is ready to load the full core code.</li>
+			 *			<li><b>loading</b>: the full API is being loaded.</li>
+			 *			<li><b>ready</b>: the API can be fully used.</li>
+			 *		</ul>
+			 * @type String
+			 * @example
+			 * if ( <b>CKEDITOR.status</b> == 'ready' )
+			 * {
+			 *     // The API can now be fully used.
+			 * }
+			 */
+			status : 'unloaded',
+
+			/**
+			 * Contains the full URL for the CKEditor installation directory.
+			 * @type String
+			 * @example
+			 * alert( <b>CKEDITOR.basePath</b> );  // "http://www.example.com/ckeditor/" (e.g.)
+			 */
+			basePath : (function()
+			{
+				// ATTENTION: fixes on this code must be ported to
+				// var basePath in "core/loader.js".
+
+				// Find out the editor directory path, based on its <script> tag.
+				var path = '';
+				var scripts = document.getElementsByTagName( 'script' );
+
+				for ( var i = 0 ; i < scripts.length ; i++ )
+				{
+					var match = scripts[i].src.match( /(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i );
+
+					if ( match )
+					{
+						path = match[1];
+						break;
+					}
+				}
+
+				// In IE (only) the script.src string is the raw valued entered in the
+				// HTML. Other browsers return the full resolved URL instead.
+				if ( path.indexOf('://') == -1 )
+				{
+					// Absolute path.
+					if ( path.indexOf( '/' ) === 0 )
+						path = location.href.match( /^.*?:\/\/[^\/]*/ )[0] + path;
+					// Relative path.
+					else
+						path = location.href.match( /^[^\?]*\/(?:)/ )[0] + path;
+				}
+
+				return path;
+			})(),
+
+			/**
+			 * Gets the full URL for CKEditor resources. By default, URLs
+			 * returned by this function contains a querystring parameter ("t")
+			 * set to the {@link CKEDITOR.timestamp} value.
+			 * @returns {String} The full URL.
+			 * @example
+			 * // e.g. http://www.example.com/ckeditor/skins/default/editor.css?t=87dm
+			 * alert( CKEDITOR.getUrl( 'skins/default/editor.css' ) );
+			 * @example
+			 * // e.g. http://www.example.com/skins/default/editor.css?t=87dm
+			 * alert( CKEDITOR.getUrl( '/skins/default/editor.css' ) );
+			 * @example
+			 * // e.g. http://www.somesite.com/skins/default/editor.css?t=87dm
+			 * alert( CKEDITOR.getUrl( 'http://www.somesite.com/skins/default/editor.css' ) );
+			 */
+			getUrl : function( resource )
+			{
+				// If this is not a full or absolute path.
+				if ( resource.indexOf('://') == -1 && resource.indexOf( '/' ) !== 0 )
+					resource = this.basePath + resource;
+
+				if ( this.timestamp )
+					resource += ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) + 't=' + this.timestamp;
+
+				return resource;
+			}
+		};
+	})();
+}
+
+// PACKAGER_RENAME( CKEDITOR )
Index: /CKEditor/trunk/_source/core/ckeditor_basic.js
===================================================================
--- /CKEditor/trunk/_source/core/ckeditor_basic.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/ckeditor_basic.js	(revision 2948)
@@ -0,0 +1,242 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Contains the second part of the {@link CKEDITOR} object
+ *		definition, which defines the basic editor features to be available in
+ *		the root ckeditor_basic.js file.
+ */
+
+if ( CKEDITOR.status == 'unloaded' )
+{
+	(function()
+	{
+		CKEDITOR.event.implementOn( CKEDITOR );
+
+		/**
+		 * Forces the full CKEditor core code, in the case only the basic code has been
+		 * loaded (ckeditor_basic.js). This method self-destroys (becomes undefined) in
+		 * the first call or as soon as the full code is available.
+		 * @example
+		 * // Check if the full core code has been loaded and load it.
+		 * if ( CKEDITOR.loadFullCore )
+		 *     <b>CKEDITOR.loadFullCore()</b>;
+		 */
+		CKEDITOR.loadFullCore = function()
+		{
+			// If not the basic code is not ready it, just mark it to be loaded.
+			if ( CKEDITOR.status != 'basic_ready' )
+			{
+				CKEDITOR.loadFullCore._load = true;
+				return;
+			}
+
+			// Destroy this function.
+			delete CKEDITOR.loadFullCore;
+
+			// Append the script to the head.
+			var script = document.createElement( 'script' );
+			script.type = 'text/javascript';
+			script.src = CKEDITOR.basePath + 'ckeditor.js';
+			script.src = CKEDITOR.basePath + 'ckeditor_source.js';		// %REMOVE_LINE%
+
+			document.getElementsByTagName( 'head' )[0].appendChild( script );
+		};
+
+		/**
+		 * The time to wait (in seconds) to load the full editor code after the
+		 * page load, if the "ckeditor_basic" file is used. If set to zero, the
+		 * editor is loaded on demand, as soon as an instance is created.
+		 *
+		 * This value must be set on the page before the page load completion.
+		 * @type Number
+		 * @default 0 (zero)
+		 * @example
+		 * // Loads the full source after five seconds.
+		 * CKEDITOR.loadFullCoreTimeout = 5;
+		 */
+		CKEDITOR.loadFullCoreTimeout = 0;
+
+		/**
+		 * The class name used to identify &lt;textarea&gt; elements to be replace
+		 * by CKEditor instances.
+		 * @type String
+		 * @default 'ckeditor'
+		 * @example
+		 * <b>CKEDITOR.replaceClass</b> = 'rich_editor';
+		 */
+		CKEDITOR.replaceClass = 'ckeditor';
+
+		/**
+		 * Enables the replacement of all textareas with class name matching
+		 * {@link CKEDITOR.replaceClass}.
+		 * @type Boolean
+		 * @default true
+		 * @example
+		 * // Disable the auto-replace feature.
+		 * <b>CKEDITOR.replaceByClassEnabled</b> = false;
+		 */
+		CKEDITOR.replaceByClassEnabled = true;
+
+		var createInstance = function( elementOrIdOrName, config, creationFunction )
+		{
+			if ( CKEDITOR.env.isCompatible )
+			{
+				// Load the full core.
+				if ( CKEDITOR.loadFullCore )
+					CKEDITOR.loadFullCore();
+
+				var editor = creationFunction( elementOrIdOrName, config );
+				CKEDITOR.add( editor );
+				return editor;
+			}
+
+			return null;
+		};
+
+		/**
+		 * Replaces a &lt;textarea&gt; or a DOM element (DIV) with a CKEditor
+		 * instance. For textareas, the initial value in the editor will be the
+		 * textarea value. For DOM elements, their innerHTML will be used
+		 * instead. We recommend using TEXTAREA and DIV elements only.
+		 * @param {Object|String} elementOrIdOrName The DOM element (textarea), its
+		 *		ID or name.
+		 * @param {Object} [config] The specific configurations to apply to this
+		 *		editor instance. Configurations set here will override global CKEditor
+		 *		settings.
+		 * @returns {CKEDITOR.editor} The editor instance created.
+		 * @example
+		 * &lt;textarea id="myfield" name="myfield"&gt;&lt:/textarea&gt;
+		 * ...
+		 * <b>CKEDITOR.replace( 'myfield' )</b>;
+		 * @example
+		 * var textarea = document.body.appendChild( document.createElement( 'textarea' ) );
+		 * <b>CKEDITOR.replace( textarea )</b>;
+		 */
+		CKEDITOR.replace = function( elementOrIdOrName, config )
+		{
+			return createInstance( elementOrIdOrName, config, CKEDITOR.editor.replace );
+		};
+
+		/**
+		 * Creates a new editor instance inside a specific DOM element.
+		 * @param {Object|String} elementOrId The DOM element or its ID.
+		 * @param {Object} [config] The specific configurations to apply to this
+		 *		editor instance. Configurations set here will override global CKEditor
+		 *		settings.
+		 * @returns {CKEDITOR.editor} The editor instance created.
+		 * @example
+		 * &lt;div id="editorSpace"&gt;&lt:/div&gt;
+		 * ...
+		 * <b>CKEDITOR.appendTo( 'editorSpace' )</b>;
+		 */
+		CKEDITOR.appendTo = function( elementOrId, config )
+		{
+			return createInstance( elementOrId, config, CKEDITOR.editor.appendTo );
+		};
+
+		/**
+		 * @ignore
+		 * Documented at ckeditor.js.
+		 */
+		CKEDITOR.add = function( editor )
+		{
+			// For now, just put the editor in the pending list. It will be
+			// processed as soon as the full code gets loaded.
+			var pending = this._.pending || ( this._.pending = [] );
+			pending.push( editor );
+		};
+
+		/**
+		 * Replace all &lt;textarea&gt; elements available in the document with
+		 * editor instances.
+		 * @example
+		 * // Replace all &lt;textarea&gt; elements in the page.
+		 * CKEDITOR.replaceAll();
+		 * @example
+		 * // Replace all &lt;textarea class="myClassName"&gt; elements in the page.
+		 * CKEDITOR.replaceAll( 'myClassName' );
+		 * @example
+		 * // Selectively replace &lt;textarea&gt; elements, based on custom assertions.
+		 * CKEDITOR.replaceAll( function( textarea, config )
+		 *     {
+		 *         // Custom code to evaluate the replace, returning false
+		 *         // if it must not be done.
+		 *         // It also passes the "config" parameter, so the
+		 *         // developer can customize the instance.
+		 *     } );
+		 */
+		CKEDITOR.replaceAll = function()
+		{
+			var textareas = document.getElementsByTagName( 'textarea' );
+
+			for ( var i = 0 ; i < textareas.length ; i++ )
+			{
+				var config = null;
+				var textarea = textareas[i];
+				var name = textarea.name;
+
+				// The "name" and/or "id" attribute must exist.
+				if ( !textarea.name && !textarea.id )
+					continue;
+
+				if ( typeof arguments[0] == 'string' )
+				{
+					// The textarea class name could be passed as the function
+					// parameter.
+
+					var classRegex = new RegExp( '(?:^| )' + arguments[0] + '(?:$| )' );
+
+					if ( !classRegex.test( textarea.className ) )
+						continue;
+				}
+				else if ( typeof arguments[0] == 'function' )
+				{
+					// An assertion function could be passed as the function parameter.
+					// It must explicitly return "false" to ignore a specific <textarea>.
+					config = {};
+					if ( arguments[0]( textarea, config ) === false )
+						continue;
+				}
+
+				this.replace( textarea, config );
+			}
+		};
+
+		(function()
+		{
+			var onload = function()
+			{
+				var loadFullCore = CKEDITOR.loadFullCore,
+					loadFullCoreTimeout = CKEDITOR.loadFullCoreTimeout;
+
+				// Replace all textareas with the default class name.
+				if ( CKEDITOR.replaceByClassEnabled )
+					CKEDITOR.replaceAll( CKEDITOR.replaceClass );
+
+				CKEDITOR.status = 'basic_ready';
+
+				if ( loadFullCore && loadFullCore._load )
+					loadFullCore();
+				else if ( loadFullCoreTimeout )
+				{
+					setTimeout( function()
+						{
+							if ( CKEDITOR.loadFullCore )
+								CKEDITOR.loadFullCore();
+						}
+						, loadFullCoreTimeout * 1000 );
+				}
+			};
+
+			if ( window.addEventListener )
+				window.addEventListener( 'load', onload, false );
+			else if ( window.attachEvent )
+				window.attachEvent( 'onload', onload );
+		})();
+
+		CKEDITOR.status = 'basic_loaded';
+	})();
+}
Index: /CKEditor/trunk/_source/core/command.js
===================================================================
--- /CKEditor/trunk/_source/core/command.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/command.js	(revision 2948)
@@ -0,0 +1,21 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.command = function( editor, commandDefinition )
+{
+	this.state = CKEDITOR.TRISTATE_OFF;
+
+	this.exec = function()
+	{
+		commandDefinition.exec.call( this, editor );
+	};
+
+	CKEDITOR.tools.extend( this, commandDefinition );
+
+	// Call the CKEDITOR.event constructor to initialize this instance.
+	CKEDITOR.event.call( this );
+};
+
+CKEDITOR.event.implementOn( CKEDITOR.command.prototype );
Index: /CKEditor/trunk/_source/core/commanddefinition.js
===================================================================
--- /CKEditor/trunk/_source/core/commanddefinition.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/commanddefinition.js	(revision 2948)
@@ -0,0 +1,35 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the "virtual" {@link CKEDITOR.commandDefinition} class,
+ *		which contains the defintion of a command. This file is for
+ *		documentation purposes only.
+ */
+
+/**
+ * (Virtual Class) Do not call this constructor. This class is not really part
+ *		of the API. It just illustrates the features of command objects to be
+ *		passed to the {@link CKEDITOR.editor.prototype.addCommand} function.
+ * @name CKEDITOR.commandDefinition
+ * @constructor
+ * @example
+ */
+
+ /**
+ * Executes the command.
+ * @name CKEDITOR.commandDefinition.prototype.exec
+ * @function
+ * @param {CKEDITOR.editor} editor The editor within which run the command.
+ * @param {Object} [data] Additional data to be used to execute the command.
+ * @example
+ * editorInstance.addCommand( 'sample',
+ * {
+ *     exec : function( editor )
+ *     {
+ *         alert( 'Executing a command for the editor name "' + editor.name + '"!' );
+ *     }
+ * });
+ */
Index: /CKEditor/trunk/_source/core/config.js
===================================================================
--- /CKEditor/trunk/_source/core/config.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/config.js	(revision 2948)
@@ -0,0 +1,188 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.config} object, which holds the
+ * default configuration settings.
+ */
+
+/**
+ * Holds the default configuration settings. Changes to this object are
+ * reflected in all editor instances, if not specificaly specified for those
+ * instances.
+ * @namespace
+ * @example
+ * // All editor created after the following setting will not load custom
+ * // configuration files.
+ * CKEDITOR.config.customConfig = '';
+ */
+CKEDITOR.config =
+{
+	/**
+	 * The URL path for the custom configuration file to be loaded. If not
+	 * overloaded with inline configurations, it defaults to the "config.js"
+	 * file present in the root of the CKEditor installation directory.<br /><br />
+	 *
+	 * CKEditor will recursively load custom configuration files defined inside
+	 * other custom configuration files.
+	 * @type String
+	 * @default '&lt;CKEditor folder&gt;/config.js'
+	 * @example
+	 * // Load a specific configuration file.
+	 * CKEDITOR.replace( 'myfiled', { customConfig : '/myconfig.js' } );
+	 * @example
+	 * // Do not load any custom configuration file.
+	 * CKEDITOR.replace( 'myfiled', { customConfig : '' } );
+	 */
+	customConfig : CKEDITOR.getUrl( 'config.js' ),
+
+	/**
+	 * The base href URL used to resolve relative and absolute URLs in the
+	 * editor content.
+	 * @type String
+	 * @default '' (empty string)
+	 * @example
+	 * config.baseHref = 'http://www.example.com/path/';
+	 */
+	baseHref : '',
+
+	/**
+	 * The CSS file to be used to apply style to the contents. It should
+	 * reflect the CSS used in the final pages where the contents are to be
+	 * used.
+	 * @type String
+	 * @default '&lt;CKEditor folder&gt;/contents.css'
+	 * @example
+	 * config.contentsCss = '/css/mysitestyles.css';
+	 */
+	contentsCss : CKEDITOR.basePath + 'contents.css',
+
+	/**
+	 * The writting direction of the language used to write the editor
+	 * contents. Allowed values are 'ltr' for Left-To-Right language (like
+	 * English), or 'rtl' for Right-To-Left languages (like Arabic).
+	 * @default 'ltr'
+	 * @type String
+	 * @example
+	 * config.contentsLangDirection = 'rtl';
+	 */
+	contentsLangDirection : 'ltr',
+
+	/**
+	 * Instructs the editor to automatically localize the editor to the user
+	 * language, if possible. If set to false, the [@link #defaultLanguage]
+	 * language is used.
+	 * @default true
+	 * @type Boolean
+	 * @example
+	 * // Forces the editor to always load the German interface.
+	 * config.autoLanguage = false;
+	 * config.defaultLanguage = 'de';
+	 */
+	autoLanguage : true,
+
+	/**
+	 * The language to be used if [@link #autoLanguage] is set to false, or
+	 * when it's not possible to localize the editor to the user language.
+	 * @default 'en'
+	 * @type String
+	 * @example
+	 * config.defaultLanguage = 'it';
+	 */
+	defaultLanguage : 'en',
+
+	enterMode : 'p',
+	shiftEnterMode : 'br',
+
+	/**
+	 * A comma separated list of plugins that are not related to editor
+	 * instances. Reserved to plugins that extend the core code only.<br /><br />
+	 *
+	 * There are no ways to override this setting, except by editing the source
+	 * code of CKEditor (_source/core/config.js).
+	 * @type String
+	 * @example
+	 */
+	corePlugins : '',
+
+	/**
+	 * Sets the doctype to be used when loading the editor content as HTML.
+	 * @type String
+	 * @default '&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;'
+	 * @example
+	 * // Set the doctype to the HTML 4 (quirks) mode.
+	 * config.docType = '&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;';
+	 */
+	docType : '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
+
+	/**
+	 * Indicates whether the contents to be edited are being inputted as a full
+	 * HTML page. A full page includes the &lt;html&gt;, &lt;head&gt; and
+	 * &lt;body&gt; tags. The final output will also reflect this setting,
+	 * including the &lt;body&gt; contents only if this setting is disabled.
+	 * @type Boolean
+	 * @default false
+	 * @example
+	 * config.fullPage = true;
+	 */
+	fullPage : false,
+
+	/**
+	 * The editor height, in CSS size format or pixel integer.
+	 * @type String|Number
+	 * @default '200'
+	 * @example
+	 */
+	height : 200,
+
+	/**
+	 * Comma separated list of plugins to load and initialize for an editor
+	 * instance.
+	 * @type String
+	 * @default 'editingblock,elementspath,sourcearea,toolbar,wysiwygarea'
+	 * @example
+	 * 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,flash,print,pagebreak,newpage',
+
+	/**
+	 * The theme to be used to build the UI.
+	 * @type String
+	 * @default 'default'
+	 * @see CKEDITOR.config.skin
+	 * @example
+	 * config.theme = 'default';
+	 */
+	theme : 'default',
+
+	/**
+	 * The skin to load.
+	 * @type String
+	 * @default 'default'
+	 * @example
+	 * config.skin = 'v2';
+	 */
+	skin : 'default',
+
+	/**
+	 * The editor width in CSS size format or pixel integer.
+	 * @type String|Number
+	 * @default '100%'
+	 * @example
+	 */
+	width : '100%',
+
+	/**
+	 * The base Z-index for floating dialogs and popups.
+	 * @type Number
+	 * @default 10000
+	 * @example
+	 * config.baseFloatZIndex = 2000
+	 */
+	baseFloatZIndex : 10000
+
+};
+
+// PACKAGER_RENAME( CKEDITOR.config )
Index: /CKEditor/trunk/_source/core/dom.js
===================================================================
--- /CKEditor/trunk/_source/core/dom.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom.js	(revision 2948)
@@ -0,0 +1,21 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom} object, which contains DOM
+ *		manipulation objects and function.
+ */
+
+/**
+ * DOM manipulation objects and function.<br /><br />
+ * @see CKEDITOR.dom.element
+ * @see CKEDITOR.dom.node
+ * @namespace
+ * @example
+ */
+CKEDITOR.dom =
+{};
+
+// PACKAGER_RENAME( CKEDITOR.dom )
Index: /CKEditor/trunk/_source/core/dom/document.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/document.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/document.js	(revision 2948)
@@ -0,0 +1,145 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
+ *		represents a DOM document.
+ */
+
+/**
+ * Represents a DOM document.
+ * @constructor
+ * @augments CKEDITOR.dom.domObject
+ * @param {Object} domDocument A native DOM document.
+ * @example
+ * var document = new CKEDITOR.dom.document( document );
+ */
+CKEDITOR.dom.document = function( domDocument )
+{
+	CKEDITOR.dom.domObject.call( this, domDocument );
+};
+
+// PACKAGER_RENAME( CKEDITOR.dom.document )
+
+CKEDITOR.dom.document.prototype = new CKEDITOR.dom.domObject();
+
+CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype,
+	/** @lends CKEDITOR.dom.document.prototype */
+	{
+		/**
+		 * Appends a CSS file to the document.
+		 * @param {String} cssFileUrl The CSS file URL.
+		 * @example
+		 * <b>CKEDITOR.document.appendStyleSheet( '/mystyles.css' )</b>;
+		 */
+		appendStyleSheet : function( cssFileUrl )
+		{
+			if ( this.$.createStyleSheet )
+				this.$.createStyleSheet( cssFileUrl );
+			else
+			{
+				var link = new CKEDITOR.dom.element( 'link' );
+				link.setAttributes(
+					{
+						rel		:'stylesheet',
+						type	: 'text/css',
+						href	: cssFileUrl
+					});
+
+				this.getHead().append( link );
+			}
+		},
+
+		createElement : function( name, attribsAndStyles )
+		{
+			var element = new CKEDITOR.dom.element( name, this );
+
+			if ( attribsAndStyles )
+			{
+				if ( attribsAndStyles.attributes )
+					element.setAttributes( attribsAndStyles.attributes );
+
+				if ( attribsAndStyles.styles )
+					element.setStyles( attribsAndStyles.styles );
+			}
+
+			return element;
+		},
+
+		createText : function( text )
+		{
+			return new CKEDITOR.dom.text( '', this );
+		},
+
+		/**
+		 * Gets and element based on its id.
+		 * @param {String} elementId The element id.
+		 * @returns {CKEDITOR.dom.element} The element instance, or null if not found.
+		 * @example
+		 * var element = <b>CKEDITOR.document.getById( 'myElement' )</b>;
+		 * alert( element.getId() );  // "myElement"
+		 */
+		getById : function( elementId )
+		{
+			var $ = this.$.getElementById( elementId );
+			return $ ? new CKEDITOR.dom.element( $ ) : null;
+		},
+
+		/**
+		 * Gets the &lt;head&gt; element for this document.
+		 * @returns {CKEDITOR.dom.element} The &lt;head&gt; element.
+		 * @example
+		 * var element = <b>CKEDITOR.document.getHead()</b>;
+		 * alert( element.getName() );  // "head"
+		 */
+		getHead : function()
+		{
+			var head = this.$.getElementsByTagName( 'head' )[0];
+			head = new CKEDITOR.dom.element( head );
+
+			return (
+			/** @ignore */
+			this.getHead = function()
+				{
+					return head;
+				})();
+		},
+
+		/**
+		 * Gets the &lt;body&gt; element for this document.
+		 * @returns {CKEDITOR.dom.element} The &lt;body&gt; element.
+		 * @example
+		 * var element = <b>CKEDITOR.document.getBody()</b>;
+		 * alert( element.getName() );  // "body"
+		 */
+		getBody : function()
+		{
+			var body = new CKEDITOR.dom.element( this.$.body );
+
+			return (
+			/** @ignore */
+			this.getBody = function()
+				{
+					return body;
+				})();
+		},
+
+		/**
+		 * Gets the window object that holds this document.
+		 * @returns {CKEDITOR.dom.window} The window object.
+		 * @example
+		 */
+		getWindow : function()
+		{
+			var win = new CKEDITOR.dom.window( this.$.parentWindow || this.$.defaultView );
+
+			return (
+			/** @ignore */
+			this.getWindow = function()
+				{
+					return win;
+				})();
+		}
+	});
Index: /CKEditor/trunk/_source/core/dom/documentFragment.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/documentFragment.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/documentFragment.js	(revision 2948)
@@ -0,0 +1,44 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dom.documentFragment = function( ownerDocument )
+{
+	this.$ = CKEDITOR.env.ie ? ownerDocument.$.createElement( 'div' ) : ownerDocument.$.createDocumentFragment();
+};
+
+(function()
+{
+	var elementPrototype = CKEDITOR.dom.element.prototype;
+
+	CKEDITOR.dom.documentFragment.prototype =
+	{
+		type : CKEDITOR.NODE_DOCUMENT_FRAGMENT,
+
+		append : elementPrototype.append,
+
+		appendTo : function( targetElement )
+		{
+			if ( CKEDITOR.env.ie )
+				elementPrototype.moveChildren.call( this, targetElement );
+			else
+				targetElement.$.appendChild( this.$ );
+		},
+
+		insertAfterNode : function( node )
+		{
+			var $ = this.$;
+			var $node = node.$;
+			var $parent = $node.parentNode;
+
+			if ( CKEDITOR.env.ie )
+			{
+				for ( var child ; child = $.lastChild ; )
+					$parent.insertBefore( $.removeChild( child ), $node.nextSibling );
+			}
+			else
+				$parent.insertBefore( $, $node.nextSibling );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/dom/domobject.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/domobject.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/domobject.js	(revision 2948)
@@ -0,0 +1,174 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.editor} class, which is the base
+ *		for other classes representing DOM objects.
+ */
+
+/**
+ * Represents a DOM object. This class is not intended to be used directly. It
+ * serves as the base class for other classes representing specific DOM
+ * objects.
+ * @constructor
+ * @param {Object} nativeDomObject A native DOM object.
+ * @augments CKEDITOR.event
+ * @example
+ */
+CKEDITOR.dom.domObject = function( nativeDomObject )
+{
+	if ( nativeDomObject )
+	{
+		/**
+		 * The native DOM object represented by this class instance.
+		 * @type Object
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'span' );
+		 * alert( element.$.nodeType );  // "1"
+		 */
+		this.$ = nativeDomObject;
+
+		// Get the main private function from the custom data. Create it if not
+		// defined.
+		if ( !( this._ = this.getCustomData( '_' ) ) )
+			this.setCustomData( '_', ( this._ = {} ) );
+
+		// Call the base event constructor.
+		if ( !this._.events )
+			CKEDITOR.event.call( this );
+	}
+};
+
+CKEDITOR.dom.domObject.prototype = (function()
+{
+	// Do not define other local variables here. We want to keep the native
+	// listener closures as clean as possible.
+
+	var getNativeListener = function( domObject, eventName )
+	{
+		return function( domEvent )
+		{
+			domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );
+		};
+	};
+
+	return /** @lends CKEDITOR.dom.domObject.prototype */ {
+
+		/** @ignore */
+		on  : function( eventName )
+		{
+			// We customize the "on" function here. The basic idea is that we'll have
+			// only one listener for a native event, which will then call all listeners
+			// set to the event.
+
+			// Get the listeners holder object.
+			var nativeListeners = this.getCustomData( '_cke_nativeListeners' ) || this.setCustomData( '_cke_nativeListeners', {} );
+
+			// Check if we have a listener for that event.
+			if ( !nativeListeners[ eventName ] )
+			{
+				var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );
+
+				if ( this.$.addEventListener )
+					this.$.addEventListener( eventName, listener, false );
+				else if ( this.$.attachEvent )
+					this.$.attachEvent( 'on' + eventName, listener );
+			}
+
+			// Call the original implementation.
+			return CKEDITOR.event.prototype.on.apply( this, arguments );
+		},
+
+		/** @ignore */
+		removeListener : function( eventName )
+		{
+			// Call the original implementation.
+			CKEDITOR.event.prototype.removeListener.apply( this, arguments );
+
+			// If we don't have listeners for this event, clean the DOM up.
+			if ( !this.hasListeners( eventName ) )
+			{
+				var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
+				var listener = nativeListeners && nativeListeners[ eventName ];
+				if ( listener )
+				{
+					if ( this.$.removeEventListener )
+						this.$.removeEventListener( eventName, listener, false );
+					else if ( this.$.detachEvent )
+						this.$.detachEvent( eventName, listener );
+
+					delete nativeListeners[ eventName ];
+				}
+			}
+		}
+	};
+})();
+
+(function( domObjectProto )
+{
+	var customData = {};
+
+	/**
+	 * Determines whether the specified object is equal to the current object.
+	 * @name CKEDITOR.dom.domObject.prototype.equals
+	 * @function
+	 * @param {Object} object The object to compare with the current object.
+	 * @returns {Boolean} "true" if the object is equal.
+	 * @example
+	 * var doc = new CKEDITOR.dom.document( document );
+	 * alert( doc.equals( CKEDITOR.document ) );  // "true"
+	 * alert( doc == CKEDITOR.document );         // "false"
+	 */
+	domObjectProto.equals = function( object )
+	{
+		return ( object && object.$ === this.$ );
+	};
+
+	/**
+	 * Sets a data slot value for this object. These values are shared by all
+	 * instances pointing to that same DOM object.
+	 * @name CKEDITOR.dom.domObject.prototype.setCustomData
+	 * @function
+	 * @param {String} key A key used to identify the data slot.
+	 * @param {Object} value The value to set to the data slot.
+	 * @returns {CKEDITOR.dom.domObject} This DOM object instance.
+	 * @see CKEDITOR.dom.domObject.prototype.getCustomData
+	 * @example
+	 * var element = new CKEDITOR.dom.element( 'span' );
+	 * element.setCustomData( 'hasCustomData', true );
+	 */
+	domObjectProto.setCustomData = function( key, value )
+	{
+		var expandoNumber = this.$._cke_expando || ( this.$._cke_expando = CKEDITOR.tools.getNextNumber() ),
+			dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );
+
+		dataSlot[ key ] = value;
+
+		return this;
+	};
+
+	/**
+	 * Gets the value set to a data slot in this object.
+	 * @name CKEDITOR.dom.domObject.prototype.getCustomData
+	 * @function
+	 * @param {String} key The key used to identify the data slot.
+	 * @returns {Object} This value set to the data slot.
+	 * @see CKEDITOR.dom.domObject.prototype.setCustomData
+	 * @example
+	 * var element = new CKEDITOR.dom.element( 'span' );
+	 * alert( element.getCustomData( 'hasCustomData' ) );  // e.g. 'true'
+	 */
+	domObjectProto.getCustomData = function( key )
+	{
+		var expandoNumber = this.$._cke_expando,
+			dataSlot = expandoNumber && customData[ expandoNumber ];
+
+		return ( dataSlot && dataSlot[ key ] ) || null;
+	};
+
+	// Implement CKEDITOR.event.
+	CKEDITOR.event.implementOn( domObjectProto );
+
+})( CKEDITOR.dom.domObject.prototype );
Index: /CKEditor/trunk/_source/core/dom/domwalker.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/domwalker.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/domwalker.js	(revision 2948)
@@ -0,0 +1,222 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	var fireDomWalkerEvent = function( transistionType, fromNode, toNode )
+		{
+			var eventData = { from : fromNode, to : toNode, type : transistionType };
+			this.fire( transistionType, eventData );
+			this._.actionEvents.push( eventData );
+		};
+
+	CKEDITOR.dom.domWalker = function( node )
+	{
+		if ( arguments.length < 1 )
+			return;
+
+		this._ = { currentNode : node, actionEvents : [], stopFlag : false };
+		CKEDITOR.event.implementOn( this );
+	};
+
+	CKEDITOR.dom.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 to stop once the end of document is reached.
+			guardFunc = guardFunc || function( evt ) {};
+
+			this.on( 'sibling', guardFunc );
+			this.on( 'up', guardFunc );
+			this.on( 'down', guardFunc );
+			while( ( !retval || retval.node ) && !this._.stopFlag )
+			{
+				retval = this.next();
+				this.fire( 'step', retval );
+			}
+			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 ) {};
+
+			this.on( 'sibling', guardFunc );
+			this.on( 'up', guardFunc );
+			this.on( 'down', guardFunc );
+			while( ( !retval || retval.node ) && !this._.stopFlag )
+			{
+				retval = this.back();
+				this.fire( 'step', retval );
+			}
+			this.removeListener( 'sibling', guardFunc );
+			this.removeListener( 'up', guardFunc );
+			this.removeListener( 'down', guardFunc );
+			return retval;
+		},
+
+		stop : function()
+		{
+			this._.stopFlag = true;
+			return this;
+		},
+
+		stopped : function()
+		{
+			return this._.stopFlag;
+		},
+
+		setNode : function( node )
+		{
+			this._.currentNode = node;
+			return this;
+		}
+	};
+
+	CKEDITOR.dom.domWalker.blockBoundary = function( customNodeNames )
+	{
+		return function( evt )
+		{
+			/*
+			 * Anything whose display computed style is block, list-item, table,
+			 * table-row-group, table-header-group, table-footer-group, table-row,
+			 * table-column-group, table-column, table-cell, table-caption, or whose node
+			 * name is hr, br (when enterMode is br only) is a block boundary.
+			 */
+			var displayMatches = { block : 1, 'list-item' : 1, table : 1, 'table-row-group' : 1, 'table-header-group' : 1,
+				'table-footer-group' : 1, 'table-row' : 1, 'table-column-group' : 1, 'table-column' : 1, 'table-cell' : 1,
+				'table-caption' : 1 },
+				nodeNameMatches = CKEDITOR.tools.extend( { hr : 1 }, customNodeNames || {} ),
+				to = evt.data.to,
+				from = evt.data.from;
+			if ( to && to.type == CKEDITOR.NODE_ELEMENT )
+			{
+				if ( displayMatches[ to.getComputedStyle( 'display' ) ] || nodeNameMatches[ to.getName() ] )
+				{
+					evt.stop();
+					this.stop();
+					return;
+				}
+			}
+			if ( ( evt.data.type == 'up' || evt.data.type == 'sibling' ) && from && from.type == CKEDITOR.NODE_ELEMENT )
+			{
+				if ( displayMatches[ from.getComputedStyle( 'display' ) ] || nodeNameMatches[ from.getName() ] )
+				{
+					evt.stop();
+					this.stop();
+				}
+			}
+		};
+	};
+
+	CKEDITOR.dom.domWalker.listItemBoundary = function()
+	{
+		return CKEDITOR.dom.domWalker.blockBoundary( { br : 1 } );
+	};
+})();
Index: /CKEditor/trunk/_source/core/dom/element.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/element.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/element.js	(revision 2948)
@@ -0,0 +1,903 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.element} class, which
+ *		represents a DOM element.
+ */
+
+/**
+ * Represents a DOM element.
+ * @constructor
+ * @augments CKEDITOR.dom.node
+ * @param {Object|String} element A native DOM element or the element name for
+ *		new elements.
+ * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
+ *		the element in case of element creation.
+ * @example
+ * // Create a new &lt;span&gt; element.
+ * var element = new CKEDITOR.dom.element( 'span' );
+ * @example
+ * // Create an element based on a native DOM element.
+ * var element = new CKEDITOR.dom.element( document.getElementById( 'myId' ) );
+ */
+CKEDITOR.dom.element = function( element, ownerDocument )
+{
+	if ( typeof element == 'string' )
+		element = ( ownerDocument ? ownerDocument.$ : document ).createElement( element );
+
+	// Call the base constructor (we must not call CKEDITOR.dom.node).
+	CKEDITOR.dom.domObject.call( this, element );
+};
+
+// PACKAGER_RENAME( CKEDITOR.dom.element )
+
+/**
+ * The the {@link CKEDITOR.dom.element} representing and element. If the
+ * element is a native DOM element, it will be transformed into a valid
+ * CKEDITOR.dom.element object.
+ * @returns {CKEDITOR.dom.element} The transformed element.
+ * @example
+ * var element = new CKEDITOR.dom.element( 'span' );
+ * alert( element == <b>CKEDITOR.dom.element.get( element )</b> );  "true"
+ * @example
+ * var element = document.getElementById( 'myElement' );
+ * alert( <b>CKEDITOR.dom.element.get( element )</b>.getName() );  e.g. "p"
+ */
+CKEDITOR.dom.element.get = function( element )
+{
+	return element && ( element.$ ? element : new CKEDITOR.dom.element( element ) );
+};
+
+CKEDITOR.dom.element.prototype = new CKEDITOR.dom.node();
+
+/**
+ * Creates an instance of the {@link CKEDITOR.dom.element} class based on the
+ * HTML representation of an element.
+ * @param {String} html The element HTML. It should define only one element in
+ *		the "root" level. The "root" element can have child nodes, but not
+ *		siblings.
+ * @returns {CKEDITOR.dom.element} The element instance.
+ * @example
+ * var element = <b>CKEDITOR.dom.element.createFromHtml( '&lt;strong class="anyclass"&gt;My element&lt;/strong&gt;' )</b>;
+ * alert( element.getName() );  // "strong"
+ */
+CKEDITOR.dom.element.createFromHtml = function( html, ownerDocument )
+{
+	var temp = new CKEDITOR.dom.element( 'div', ownerDocument );
+	temp.setHtml( html );
+
+	// When returning the node, remove it from its parent to detach it.
+	return temp.getFirst().remove();
+};
+
+CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
+	/** @lends CKEDITOR.dom.element.prototype */
+	{
+		/**
+		 * The node type. This is a constant value set to
+		 * {@link CKEDITOR.NODE_ELEMENT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_ELEMENT,
+
+		/**
+		 * Adds a CSS class to the element. It appends the class to the
+		 * already existing names.
+		 * @param {String} className The name of the class to be added.
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'div' );
+		 * element.addClass( 'classA' );  // &lt;div class="classA"&gt;
+		 * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;
+		 * element.addClass( 'classA' );  // &lt;div class="classA classB"&gt;
+		 */
+		addClass : function( className )
+		{
+			var c = this.$.className;
+			if ( c )
+			{
+				var regex = new RegExp( '(?:^|\\s)' + className + '(?:\\s|$)', '' );
+				if ( !regex.test( c ) )
+					c += ' ' + className;
+			}
+			this.$.className = c || className;
+		},
+
+		/**
+		 * Removes a CSS class name from the elements classes. Other classes
+		 * remain untouched.
+		 * @param {String} className The name of the class to remove.
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'div' );
+		 * element.addClass( 'classA' );  // &lt;div class="classA"&gt;
+		 * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;
+		 * element.removeClass( 'classA' );  // &lt;div class="classB"&gt;
+		 * element.removeClass( 'classB' );  // &lt;div&gt;
+		 */
+		removeClass : function( className )
+		{
+			var c = this.$.className;
+			if ( c )
+			{
+				var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', '' );
+				if ( regex.test( c ) )
+				{
+					c = c.replace( regex, '' ).replace( /^\s+/, '' );
+
+					if ( c )
+						this.$.className = c;
+					else
+						this.removeAttribute( 'class' );
+				}
+			}
+		},
+
+		/**
+		 * Append a node as a child of this element.
+		 * @param {CKEDITOR.dom.node|String} node The node or element name to be
+		 *		appended.
+		 * @param {Boolean} [toStart] Indicates that the element is to be
+		 *		appended at the start.
+		 * @returns {CKEDITOR.dom.node} The appended node.
+		 * @example
+		 * var p = new CKEDITOR.dom.element( 'p' );
+		 *
+		 * var strong = new CKEDITOR.dom.element( 'strong' );
+		 * <b>p.append( strong );</b>
+		 *
+		 * var em = <b>p.append( 'em' );</b>
+		 *
+		 * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;"
+		 */
+		append : function( node, toStart )
+		{
+			if ( typeof node == 'string' )
+				node = new CKEDITOR.dom.element( node );
+
+			if ( toStart )
+				this.$.insertBefore( node.$, this.$.firstChild );
+			else
+				this.$.appendChild( node.$ );
+
+			return node;
+		},
+
+		/**
+		 * Append text to this element.
+		 * @param {String} text The text to be appended.
+		 * @returns {CKEDITOR.dom.node} The appended node.
+		 * @example
+		 * var p = new CKEDITOR.dom.element( 'p' );
+		 * p.appendText( 'This is' );
+		 * p.appendText( ' some text' );
+		 *
+		 * // result: "&lt;p&gt;This is some text&lt;/p&gt;"
+		 */
+		appendText : function( text )
+		{
+			if ( this.$.text != undefined )
+				this.$.text += text;
+			else
+				this.append( new CKEDITOR.dom.text( text ) );
+		},
+
+		/**
+		 * Breaks one of the ancestor element in the element position, moving
+		 * this element between the broken parts.
+		 * @param {CKEDITOR.dom.element} parent The anscestor element to get broken.
+		 * @example
+		 * // Before breaking:
+		 * //     <b>This <i>is some<span /> sample</i> test text</b>
+		 * // If "element" is <span /> and "parent" is <i>:
+		 * //     <b>This <i>is some</i><span /><i> sample</i> test text</b>
+		 * element.breakParent( parent );
+		 * @example
+		 * // Before breaking:
+		 * //     <b>This <i>is some<span /> sample</i> test text</b>
+		 * // If "element" is <span /> and "parent" is <b>:
+		 * //     <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b>
+		 * element.breakParent( parent );
+		 */
+		breakParent : function( parent )
+		{
+			var range = new CKEDITOR.dom.range( this.getDocument() );
+
+			// We'll be extracting part of this element, so let's use our
+			// range to get the correct piece.
+			range.setStartAfter( this );
+			range.setEndAfter( parent );
+
+			// Extract it.
+			var docFrag = range.extractContents();
+
+			// Move the element outside the broken element.
+			range.insertNode( this.remove() );
+
+			// Re-insert the extracted piece after the element.
+			docFrag.insertAfterNode( this );
+		},
+
+		contains :
+			CKEDITOR.env.ie || CKEDITOR.env.webkit ?
+				function( node )
+				{
+					var $ = this.$;
+
+					return node.type != CKEDITOR.NODE_ELEMENT ?
+						$.contains( node.getParent().$ ) :
+						$ != node.$ && $.contains( node.$ );
+				}
+			:
+				function( node )
+				{
+					return !!( this.$.compareDocumentPosition( node.$ ) & 16 );
+				},
+
+		/**
+		 * Moves the selection focus to this element.
+		 * @example
+		 * var element = CKEDITOR.document.getById( 'myTextarea' );
+		 * <b>element.focus()</b>;
+		 */
+		focus : function()
+		{
+			this.$.focus();
+		},
+
+		/**
+		 * Gets the inner HTML of this element.
+		 * @returns {String} The inner HTML of this element.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );
+		 * alert( <b>p.getHtml()</b> );  // "&lt;b&gt;Example&lt;/b&gt;"
+		 */
+		getHtml : function()
+		{
+			return this.$.innerHTML;
+		},
+
+		/**
+		 * Sets the inner HTML of this element.
+		 * @param {String} html The HTML to be set for this element.
+		 * @returns {String} The inserted HTML.
+		 * @example
+		 * var p = new CKEDITOR.dom.element( 'p' );
+		 * <b>p.setHtml( '&lt;b&gt;Inner&lt;/b&gt; HTML' );</b>
+		 *
+		 * // result: "&lt;p&gt;&lt;b&gt;Inner&lt;/b&gt; HTML&lt;/p&gt;"
+		 */
+		setHtml : function( html )
+		{
+			return ( this.$.innerHTML = html );
+		},
+
+		/**
+		 * Sets the element contents as plain text.
+		 * @param {String} text The text to be set.
+		 * @returns {String} The inserted text.
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'div' );
+		 * element.setText( 'A > B & C < D' );
+		 * alert( element.innerHTML );  // "A &amp;gt; B &amp;amp; C &amp;lt; D"
+		 */
+		setText : function( text )
+		{
+			CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?
+				function ( text )
+				{
+					return this.$.innerText = text;
+				} :
+				function ( text )
+				{
+					return this.$.textContent = text;
+				};
+
+			return this.setText( text );
+		},
+
+		/**
+		 * Gets the value of an element attribute.
+		 * @function
+		 * @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;
+		})(),
+
+		getChildren : function()
+		{
+			return new CKEDITOR.dom.nodeList( this.$.childNodes );
+		},
+
+		/**
+		 * Gets the current computed value of one of the element CSS style
+		 * properties.
+		 * @function
+		 * @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[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ];
+				}
+			:
+				function( propertyName )
+				{
+					return this.getWindow().$.getComputedStyle( this.$, '' ).getPropertyValue( propertyName );
+				},
+
+		/**
+		 * Gets the DTD entries for this element.
+		 * @returns {Object} An object containing the list of elements accepted
+		 *		by this element.
+		 */
+		getDtd : function()
+		{
+			var dtd = CKEDITOR.dtd[ this.getName() ];
+
+			this.getDtd = function()
+			{
+				return dtd;
+			};
+
+			return dtd;
+		},
+
+		getElementsByTag : function( tagName )
+		{
+			return new CKEDITOR.dom.nodeList( this.$.getElementsByTagName( tagName ) );
+		},
+
+		/**
+		 * Gets the computed tabindex for this element.
+		 * @function
+		 * @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;
+				},
+
+		/**
+		 * Gets the text value of this element.
+		 *
+		 * Only in IE (which uses innerText), &lt;br&gt; will cause linebreaks,
+		 * and sucessive whitespaces (including line breaks) will be reduced to
+		 * a single space. This behavior is ok for us, for now. It may change
+		 * in the future.
+		 * @returns {String} The text value.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;Same &lt;i&gt;text&lt;/i&gt;.&lt;/div&gt;' );
+		 * alert( <b>element.getText()</b> );  // "Sample text."
+		 */
+		getText : function()
+		{
+			return this.$.textContent || this.$.innerText;
+		},
+
+		/**
+		 * Gets the window object that contains this element.
+		 * @returns {CKEDITOR.dom.window} The window object.
+		 * @example
+		 */
+		getWindow : function()
+		{
+			return this.getDocument().getWindow();
+		},
+
+		/**
+		 * Gets the value of the "id" attribute of this element.
+		 * @returns {String} The element id, or null if not available.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;p id="myId"&gt;&lt;/p&gt;' );
+		 * alert( <b>element.getId()</b> );  // "myId"
+		 */
+		getId : function()
+		{
+			return this.$.id || null;
+		},
+
+		/**
+		 * Gets the value of the "name" attribute of this element.
+		 * @returns {String} The element name, or null if not available.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input name="myName"&gt;&lt;/input&gt;' );
+		 * alert( <b>element.getNameAtt()</b> );  // "myName"
+		 */
+		getNameAtt : function()
+		{
+			return this.$.name || null;
+		},
+
+		/**
+		 * Gets the element name (tag name). The returned name is guaranteed to
+		 * be always full lowercased.
+		 * @returns {String} The element name.
+		 * @example
+		 * var element = new CKEDITOR.dom.element( 'span' );
+		 * alert( <b>element.getName()</b> );  // "span"
+		 */
+		getName : function()
+		{
+			// Cache the lowercased name inside a closure.
+			var nodeName = this.$.nodeName.toLowerCase();
+
+			return (
+			/** @ignore */
+			this.getName = function()
+				{
+					return nodeName;
+				})();
+		},
+
+		/**
+		 * Gets the value set to this element. This value is usually available
+		 * for form field elements.
+		 * @returns {String} The element value.
+		 */
+		getValue : function()
+		{
+			return this.$.value;
+		},
+
+		/**
+		 * Gets the first child node of this element.
+		 * @returns {CKEDITOR.dom.node} The first child node or null if not
+		 *		available.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );
+		 * var first = <b>element.getFirst()</b>;
+		 * alert( first.getName() );  // "b"
+		 */
+		getFirst : function()
+		{
+			var $ = this.$.firstChild;
+			return $ ? new CKEDITOR.dom.node( $ ) : null;
+		},
+
+		getLast : function()
+		{
+			var $ = this.$.lastChild;
+			return $ ? new CKEDITOR.dom.node( $ ) : null;
+		},
+
+		/**
+		 * Gets the node that follows this element in its parent's child list.
+		 * @returns {CKEDITOR.dom.node} The next node or null if not
+		 *		available.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt; &lt;i&gt;next&lt;/i&gt;&lt;/div&gt;' );
+		 * var first = <b>element.getFirst().getNext()</b>;
+		 * alert( first.getName() );  // "i"
+		 */
+		getNext : function()
+		{
+			var $ = this.$.nextSibling;
+			return $ ? new CKEDITOR.dom.node( $ ) : null;
+		},
+
+		/**
+		 * Checks if the element name matches one or more names.
+		 * @param {String} name[,name[,...]] One or more names to be checked.
+		 * @returns {Boolean} true if the element name matches any of the names.
+		 * @example
+		 * var element = new CKEDITOR.element( 'span' );
+		 * alert( <b>element.is( 'span' )</b> );  "true"
+		 * alert( <b>element.is( 'p', 'span' )</b> );  "true"
+		 * alert( <b>element.is( 'p' )</b> );  "false"
+		 * alert( <b>element.is( 'p', 'div' )</b> );  "false"
+		 */
+		is : function()
+		{
+			var name = this.getName();
+			for ( var i = 0 ; i < arguments.length ; i++ )
+			{
+				if ( arguments[ i ] == name )
+					return true;
+			}
+			return false;
+		},
+
+		/**
+		 * Indicates that the element has defined attributes.
+		 * @returns {Boolean} True if the element has attributes.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div title="Test">Example</div>' );
+		 * alert( <b>element.hasAttributes()</b> );  "true"
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div>Example</div>' );
+		 * alert( <b>element.hasAttributes()</b> );  "false"
+		 */
+		hasAttributes :
+			CKEDITOR.env.ie ?
+				function()
+				{
+					var attributes = this.$.attributes;
+
+					for ( var i = 0 ; i < attributes.length ; i++ )
+					{
+						var attribute = attributes[i];
+
+						switch ( attribute.nodeName )
+						{
+							case 'class' :
+								// IE has a strange bug. If calling removeAttribute('className'),
+								// the attributes collection will still contain the "class"
+								// attribute, which will be marked as "specified", even if the
+								// outerHTML of the element is not displaying the class attribute.
+								// Note : I was not able to reproduce it outside the editor,
+								// but I've faced it while working on the TC of #1391.
+								if ( this.$.className.length > 0 )
+									return true;
+
+							// Attributes to be ignored.
+							case '_cke_expando' :
+								continue;
+
+							/*jsl:fallthru*/
+
+							default :
+								if ( attribute.specified )
+									return true;
+						}
+					}
+
+					return false;
+				}
+			:
+				function()
+				{
+					return this.$.attributes.length > 0;
+				},
+
+		/**
+		 * Hides this element (display:none).
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.hide()</b>;
+		 */
+		hide : function()
+		{
+			this.setStyle( 'display', 'none' );
+		},
+
+		moveChildren : function( target, toStart )
+		{
+			var $ = this.$;
+			target = target.$;
+
+			if ( $ == target )
+				return;
+
+			var child;
+
+			if ( toStart )
+			{
+				while ( (child = $.lastChild) )
+					target.insertBefore( $.removeChild( child ), target.firstChild );
+			}
+			else
+			{
+				while ( (child = $.firstChild) )
+					target.appendChild( $.removeChild( child ) );
+			}
+		},
+
+		/**
+		 * Shows this element (display it).
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.show()</b>;
+		 */
+		show : function()
+		{
+			this.setStyles(
+				{
+					display : '',
+					visibility : ''
+				});
+		},
+
+		/**
+		 * Sets the value of an element attribute.
+		 * @param {String} name The name of the attribute.
+		 * @param {String} value The value to be set to the attribute.
+		 * @function
+		 * @returns {CKEDITOR.dom.element} This element instance.
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.setAttribute( 'class', 'myClass' )</b>;
+		 * <b>element.setAttribute( 'title', 'This is an example' )</b>;
+		 */
+		setAttribute : (function()
+		{
+			var standard = function( name, value )
+			{
+				this.$.setAttribute( name, value );
+				return this;
+			};
+
+			if ( CKEDITOR.env.ie )
+			{
+				return function( name, value )
+				{
+					if ( name == 'class' )
+						this.$.className = value;
+					if ( name == 'style' )
+						this.$.style.cssText = value;
+					else
+						standard.apply( this, arguments );
+					return this;
+				};
+			}
+			else
+				return standard;
+		})(),
+
+		/**
+		 * Sets the value of several element attributes.
+		 * @param {Object} attributesPairs An object containing the names and
+		 *		values of the attributes.
+		 * @returns {CKEDITOR.dom.element} This element instance.
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.setAttributes({
+		 *     'class' : 'myClass',
+		 *     'title' : 'This is an example' })</b>;
+		 */
+		setAttributes : function( attributesPairs )
+		{
+			for ( var name in attributesPairs )
+				this.setAttribute( name, attributesPairs[ name ] );
+			return this;
+		},
+
+		/**
+		 * Sets the element value. This function is usually used with form
+		 * field element.
+		 * @param {String} value The element value.
+		 * @returns {CKEDITOR.dom.element} This element instance.
+		 */
+		setValue : function( value )
+		{
+			this.$.value = value;
+			return this;
+		},
+
+		/**
+		 * Removes an attribute from the element.
+		 * @param {String} name The attribute name.
+		 * @function
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
+		 * element.removeAttribute( 'class' );
+		 */
+		removeAttribute : (function()
+		{
+			var standard = function( name )
+			{
+				this.$.removeAttribute( name );
+			};
+
+			if ( CKEDITOR.env.ie )
+			{
+				return function( name )
+				{
+					if ( name == 'class' )
+						name = 'className';
+					standard.call( this, name );
+				};
+			}
+			else
+				return standard;
+		})(),
+
+		removeAttributes : function ( attributes )
+		{
+			for ( var i = 0 ; i < attributes.length ; i++ )
+				this.removeAttribute( attributes[ i ] );
+		},
+
+		/**
+		 * Removes a style from the element.
+		 * @param {String} name The style name.
+		 * @function
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<div style="display:none"></div>' );
+		 * element.removeStyle( 'display' );
+		 */
+		removeStyle : function( name )
+		{
+			this.setStyle( name, '' );
+
+			if ( !this.$.style.cssText )
+				this.removeAttribute( 'style' );
+		},
+
+		/**
+		 * Sets the value of an element style.
+		 * @param {String} name The name of the style. The CSS naming notation
+		 *		must be used (e.g. "background-color").
+		 * @param {String} value The value to be set to the style.
+		 * @returns {CKEDITOR.dom.element} This element instance.
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.setStyle( 'background-color', '#ff0000' )</b>;
+		 * <b>element.setStyle( 'margin-top', '10px' )</b>;
+		 * <b>element.setStyle( 'float', 'right' )</b>;
+		 */
+		setStyle : function( name, value )
+		{
+			this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;
+			return this;
+		},
+
+		/**
+		 * Sets the value of several element styles.
+		 * @param {Object} stylesPairs An object containing the names and
+		 *		values of the styles.
+		 * @returns {CKEDITOR.dom.element} This element instance.
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.setStyles({
+		 *     'position' : 'absolute',
+		 *     'float' : 'right' })</b>;
+		 */
+		setStyles : function( stylesPairs )
+		{
+			for ( var name in stylesPairs )
+				this.setStyle( name, stylesPairs[ name ] );
+			return this;
+		},
+
+		/**
+		 * Sets the opacity of an element.
+		 * @param {Number} opacity A number within the range [0.0, 1.0].
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * <b>element.setOpacity( 0.75 )</b>;
+		 */
+		setOpacity : function( opacity )
+		{
+			if ( CKEDITOR.env.ie )
+			{
+				opacity = Math.round( opacity * 100 );
+				this.setStyle( 'filter', opacity >= 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' );
+			}
+			else
+				this.setStyle( 'opacity', opacity );
+		},
+
+		/**
+		 * Makes the element and its children unselectable.
+		 * @function
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'myElement' );
+		 * element.unselectable();
+		 */
+		unselectable :
+			CKEDITOR.env.gecko ?
+				function()
+				{
+					this.$.style.MozUserSelect = 'none';
+				}
+			: CKEDITOR.env.webkit ?
+				function()
+				{
+					this.$.style.KhtmlUserSelect = 'none';
+				}
+			:
+				function()
+				{
+					if ( CKEDITOR.env.ie || CKEDITOR.env.opera )
+					{
+						var element = this.$,
+							e,
+							i = 0;
+
+						element.unselectable = 'on';
+
+						while ( ( e = element.all[ i++ ] ) )
+						{
+							switch ( e.tagName.toLowerCase() )
+							{
+								case 'iframe' :
+								case 'textarea' :
+								case 'input' :
+								case 'select' :
+									/* Ignore the above tags */
+									break;
+								default :
+									e.unselectable = 'on';
+							}
+						}
+					}
+				}
+	});
Index: /CKEditor/trunk/_source/core/dom/elementpath.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/elementpath.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/elementpath.js	(revision 2948)
@@ -0,0 +1,104 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	// Elements that may be considered the "Block boundary" in an element path.
+	var pathBlockElements = { address:1,blockquote:1,dl:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1,li:1,dt:1,de:1 };
+
+	// Elements that may be considered the "Block limit" in an element path.
+	var pathBlockLimitElements = { body:1,div:1,td:1,th:1,caption:1,form:1 };
+
+	// Check if an element contains any block element.
+	var checkHasBlock = function( element )
+	{
+		var childNodes = element.getChildren();
+
+		for ( var i = 0, count = childNodes.count() ; i < count ; i++ )
+		{
+			var child = childNodes.getItem( i );
+
+			if ( child.type == CKEDITOR.NODE_ELEMENT && CKEDITOR.dtd.$block[ child.getName() ] )
+				return true;
+		}
+
+		return false;
+	};
+
+	CKEDITOR.dom.elementPath = function( lastNode )
+	{
+		var block = null;
+		var blockLimit = null;
+		var elements = [];
+
+		var e = lastNode;
+
+		while ( e )
+		{
+			if ( e.type == CKEDITOR.NODE_ELEMENT )
+			{
+				if ( !this.lastElement )
+					this.lastElement = e;
+
+				var elementName = e.getName();
+				if ( CKEDITOR.env.ie && e.$.scopeName != 'HTML' )
+					elementName = e.$.scopeName.toLowerCase() + ':' + elementName;
+
+				if ( !blockLimit )
+				{
+					if ( !block && pathBlockElements[ elementName ] )
+						block = e;
+
+					if ( pathBlockLimitElements[ elementName ] )
+					{
+						// DIV is considered the Block, if no block is available (#525)
+						// and if it doesn't contain other blocks.
+						if ( !block && elementName == 'div' && !checkHasBlock( e ) )
+							block = e;
+						else
+							blockLimit = e;
+					}
+				}
+
+				elements.push( e );
+
+				if ( elementName == 'body' )
+					break;
+			}
+			e = e.getParent();
+		}
+
+		this.block = block;
+		this.blockLimit = blockLimit;
+		this.elements = elements;
+	};
+})();
+
+CKEDITOR.dom.elementPath.prototype =
+{
+	/**
+	 * Compares this element path with another one.
+	 * @param {CKEDITOR.dom.elementPath} otherPath The elementPath object to be
+	 * compared with this one.
+	 * @returns {Boolean} "true" if the paths are equal, containing the same
+	 * number of elements and the same elements in the same order.
+	 */
+	compare : function( otherPath )
+	{
+		var thisElements = this.elements;
+		var otherElements = otherPath && otherPath.elements;
+
+		if ( !otherElements || thisElements.length != otherElements.length )
+			return false;
+
+		for ( var i = 0 ; i < thisElements.length ; i++ )
+		{
+			if ( !thisElements[ i ].equals( otherElements[ i ] ) )
+				return false;
+		}
+
+		return true;
+	}
+};
Index: /CKEditor/trunk/_source/core/dom/event.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/event.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/event.js	(revision 2948)
@@ -0,0 +1,137 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.event} class, which
+ *		represents the a native DOM event object.
+ */
+
+/**
+ * Represents a native DOM event object.
+ * @constructor
+ * @param {Object} domEvent A native DOM event object.
+ * @example
+ */
+CKEDITOR.dom.event = function( domEvent )
+{
+	/**
+	 * The native DOM event object represented by this class instance.
+	 * @type Object
+	 * @example
+	 */
+	this.$ = domEvent;
+};
+
+CKEDITOR.dom.event.prototype =
+{
+	/**
+	 * Gets the key code associated to the event.
+	 * @returns {Number} The key code.
+	 * @example
+	 * alert( event.getKey() );  "65" is "a" has been pressed
+	 */
+	getKey : function()
+	{
+		return this.$.keyCode || this.$.which;
+	},
+
+	/**
+	 * Gets a number represeting the combination of the keys pressed during the
+	 * event. It is the sum with the current key code and the {@link CKEDITOR.CTRL},
+	 * {@link CKEDITOR.SHIFT} and {@link CKEDITOR.ALT} constants.
+	 * @returns {Number} The number representing the keys combination.
+	 * @example
+	 * alert( event.getKeystroke() == 65 );                                   // "a" key
+	 * alert( event.getKeystroke() == CKEDITOR.CTRL + 65 );                   // CTRL + "a" key
+	 * alert( event.getKeystroke() == CKEDITOR.CTRL + CKEDITOR.SHIFT + 65 );  // CTRL + SHIFT + "a" key
+	 */
+	getKeystroke : function()
+	{
+		var keystroke = this.getKey();
+
+		if ( this.$.ctrlKey || this.$.metaKey )
+			keystroke += CKEDITOR.CTRL;
+
+		if ( this.$.shiftKey )
+			keystroke += CKEDITOR.SHIFT;
+
+		if ( this.$.altKey )
+			keystroke += CKEDITOR.ALT;
+
+		return keystroke;
+	},
+
+	/**
+	 * Prevents the original behavior of the event to happen. It can optionally
+	 * stop propagating the event in the event chain.
+	 * @param {Boolean} [stopPropagation] Stop propagating this event in the
+	 *		event chain.
+	 * @example
+	 * var element = CKEDITOR.document.getById( 'myElement' );
+	 * element.on( 'click', function( ev )
+	 *     {
+	 *         // The DOM event object is passed by the "data" property.
+	 *         var domEvent = ev.data;
+	 *         // Prevent the click to chave any effect in the element.
+	 *         domEvent.preventDefault();
+	 *     });
+	 */
+	preventDefault : function( stopPropagation )
+	{
+		var $ = this.$;
+		if ( $.preventDefault )
+			$.preventDefault();
+		else
+			$.returnValue = false;
+
+		if ( stopPropagation )
+		{
+			if ( $.stopPropagation )
+				$.stopPropagation();
+			else
+				$.cancelBubble = true;
+		}
+	},
+	/**
+	 * Returns the DOM node where the event was targeted to.
+	 * @returns {CKEDITOR.dom.node} The target DOM node.
+	 * @example
+	 * var element = CKEDITOR.document.getById( 'myElement' );
+	 * element.on( 'click', function( ev )
+	 *     {
+	 *         // The DOM event object is passed by the "data" property.
+	 *         var domEvent = ev.data;
+	 *         // Add a CSS class to the event target.
+	 *         domEvent.getTarget().addClass( 'clicked' );
+	 *     });
+	 */
+
+	getTarget : function()
+	{
+		var rawNode = this.$.target || this.$.srcElement;
+		return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
+	}
+};
+
+/**
+ * CTRL key (1000).
+ * @constant
+ * @example
+ */
+CKEDITOR.CTRL = 1000;
+
+/**
+ * SHIFT key (2000).
+ * @constant
+ * @example
+ */
+CKEDITOR.SHIFT = 2000;
+
+/**
+ * ALT key (4000).
+ * @constant
+ * @example
+ */
+CKEDITOR.ALT = 4000;
Index: /CKEditor/trunk/_source/core/dom/node.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/node.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/node.js	(revision 2948)
@@ -0,0 +1,421 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.node} class, which is the base
+ *		class for classes that represent DOM nodes.
+ */
+
+/**
+ * Base class for classes representing DOM nodes. This constructor may return
+ * and instance of classes that inherits this class, like
+ * {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
+ * @augments CKEDITOR.dom.domObject
+ * @param {Object} domNode A native DOM node.
+ * @constructor
+ * @see CKEDITOR.dom.element
+ * @see CKEDITOR.dom.text
+ * @example
+ */
+CKEDITOR.dom.node = function( domNode )
+{
+	if ( domNode )
+	{
+		switch ( domNode.nodeType )
+		{
+			case CKEDITOR.NODE_ELEMENT :
+				return new CKEDITOR.dom.element( domNode );
+
+			case CKEDITOR.NODE_TEXT :
+				return new CKEDITOR.dom.text( domNode );
+		}
+
+		// Call the base constructor.
+		CKEDITOR.dom.domObject.call( this, domNode );
+	}
+
+	return this;
+};
+
+CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
+
+/**
+ * Element node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_ELEMENT = 1;
+
+/**
+ * Text node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_TEXT = 3;
+
+/**
+ * Comment node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_COMMENT = 8;
+
+CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
+
+CKEDITOR.POSITION_IDENTICAL = 0;
+CKEDITOR.POSITION_DISCONNECTED = 1;
+CKEDITOR.POSITION_FOLLOWING = 2;
+CKEDITOR.POSITION_PRECEDING = 4;
+CKEDITOR.POSITION_IS_CONTAINED = 8;
+CKEDITOR.POSITION_CONTAINS = 16;
+
+CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype,
+	/** @lends CKEDITOR.dom.node.prototype */
+	{
+		/**
+		 * Makes this node child of another element.
+		 * @param {CKEDITOR.dom.element} element The target element to which append
+		 *		this node.
+		 * @returns {CKEDITOR.dom.element} The target element.
+		 * @example
+		 * var p = new CKEDITOR.dom.element( 'p' );
+		 * var strong = new CKEDITOR.dom.element( 'strong' );
+		 * strong.appendTo( p );
+		 *
+		 * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;"
+		 */
+		appendTo : function( element, toStart )
+		{
+			element.append( this, toStart );
+			return element;
+		},
+
+		clone : function( includeChildren )
+		{
+			return new CKEDITOR.dom.node( this.$.cloneNode( includeChildren ) );
+		},
+
+		hasPrevious : function()
+		{
+			return !!this.$.previousSibling;
+		},
+
+		hasNext : function()
+		{
+			return !!this.$.nextSibling;
+		},
+
+		/**
+		 * Inserts this element after a node.
+		 * @param {CKEDITOR.dom.node} node The that will preceed this element.
+		 * @returns {CKEDITOR.dom.node} The node preceeding this one after
+		 *		insertion.
+		 * @example
+		 * var em = new CKEDITOR.dom.element( 'em' );
+		 * var strong = new CKEDITOR.dom.element( 'strong' );
+		 * strong.insertAfter( em );
+		 *
+		 * // result: "&lt;em&gt;&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;"
+		 */
+		insertAfter : function( node )
+		{
+			node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
+			return node;
+		},
+
+		/**
+		 * Inserts this element before a node.
+		 * @param {CKEDITOR.dom.node} node The that will be after this element.
+		 * @returns {CKEDITOR.dom.node} The node being inserted.
+		 * @example
+		 * var em = new CKEDITOR.dom.element( 'em' );
+		 * var strong = new CKEDITOR.dom.element( 'strong' );
+		 * strong.insertBefore( em );
+		 *
+		 * // result: "&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;"
+		 */
+		insertBefore : function( node )
+		{
+			node.$.parentNode.insertBefore( this.$, node.$ );
+			return node;
+		},
+
+		insertBeforeMe : function( node )
+		{
+			this.$.parentNode.insertBefore( node.$, this.$ );
+			return node;
+		},
+
+		/**
+		 * Gets a DOM tree descendant under the current node.
+		 * @param {Array|Number} indices The child index or array of child indices under the node.
+		 * @returns {CKEDITOR.dom.node} The specified DOM child under the current node. Null if child does not exist.
+		 * @example
+		 * var strong = p.getChild(0);
+		 */
+		getChild : function( indices )
+		{
+			var rawNode = this.$;
+
+			if ( !indices.slice )
+				rawNode = rawNode.childNodes[ indices ];
+			else
+			{
+				while ( indices.length > 0 && rawNode )
+					rawNode = rawNode.childNodes[ indices.shift() ];
+			}
+
+			return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
+		},
+
+		getChildCount : function()
+		{
+			return this.$.childNodes.length;
+		},
+
+		/**
+		 * Gets the document containing this element.
+		 * @returns {CKEDITOR.dom.document} The document.
+		 * @example
+		 * var element = CKEDITOR.document.getById( 'example' );
+		 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
+		 */
+		getDocument : function()
+		{
+			var document = new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
+
+			return (
+			/** @ignore */
+			this.getDocument = function()
+				{
+					return document;
+				})();
+		},
+
+		getIndex : function()
+		{
+			var $ = this.$;
+
+			var currentNode = $.parentNode && $.parentNode.firstChild;
+			var currentIndex = -1;
+
+			while ( currentNode )
+			{
+				currentIndex++;
+
+				if ( currentNode == $ )
+					return currentIndex;
+
+				currentNode = currentNode.nextSibling;
+			}
+
+			return -1;
+		},
+
+		/**
+		 * Gets the node following this node (next sibling).
+		 * @returns {CKEDITOR.dom.node} The next node.
+		 */
+		getNext : function()
+		{
+			var next = this.$.nextSibling;
+			return next ? new CKEDITOR.dom.node( next ) : null;
+		},
+
+		getNextSourceNode : function( startFromSibling, nodeType )
+		{
+			var $ = this.$;
+
+			var node = ( !startFromSibling && $.firstChild ) ?
+				$.firstChild :
+				$.nextSibling;
+
+			var parent;
+
+			while ( !node && ( parent = ( parent || $ ).parentNode ) )
+				node = parent.nextSibling;
+
+			if ( !node )
+				return null;
+
+			if ( nodeType && nodeType != node.nodeType )
+				return arguments.callee.call( { $ : node }, false, nodeType );
+
+			return new CKEDITOR.dom.node( node );
+		},
+
+		getPreviousSourceNode : function( startFromSibling, nodeType )
+		{
+			var $ = startFromSibling ? this.$.previousSibling : this.$,
+				node = null;
+
+			if ( !$ )
+				return null;
+
+			if ( ( node = $.previousSibling ) )
+			{
+				while ( node.lastChild )
+					node = node.lastChild;
+			}
+			else
+				node = $.parentNode;
+
+			if ( !node )
+				return null;
+
+			if ( nodeType && node.nodeType != nodeType )
+				return arguments.callee.apply( { $ : node }, false, nodeType );
+
+			return new CKEDITOR.dom.node( node );
+		},
+
+		getPrevious : function()
+		{
+			var previous = this.$.previousSibling;
+			return previous ? new CKEDITOR.dom.node( previous ) : null;
+		},
+
+		/**
+		 * Gets the parent element for this node.
+		 * @returns {CKEDITOR.dom.element} The parent element.
+		 * @example
+		 * var node = editor.document.getBody().getFirst();
+		 * var parent = node.<b>getParent()</b>;
+		 * alert( node.getName() );  // "body"
+		 */
+		getParent : function()
+		{
+			var parent = this.$.parentNode;
+			return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.node( parent ) : null;
+		},
+
+		getParents : function()
+		{
+			var node = this;
+			var parents = [];
+
+			do
+			{
+				parents.unshift( node );
+			}
+			while ( ( node = node.getParent() ) )
+
+			return parents;
+		},
+
+		getPosition : function( otherNode )
+		{
+			var $ = this.$;
+			var $other = otherNode.$;
+
+			if ( $.compareDocumentPosition )
+				return $.compareDocumentPosition( $other );
+
+			// IE and Safari have no support for compareDocumentPosition.
+
+			if ( $ == $other )
+				return CKEDITOR.POSITION_IDENTICAL;
+
+			// Handle non element nodes (don't support contains nor sourceIndex).
+			if ( this.type != CKEDITOR.NODE_ELEMENT || otherNode.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( $.parentNode == $other )
+					return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+				else if ( $other.parentNode == $ )
+					return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
+				else if ( $.parentNode == $other.parentNode )
+					return this.getIndex() < otherNode.getIndex() ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
+				else
+				{
+					$ = $.parentNode;
+					$other = $other.parentNode;
+				}
+			}
+
+			if ( $.contains( $other ) )
+				return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
+
+			if ( $other.contains( $ ) )
+				return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+
+			if ( 'sourceIndex' in $ )
+			{
+				return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED :
+					( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING :
+					CKEDITOR.POSITION_FOLLOWING;
+			}
+
+			// WebKit has no support for sourceIndex.
+
+			var doc = this.getDocument().$;
+
+			var range1 = doc.createRange();
+			var range2 = doc.createRange();
+
+			range1.selectNode( $ );
+			range2.selectNode( $other );
+
+			return range1.compareBoundaryPoints( 1, range2 ) > 0 ?
+				CKEDITOR.POSITION_FOLLOWING :
+				CKEDITOR.POSITION_PRECEDING;
+		},
+
+		/**
+		 * Gets the closes ancestor node of a specified node name.
+		 * @param {String} name Node name of ancestor node.
+		 * @param {Boolean} includeSelf (Optional) Whether to include the current
+		 * node in the calculation or not.
+		 * @returns {CKEDITOR.dom.node} Ancestor node.
+		 */
+		getAscendant : function( name, includeSelf )
+		{
+			var node = this.$;
+			if ( includeSelf && node.nodeName.toLowerCase() == name )
+				return this;
+			while ( ( node = node.parentNode ) )
+			{
+				if ( node.nodeName && node.nodeName.toLowerCase() == name )
+					return new CKEDITOR.dom.node( node );
+			}
+			return null;
+		},
+
+		move : function( target, toStart )
+		{
+			target.append( this.remove(), toStart );
+		},
+
+		/**
+		 * Removes this node from the document DOM.
+		 * @param {Boolean} [preserveChildren] Indicates that the children
+		 *		elements must remain in the document, removing only the outer
+		 *		tags.
+		 * @example
+		 * var element = CKEDITOR.dom.element.getById( 'MyElement' );
+		 * <b>element.remove()</b>;
+		 */
+		remove : function( preserveChildren )
+		{
+			var $ = this.$;
+			var parent = $.parentNode;
+
+			if ( parent )
+			{
+				if ( preserveChildren )
+				{
+					// Move all children before the node.
+					for ( var child ; ( child = $.firstChild ) ; )
+					{
+						parent.insertBefore( $.removeChild( child ), $ );
+					}
+				}
+
+				parent.removeChild( $ );
+			}
+
+			return this;
+		}
+	}
+);
Index: /CKEditor/trunk/_source/core/dom/nodelist.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/nodelist.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/nodelist.js	(revision 2948)
@@ -0,0 +1,22 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dom.nodeList = function( nativeList )
+{
+	this.$ = nativeList;
+};
+
+CKEDITOR.dom.nodeList.prototype =
+{
+	count : function()
+	{
+		return this.$.length;
+	},
+
+	getItem : function( index )
+	{
+		return new CKEDITOR.dom.node( this.$[ index ] );
+	}
+};
Index: /CKEditor/trunk/_source/core/dom/range.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/range.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/range.js	(revision 2948)
@@ -0,0 +1,1305 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dom.range = function( document )
+{
+	this.startContainer	= null;
+	this.startOffset	= null;
+	this.endContainer	= null;
+	this.endOffset		= null;
+	this.collapsed		= true;
+
+	this.document = document;
+};
+
+(function()
+{
+	// Updates the "collapsed" property for the given range object.
+	var updateCollapsed = function( range )
+	{
+		range.collapsed = (
+			range.startContainer &&
+			range.endContainer &&
+			range.startContainer.equals( range.endContainer ) &&
+			range.startOffset == range.endOffset );
+	};
+
+	// This is a shared function used to delete, extract and clone the range
+	// contents.
+	// V2
+	var execContentsAction = function( range, action, docFrag )
+	{
+		var startNode	= range.startContainer;
+		var endNode		= range.endContainer;
+
+		var startOffset	= range.startOffset;
+		var endOffset	= range.endOffset;
+
+		var removeStartNode;
+		var removeEndNode;
+
+		// For text containers, we must simply split the node and point to the
+		// second part. The removal will be handled by the rest of the code .
+		if ( endNode.type == CKEDITOR.NODE_TEXT )
+			endNode = endNode.split( endOffset );
+		else
+		{
+			// If the end container has children and the offset is pointing
+			// to a child, then we should start from it.
+			if ( endNode.getChildCount() > 0 )
+			{
+				// If the offset points after the last node.
+				if ( endOffset >= endNode.getChildCount() )
+				{
+					// Let's create a temporary node and mark it for removal.
+					endNode = endNode.append( range.document.createText( '' ) );
+					removeEndNode = true;
+				}
+				else
+					endNode = endNode.getChild( endOffset );
+			}
+		}
+
+		// For text containers, we must simply split the node. The removal will
+		// be handled by the rest of the code .
+		if ( startNode.type == CKEDITOR.NODE_TEXT )
+		{
+			startNode.split( startOffset );
+
+			// In cases the end node is the same as the start node, the above
+			// splitting will also split the end, so me must move the end to
+			// the second part of the split.
+			if ( startNode.equals( endNode ) )
+				endNode = startNode.getNext();
+		}
+		else
+		{
+			// If the start container has children and the offset is pointing
+			// to a child, then we should start from its previous sibling.
+
+			// If the offset points to the first node, we don't have a
+			// sibling, so let's use the first one, but mark it for removal.
+			if ( !startOffset )
+			{
+				// Let's create a temporary node and mark it for removal.
+				startNode = startNode.getFirst().insertBeforeMe( range.document.createText( '' ) );
+				removeStartNode = true;
+			}
+			else if ( startOffset >= startNode.getChildCount() )
+			{
+				// Let's create a temporary node and mark it for removal.
+				startNode = startNode.append( range.document.createText( '' ) );
+				removeStartNode = true;
+			}
+			else
+				startNode = startNode.getChild( startOffset ).getPrevious();
+		}
+
+		// Get the parent nodes tree for the start and end boundaries.
+		var startParents	= startNode.getParents();
+		var endParents		= endNode.getParents();
+
+		// Compare them, to find the top most siblings.
+		var i, topStart, topEnd;
+
+		for ( i = 0 ; i < startParents.length ; i++ )
+		{
+			topStart = startParents[ i ];
+			topEnd = endParents[ i ];
+
+			// The compared nodes will match until we find the top most
+			// siblings (different nodes that have the same parent).
+			// "i" will hold the index in the parents array for the top
+			// most element.
+			if ( !topStart.equals( topEnd ) )
+				break;
+		}
+
+		var clone = docFrag, levelStartNode, levelClone, currentNode, currentSibling;
+
+		// Remove all successive sibling nodes for every node in the
+		// startParents tree.
+		for ( var j = i ; j < startParents.length ; j++ )
+		{
+			levelStartNode = startParents[j];
+
+			// For Extract and Clone, we must clone this level.
+			if ( clone && !levelStartNode.equals( startNode ) )		// action = 0 = Delete
+				levelClone = clone.append( levelStartNode.clone() );
+
+			currentNode = levelStartNode.getNext();
+
+			while( currentNode )
+			{
+				// Stop processing when the current node matches a node in the
+				// endParents tree or if it is the endNode.
+				if ( currentNode.equals( endParents[ j ] ) || currentNode.equals( endNode ) )
+					break;
+
+				// Cache the next sibling.
+				currentSibling = currentNode.getNext();
+
+				// If cloning, just clone it.
+				if ( action == 2 )	// 2 = Clone
+					clone.append( currentNode.clone( true ) );
+				else
+				{
+					// Both Delete and Extract will remove the node.
+					currentNode.remove();
+
+					// When Extracting, move the removed node to the docFrag.
+					if ( action == 1 )	// 1 = Extract
+						clone.append( currentNode );
+				}
+
+				currentNode = currentSibling;
+			}
+
+			if ( clone )
+				clone = levelClone;
+		}
+
+		clone = docFrag;
+
+		// Remove all previous sibling nodes for every node in the
+		// endParents tree.
+		for ( var k = i ; k < endParents.length ; k++ )
+		{
+			levelStartNode = endParents[ k ];
+
+			// For Extract and Clone, we must clone this level.
+			if ( action > 0 && !levelStartNode.equals( endNode ) )		// action = 0 = Delete
+				levelClone = clone.append( levelStartNode.clone() );
+
+			// The processing of siblings may have already been done by the parent.
+			if ( !startParents[ k ] || levelStartNode.$.parentNode != startParents[ k ].$.parentNode )
+			{
+				currentNode = levelStartNode.getPrevious();
+
+				while( currentNode )
+				{
+					// Stop processing when the current node matches a node in the
+					// startParents tree or if it is the startNode.
+					if ( currentNode.equals( startParents[ k ] ) || currentNode.equals( startNode ) )
+						break;
+
+					// Cache the next sibling.
+					currentSibling = currentNode.getPrevious();
+
+					// If cloning, just clone it.
+					if ( action == 2 )	// 2 = Clone
+						clone.$.insertBefore( currentNode.$.cloneNode( true ), clone.$.firstChild ) ;
+					else
+					{
+						// Both Delete and Extract will remove the node.
+						currentNode.remove();
+
+						// When Extracting, mode the removed node to the docFrag.
+						if ( action == 1 )	// 1 = Extract
+							clone.$.insertBefore( currentNode.$, clone.$.firstChild );
+					}
+
+					currentNode = currentSibling;
+				}
+			}
+
+			if ( clone )
+				clone = levelClone;
+		}
+
+		if ( action == 2 )		// 2 = Clone.
+		{
+			// No changes in the DOM should be done, so fix the split text (if any).
+
+			var startTextNode = range.startContainer;
+			if ( startTextNode.type == CKEDITOR.NODE_TEXT )
+			{
+				startTextNode.$.data += startTextNode.$.nextSibling.data;
+				startTextNode.$.parentNode.removeChild( startTextNode.$.nextSibling );
+			}
+
+			var endTextNode = range.endContainer;
+			if ( endTextNode.type == CKEDITOR.NODE_TEXT && endTextNode.$.nextSibling )
+			{
+				endTextNode.$.data += endTextNode.$.nextSibling.data;
+				endTextNode.$.parentNode.removeChild( endTextNode.$.nextSibling );
+			}
+		}
+		else
+		{
+			// Collapse the range.
+
+			// If a node has been partially selected, collapse the range between
+			// topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
+			if ( topStart && topEnd && ( startNode.$.parentNode != topStart.$.parentNode || endNode.$.parentNode != topEnd.$.parentNode ) )
+			{
+				var endIndex = topEnd.getIndex();
+
+				// If the start node is to be removed, we must correct the
+				// index to reflect the removal.
+				if ( removeStartNode && topEnd.$.parentNode == startNode.$.parentNode )
+					endIndex--;
+
+				range.setStart( topEnd.getParent(), endIndex );
+			}
+
+			// Collapse it to the start.
+			range.collapse( true );
+		}
+
+		// Cleanup any marked node.
+		if( removeStartNode )
+			startNode.remove();
+
+		if( removeEndNode && endNode.$.parentNode )
+			endNode.remove();
+	};
+
+	var inlineChildReqElements = { abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 };
+
+	var getBoundaryNodes = function()
+	{
+		var startNode = this.startContainer,
+			endNode = this.endContainer,
+			startOffset = this.startOffset,
+			endOffset = this.endOffset;
+
+		if ( startNode.type == CKEDITOR.NODE_ELEMENT )
+		{
+			var childCount = startNode.getChildCount();
+			if ( childCount > startOffset )
+				startNode = startNode.getChild( startOffset );
+			else if ( childCount < 1 )
+				startNode = startNode.getPreviousSourceNode();
+			else		// startOffset > childCount but childCount is not 0
+			{
+				// Try to take the node just after the current position.
+				startNode = startNode.$;
+				while ( startNode.lastChild )
+					startNode = startNode.lastChild;
+				startNode = new CKEDITOR.dom.node( startNode );
+
+				// Normally we should take the next node in DFS order. But it
+				// is also possible that we've already reached the end of
+				// document.
+				startNode = startNode.getNextSourceNode() || startNode;
+			}
+		}
+		if ( endNode.type == CKEDITOR.NODE_ELEMENT )
+		{
+			var childCount = endNode.getChildCount();
+			if ( childCount > endOffset )
+				endNode = endNode.getChild( endOffset ).getPreviousSourceNode();
+			else if ( childCount < 1 )
+				endNode = endNode.getPreviousSourceNode();
+			else		// endOffset > childCount but childCount is not 0
+			{
+				// Try to take the node just before the current position.
+				endNode = endNode.$;
+				while ( endNode.lastChild )
+					endNode = endNode.lastChild;
+				endNode = new CKEDITOR.dom.node( endNode );
+			}
+		}
+
+		return { startNode : startNode, endNode : endNode };
+	};
+
+	// Check every node between the block boundary and the startNode or endNode.
+	var getCheckStartEndBlockFunction = function( isStart )
+	{
+		return function( evt )
+		{
+			// Don't check the block boundary itself.
+			if ( this.stopped() || !evt.data.node )
+				return;
+
+			var node = evt.data.node,
+				hadBr = false;
+			if ( node.type == CKEDITOR.NODE_ELEMENT )
+			{
+				// If there are non-empty inline elements (e.g. <img />), then we're not
+				// at the start.
+				if ( !inlineChildReqElements[ node.getName() ] )
+				{
+					// If we're working at the end-of-block, forgive the first <br />.
+					if ( !isStart && node.getName() == 'br' && !hadBr )
+						hadBr = true;
+					else
+					{
+						this.checkFailed = true;
+						this.stop();
+					}
+				}
+			}
+			else if ( node.type == CKEDITOR.NODE_TEXT )
+			{
+				// If there's any visible text, then we're not at the start.
+				var visibleText = CKEDITOR.tools.trim( node.getText() );
+				if ( visibleText.length > 0 )
+				{
+					this.checkFailed = true;
+					this.stop();
+				}
+			}
+		};
+	};
+
+
+	CKEDITOR.dom.range.prototype =
+	{
+		clone : function()
+		{
+			var clone = new CKEDITOR.dom.range( this.document );
+
+			clone.startContainer = this.startContainer;
+			clone.startOffset = this.startOffset;
+			clone.endContainer = this.endContainer;
+			clone.endOffset = this.endOffset;
+			clone.collapsed = this.collapsed;
+
+			return clone;
+		},
+
+		collapse : function( toStart )
+		{
+			if ( toStart )
+			{
+				this.endContainer	= this.startContainer;
+				this.endOffset		= this.startOffset;
+			}
+			else
+			{
+				this.startContainer	= this.endContainer;
+				this.startOffset	= this.endOffset;
+			}
+
+			this.collapsed = true;
+		},
+
+		// The selection may be lost when cloning (due to the splitText() call).
+		cloneContents : function()
+		{
+			var docFrag = new CKEDITOR.dom.documentFragment( this.document );
+
+			if ( !this.collapsed )
+				execContentsAction( this, 2, docFrag );
+
+			return docFrag;
+		},
+
+		deleteContents : function()
+		{
+			if ( this.collapsed )
+				return;
+
+			execContentsAction( this, 0 );
+		},
+
+		extractContents : function()
+		{
+			var docFrag = new CKEDITOR.dom.documentFragment( this.document );
+
+			if ( !this.collapsed )
+				execContentsAction( this, 1, docFrag );
+
+			return docFrag;
+		},
+
+		// This is an "intrusive" way to create a bookmark. It includes <span> tags
+		// in the range boundaries. The advantage of it is that it is possible to
+		// handle DOM mutations when moving back to the bookmark.
+		// Attention: the inclusion of nodes in the DOM is a design choice and
+		// should not be changed as there are other points in the code that may be
+		// using those nodes to perform operations. See GetBookmarkNode.
+		createBookmark : function()
+		{
+			var startNode, endNode;
+			var clone;
+
+			startNode = this.document.createElement( 'span' );
+			startNode.setAttribute( '_fck_bookmark', 1 );
+			startNode.setStyle( 'display', 'none' );
+
+			// For IE, it must have something inside, otherwise it may be
+			// removed during DOM operations.
+			startNode.setHtml( '&nbsp;' );
+
+			// If collapsed, the endNode will not be created.
+			if ( !this.collapsed )
+			{
+				endNode = startNode.clone();
+				endNode.setHtml( '&nbsp;' );
+
+				clone = this.clone();
+				clone.collapse();
+				clone.insertNode( endNode );
+			}
+
+			clone = this.clone();
+			clone.collapse( true );
+			clone.insertNode( startNode );
+
+			// Update the range position.
+			if ( endNode )
+			{
+				this.setStartAfter( startNode );
+				this.setEndBefore( endNode );
+			}
+			else
+				this.moveToPosition( startNode, CKEDITOR.POSITION_AFTER_END );
+
+			return {
+				startNode : startNode,
+				endNode : endNode
+			};
+		},
+
+		moveToBookmark : function( bookmark )
+		{
+			// Set the range start at the bookmark start node position.
+			this.setStartBefore( bookmark.startNode );
+
+			// Remove it, because it may interfere in the setEndBefore call.
+			bookmark.startNode.remove();
+
+			// Set the range end at the bookmark end node position, or simply
+			// collapse it if it is not available.
+			var endNode = bookmark.endNode;
+			if ( endNode )
+			{
+				this.setEndBefore( endNode );
+				endNode.remove();
+			}
+			else
+				this.collapse( true );
+		},
+
+		getCommonAncestor : function( includeSelf )
+		{
+			var start = this.startContainer;
+			var end = this.endContainer;
+
+			if ( start.equals( end ) )
+			{
+				if ( includeSelf && start.type == CKEDITOR.NODE_ELEMENT && this.startOffset == this.endOffset - 1 )
+					return start.getChild( this.startOffset );
+				return start;
+			}
+
+			if ( end.type == CKEDITOR.NODE_ELEMENT && end.contains( start ) )
+				return end;
+
+			if ( start.type != CKEDITOR.NODE_ELEMENT )
+				start = start.getParent();
+
+			do
+			{
+				if ( start.contains( end ) )
+					return start;
+			}
+			while( ( start = start.getParent() ) )
+
+			return null;
+		},
+
+		/**
+		 * Transforms the startContainer and endContainer properties from text
+		 * nodes to element nodes, whenever possible. This is actually possible
+		 * if either of the boundary containers point to a text node, and its
+		 * offset is set to zero, or after the last char in the node.
+		 */
+		optimize : function()
+		{
+			var container = this.startContainer;
+			var offset = this.startOffset;
+
+			if ( container.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( !offset )
+					this.setStartBefore( container );
+				else if ( offset >= container.getLength() )
+					this.setStartAfter( container );
+			}
+
+			container = this.endContainer;
+			offset = this.endOffset;
+
+			if ( container.type != CKEDITOR.NODE_ELEMENT )
+			{
+				if ( !offset )
+					this.setEndBefore( container );
+				else if ( offset >= container.getLength() )
+					this.setEndAfter( container );
+			}
+		},
+
+		trim : function( ignoreStart, ignoreEnd )
+		{
+			var startContainer = this.startContainer;
+			var startOffset = this.startOffset;
+
+			var endContainer = this.endContainer;
+			var endOffset = this.endOffset;
+
+			if ( !ignoreStart && startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				// If the offset is zero, we just insert the new node before
+				// the start.
+				if ( !startOffset )
+				{
+					startOffset = startContainer.getIndex();
+					startContainer = startContainer.getParent();
+				}
+				// If the offset is at the end, we'll insert it after the text
+				// node.
+				else if ( startOffset >= startContainer.getLength() )
+				{
+					startOffset = startContainer.getIndex() + 1;
+					startContainer = startContainer.getParent();
+				}
+				// In other case, we split the text node and insert the new
+				// node at the split point.
+				else
+				{
+					var nextText = startContainer.split( startOffset );
+
+					startOffset = startContainer.getIndex() + 1;
+					startContainer = startContainer.getParent();
+
+					// Check if it is necessary to update the end boundary.
+					if ( this.collapsed )
+						this.setEnd( startContainer, startOffset );
+					else if ( this.startContainer.equals( this.endContainer ) )
+						this.setEnd( nextText, this.endOffset - this.startOffset );
+				}
+
+				this.setStart( startContainer, startOffset );
+			}
+
+			if ( !ignoreEnd && endContainer && !this.collapsed && endContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				// If the offset is zero, we just insert the new node before
+				// the start.
+				if ( !endOffset )
+				{
+					endOffset = endContainer.getIndex();
+					endContainer = endContainer.getParent();
+				}
+				// If the offset is at the end, we'll insert it after the text
+				// node.
+				else if ( endOffset >= endContainer.getLength() )
+				{
+					endOffset = endContainer.getIndex() + 1;
+					endContainer = endContainer.getParent();
+				}
+				// In other case, we split the text node and insert the new
+				// node at the split point.
+				else
+				{
+					endContainer.split( endOffset );
+
+					endOffset = endContainer.getIndex() + 1;
+					endContainer = endContainer.getParent();
+				}
+
+				this.setEnd( endContainer, endOffset );
+			}
+		},
+
+		enlarge : function( unit )
+		{
+			switch ( unit )
+			{
+				case CKEDITOR.ENLARGE_ELEMENT :
+
+					if ( this.collapsed )
+						return;
+
+					// Get the common ancestor.
+					var commonAncestor = this.getCommonAncestor();
+
+					var body = this.document.getBody();
+
+					// For each boundary
+					//		a. Depending on its position, find out the first node to be checked (a sibling) or, if not available, to be enlarge.
+					//		b. Go ahead checking siblings and enlarging the boundary as much as possible until the common ancestor is not reached. After reaching the common ancestor, just save the enlargeable node to be used later.
+
+					var startTop, endTop;
+
+					var enlargeable, sibling, commonReached;
+
+					// Indicates that the node can be added only if whitespace
+					// is available before it.
+					var needsWhiteSpace = false;
+					var isWhiteSpace;
+					var siblingText;
+
+					// Process the start boundary.
+
+					var container = this.startContainer;
+					var offset = this.startOffset;
+
+					if ( container.type == CKEDITOR.NODE_TEXT )
+					{
+						if ( offset )
+						{
+							// Check if there is any non-space text before the
+							// offset. Otherwise, container is null.
+							container = !CKEDITOR.tools.trim( container.substring( 0, offset ) ).length && container;
+
+							// If we found only whitespace in the node, it
+							// means that we'll need more whitespace to be able
+							// to expand. For example, <i> can be expanded in
+							// "A <i> [B]</i>", but not in "A<i> [B]</i>".
+							needsWhiteSpace = !!container;
+						}
+
+						if ( container )
+						{
+							if ( !( sibling = container.getPrevious() ) )
+								enlargeable = container.getParent();
+						}
+					}
+					else
+					{
+						// If we have offset, get the node preceeding it as the
+						// first sibling to be checked.
+						if ( offset )
+							sibling = container.getChild( offset - 1 ) || container.getLast();
+
+						// If there is no sibling, mark the container to be
+						// enlarged.
+						if ( !sibling )
+							enlargeable = container;
+					}
+
+					while ( enlargeable || sibling )
+					{
+						if ( enlargeable && !sibling )
+						{
+							// If we reached the common ancestor, mark the flag
+							// for it.
+							if ( !commonReached && enlargeable.equals( commonAncestor ) )
+								commonReached = true;
+
+							if ( !body.contains( enlargeable ) )
+								break;
+
+							// If we don't need space or this element breaks
+							// the line, then enlarge it.
+							if ( !needsWhiteSpace || enlargeable.getComputedStyle( 'display' ) != 'inline' )
+							{
+								needsWhiteSpace = false;
+
+								// If the common ancestor has been reached,
+								// we'll not enlarge it immediately, but just
+								// mark it to be enlarged later if the end
+								// boundary also enlarges it.
+								if ( commonReached )
+									startTop = enlargeable;
+								else
+									this.setStartBefore( enlargeable );
+							}
+
+							sibling = enlargeable.getPrevious();
+						}
+
+						// Check all sibling nodes preceeding the enlargeable
+						// node. The node wil lbe enlarged only if none of them
+						// blocks it.
+						while ( sibling )
+						{
+							// This flag indicates that this node has
+							// whitespaces at the end.
+							isWhiteSpace = false;
+
+							if ( sibling.type == CKEDITOR.NODE_TEXT )
+							{
+								siblingText = sibling.getText();
+
+								if ( /[^\s\ufeff]/.test( siblingText ) )
+									sibling = null;
+
+								isWhiteSpace = /[\s\ufeff]$/.test( siblingText );
+							}
+							else
+							{
+								// If this is a visible element.
+								if ( sibling.$.offsetWidth > 0 )
+								{
+									// We'll accept it only if we need
+									// whitespace, and this is an inline
+									// element with whitespace only.
+									if ( needsWhiteSpace && CKEDITOR.dtd.$removeEmpty[ sibling.getName() ] )
+									{
+										// It must contains spaces and inline elements only.
+
+										siblingText = sibling.getText();
+
+										if ( !(/[^\s\ufeff]/).test( siblingText ) )	// Spaces + Zero Width No-Break Space (U+FEFF)
+											sibling = null;
+										else
+										{
+											var allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
+											for ( var i = 0, child ; child = allChildren[ i++ ] ; )
+											{
+												if ( !CKEDITOR.dtd.$removeEmpty[ child.nodeName.toLowerCase() ] )
+												{
+													sibling = null;
+													break;
+												}
+											}
+										}
+
+										if ( sibling )
+											isWhiteSpace = !!siblingText.length;
+									}
+									else
+										sibling = null;
+								}
+							}
+
+							// A node with whitespaces has been found.
+							if ( isWhiteSpace )
+							{
+								// Enlarge the last enlargeable node, if we
+								// were waiting for spaces.
+								if ( needsWhiteSpace )
+								{
+									if ( commonReached )
+										startTop = enlargeable;
+									else if ( enlargeable )
+										this.setStartBefore( enlargeable );
+								}
+								else
+									needsWhiteSpace = true;
+							}
+
+							if ( sibling )
+							{
+								var next = sibling.getPrevious();
+
+								if ( !enlargeable && !next )
+								{
+									// Set the sibling as enlargeable, so it's
+									// parent will be get later outside this while.
+									enlargeable = sibling;
+									sibling = null;
+									break;
+								}
+
+								sibling = next;
+							}
+							else
+							{
+								// If sibling has been set to null, then we
+								// need to stop enlarging.
+								enlargeable = null;
+							}
+						}
+
+						if ( enlargeable )
+							enlargeable = enlargeable.getParent();
+					}
+
+					// Process the end boundary. This is basically the same
+					// code used for the start boundary, with small changes to
+					// make it work in the oposite side (to the right). This
+					// makes it difficult to reuse the code here. So, fixes to
+					// the above code are likely to be replicated here.
+
+					container = this.endContainer;
+					offset = this.endOffset;
+
+					// Reset the common variables.
+					enlargeable = sibling = null;
+					commonReached = needsWhiteSpace = false;
+
+					if ( container.type == CKEDITOR.NODE_TEXT )
+					{
+						// Check if there is any non-space text after the
+						// offset. Otherwise, container is null.
+						container = !CKEDITOR.tools.trim( container.substring( offset ) ).length && container;
+
+						// If we found only whitespace in the node, it
+						// means that we'll need more whitespace to be able
+						// to expand. For example, <i> can be expanded in
+						// "A <i> [B]</i>", but not in "A<i> [B]</i>".
+						needsWhiteSpace = !( container && container.getLength() );
+
+						if ( container )
+						{
+							if ( !( sibling = container.getNext() ) )
+								enlargeable = container.getParent();
+						}
+					}
+					else
+					{
+						// Get the node right after the boudary to be checked
+						// first.
+						sibling = container.getChild( offset );
+
+						if ( !sibling )
+							enlargeable = container;
+					}
+
+					while ( enlargeable || sibling )
+					{
+						if ( enlargeable && !sibling )
+						{
+							if ( !commonReached && enlargeable.equals( commonAncestor ) )
+								commonReached = true;
+
+							if ( !body.contains( enlargeable ) )
+								break;
+
+							if ( !needsWhiteSpace || enlargeable.getComputedStyle( 'display' ) != 'inline' )
+							{
+								needsWhiteSpace = false;
+
+								if ( commonReached )
+									endTop = enlargeable;
+								else if ( enlargeable )
+									this.setEndAfter( enlargeable );
+							}
+
+							sibling = enlargeable.getNext();
+						}
+
+						while ( sibling )
+						{
+							isWhiteSpace = false;
+
+							if ( sibling.type == CKEDITOR.NODE_TEXT )
+							{
+								siblingText = sibling.getText();
+
+								if ( /[^\s\ufeff]/.test( siblingText ) )
+									sibling = null;
+
+								isWhiteSpace = /^[\s\ufeff]/.test( siblingText );
+							}
+							else
+							{
+								// If this is a visible element.
+								if ( sibling.$.offsetWidth > 0 )
+								{
+									// We'll accept it only if we need
+									// whitespace, and this is an inline
+									// element with whitespace only.
+									if ( needsWhiteSpace && CKEDITOR.dtd.$removeEmpty[ sibling.getName() ] )
+									{
+										// It must contains spaces and inline elements only.
+
+										siblingText = sibling.getText();
+
+										if ( !(/[^\s\ufeff]/).test( siblingText ) )
+											sibling = null;
+										else
+										{
+											allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
+											for ( i = 0 ; child = allChildren[ i++ ] ; )
+											{
+												if ( !CKEDITOR.dtd.$removeEmpty[ child.nodeName.toLowerCase() ] )
+												{
+													sibling = null;
+													break;
+												}
+											}
+										}
+
+										if ( sibling )
+											isWhiteSpace = !!siblingText.length;
+									}
+									else
+										sibling = null;
+								}
+							}
+
+							if ( isWhiteSpace )
+							{
+								if ( needsWhiteSpace )
+								{
+									if ( commonReached )
+										endTop = enlargeable;
+									else
+										this.setEndAfter( enlargeable );
+								}
+							}
+
+							if ( sibling )
+							{
+								next = sibling.getNext();
+
+								if ( !enlargeable && !next )
+								{
+									enlargeable = sibling;
+									sibling = null;
+									break;
+								}
+
+								sibling = next;
+							}
+							else
+							{
+								// If sibling has been set to null, then we
+								// need to stop enlarging.
+								enlargeable = null;
+							}
+						}
+
+						if ( enlargeable )
+							enlargeable = enlargeable.getParent();
+					}
+
+					// If the common ancestor can be enlarged by both boundaries, then include it also.
+					if ( startTop && endTop )
+					{
+						commonAncestor = startTop.contains( endTop ) ? endTop : startTop;
+
+						this.setStartBefore( commonAncestor );
+						this.setEndAfter( commonAncestor );
+					}
+					break;
+
+				case CKEDITOR.ENLARGE_BLOCK_CONTENTS:
+				case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:
+					// DFS backward to get the block/list item boundary at or before the start.
+					var boundaryNodes = getBoundaryNodes.apply( this ),
+						startNode = boundaryNodes.startNode,
+						endNode = boundaryNodes.endNode,
+						guardFunction = ( unit == CKEDITOR.ENLARGE_BLOCK_CONTENTS ?
+							CKEDITOR.dom.domWalker.blockBoundary() :
+							CKEDITOR.dom.domWalker.listItemBoundary() ),
+						walker = new CKEDITOR.dom.domWalker( startNode ),
+						data = walker.reverse( guardFunction ),
+						boundaryEvent = data.events.shift();
+
+					this.setStartBefore( boundaryEvent.from );
+
+					// DFS forward to get the block/list item boundary at or before the end.
+					walker.setNode( endNode );
+					data = walker.forward( guardFunction );
+					boundaryEvent = data.events.shift();
+
+					this.setEndAfter( boundaryEvent.from );
+					break;
+
+				default:
+			}
+		},
+
+		/**
+		 * Inserts a node at the start of the range. The range will be expanded
+		 * the contain the node.
+		 */
+		insertNode : function( node )
+		{
+			this.trim( false, true );
+
+			var startContainer = this.startContainer;
+			var startOffset = this.startOffset;
+
+			var nextNode = startContainer.getChild( startOffset );
+
+			if ( nextNode )
+				node.insertBefore( nextNode );
+			else
+				startContainer.append( node );
+
+			// Check if we need to update the end boundary.
+			if ( node.getParent().equals( this.endContainer ) )
+				this.endOffset++;
+
+			// Expand the range to embrace the new node.
+			this.setStartBefore( node );
+		},
+
+		moveToPosition : function( node, position )
+		{
+			this.setStartAt( node, position );
+			this.collapse( true );
+		},
+
+		selectNodeContents : function( node )
+		{
+			this.setStart( node, 0 );
+			this.setEnd( node, node.type == CKEDITOR.NODE_TEXT ? node.getLength() : node.getChildCount() );
+		},
+
+		/**
+		 * Sets the start position of a Range.
+		 * @param {CKEDITOR.dom.node} startNode The node to start the range.
+		 * @param {Number} startOffset An integer greater than or equal to zero
+		 *		representing the offset for the start of the range from the start
+		 *		of startNode.
+		 */
+		setStart : function( startNode, startOffset )
+		{
+			// W3C requires a check for the new position. If it is after the end
+			// boundary, the range should be collapsed to the new start. It seams
+			// we will not need this check for our use of this class so we can
+			// ignore it for now.
+
+			this.startContainer	= startNode;
+			this.startOffset	= startOffset;
+
+			if ( !this.endContainer )
+			{
+				this.endContainer	= startNode;
+				this.endOffset		= startOffset;
+			}
+
+			updateCollapsed( this );
+		},
+
+		/**
+		 * Sets the end position of a Range.
+		 * @param {CKEDITOR.dom.node} endNode The node to end the range.
+		 * @param {Number} endOffset An integer greater than or equal to zero
+		 *		representing the offset for the end of the range from the start
+		 *		of endNode.
+		 */
+		setEnd : function( endNode, endOffset )
+		{
+			// W3C requires a check for the new position. If it is before the start
+			// boundary, the range should be collapsed to the new end. It seams we
+			// will not need this check for our use of this class so we can ignore
+			// it for now.
+
+			this.endContainer	= endNode;
+			this.endOffset		= endOffset;
+
+			if ( !this.startContainer )
+			{
+				this.startContainer	= endNode;
+				this.startOffset	= endOffset;
+			}
+
+			updateCollapsed( this );
+		},
+
+		setStartAfter : function( node )
+		{
+			this.setStart( node.getParent(), node.getIndex() + 1 );
+		},
+
+		setStartBefore : function( node )
+		{
+			this.setStart( node.getParent(), node.getIndex() );
+		},
+
+		setEndAfter : function( node )
+		{
+			this.setEnd( node.getParent(), node.getIndex() + 1 );
+		},
+
+		setEndBefore : function( node )
+		{
+			this.setEnd( node.getParent(), node.getIndex() );
+		},
+
+		setStartAt : function( node, position )
+		{
+			switch( position )
+			{
+				case CKEDITOR.POSITION_AFTER_START :
+					this.setStart( node, 0 );
+					break;
+
+				case CKEDITOR.POSITION_BEFORE_END :
+					if ( node.type == CKEDITOR.NODE_TEXT )
+						this.setStart( node, node.getLength() );
+					else
+						this.setStart( node, node.getChildCount() );
+					break;
+
+				case CKEDITOR.POSITION_BEFORE_START :
+					this.setStartBefore( node );
+					break;
+
+				case CKEDITOR.POSITION_AFTER_END :
+					this.setStartAfter( node );
+			}
+
+			updateCollapsed( this );
+		},
+
+		setEndAt : function( node, position )
+		{
+			switch( position )
+			{
+				case CKEDITOR.POSITION_AFTER_START :
+					this.setEnd( node, 0 );
+					break;
+
+				case CKEDITOR.POSITION_BEFORE_END :
+					if ( node.type == CKEDITOR.NODE_TEXT )
+						this.setEnd( node, node.getLength() );
+					else
+						this.setEnd( node, node.getChildCount() );
+					break;
+
+				case CKEDITOR.POSITION_BEFORE_START :
+					this.setEndBefore( node );
+					break;
+
+				case CKEDITOR.POSITION_AFTER_END :
+					this.setEndAfter( node );
+			}
+
+			updateCollapsed( this );
+		},
+
+		// TODO: The fixed block isn't trimmed, does not work for <pre>.
+		// TODO: Does not add bogus <br> to empty fixed blocks.
+		fixBlock : function( isStart, blockTag )
+		{
+			var bookmark = this.createBookmark(),
+				fixedBlock = new CKEDITOR.dom.element( blockTag, this.document );
+			this.collapse( isStart );
+			this.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+			this.extractContents().appendTo( fixedBlock );
+			this.insertNode( fixedBlock );
+			this.moveToBookmark( bookmark );
+			return fixedBlock;
+		},
+
+		splitBlock : function( blockTag )
+		{
+			var startPath = new CKEDITOR.dom.elementPath( this.startContainer ),
+				endPath = new CKEDITOR.dom.elementPath( this.endContainer ),
+				startBlockLimit = startPath.blockLimit,
+				endBlockLimit = endPath.blockLimit,
+				startBlock = startPath.block,
+				endBlock = endPath.block,
+				elementPath = null;
+
+			if ( !startBlockLimit.equals( endBlockLimit ) )
+				return null;
+
+			// Get or fix current blocks.
+			if ( blockTag != 'br' )
+			{
+				if ( !startBlock )
+				{
+					startBlock = this.fixBlock( true, blockTag );
+					endBlock = new CKEDITOR.dom.elementPath( this.endContainer );
+				}
+
+				if ( !endBlock )
+					endBlock = this.fixBlock( false, blockTag );
+			}
+
+			// Get the range position.
+			var isStartOfBlock = startBlock && this.checkStartOfBlock(),
+				isEndOfBlock = endBlock && this.checkEndOfBlock();
+
+			// Delete the current contents.
+			// TODO: Why is 2.x doing CheckIsEmpty()?
+			this.deleteContents();
+
+			if ( startBlock && startBlock.equals( endBlock ) )
+			{
+				if ( isEndOfBlock )
+				{
+					elementPath = new CKEDITOR.dom.elementPath( this.startContainer );
+					this.moveToPosition( endBlock, CKEDITOR.POSITION_AFTER_END );
+					endBlock = null;
+				}
+				else if ( isStartOfBlock )
+				{
+					elementPath = new CKEDITOR.dom.elementPath( this.startContainer );
+					this.moveToPosition( startBlock, CKEDITOR.POSITION_BEFORE_START );
+					startBlock = null;
+				}
+				else
+				{
+					// Extract the contents of the block from the selection point to the end
+					// of its contents.
+					this.setEndAt( startBlock, CKEDITOR.POSITION_BEFORE_END );
+					var documentFragment = this.extractContents();
+
+					// Duplicate the block element after it.
+					endBlock = startBlock.clone( false );
+					endBlock.removeAttribute( 'id' );
+
+					// Place the extracted contents into the duplicated block.
+					documentFragment.appendTo( endBlock );
+					endBlock.insertAfter( startBlock );
+					this.moveToPosition( startBlock, CKEDITOR.POSITION_AFTER_END );
+
+					// TODO: Append bogus br to startBlock for Gecko
+				}
+			}
+
+			return {
+				previousBlock : startBlock,
+				nextBlock : endBlock,
+				wasStartOfBlock : isStartOfBlock,
+				wasEndOfBlock : isEndOfBlock,
+				elementPath : elementPath
+			};
+		},
+
+		checkStartOfBlock : function()
+		{
+			var startContainer = this.startContainer,
+				startOffset = this.startOffset;
+
+			// If the starting node is a text node, and non-empty before the offset,
+			// then we're surely not at the start of block.
+			if ( startContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				var textBefore = CKEDITOR.tools.ltrim( startContainer.getText().substr( 0, startOffset ) );
+				if ( textBefore.length > 0 )
+					return false;
+			}
+
+			var startNode = getBoundaryNodes.apply( this ).startNode,
+				walker = new CKEDITOR.dom.domWalker( startNode );
+
+			// DFS backwards until the block boundary, with the checker function.
+			walker.on( 'step', getCheckStartEndBlockFunction( true ), null, null, 20 );
+			walker.reverse( CKEDITOR.dom.domWalker.blockBoundary() );
+
+			return !walker.checkFailed;
+		},
+
+		checkEndOfBlock : function()
+		{
+			var endContainer = this.endContainer,
+				endOffset = this.endOffset;
+
+			// If the ending node is a text node, and non-empty after the offset,
+			// then we're surely not at the end of block.
+			if ( endContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				var textAfter = CKEDITOR.tools.rtrim( endContainer.getText().substr( endOffset ) );
+				if ( textAfter.length > 0 )
+					return false;
+			}
+
+			var endNode = getBoundaryNodes.apply( this ).endNode,
+				walker = new CKEDITOR.dom.domWalker( endNode );
+
+			// DFS forward until the block boundary, with the checker function.
+			walker.on( 'step', getCheckStartEndBlockFunction( false ), null, null, 20 );
+			walker.forward( CKEDITOR.dom.domWalker.blockBoundary() );
+
+			return !walker.checkFailed;
+		}
+	};
+})();
+
+CKEDITOR.POSITION_AFTER_START	= 1;	// <element>^contents</element>		"^text"
+CKEDITOR.POSITION_BEFORE_END	= 2;	// <element>contents^</element>		"text^"
+CKEDITOR.POSITION_BEFORE_START	= 3;	// ^<element>contents</element>		^"text"
+CKEDITOR.POSITION_AFTER_END		= 4;	// <element>contents</element>^		"text"
+
+CKEDITOR.ENLARGE_ELEMENT = 1;
+CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2;
+CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3;
Index: /CKEditor/trunk/_source/core/dom/text.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/text.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/text.js	(revision 2948)
@@ -0,0 +1,101 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.text} class, which represents
+ *		a DOM text node.
+ */
+
+/**
+ * Represents a DOM text node.
+ * @constructor
+ * @augments CKEDITOR.dom.node
+ * @param {Object|String} text A native DOM text node or a string containing
+ *		the text to use to create a new text node.
+ * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
+ *		the node in case of new node creation. Defaults to the current document.
+ * @example
+ * var nativeNode = document.createTextNode( 'Example' );
+ * var text = CKEDITOR.dom.text( nativeNode );
+ * @example
+ * var text = CKEDITOR.dom.text( 'Example' );
+ */
+CKEDITOR.dom.text = function( text, ownerDocument )
+{
+	if ( typeof text == 'string' )
+		text = ( ownerDocument ? ownerDocument.$ : document ).createTextNode( text );
+
+	// Theoretically, we should call the base constructor here
+	// (not CKEDITOR.dom.node though). But, IE doesn't support expando
+	// properties on text node, so the features provided by domObject will not
+	// work for text nodes (which is not a big issue for us).
+	//
+	// CKEDITOR.dom.domObject.call( this, element );
+
+	/**
+	 * The native DOM text node represented by this class instance.
+	 * @type Object
+	 * @example
+	 * var element = new CKEDITOR.dom.text( 'Example' );
+	 * alert( element.$.nodeType );  // "3"
+	 */
+	this.$ = text;
+};
+
+CKEDITOR.dom.text.prototype = new CKEDITOR.dom.node();
+
+CKEDITOR.tools.extend( CKEDITOR.dom.text.prototype,
+	/** @lends CKEDITOR.dom.text.prototype */
+	{
+		/**
+		 * The node type. This is a constant value set to
+		 * {@link CKEDITOR.NODE_TEXT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_TEXT,
+
+		getLength : function()
+		{
+			return this.$.nodeValue.length;
+		},
+
+		getText : function()
+		{
+			return this.$.nodeValue;
+		},
+
+		/**
+		 * Breaks this text node into two nodes at the specified offset,
+		 * keeping both in the tree as siblings. This node then only contains
+		 * all the content up to the offset point. A new text node, which is
+		 * inserted as the next sibling of this node, contains all the content
+		 * at and after the offset point. When the offset is equal to the
+		 * length of this node, the new node has no data.
+		 * @param {Number} The position at which to split, starting from zero.
+		 * @returns {CKEDITOR.dom.text} The new text node.
+		 */
+		split : function( offset )
+		{
+			return new CKEDITOR.dom.text( this.$.splitText( offset ) );
+		},
+
+		/**
+		 * Extracts characters from indexA up to but not including indexB.
+		 * @param {Number} indexA An integer between 0 and one less than the
+		 *		length of the text.
+		 * @param {Number} [indexB] An integer between 0 and the length of the
+		 *		string. If omitted, extracts characters to the end of the text.
+		 */
+		substring : function( indexA, indexB )
+		{
+			// We need the following check due to a Firefox bug
+			// https://bugzilla.mozilla.org/show_bug.cgi?id=458886
+			if ( typeof indexB != 'number' )
+				return this.$.nodeValue.substr( indexA );
+			else
+				return this.$.nodeValue.substring( indexA, indexB );
+		}
+	});
Index: /CKEditor/trunk/_source/core/dom/window.js
===================================================================
--- /CKEditor/trunk/_source/core/dom/window.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dom/window.js	(revision 2948)
@@ -0,0 +1,93 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
+ *		represents a DOM document.
+ */
+
+/**
+ * Represents a DOM window.
+ * @constructor
+ * @augments CKEDITOR.dom.domObject
+ * @param {Object} domWindow A native DOM window.
+ * @example
+ * var document = new CKEDITOR.dom.window( window );
+ */
+CKEDITOR.dom.window = function( domWindow )
+{
+	CKEDITOR.dom.domObject.call( this, domWindow );
+};
+
+CKEDITOR.dom.window.prototype = new CKEDITOR.dom.domObject();
+
+CKEDITOR.tools.extend( CKEDITOR.dom.window.prototype,
+	/** @lends CKEDITOR.dom.window.prototype */
+	{
+		/**
+		 * Moves the selection focus to this window.
+		 * @function
+		 * @example
+		 * var win = new CKEDITOR.dom.window( window );
+		 * <b>win.focus()</b>;
+		 */
+		focus : function()
+		{
+			this.$.focus();
+		},
+
+		/**
+		 * Gets the width and height of this window's viewable area.
+		 * @function
+		 * @returns {Object} An object with the "width" and "height"
+		 *		properties containing the size.
+		 * @example
+		 * var win = new CKEDITOR.dom.window( window );
+		 * var size = <b>win.getViewPaneSize()</b>;
+		 * alert( size.width );
+		 * alert( size.height );
+		 */
+		getViewPaneSize : function()
+		{
+			var doc = this.$.document,
+				stdMode = doc.compatMode == 'CSS1Compat';
+			return {
+				width : ( stdMode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
+				height : ( stdMode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
+			};
+		},
+
+		/**
+		 * Gets the current position of the window's scroll.
+		 * @function
+		 * @returns {Object} An object with the "x" and "y" properties
+		 *		containing the scroll position.
+		 * @example
+		 * var win = new CKEDITOR.dom.window( window );
+		 * var pos = <b>win.getScrollPosition()</b>;
+		 * alert( pos.x );
+		 * alert( pos.y );
+		 */
+		getScrollPosition : function()
+		{
+			var $ = this.$;
+
+			if ( 'pageXOffset' in $ )
+			{
+				return {
+					x : $.pageXOffset || 0,
+					y : $.pageYOffset || 0
+				};
+			}
+			else
+			{
+				var doc = $.document;
+				return {
+					x : doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
+					y : doc.documentElement.scrollTop || doc.body.scrollTop || 0
+				};
+			}
+		}
+	});
Index: /CKEditor/trunk/_source/core/dtd.js
===================================================================
--- /CKEditor/trunk/_source/core/dtd.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/dtd.js	(revision 2948)
@@ -0,0 +1,186 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dtd} object, which holds the DTD
+ *		mapping for XHTML 1.0 Transitional. This file was automatically
+ *		generated from the file: xhtml1-transitional.dtd.
+ */
+
+/**
+ * Holds and object representation of the HTML DTD to be used by the editor in
+ * its internal operations.
+ *
+ * Each element in the DTD is represented by a
+ * property in this object. Each property contains the list of elements that
+ * can be contained by the element. Text is represented by the "#" property.
+ *
+ * Several special grouping properties are also available. Their names start
+ * with the "$" character.
+ * @namespace
+ * @example
+ * // Check if "div" can be contained in a "p" element.
+ * alert( !!CKEDITOR.dtd[ 'p' ][ 'div' ] );  "false"
+ * @example
+ * // Check if "p" can be contained in a "div" element.
+ * alert( !!CKEDITOR.dtd[ 'div' ][ 'p' ] );  "true"
+ * @example
+ * // Check if "p" is a block element.
+ * alert( !!CKEDITOR.dtd.$block[ 'p' ] );  "true"
+ */
+CKEDITOR.dtd = (function()
+{
+    var X = CKEDITOR.tools.extend,
+
+		A = {isindex:1,fieldset:1},
+		B = {input:1,button:1,select:1,textarea:1,label:1},
+		C = X({a:1},B),
+		D = X({iframe:1},C),
+		E = {hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1},
+		F = {ins:1,del:1,script:1},
+		G = X({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1},F),
+		H = X({sub:1,img:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1},G),
+		I = X({p:1},H),
+		J = X({iframe:1},H,B),
+		K = {img:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1},
+
+		L = X({a:1},J),
+		M = {tr:1},
+		N = {'#':1},
+		O = X({param:1},K),
+		P = X({form:1},A,D,E,I),
+		Q = {li:1};
+
+    return /** @lends CKEDITOR.dtd */ {
+
+		// The "$" items have been added manually.
+
+		/**
+		 * List of block elements, like "p" or "div".
+		 * @type Object
+		 * @example
+		 */
+		$block : {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1},
+
+		/**
+		 * List of empty (self-closing) elements, like "br" or "img".
+		 * @type Object
+		 * @example
+		 */
+		$empty : {area:1,base:1,br:1,col:1,hr:1,img:1,input:1,link:1,meta:1,param:1},
+
+		/**
+		 * List of list item elements, like "li" or "dd".
+		 * @type Object
+		 * @example
+		 */
+		$listItem : {dd:1,dt:1,li:1},
+
+		/**
+		 * List of elements that can be ignored if empty, like "b" or "span".
+		 * @type Object
+		 * @example
+		 */
+		$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},
+
+		/**
+		 * List of elements used inside the "table" element, like "tbody" or "td".
+		 * @type Object
+		 * @example
+		 */
+		$tableContent : {caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1},
+
+        col : {},
+        tr : {td:1,th:1},
+        img : {},
+        colgroup : {col:1},
+        noscript : P,
+        td : P,
+        br : {},
+        th : P,
+        center : P,
+        kbd : L,
+        button : X(I,E),
+        basefont : {},
+        h5 : L,
+        h4 : L,
+        samp : L,
+        h6 : L,
+        ol : Q,
+        h1 : L,
+        h3 : L,
+        option : N,
+        h2 : L,
+        form : X(A,D,E,I),
+        select : {optgroup:1,option:1},
+        font : L,
+        ins : P,
+        menu : Q,
+        abbr : L,
+        label : L,
+        table : {thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1},
+        code : L,
+        script : N,
+        tfoot : M,
+        cite : L,
+        li : P,
+        input : {},
+        iframe : P,
+        strong : L,
+        textarea : N,
+        noframes : P,
+        big : L,
+        small : L,
+        span : L,
+        hr : {},
+        dt : L,
+        sub : L,
+        optgroup : {option:1},
+        param : {},
+        bdo : L,
+        'var' : L,
+        div : P,
+        object : O,
+        sup : L,
+        dd : P,
+        strike : L,
+        area : {},
+        dir : Q,
+        map : X({area:1,form:1,p:1},A,F,E),
+        applet : O,
+        dl : {dt:1,dd:1},
+        del : P,
+        isindex : {},
+        fieldset : X({legend:1},K),
+        thead : M,
+        ul : Q,
+        acronym : L,
+        b : L,
+        a : J,
+        blockquote : P,
+        caption : L,
+        i : L,
+        u : L,
+        tbody : M,
+        s : L,
+        address : X(D,I),
+        tt : L,
+        legend : L,
+        q : L,
+        pre : X(G,C),
+        p : L,
+        em : L,
+        dfn : L
+    };
+})();
+
+// PACKAGER_RENAME( CKEDITOR.dtd )
Index: /CKEditor/trunk/_source/core/editor.js
===================================================================
--- /CKEditor/trunk/_source/core/editor.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/editor.js	(revision 2948)
@@ -0,0 +1,479 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.editor} class, which represents an
+ *		editor instance.
+ */
+
+(function()
+{
+	// The counter for automatic instance names.
+	var nameCounter = 0;
+
+	var getNewName = function()
+	{
+		var name = 'editor' + ( ++nameCounter );
+		return ( CKEDITOR.instances && CKEDITOR.instances[ name ] ) ? getNewName() : name;
+	};
+
+	// ##### START: Config Privates
+
+	// These function loads custom configuration files and cache the
+	// CKEDITOR.editorConfig functions defined on them, so there is no need to
+	// download them more than once for several instances.
+	var loadConfigLoaded = {};
+	var loadConfig = function( editor )
+	{
+		var customConfig = editor.config.customConfig;
+
+		// Check if there is a custom config to load.
+		if ( !customConfig )
+			return false;
+
+		var loadedConfig = loadConfigLoaded[ customConfig ] || ( loadConfigLoaded[ customConfig ] = {} );
+
+
+		// If the custom config has already been downloaded, reuse it.
+		if ( loadedConfig.fn )
+		{
+			// Call the cached CKEDITOR.editorConfig defined in the custom
+			// config file for the editor instance depending on it.
+			loadedConfig.fn.call( editor, editor.config );
+
+			// If there is no other customConfig in the chain, fire the
+			// "configLoaded" event.
+			if ( editor.config.customConfig == customConfig || !loadConfig( editor ) )
+				editor.fireOnce( 'customConfigLoaded' );
+		}
+		else
+		{
+			// Load the custom configuration file.
+			CKEDITOR.scriptLoader.load( customConfig, function()
+				{
+					// If the CKEDITOR.editorConfig function has been properly
+					// defined in the custom configuration file, cache it.
+					if ( CKEDITOR.editorConfig )
+						loadedConfig.fn = CKEDITOR.editorConfig;
+					else
+						loadedConfig.fn = function(){};
+
+					// Call the load config again. This time the custom
+					// config is already cached and so it will get loaded.
+					loadConfig( editor );
+				});
+		}
+
+		return true;
+	};
+
+	var initConfig = function( editor, instanceConfig )
+	{
+		// Setup the lister for the "customConfigLoaded" event.
+		editor.on( 'customConfigLoaded', function()
+			{
+				// Overwrite the settings from the in-page config.
+				if ( instanceConfig )
+					CKEDITOR.tools.extend( editor.config, instanceConfig, true );
+
+				// Fire the "configLoaded" event.
+				editor.fireOnce( 'configLoaded' );
+
+				loadLang( editor );
+			});
+
+		// The instance config may override the customConfig setting to avoid
+		// loading the default ~/config.js file.
+		if ( instanceConfig && instanceConfig.customConfig != undefined )
+			editor.config.customConfig = instanceConfig.customConfig;
+
+		// Load configs from the custom configuration files.
+		if ( !loadConfig( editor ) )
+			editor.fireOnce( 'customConfigLoaded' );
+	};
+
+	// ##### END: Config Privates
+
+	var loadLang = function( editor )
+	{
+		CKEDITOR.lang.load( editor.config.defaultLanguage, editor.config.autoLanguage, function( languageCode, lang )
+			{
+				editor.langCode = languageCode;
+
+				// As we'll be adding plugin specific entries that could come
+				// from different language code files, we need a copy of lang,
+				// not a direct reference to it.
+				editor.lang = CKEDITOR.tools.prototypedCopy( lang );
+
+				loadPlugins( editor );
+			});
+	};
+
+	var loadPlugins = function( editor )
+	{
+		// Load all plugins defined in the "plugins" setting.
+		CKEDITOR.plugins.load( editor.config.plugins.split( ',' ), function( plugins )
+			{
+				// The list of plugins.
+				var pluginsArray = [];
+
+				// The language code to get loaded for each plugin. Null
+				// entries will be appended for plugins with no language files.
+				var languageCodes = [];
+
+				// The list of URLs to language files.
+				var languageFiles = [];
+
+				// Cache the loaded plugin names.
+				editor.plugins = plugins;
+
+				// Loop through all plugins, to build the list of language
+				// files to get loaded.
+				for ( var pluginName in plugins )
+				{
+					var plugin = plugins[ pluginName ],
+						pluginLangs = plugin.lang,
+						pluginPath = CKEDITOR.plugins.getPath( pluginName ),
+						lang = null;
+
+					// Set the plugin path in the plugin.
+					plugin.path = pluginPath;
+
+					// If the plugin has "lang".
+					if ( pluginLangs )
+					{
+						// Resolve the plugin language. If the current language
+						// is not available, get the first one (default one).
+						lang = ( CKEDITOR.tools.indexOf( pluginLangs, editor.langCode ) >= 0 ? editor.langCode : pluginLangs[ 0 ] );
+
+						if ( !plugin.lang[ lang ] )
+						{
+							// Put the language file URL into the list of files to
+							// get downloaded.
+							languageFiles.push( CKEDITOR.getUrl( pluginPath + 'lang/' + lang + '.js' ) );
+						}
+						else
+						{
+							CKEDITOR.tools.extend( editor.lang, plugin.lang[ lang ] );
+							lang = null;
+						}
+					}
+
+					// Save the language code, so we know later which
+					// language has been resolved to this plugin.
+					languageCodes.push( lang );
+
+					pluginsArray.push( plugin );
+				}
+
+				// Load all plugin specific language files in a row.
+				CKEDITOR.scriptLoader.load( languageFiles, function()
+					{
+						// Initialize all plugins that have the "beforeInit" and "init" methods defined.
+						var methods = [ 'beforeInit', 'init' ];
+						for ( var m = 0 ; m < methods.length ; m++ )
+						{
+							for ( var i = 0 ; i < pluginsArray.length ; i++ )
+							{
+								var plugin = pluginsArray[ i ];
+
+								// Uses the first loop to update the language entries also.
+								if ( m === 0 && languageCodes[ i ] && plugin.lang )
+									CKEDITOR.tools.extend( editor.lang, plugin.lang[ languageCodes[ i ] ] );
+
+								// Call the plugin method (beforeInit and init).
+								if ( plugin[ methods[ m ] ] )
+									plugin[ methods[ m ] ]( editor );
+							}
+						}
+
+						// Load the editor skin.
+						loadSkin( editor );
+					});
+			});
+	};
+
+	var loadSkin = function( editor )
+	{
+		CKEDITOR.skins.load( editor.config.skin, 'editor', function()
+			{
+				loadTheme( editor );
+			});
+	};
+
+	var loadTheme = function( editor )
+	{
+		var theme = editor.config.theme;
+		CKEDITOR.themes.load( theme, function()
+			{
+				var editorTheme = editor.theme = CKEDITOR.themes.get( theme );
+				editorTheme.path = CKEDITOR.themes.getPath( theme );
+				editorTheme.build( editor );
+			});
+	};
+
+	/**
+	 * Initializes the editor instance. This function is called by the editor
+	 * contructor (editor_basic.js).
+	 * @private
+	 */
+	CKEDITOR.editor.prototype._init = function()
+		{
+			// Get the properties that have been saved in the editor_base
+			// implementation.
+			var element			= CKEDITOR.dom.element.get( this._.element ),
+				instanceConfig	= this._.instanceConfig;
+			delete this._.element;
+			delete this._.instanceConfig;
+
+			this._.commands = {};
+
+			/**
+			 * The DOM element that has been replaced by this editor instance. This
+			 * element holds the editor data on load and post.
+			 * @name CKEDITOR.editor.prototype.element
+			 * @type CKEDITOR.dom.element
+			 * @example
+			 * var editor = CKEDITOR.instances.editor1;
+			 * alert( <b>editor.element</b>.getName() );  "textarea"
+			 */
+			this.element = element && element;
+
+			/**
+			 * The editor instance name. It hay be the replaced element id, name or
+			 * a default name using a progressive counter (editor1, editor2, ...).
+			 * @name CKEDITOR.editor.prototype.name
+			 * @type String
+			 * @example
+			 * var editor = CKEDITOR.instances.editor1;
+			 * alert( <b>editor.name</b> );  "editor1"
+			 */
+			this.name = ( element && ( this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE )
+							&& ( element.getId() || element.getNameAtt() ) )
+						|| getNewName();
+
+			/**
+			 * The configurations for this editor instance. It inherits all
+			 * settings defined in (@link CKEDITOR.config}, combined with settings
+			 * loaded from custom configuration files and those defined inline in
+			 * the page when creating the editor.
+			 * @name CKEDITOR.editor.prototype.config
+			 * @type Object
+			 * @example
+			 * var editor = CKEDITOR.instances.editor1;
+			 * alert( <b>editor.config.theme</b> );  "default" e.g.
+			 */
+			this.config = CKEDITOR.tools.prototypedCopy( CKEDITOR.config );
+
+			/**
+			 * Namespace containing UI features related to this editor instance.
+			 * @name CKEDITOR.editor.prototype.ui
+			 * @type CKEDITOR.ui
+			 * @example
+			 */
+			this.ui = new CKEDITOR.ui( this );
+
+			/**
+			 * Controls the focus state of this editor instance. This property
+			 * is rarely used for normal API operations. It is mainly
+			 * destinated to developer adding UI elements to the editor interface.
+			 * @name CKEDITOR.editor.prototype.focusManager
+			 * @type CKEDITOR.focusManager
+			 * @example
+			 */
+			this.focusManager = new CKEDITOR.focusManager( this );
+
+			CKEDITOR.fire( 'instanceCreated', null, this );
+
+			initConfig( this, instanceConfig );
+		};
+})();
+
+CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
+	/** @lends CKEDITOR.editor.prototype */
+	{
+		/**
+		 * Adds a command definition to the editor instance. Commands added with
+		 * this function can be later executed with {@link #execCommand}.
+		 * @param {String} commandName The indentifier name of the command.
+		 * @param {CKEDITOR.commandDefinition} commandDefinition The command definition.
+		 * @example
+		 * editorInstance.addCommand( 'sample',
+		 * {
+		 *     exec : function( editor )
+		 *     {
+		 *         alert( 'Executing a command for the editor name "' + editor.name + '"!' );
+		 *     }
+		 * });
+		 */
+		addCommand : function( commandName, commandDefinition )
+		{
+			this._.commands[ commandName ] = new CKEDITOR.command( this, commandDefinition );
+		},
+
+		/**
+		 * Destroys the editor instance, releasing all resources used by it.
+		 * If the editor replaced an element, the element will be recovered.
+		 * @param {Boolean} [noUpdate] If the instance is replacing a DOM
+		 *		element, this parameter indicates whether or not to update the
+		 *		element with the instance contents.
+		 * @example
+		 * alert( CKEDITOR.instances.editor1 );  e.g "object"
+		 * <b>CKEDITOR.instances.editor1.destroy()</b>;
+		 * alert( CKEDITOR.instances.editor1 );  "undefined"
+		 */
+		destroy : function( noUpdate )
+		{
+			if ( !noUpdate )
+				this.updateElement();
+
+			this.theme.destroy( this );
+			CKEDITOR.remove( this );
+		},
+
+		/**
+		 * Executes a command.
+		 * @param {String} commandName The indentifier name of the command.
+		 * @param {Object} [data] Data to be passed to the command
+		 * @returns {Boolean} "true" if the command has been successfuly
+		 *		executed, otherwise "false".
+		 * @example
+		 * editorInstance.execCommand( 'Bold' );
+		 */
+		execCommand : function( commandName, data )
+		{
+			var command = this.getCommand( commandName );
+			if ( command && command.state != CKEDITOR.TRISTATE_DISABLED )
+				return command.exec( this, data );
+
+			// throw 'Unknown command name "' + commandName + '"';
+			return false;
+		},
+
+		/**
+		 * Gets one of the registered commands. Note that, after registering a
+		 * command definition with addCommand, it is transformed internally
+		 * into an instance of {@link CKEDITOR.command}, which will be then
+		 * returned by this function.
+		 * @param {String} commandName The name of the command to be returned.
+		 * This is the same used to register the command with addCommand.
+		 * @returns {CKEDITOR.command} The command object identified by the
+		 * provided name.
+		 */
+		getCommand : function( commandName )
+		{
+			return this._.commands[ commandName ];
+		},
+
+		/**
+		 * Gets the editor data. The data will be in raw format. It is the same
+		 * data that is posted by the editor.
+		 * @type String
+		 * @returns (String) The editor data.
+		 * @example
+		 * if ( CKEDITOR.instances.editor1.<b>getData()</b> == '' )
+		 *     alert( 'There is no data available' );
+		 */
+		getData : function()
+		{
+			this.fire( 'beforeGetData' );
+
+			var eventData = this._.data;
+
+			if ( !eventData )
+			{
+				var element = this.element;
+				if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE )
+					eventData = element.is( 'textarea' ) ? element.getValue() : element.getHtml();
+				else
+					eventData = '';
+			}
+
+			eventData = { dataValue : eventData };
+
+			// Fire "getData" so data manipulation may happen.
+			this.fire( 'getData', eventData );
+
+			return eventData.dataValue;
+		},
+
+		/**
+		 * Sets the editor data. The data must be provided in raw format.
+		 * @param {String} data HTML code to replace the curent content in the editor.
+		 * @example
+		 * CKEDITOR.instances.editor1.<b>setData( '&lt;p&gt;This is the editor data.&lt;/p&gt;' )</b>;
+		 */
+		setData : function( data )
+		{
+			// Fire "setData" so data manipulation may happen.
+			var eventData = { dataValue : data };
+			this.fire( 'setData', eventData );
+
+			this._.data = eventData.dataValue;
+
+			this.fire( 'afterSetData', eventData );
+		},
+
+		/**
+		 * Inserts HTML into the currently selected position in the editor.
+		 * @param {String} data HTML code to be inserted into the editor.
+		 * @example
+		 * CKEDITOR.instances.editor1.<b>insertHtml( '&lt;p&gt;This is a new paragraph.&lt;/p&gt;' )</b>;
+		 */
+		insertHtml : function( data )
+		{
+			var eventData = { insert : data };
+			this.fire( 'insertHtml', eventData );
+			this.fire( 'afterInsertHtml', eventData );
+			this._.data = this.document.getBody().getHtml();
+		},
+
+		/**
+		 * Inserts an element into the currently selected position in the editor.
+		 * @param {CKEDITOR.dom.element} element Element to be inserted into the editor.
+		 * @example
+		 * var element = CKEDITOR.dom.element.createFromHtml( '<img src="hello.png" border="0" title="Hello" />' );
+		 * CKEDITOR.instances.editor1.<b>insertElement( element )</b>;
+		 */
+		insertElement : function( element )
+		{
+			var eventData = { insert : element };
+			this.fire( 'insertElement', eventData );
+			this.fire( 'afterInsertElement', eventData );
+			this._.data = this.document.getBody().getHtml();
+		},
+
+		/**
+		 * Updates the &lt;textarea&gt; element that has been replaced by the editor with
+		 * the current data available in the editor.
+		 * @example
+		 * CKEDITOR.instances.editor1.updateElement();
+		 * alert( document.getElementById( 'editor1' ).value );  // The current editor data.
+		 */
+		updateElement : function()
+		{
+			var element = this.element;
+			if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE )
+			{
+				if ( element.is( 'textarea' ) )
+					element.setValue( this.getData() );
+				else
+					element.setHtml( this.getData() );
+			}
+		}
+	});
+
+CKEDITOR.on( 'loaded', function()
+	{
+		// Run the full initialization for pending editors.
+		var pending = CKEDITOR.editor._pending;
+		if ( pending )
+		{
+			delete CKEDITOR.editor._pending;
+
+			for ( var i = 0 ; i < pending.length ; i++ )
+				pending[ i ]._init();
+		}
+	});
Index: /CKEditor/trunk/_source/core/editor_basic.js
===================================================================
--- /CKEditor/trunk/_source/core/editor_basic.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/editor_basic.js	(revision 2948)
@@ -0,0 +1,178 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+if ( !CKEDITOR.editor )
+{
+	/**
+	 * No element is linked to the editor instance.
+	 * @constant
+	 * @example
+	 */
+	CKEDITOR.ELEMENT_MODE_NONE = 0;
+
+	/**
+	 * The element is to be replaced by the editor instance.
+	 * @constant
+	 * @example
+	 */
+	CKEDITOR.ELEMENT_MODE_REPLACE = 1;
+
+	/**
+	 * The editor is to be created inside the element.
+	 * @constant
+	 * @example
+	 */
+	CKEDITOR.ELEMENT_MODE_APPENDTO = 2;
+
+	/**
+	 * Represents an editor instance. This constructor should be rarely used,
+	 * being the {@link CKEDITOR} methods preferible.
+	 * @constructor
+	 * @param {Object} instanceConfig Configuration values for this specific
+	 *		instance.
+	 * @param {CKEDITOR.dom.element} [element] The element linked to this
+	 *		instance.
+	 * @param {Number} [mode] The mode in which the element is linked to this
+	 *		instance.
+	 * @augments CKEDITOR.event
+	 * @example
+	 */
+	CKEDITOR.editor = function( instanceConfig, element, mode )
+	{
+		this._ =
+		{
+			// Save the config to be processed later by the full core code.
+			instanceConfig : instanceConfig,
+			element : element
+		};
+
+		/**
+		 * The mode in which the {@link #element} is linked to this editor
+		 * instance. It can be any of the following values:
+		 * <ul>
+		 * <li><b>CKEDITOR.ELEMENT_MODE_NONE</b>: No element is linked to the
+		 *		editor instance.</li>
+		 * <li><b>CKEDITOR.ELEMENT_MODE_REPLACE</b>: The element is to be
+		 *		replaced by the editor instance.</li>
+		 * <li><b>CKEDITOR.ELEMENT_MODE_APPENDTO</b>: The editor is to be
+		 *		created inside the element.</li>
+		 * </ul>
+		 * @name CKEDITOR.editor.prototype.elementMode
+		 * @type Number
+		 * @example
+		 * var editor = CKEDITOR.replace( 'editor1' );
+		 * alert( <b>editor.elementMode</b> );  "1"
+		 */
+		this.elementMode = mode || CKEDITOR.ELEMENT_MODE_NONE;
+
+		// Call the CKEDITOR.event constructor to initialize this instance.
+		CKEDITOR.event.call( this );
+
+		this._init();
+	};
+
+	/**
+	 * Replaces a &lt;textarea&gt; or a DOM element (DIV) with a CKEditor
+	 * instance. For textareas, the initial value in the editor will be the
+	 * textarea value. For DOM elements, their innerHTML will be used
+	 * instead. We recommend using TEXTAREA and DIV elements only. Do not use
+	 * this function directly. Use {@link CKEDITOR.replace} instead.
+	 * @param {Object|String} elementOrIdOrName The DOM element (textarea), its
+	 *		ID or name.
+	 * @param {Object} [config] The specific configurations to apply to this
+	 *		editor instance. Configurations set here will override global CKEditor
+	 *		settings.
+	 * @returns {CKEDITOR.editor} The editor instance created.
+	 * @example
+	 */
+	CKEDITOR.editor.replace = function( elementOrIdOrName, config )
+	{
+		var element = elementOrIdOrName;
+
+		if ( typeof element != 'object' )
+		{
+			// Look for the element by id. We accept any kind of element here.
+			element = document.getElementById( elementOrIdOrName );
+
+			// If not found, look for elements by name. In this case we accept only
+			// textareas.
+			if ( !element )
+			{
+				var i = 0,
+					textareasByName	= document.getElementsByName( elementOrIdOrName );
+
+				while ( ( element = textareasByName[ i++ ] ) && element.tagName.toLowerCase() != 'textarea' )
+				{ /*jsl:pass*/ }
+			}
+
+			if ( !element )
+				throw '[CKEDITOR.editor.replace] The element with id or name "' + elementOrIdOrName + '" was not found.';
+		}
+
+		// Do not replace the textarea right now, just hide it. The effective
+		// replacement will be done by the _init function.
+		element.style.visibility = 'hidden';
+
+		// Create the editor instance.
+		return new CKEDITOR.editor( config, element, CKEDITOR.ELEMENT_MODE_REPLACE );
+	};
+
+	/**
+	 * Creates a new editor instance inside a specific DOM element. Do not use
+	 * this function directly. Use {@link CKEDITOR.appendTo} instead.
+	 * @param {Object|String} elementOrId The DOM element or its ID.
+	 * @param {Object} [config] The specific configurations to apply to this
+	 *		editor instance. Configurations set here will override global CKEditor
+	 *		settings.
+	 * @returns {CKEDITOR.editor} The editor instance created.
+	 * @example
+	 */
+	CKEDITOR.editor.appendTo = function( elementOrId, config )
+	{
+		if ( typeof elementOrId != 'object' )
+		{
+			elementOrId = document.getElementById( elementOrId );
+
+			if ( !elementOrId )
+				throw '[CKEDITOR.editor.appendTo] The element with id "' + elementOrId + '" was not found.';
+		}
+
+		// Create the editor instance.
+		return new CKEDITOR.editor( config, elementOrId, CKEDITOR.ELEMENT_MODE_APPENDTO );
+	};
+
+	CKEDITOR.editor.prototype =
+	{
+		/**
+		 * Initializes the editor instance. This function will be overriden by the
+		 * full CKEDITOR.editor implementation (editor.js).
+		 * @private
+		 */
+		_init : function()
+		{
+			var pending = CKEDITOR.editor._pending || ( CKEDITOR.editor._pending = [] );
+			pending.push( this );
+		},
+
+		// Both fire and fireOnce will always pass this editor instance as the
+		// "editor" param in CKEDITOR.event.fire. So, we override it to do that
+		// automaticaly.
+
+		/** @ignore */
+		fire : function( eventName, data )
+		{
+			return CKEDITOR.event.prototype.fire.call( this, eventName, data, this );
+		},
+
+		/** @ignore */
+		fireOnce : function( eventName, data )
+		{
+			return CKEDITOR.event.prototype.fireOnce.call( this, eventName, data, this );
+		}
+	};
+
+	// "Inherit" (copy actually) from CKEDITOR.event.
+	CKEDITOR.event.implementOn( CKEDITOR.editor.prototype );
+}
Index: /CKEditor/trunk/_source/core/env.js
===================================================================
--- /CKEditor/trunk/_source/core/env.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/env.js	(revision 2948)
@@ -0,0 +1,161 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.env} object, which constains
+ *		environment and browser information.
+ */
+
+if ( !CKEDITOR.env )
+{
+	/**
+	 * Environment and browser information.
+	 * @namespace
+	 * @example
+	 */
+	CKEDITOR.env = (function()
+	{
+		var agent = navigator.userAgent.toLowerCase();
+		var opera = window.opera;
+
+		var env =
+		/** @lends CKEDITOR.env */
+		{
+			/**
+			 * Indicates that CKEditor is running on Internet Explorer.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.ie )
+			 *     alert( "I'm on IE!" );
+			 */
+			ie		: /*@cc_on!@*/false,
+			/**
+			 * Indicates that CKEditor is running on Opera.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.opera )
+			 *     alert( "I'm on Opera!" );
+			 */
+			opera	: ( !!opera && opera.version ),
+			/**
+			 * Indicates that CKEditor is running on a WebKit based browser, like
+			 * Safari.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.webkit )
+			 *     alert( "I'm on WebKit!" );
+			 */
+			webkit	: ( agent.indexOf( ' applewebkit/' ) > -1 ),
+			/**
+			 * Indicates that CKEditor is running on Adobe AIR.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.air )
+			 *     alert( "I'm on AIR!" );
+			 */
+			air		: ( agent.indexOf( ' adobeair/' ) > -1 ),
+			/**
+			 * Indicates that CKEditor is running on Macintosh.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.mac )
+			 *     alert( "I love apples!" );
+			 */
+			mac	: ( agent.indexOf( 'macintosh' ) > -1 )
+		};
+
+		/**
+		 * Indicates that CKEditor is running on a Gecko based browser, like
+		 * Firefox.
+		 * @name CKEDITOR.env.gecko
+		 * @type Boolean
+		 * @example
+		 * if ( CKEDITOR.env.gecko )
+		 *     alert( "I'm riding a gecko!" );
+		 */
+		env.gecko = ( navigator.product == 'Gecko' && !env.webkit && !env.opera );
+
+		var version = 0;
+
+		// Internet Explorer 6.0+
+		if ( env.ie )
+		{
+			version = parseFloat( agent.match( /msie (\d+)/ )[1] );
+
+			/**
+			 * Indicates that CKEditor is running on an IE6-like environment, which
+			 * includes IE6 itself and IE7 and IE8 quirks mode.
+			 * @type Boolean
+			 * @example
+			 * if ( CKEDITOR.env.ie6Compat )
+			 *     alert( "I'm on IE6 or quirks mode!" );
+			 */
+			env.ie6Compat = ( version < 7 || document.compatMode == 'BackCompat' );
+		}
+
+		// Gecko.
+		if ( env.gecko )
+		{
+			var geckoRelease = agent.match( /rv:([\d\.]+)/ );
+			if ( geckoRelease )
+			{
+				geckoRelease = geckoRelease[1].split( '.' );
+				version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 );
+			}
+		}
+
+		// Opera 9.50+
+		if ( env.opera )
+			version = parseFloat( opera.version() );
+
+		// Adobe AIR 1.0+
+		// Checked before Safari because AIR have the WebKit rich text editor
+		// features from Safari 3.0.4, but the version reported is 420.
+		if ( env.air )
+			version = parseFloat( agent.match( / adobeair\/(\d+)/ )[1] );
+
+		// WebKit 522+ (Safari 3+)
+		if ( env.webkit )
+			version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
+
+		/**
+		 * Contains the browser version.
+		 *
+		 * For gecko based browsers (like Firefox) it contains the revision
+		 * number with first three parts concatenated with a padding zero
+		 * (e.g. for revision 1.9.0.2 we have 10900).
+		 *
+		 * For webkit based browser (like Safari and Chrome) it contains the
+		 * WebKit build version (e.g. 522).
+		 * @name CKEDITOR.env.version
+		 * @type Boolean
+		 * @example
+		 * if ( CKEDITOR.env.ie && <b>CKEDITOR.env.version</b> <= 6 )
+		 *     alert( "Ouch!" );
+		 */
+		env.version = version;
+
+		/**
+		 * Indicates that CKEditor is running on a compatible browser.
+		 * @name CKEDITOR.env.isCompatible
+		 * @type Boolean
+		 * @example
+		 * if ( CKEDITOR.env.isCompatible )
+		 *     alert( "Your browser is pretty cool!" );
+		 */
+		env.isCompatible =
+			( env.ie && version >= 6 ) ||
+			( env.gecko && version >= 10801 ) ||
+			( env.opera && version >= 9.5 ) ||
+			( env.air && version >= 1 ) ||
+			( env.webkit && version >= 522 ) ||
+			false;
+
+		return env;
+	})();
+}
+
+// PACKAGER_RENAME( CKEDITOR.env )
+// PACKAGER_RENAME( CKEDITOR.env.ie )
Index: /CKEditor/trunk/_source/core/event.js
===================================================================
--- /CKEditor/trunk/_source/core/event.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/event.js	(revision 2948)
@@ -0,0 +1,312 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the
+ *		base for classes and objects that require event handling features.
+ */
+
+if ( !CKEDITOR.event )
+{
+	/**
+	 * This is a base class for classes and objects that require event handling
+	 * features.
+	 * @constructor
+	 * @example
+	 */
+	CKEDITOR.event = function()
+	{
+		( this._ || ( this._ = {} ) ).events = {};
+	};
+
+	/**
+	 * Implements the {@link CKEDITOR.event} features in an object.
+	 * @param {Object} targetObject The object in which implement the features.
+	 * @example
+	 * var myObject = { message : 'Example' };
+	 * <b>CKEDITOR.event.implementOn( myObject }</b>;
+	 * myObject.on( 'testEvent', function()
+	 *     {
+	 *         alert( this.message );  // "Example"
+	 *     });
+	 * myObject.fire( 'testEvent' );
+	 */
+	CKEDITOR.event.implementOn = function( targetObject )
+	{
+		CKEDITOR.event.call( targetObject );
+
+		for ( var prop in CKEDITOR.event.prototype )
+		{
+			if ( targetObject[ prop ] == undefined )
+				targetObject[ prop ] = CKEDITOR.event.prototype[ prop ];
+		}
+	};
+
+	CKEDITOR.event.prototype = (function()
+	{
+		var eventEntry = function( eventName )
+		{
+			this.name = eventName;
+			this.listeners = [];
+		};
+
+		eventEntry.prototype =
+		{
+			// Get the listener index for a specified function.
+			// Returns -1 if not found.
+			getListenerIndex : function( listenerFunction )
+			{
+				for ( var i = 0, listeners = this.listeners ; i < listeners.length ; i++ )
+				{
+					if ( listeners[i].fn == listenerFunction )
+						return i;
+				}
+				return -1;
+			}
+		};
+
+		return /** @lends CKEDITOR.event.prototype */ {
+			/**
+			 * Registers a listener to a specific event in the current object.
+			 * @param {String} eventName The event name to which listen.
+			 * @param {Function} listenerFunction The function listening to the
+			 *		event.
+			 * @param {Object} [scopeObj] The object used to scope the listener
+			 *		call (the this object. If omitted, the current object is used.
+			 * @param {Object} [listenerData] Data to be sent as the
+			 *		{@link CKEDITOR.eventInfo#listenerData} when calling the
+			 *		listener.
+			 * @param {Number} [priority] The listener priority. Lower priority
+			 *		listeners are called first. Listeners with the same priority
+			 *		value are called in registration order. Defaults to 10.
+			 * @example
+			 * someObject.on( 'someEvent', function()
+			 *     {
+			 *         alert( this == someObject );  // "true"
+			 *     });
+			 * @example
+			 * someObject.on( 'someEvent', function()
+			 *     {
+			 *         alert( this == anotherObject );  // "true"
+			 *     }
+			 *     , anotherObject );
+			 * @example
+			 * someObject.on( 'someEvent', function( event )
+			 *     {
+			 *         alert( event.listenerData );  // "Example"
+			 *     }
+			 *     , null, 'Example' );
+			 * @example
+			 * someObject.on( 'someEvent', function() { ... } );                   // 2nd called
+			 * someObject.on( 'someEvent', function() { ... }, null, null, 100 );  // 3rd called
+			 * someObject.on( 'someEvent', function() { ... }, null, null, 1 );    // 1st called
+			 */
+			on : function( eventName, listenerFunction, scopeObj, listenerData, priority )
+			{
+				// Get the event entry (create it if needed).
+				var event = this._.events[ eventName ] || ( this._.events[ eventName ] = new eventEntry( eventName ) );
+
+				if ( event.getListenerIndex( listenerFunction ) < 0 )
+				{
+					// Get the listeners.
+					var listeners = event.listeners;
+
+					// Fill the scope.
+					if ( !scopeObj )
+						scopeObj = this;
+
+					// Default the priority, if needed.
+					if ( isNaN( priority ) )
+						priority = 10;
+
+					// Create the function to be fired for this listener.
+					var listenerFirer = function( editor, publisherData, stopFn, cancelFn )
+					{
+						var ev =
+						{
+							name : eventName,
+							sender : this,
+							editor : editor,
+							data : publisherData,
+							listenerData : listenerData,
+							stop : stopFn,
+							cancel : cancelFn
+						};
+
+						listenerFunction.call( scopeObj, ev );
+
+						return ev.data;
+					};
+					listenerFirer.fn = listenerFunction;
+					listenerFirer.priority = priority;
+
+					// Search for the right position for this new listener, based on its
+					// priority.
+					for ( var i = listeners.length - 1 ; i >= 0 ; i-- )
+					{
+						// Find the item which should be before the new one.
+						if ( listeners[ i ].priority <= priority )
+						{
+							// Insert the listener in the array.
+							listeners.splice( i + 1, 0, listenerFirer );
+							return;
+						}
+					}
+
+					// If no position has been found (or zero length), put it in
+					// the front of list.
+					listeners.unshift( listenerFirer );
+				}
+			},
+
+			/**
+			 * Fires an specific event in the object. All registered listeners are
+			 * called at this point.
+			 * @function
+			 * @param {String} eventName The event name to fire.
+			 * @param {Object} [data] Data to be sent as the
+			 *		{@link CKEDITOR.eventInfo#data} when calling the
+			 *		listeners.
+			 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
+			 *		{@link CKEDITOR.eventInfo#editor} when calling the
+			 *		listener.
+			 * @returns {Boolean|Object} A booloan indicating that the event is to be
+			 *		canceled, or data returned by one of the listeners.
+			 * @example
+			 * someObject.on( 'someEvent', function() { ... } );
+			 * someObject.on( 'someEvent', function() { ... } );
+			 * <b>someObject.fire( 'someEvent' )</b>;  // both listeners are called
+			 * @example
+			 * someObject.on( 'someEvent', function( event )
+			 *     {
+			 *         alert( event.data );  // "Example"
+			 *     });
+			 * <b>someObject.fire( 'someEvent', 'Example' )</b>;
+			 */
+			fire : (function()
+			{
+				// Create the function that marks the event as stopped.
+				var stopped = false;
+				var stopEvent = function()
+				{
+					stopped = true;
+				};
+
+				// Create the function that marks the event as canceled.
+				var canceled = false;
+				var cancelEvent = function()
+				{
+					canceled = true;
+				};
+
+				return function( eventName, data, editor )
+				{
+					// Get the event entry.
+					var event = this._.events[ eventName ];
+
+					// Save the previous stopped and cancelled states. We may
+					// be nesting fire() calls.
+					var previousStopped = stopped,
+						previousCancelled = canceled;
+
+					// Reset the stopped and canceled flags.
+					stopped = canceled = false;
+
+					if ( event )
+					{
+						// Loop through all listeners.
+						for ( var i = 0, listeners = event.listeners ; i < listeners.length ; i++ )
+						{
+							// Call the listener, passing the event data.
+							var retData = listeners[i].call( this, editor, data, stopEvent, cancelEvent );
+
+							if ( typeof retData != 'undefined' )
+								data = retData;
+
+							// No further calls is stopped or canceled.
+							if ( stopped || canceled )
+								break;
+						}
+					}
+
+					var ret = canceled || ( typeof data == 'undefined' ? false : data );
+
+					// Restore the previous stopped and canceled states.
+					stopped = previousStopped;
+					canceled = previousCancelled;
+
+					return ret;
+				};
+			})(),
+
+			/**
+			 * Fires an specific event in the object, releasing all listeners
+			 * registered to that event. The same listeners are not called again on
+			 * successive calls of it or of {@link #fire}.
+			 * @param {String} eventName The event name to fire.
+			 * @param {Object} [data] Data to be sent as the
+			 *		{@link CKEDITOR.eventInfo#data} when calling the
+			 *		listeners.
+			 * @param {CKEDITOR.editor} [editor] The editor instance to send as the
+			 *		{@link CKEDITOR.eventInfo#editor} when calling the
+			 *		listener.
+			 * @returns {Boolean|Object} A booloan indicating that the event is to be
+			 *		canceled, or data returned by one of the listeners.
+			 * @example
+			 * someObject.on( 'someEvent', function() { ... } );
+			 * someObject.fire( 'someEvent' );  // above listener called
+			 * <b>someObject.fireOnce( 'someEvent' )</b>;  // above listener called
+			 * someObject.fire( 'someEvent' );  // no listeners called
+			 */
+			fireOnce : function( eventName, data, editor )
+			{
+				var ret = this.fire( eventName, data, editor );
+				delete this._.events[ eventName ];
+				return ret;
+			},
+
+			/**
+			 * Unregisters a listener function from being called at the specified
+			 *		event. No errors are thrown if the listener has not been
+			 *		registered previously.
+			 * @param {String} eventName The event name.
+			 * @param {Function} listenerFunction The listener function to unregister.
+			 * @example
+			 * var myListener = function() { ... };
+			 * someObject.on( 'someEvent', myListener );
+			 * someObject.fire( 'someEvent' );  // myListener called
+			 * <b>someObject.removeListener( 'someEvent', myListener )</b>;
+			 * someObject.fire( 'someEvent' );  // myListener not called
+			 */
+			removeListener : function( eventName, listenerFunction )
+			{
+				// Get the event entry.
+				var event = this._.events[ eventName ];
+
+				if ( event )
+				{
+					var index = event.getListenerIndex( listenerFunction );
+					if ( index >= 0 )
+						event.listeners.splice( index, 1 );
+				}
+			},
+
+			/**
+			 * Checks if there is any listener registered to a given event.
+			 * @param {String} eventName The event name.
+			 * @example
+			 * var myListener = function() { ... };
+			 * someObject.on( 'someEvent', myListener );
+			 * alert( someObject.<b>hasListeners( 'someEvent' )</b> );  // "true"
+			 * alert( someObject.<b>hasListeners( 'noEvent' )</b> );    // "false"
+			 */
+			hasListeners : function( eventName )
+			{
+				var event = this._.events[ eventName ];
+				return ( event && event.listeners.length > 0 ) ;
+			}
+		};
+	})();
+}
Index: /CKEditor/trunk/_source/core/eventInfo.js
===================================================================
--- /CKEditor/trunk/_source/core/eventInfo.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/eventInfo.js	(revision 2948)
@@ -0,0 +1,120 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the "virtual" {@link CKEDITOR.eventInfo} class, which
+ *		contains the defintions of the event object passed to event listeners.
+ *		This file is for documentation purposes only.
+ */
+
+/**
+ * This class is not really part of the API. It just illustrates the features
+ * of the event object passed to event listeners by a {@link CKEDITOR.event}
+ * based object.
+ * @name CKEDITOR.eventInfo
+ * @constructor
+ * @example
+ * // Do not do this.
+ * var myEvent = new CKEDITOR.eventInfo();  // Error: CKEDITOR.eventInfo is undefined
+ */
+
+/**
+ * The event name.
+ * @name CKEDITOR.eventInfo.prototype.name
+ * @field
+ * @type String
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         alert( <b>event.name</b> );  // "someEvent"
+ *     });
+ * someObject.fire( 'someEvent' );
+ */
+
+/**
+ * The object that publishes (sends) the event.
+ * @name CKEDITOR.eventInfo.prototype.sender
+ * @field
+ * @type Object
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         alert( <b>event.sender</b> == someObject );  // "true"
+ *     });
+ * someObject.fire( 'someEvent' );
+ */
+
+/**
+ * The editor instance that holds the sender. May be the same as sender. May be
+ * null if the sender is not part of an editor instance, like a component
+ * running in standalone mode.
+ * @name CKEDITOR.eventInfo.prototype.editor
+ * @field
+ * @type CKEDITOR.editor
+ * @example
+ * myButton.on( 'someEvent', function( event )
+ *     {
+ *         alert( <b>event.editor</b> == myEditor );  // "true"
+ *     });
+ * myButton.fire( 'someEvent', null, <b>myEditor</b> );
+ */
+
+/**
+ * Any kind of additional data. Its format and usage is event dependent.
+ * @name CKEDITOR.eventInfo.prototype.data
+ * @field
+ * @type Object
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         alert( <b>event.data</b> );  // "Example"
+ *     });
+ * someObject.fire( 'someEvent', <b>'Example'</b> );
+ */
+
+/**
+ * Any extra data appended during the listener registration.
+ * @name CKEDITOR.eventInfo.prototype.listenerData
+ * @field
+ * @type Object
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         alert( <b>event.listenerData</b> );  // "Example"
+ *     }
+ *     , null, <b>'Example'</b> );
+ */
+
+/**
+ * Indicates that no further listeners are to be called.
+ * @name CKEDITOR.eventInfo.prototype.stop
+ * @function
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         <b>event.stop()</b>;
+ *     });
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         // This one will not be called.
+ *     });
+ * alert( someObject.fire( 'someEvent' ) );  // "false"
+ */
+
+/**
+ * Indicates that the event is to be cancelled (if cancelable).
+ * @name CKEDITOR.eventInfo.prototype.cancel
+ * @function
+ * @example
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         <b>event.cancel()</b>;
+ *     });
+ * someObject.on( 'someEvent', function( event )
+ *     {
+ *         // This one will not be called.
+ *     });
+ * alert( someObject.fire( 'someEvent' ) );  // "true"
+ */
Index: /CKEditor/trunk/_source/core/focusmanager.js
===================================================================
--- /CKEditor/trunk/_source/core/focusmanager.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/focusmanager.js	(revision 2948)
@@ -0,0 +1,115 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.focusManager} class, which is used
+ *		to handle the focus on editor instances..
+ */
+
+/**
+ * Manages the focus activity in an editor instance. This class is to be used
+ * mainly by UI elements coders when adding interface elements to CKEditor.
+ * @constructor
+ * @param {CKEDITOR.editor} editor The editor instance.
+ * @example
+ */
+CKEDITOR.focusManager = function( editor )
+{
+	if ( editor.focusManager )
+		return editor.focusManager;
+
+	/**
+	 * Indicates that the editor instance has focus.
+	 * @type Boolean
+	 * @example
+	 * alert( CKEDITOR.instances.editor1.focusManager.hasFocus );  // e.g "true"
+	 */
+	this.hasFocus = false;
+
+	/**
+	 * Object used to hold private stuff.
+	 * @private
+	 */
+	this._ =
+	{
+		editor : editor
+	};
+
+	return this;
+};
+
+CKEDITOR.focusManager.prototype =
+{
+	/**
+	 * Indicates that the editor instance has the focus.
+	 *
+	 * This function is not used to set the focus in the editor. Use
+	 * {@link CKEDITOR.editor#focus} for it instead.
+	 * @example
+	 * var editor = CKEDITOR.instances.editor1;
+	 * <b>editor.focusManager.focus()</b>;
+	 */
+	focus : function()
+	{
+		if ( this._.timer )
+			clearTimeout( this._.timer );
+
+		if ( !this.hasFocus )
+		{
+			// If another editor has the current focus, we first "blur" it. In
+			// this way the events happen in a more logical sequence, like:
+			//		"focus 1" > "blur 1" > "focus 2"
+			// ... instead of:
+			//		"focus 1" > "focus 2" > "blur 1"
+			if ( CKEDITOR.currentInstance )
+				CKEDITOR.currentInstance.focusManager.forceBlur();
+
+			this.hasFocus = true;
+			this._.editor.fire( 'focus' );
+		}
+	},
+
+	/**
+	 * Indicates that the editor instance has lost the focus. Note that this
+	 * functions acts asynchronously with a delay of 100ms to avoid subsequent
+	 * blur/focus effects. If you want the "blur" to happen immediately, use
+	 * the {@link #forceBlur} function instead.
+	 * @example
+	 * var editor = CKEDITOR.instances.editor1;
+	 * <b>editor.focusManager.blur()</b>;
+	 */
+	blur : function()
+	{
+		var focusManager = this;
+
+		if ( focusManager._.timer )
+			clearTimeout( focusManager._.timer );
+
+		focusManager._.timer = setTimeout(
+			function()
+			{
+				delete focusManager._.timer;
+				focusManager.forceBlur();
+			}
+			, 100 );
+	},
+
+	/**
+	 * Indicates that the editor instance has lost the focus. Unlike
+	 * {@link #blur}, this function is synchronous, marking the instance as
+	 * "blured" immediately.
+	 * @example
+	 * var editor = CKEDITOR.instances.editor1;
+	 * <b>editor.focusManager.forceBlur()</b>;
+	 */
+	forceBlur : function()
+	{
+		if ( this.hasFocus )
+		{
+			this.hasFocus = false;
+			this._.editor.fire( 'blur' );
+		}
+	}
+};
Index: /CKEditor/trunk/_source/core/htmlparser.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/htmlparser.js	(revision 2948)
@@ -0,0 +1,165 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * HTML text parser.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser = function()
+{
+	this._ =
+	{
+		htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' )
+	};
+};
+
+(function()
+{
+	var attribsRegex	= /([\w:]+)\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+))?/g,
+		emptyAttribs	= {checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};
+
+	CKEDITOR.htmlParser.prototype =
+	{
+		/**
+		 * Function to be fired when a tag opener is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} tagName The tag name. The name is guarantted to be
+		 *		lowercased.
+		 * @param {Object} attributes An object containing all tag attributes. Each
+		 *		property in this object represent and attribute name and its
+		 *		value is the attribute value.
+		 * @param {Boolean} selfClosing true if the tag closes itself, false if the
+		 * 		tag doesn't.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onTagOpen = function( tagName, attributes, selfClosing )
+		 *     {
+		 *         alert( tagName );  // e.g. "b"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onTagOpen	: function() {},
+
+		/**
+		 * Function to be fired when a tag closer is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} tagName The tag name. The name is guarantted to be
+		 *		lowercased.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onTagClose = function( tagName )
+		 *     {
+		 *         alert( tagName );  // e.g. "b"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onTagClose	: function() {},
+
+		/**
+		 * Function to be fired when text is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} text The text found.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onText = function( text )
+		 *     {
+		 *         alert( text );  // e.g. "Hello"
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onText		: function() {},
+
+		/**
+		 * Function to be fired when a commend is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} comment The comment text.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * parser.onText = function( comment )
+		 *     {
+		 *         alert( comment );  // e.g. " Example "
+		 *     });
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		onComment	: function() {},
+
+		/**
+		 * Parses text, looking for HTML tokens, like tag openers or closers,
+		 * or comments. This function fires the onTagOpen, onTagClose, onText
+		 * and onComment function during its execution.
+		 * @param {String} html The HTML to be parsed.
+		 * @example
+		 * var parser = new CKEDITOR.htmlParser();
+		 * // The onTagOpen, onTagClose, onText and onComment should be overriden
+		 * // at this point.
+		 * parser.parse( "&lt;!-- Example --&gt;&lt;b&gt;Hello&lt;/b&gt;" );
+		 */
+		parse : function( html )
+		{
+			var parts,
+				tagName,
+				nextIndex = 0;
+
+			while ( ( parts = this._.htmlPartsRegex.exec( html ) ) )
+			{
+				var tagIndex = parts.index;
+				if ( tagIndex > nextIndex )
+					this.onText( html.substring( nextIndex, tagIndex ) );
+
+				nextIndex = this._.htmlPartsRegex.lastIndex;
+
+				/*
+				 "parts" is an array with the following items:
+					0 : The entire match (not used)
+					1 : Group filled with the tag name for closing tags.
+					2 : Group filled with the comment text.
+					3 : Group filled with the tag name for opening tags.
+					4 : Group filled with the attributes part of opening tags.
+				 */
+
+				// Closing tag
+				if ( ( tagName = parts[ 1 ] ) )
+				{
+					this.onTagClose( tagName.toLowerCase() );
+					continue;
+				}
+
+				// Opening tag
+				if ( ( tagName = parts[ 3 ] ) )
+				{
+					var attribs = {},
+						attribMatch,
+						attribsPart = parts[ 4 ],
+						selfClosing = !!( attribsPart && attribsPart.charAt( attribsPart.length - 1 ) == '/' );
+
+					if ( attribsPart )
+					{
+						while ( ( attribMatch = attribsRegex.exec( attribsPart ) ) )
+						{
+							var attName = attribMatch[1].toLowerCase(),
+								attValue = attribMatch[2] || attribMatch[3] || attribMatch[4] || '';
+
+							if ( !attValue && emptyAttribs[ attName ] )
+								attribs[ attName ] = attName;
+							else
+								attribs[ attName ] = attValue;
+						}
+					}
+
+					this.onTagOpen( tagName.toLowerCase(), attribs, selfClosing );
+					continue;
+				}
+
+				// Comment
+				if( ( tagName = parts[ 2 ] ) )
+					this.onComment( tagName );
+			}
+
+			if ( html.length > nextIndex )
+				this.onText( html.substring( nextIndex, html.length ) );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/htmlparser/comment.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/comment.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/htmlparser/comment.js	(revision 2948)
@@ -0,0 +1,45 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * A lightweight representation of an HTML comment.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.comment = function( value )
+{
+	/**
+	 * The comment text.
+	 * @type String
+	 * @example
+	 */
+	this.value = value;
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : false
+	};
+};
+
+CKEDITOR.htmlParser.comment.prototype =
+{
+	/**
+	 * The node type. This is a constant value set to {@link CKEDITOR.NODE_COMMENT}.
+	 * @type Number
+	 * @example
+	 */
+	type : CKEDITOR.NODE_COMMENT,
+
+	/**
+	 * Writes the HTML representation of this comment to a CKEDITOR.htmlWriter.
+	 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+	 * @example
+	 */
+	writeHtml : function( writer )
+	{
+		writer.comment( this.value );
+	}
+};
Index: /CKEditor/trunk/_source/core/htmlparser/element.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/element.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/htmlparser/element.js	(revision 2948)
@@ -0,0 +1,131 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * A lightweight representation of an HTML element.
+ * @param {String} name The element name.
+ * @param {Object} attributes And object holding all attributes defined for
+ *		this element.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.element = function( name, attributes )
+{
+	/**
+	 * The element name.
+	 * @type String
+	 * @example
+	 */
+	this.name = name;
+
+	/**
+	 * Holds the attributes defined for this element.
+	 * @type Object
+	 * @example
+	 */
+	this.attributes = attributes;
+
+	/**
+	 * The nodes that are direct children of this element.
+	 * @type Array
+	 * @example
+	 */
+	this.children = [];
+
+	var dtd			= CKEDITOR.dtd,
+		isBlockLike	= !!( dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
+		isEmpty		= !!dtd.$empty[ name ];
+
+	this.isEmpty	= isEmpty;
+	this.isUnknown	= !dtd[ name ];
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : isBlockLike,
+		hasInlineStarted : isEmpty || !isBlockLike
+	};
+};
+
+(function()
+{
+	// Used to sort attribute entries in an array, where the first element of
+	// each object is the attribute name.
+	var sortAttribs = function( a, b )
+	{
+		a = a[0];
+		b = b[0];
+		return a < b ? -1 : a > b ? 1 : 0;
+	};
+
+	CKEDITOR.htmlParser.element.prototype =
+	{
+		/**
+		 * The node type. This is a constant value set to {@link CKEDITOR.NODE_ELEMENT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_ELEMENT,
+
+		/**
+		 * Adds a node to the element children list.
+		 * @param {Object} node The node to be added. It can be any of of the
+		 *		following types: {@link CKEDITOR.htmlParser.element},
+		 *		{@link CKEDITOR.htmlParser.text} and
+		 *		{@link CKEDITOR.htmlParser.comment}.
+		 * @function
+		 * @example
+		 */
+		add : CKEDITOR.htmlParser.fragment.prototype.add,
+
+		/**
+		 * Clone this element.
+		 * @returns {CKEDITOR.htmlParser.element} The element clone.
+		 * @example
+		 */
+		clone : function()
+		{
+			return new CKEDITOR.htmlParser.element( this.name, this.attributes );
+		},
+
+		/**
+		 * Writes the element HTML to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 */
+		writeHtml : function( writer )
+		{
+			// Open element tag.
+			writer.openTag( this.name, this.attributes );
+
+			// Copy all attributes to an array.
+			var attribsArray = [];
+			for ( var a in this.attributes )
+				attribsArray.push( [ a, this.attributes[ a ] ] );
+
+			// Sort the attributes by name.
+			attribsArray.sort( sortAttribs );
+
+			// Send the attributes.
+			for ( var i = 0, len = attribsArray.length ; i < len ; i++ )
+			{
+				var attrib = attribsArray[ i ];
+				writer.attribute( attrib[0], attrib[1] );
+			}
+
+			// Close the tag.
+			writer.openTagClose( this.name, this.isEmpty );
+
+			if ( !this.isEmpty )
+			{
+				// Send children.
+				CKEDITOR.htmlParser.fragment.prototype.writeHtml.apply( this, arguments );
+
+				// Close the element.
+				writer.closeTag( this.name );
+			}
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/htmlparser/fragment.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 2948)
@@ -0,0 +1,283 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * A lightweight representation of an HTML DOM structure.
+ * @constructor
+ * @example
+ */
+CKEDITOR.htmlParser.fragment = function()
+{
+	/**
+	 * The nodes contained in the root of this fragment.
+	 * @type Array
+	 * @example
+	 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
+	 * alert( fragment.children.length );  "2"
+	 */
+	this.children = [];
+
+	/**
+	 * Get the fragment parent. Should always be null.
+	 * @type Object
+	 * @default null
+	 * @example
+	 */
+	this.parent = null;
+
+	/** @private */
+	this._ =
+	{
+		isBlockLike : true,
+		hasInlineStarted : false
+	};
+};
+
+(function()
+{
+	// Elements which the end tag is marked as optional in the HTML 4.01 DTD
+	// (expect empty elements).
+	var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
+
+	/**
+	 * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
+	 * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
+	 * @returns CKEDITOR.htmlParser.fragment The fragment created.
+	 * @example
+	 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
+	 * alert( fragment.children[0].name );  "b"
+	 * alert( fragment.children[1].value );  " Text"
+	 */
+	CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml )
+	{
+		var parser = new CKEDITOR.htmlParser(),
+			html = [],
+			fragment = new CKEDITOR.htmlParser.fragment(),
+			pendingInline = [],
+			currentNode = fragment;
+
+		var checkPending = function( newTagName )
+		{
+			if ( pendingInline.length > 0 )
+			{
+				for ( var i = 0 ; i < pendingInline.length ; i++ )
+				{
+					var pendingElement = pendingInline[ i ],
+						pendingName = pendingElement.name,
+						pendingDtd = CKEDITOR.dtd[ pendingName ],
+						currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
+
+					if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) )
+					{
+						// Get a clone for the pending element.
+						pendingElement = pendingElement.clone();
+
+						// Add it to the current node and make it the current,
+						// so the new element will be added inside of it.
+						currentNode.add( pendingElement );
+						currentNode = pendingElement;
+
+						// Remove the pending element (back the index by one
+						// to properly process the next entry).
+						pendingInline.splice( i, 1 );
+						i--;
+					}
+				}
+			}
+		};
+
+		parser.onTagOpen = function( tagName, attributes, selfClosing )
+		{
+			var element = new CKEDITOR.htmlParser.element( tagName, attributes );
+
+			// "isEmpty" will be always "false" for unknown elements, so we
+			// must force it if the parser has identified it as a selfClosing tag.
+			if ( element.isUnknown && selfClosing )
+				element.isEmpty = true;
+
+			// This is a tag to be removed if empty, so do not add it immediately.
+			if ( CKEDITOR.dtd.$removeEmpty[ tagName ] )
+			{
+				pendingInline.push( element );
+				return;
+			}
+
+			var currentName = currentNode.name,
+				currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );
+
+			// If the element cannot be child of the current element.
+			if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
+			{
+				// If this is the fragment node, just ignore this tag and add
+				// its children.
+				if ( !currentName )
+					return;
+
+				var reApply = false;
+
+				// If the element name is the same as the current element name,
+				// then just close the current one and append the new one to the
+				// parent. This situation usually happens with <p>, <li>, <dt> and
+				// <dd>, specially in IE.
+				if ( tagName != currentName )
+				{
+					// If it is optional to close the current element, then
+					// close it at this point and simply add the new
+					// element after it.
+					if ( !optionalClose[ currentName ] )
+					{
+						// The current element is an inline element, which
+						// cannot hold the new one. Put it in the pending list,
+						// and try adding the new one after it.
+						pendingInline.unshift( currentNode );
+					}
+
+					reApply = true;
+				}
+
+				// In any of the above cases, we'll be adding, or trying to
+				// add it to the parent.
+				currentNode = currentNode.parent;
+
+				if ( reApply )
+				{
+					parser.onTagOpen.apply( this, arguments );
+					return;
+				}
+			}
+
+			checkPending( tagName );
+
+			currentNode.add( element );
+
+			if ( !element.isEmpty )
+				currentNode = element;
+		};
+
+		parser.onTagClose = function( tagName )
+		{
+			var closingElement = currentNode,
+				index = 0;
+
+			while ( closingElement && closingElement.name != tagName )
+			{
+				// If this is an inline element, add it to the pending list, so
+				// it will continue after the closing tag.
+				if ( !closingElement._.isBlockLike )
+				{
+					pendingInline.unshift( closingElement );
+
+					// Increase the index, so it will not get checked again in
+					// the pending list check that follows.
+					index++;
+				}
+
+				closingElement = closingElement.parent;
+			}
+
+			if ( closingElement )
+				currentNode = closingElement.parent;
+			else if ( pendingInline.length > index )
+			{
+				// If we didn't find any parent to be closed, let's check the
+				// pending list.
+				for ( ; index < pendingInline.length ; index++ )
+				{
+					// If found, just remove it from the list.
+					if ( tagName == pendingInline[ index ].name )
+					{
+						pendingInline.splice( index, 1 );
+
+						// Decrease the index so we continue from the next one.
+						index--;
+					}
+				}
+			}
+		};
+
+		parser.onText = function( text )
+		{
+			if ( !currentNode._.hasInlineStarted )
+			{
+				text = CKEDITOR.tools.ltrim( text );
+
+				if ( text.length === 0 )
+					return;
+			}
+
+			checkPending();
+			currentNode.add( new CKEDITOR.htmlParser.text( text ) );
+		};
+
+		parser.onComment = function( comment )
+		{
+			currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
+		};
+
+		parser.parse( fragmentHtml );
+
+		return fragment;
+	};
+
+	CKEDITOR.htmlParser.fragment.prototype =
+	{
+		/**
+		 * Adds a node to this fragment.
+		 * @param {Object} node The node to be added. It can be any of of the
+		 *		following types: {@link CKEDITOR.htmlParser.element},
+		 *		{@link CKEDITOR.htmlParser.text} and
+		 *		{@link CKEDITOR.htmlParser.comment}.
+		 * @example
+		 */
+		add : function( node )
+		{
+			var len = this.children.length,
+				previous = len > 0 && this.children[ len - 1 ] || null;
+
+			if ( previous )
+			{
+				// If the block to be appended is following text, trim spaces at
+				// the right of it.
+				if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT )
+				{
+					previous.value = CKEDITOR.tools.rtrim( previous.value );
+
+					// If we have completely cleared the previous node.
+					if ( previous.value.length === 0 )
+					{
+						// Remove it from the list and add the node again.
+						this.children.pop();
+						this.add( node );
+						return;
+					}
+				}
+
+				previous.next = node;
+			}
+
+			node.previous = previous;
+			node.parent = this;
+
+			this.children.push( node );
+
+			this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );
+		},
+
+		/**
+		 * Writes the fragment HTML to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 * var writer = new CKEDITOR.htmlWriter();
+		 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '&lt;P&gt;&lt;B&gt;Example' );
+		 * fragment.writeHtml( writer )
+		 * alert( writer.getHtml() );  "&lt;p&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/p&gt;"
+		 */
+		writeHtml : function( writer )
+		{
+			for ( var i = 0, len = this.children.length ; i < len ; i++ )
+				this.children[i].writeHtml( writer );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/htmlparser/text.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/text.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/htmlparser/text.js	(revision 2948)
@@ -0,0 +1,50 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	var spacesRegex = /[\t\r\n ]{2,}|[\t\r\n]/g;
+
+	/**
+	 * A lightweight representation of HTML text.
+	 * @constructor
+	 * @example
+	 */
+ 	CKEDITOR.htmlParser.text = function( value )
+	{
+		/**
+		 * The text value.
+		 * @type String
+		 * @example
+		 */
+		this.value = value.replace( spacesRegex, ' ' );
+
+		/** @private */
+		this._ =
+		{
+			isBlockLike : false
+		};
+	};
+
+	CKEDITOR.htmlParser.text.prototype =
+	{
+		/**
+		 * The node type. This is a constant value set to {@link CKEDITOR.NODE_TEXT}.
+		 * @type Number
+		 * @example
+		 */
+		type : CKEDITOR.NODE_TEXT,
+
+		/**
+		 * Writes the HTML representation of this text to a CKEDITOR.htmlWriter.
+		 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
+		 * @example
+		 */
+		writeHtml : function( writer )
+		{
+			writer.text( this.value );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/imagecacher.js
===================================================================
--- /CKEditor/trunk/_source/core/imagecacher.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/imagecacher.js	(revision 2948)
@@ -0,0 +1,58 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	var loaded = {};
+
+	var loadImage = function( image, callback )
+	{
+		var doCallback = function()
+			{
+				loaded[ image ] = 1;
+				callback();
+			};
+
+		var img = new CKEDITOR.dom.element( 'img' );
+		img.on( 'load', doCallback );
+		img.on( 'error', doCallback );
+		img.setAttribute( 'src', image );
+	};
+
+	/**
+	 * Load images into the browser cache.
+	 * @namespace
+	 * @example
+	 */
+ 	CKEDITOR.imageCacher =
+	{
+		/**
+		 * Loads one or more images.
+		 * @param {Array} images The URLs for the images to be loaded.
+		 * @param {Function} callback The function to be called once all images
+		 *		are loaded.
+		 */
+		load : function( images, callback )
+		{
+			var pendingCount = images.length;
+
+			var checkPending = function()
+			{
+				if ( --pendingCount === 0 )
+					callback();
+			};
+
+			for ( var i = 0 ; i < images.length ; i++ )
+			{
+				var image = images[ i ];
+
+				if ( loaded[ image ] )
+					checkPending();
+				else
+					loadImage( image, checkPending );
+			}
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/lang.js
===================================================================
--- /CKEditor/trunk/_source/core/lang.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/lang.js	(revision 2948)
@@ -0,0 +1,95 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	var loadedLangs = {};
+
+	CKEDITOR.lang =
+	{
+		/**
+		 * The list of languages available in the editor core.
+		 * @type Object
+		 * @example
+		 * alert( CKEDITOR.lang.en );  // "true"
+		 */
+		languages :
+		{
+			'ar'	: 1,
+			'en'	: 1,
+			'it'	: 1,
+			'pt-br'	: 1
+		},
+
+		/**
+		 * Loads a specific language file, or auto detect it. A callback is
+		 * then called when the file gets loaded.
+		 * @param {String} languageCode The code of the language file to be
+		 *		loaded. If "autoDetect" is set to true, this language will be
+		 *		used as the default one, if the detect language is not
+		 *		available in the core.
+		 * @param {Boolean} autoDetect Indicates that the function must try to
+		 *		detect the user language and load it instead.
+		 * @param {Function} callback The function to be called once the
+		 *		language file is loaded. Two parameters are passed to this
+		 *		function: the language code and the loaded language entries.
+		 * @example
+		 */
+		load : function( languageCode, autoDetect, callback )
+		{
+			if ( autoDetect )
+				languageCode = this.detect( languageCode );
+
+			if ( !this[ languageCode ] )
+			{
+				CKEDITOR.scriptLoader.load( CKEDITOR.getUrl(
+					'_source/' +	// %REMOVE_LINE%
+					'lang/' + languageCode + '.js' ),
+					function()
+						{
+							callback( languageCode, this[ languageCode ] );
+						}
+						, this );
+			}
+			else
+				callback( languageCode, this[ languageCode ] );
+		},
+
+		/**
+		 * Returns the language that best fit the user language. For example,
+		 * suppose that the user language is "pt-br". If this language is
+		 * supported by the editor, it is returned. Otherwise, if only "pt" is
+		 * supported, it is returned instead. If none of the previous are
+		 * supported, a default language is then returned.
+		 * @param {String} defaultLanguage The default language to be returned
+		 *		if the user language is not supported.
+		 * @returns {String} The detected language code.
+		 * @example
+		 * alert( CKEDITOR.lang.detect( 'en' ) );  // e.g., in a German browser: "de"
+		 */
+		detect : function( defaultLanguage )
+		{
+			var languages = this.languages;
+
+			var parts = ( navigator.userLanguage || navigator.language )
+					.toLowerCase()
+					.match( /([a-z]+)(?:-([a-z]+))?/ ),
+				lang = parts[1],
+				locale = parts[2];
+
+			if ( languages[ lang + '-' + locale ] )
+				lang = lang + '-' + locale;
+			else if ( !languages[ lang ] )
+				lang = null;
+
+			CKEDITOR.lang.detect = lang ?
+				function() { return lang; } :
+				function( defaultLanguage ) { return defaultLanguage; };
+
+			return lang || defaultLanguage;
+		}
+	};
+
+})();
Index: /CKEditor/trunk/_source/core/loader.js
===================================================================
--- /CKEditor/trunk/_source/core/loader.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/loader.js	(revision 2948)
@@ -0,0 +1,186 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.loader} objects, which is used to
+ *		load core scripts and their dependencies from _source.
+ */
+
+if ( typeof CKEDITOR == 'undefined' )
+	CKEDITOR = {};
+
+if ( !CKEDITOR.loader )
+{
+	/**
+	 * Load core scripts and their dependencies from _source.
+	 * @namespace
+	 * @example
+	 */
+	CKEDITOR.loader = (function()
+	{
+		// Table of script names and their dependencies.
+		var scripts =
+		{
+			'core/_bootstrap'		: [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/elementpath', 'core/dom/text', 'core/dom/range' ],
+			'core/ajax'				: [ 'core/xml' ],
+			'core/ckeditor'			: [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/tools' ],
+			'core/ckeditor_base'	: [],
+			'core/ckeditor_basic'	: [ 'core/editor_basic', 'core/env', 'core/event' ],
+			'core/command'			: [],
+			'core/config'			: [ 'core/ckeditor_base' ],
+			'core/dom'				: [],
+			'core/dom/document'		: [ 'core/dom','core/dom/element', 'core/dom/domobject', 'core/dom/window' ],
+			'core/dom/documentFragment'	: [ 'core/dom/element' ],
+			'core/dom/element'		: [ 'core/dom', 'core/dom/document', 'core/dom/domobject', 'core/dom/node', 'core/dom/nodelist', 'core/tools' ],
+			'core/dom/elementpath'	: [ 'core/dom/element' ],
+			'core/dom/event'		: [],
+			'core/dom/node'			: [ 'core/dom/domobject', 'core/tools' ],
+			'core/dom/nodelist'		: [ 'core/dom/node' ],
+			'core/dom/domobject'	: [ 'core/dom/event' ],
+			'core/dom/domwalker'	: [ 'core/dom/node', 'core/dom/element', 'core/dom/document' ],
+			'core/dom/range'		: [ 'core/dom/document', 'core/dom/documentFragment', 'core/dom/element', 'core/dom/domwalker' ],
+			'core/dom/text'			: [ 'core/dom/node', 'core/dom/domobject' ],
+			'core/dom/window'		: [ 'core/dom/domobject' ],
+			'core/dtd'				: [ 'core/tools' ],
+			'core/editor'			: [ 'core/command', 'core/config', 'core/editor_basic', 'core/focusmanager', 'core/lang', 'core/plugins', 'core/skins', 'core/themes', 'core/tools', 'core/ui' ],
+			'core/editor_basic'		: [ 'core/event' ],
+			'core/env'				: [],
+			'core/event'			: [],
+			'core/focusmanager'		: [],
+			'core/htmlparser'		: [],
+			'core/htmlparser/comment'	: [ 'core/htmlparser' ],
+			'core/htmlparser/element'	: [ 'core/htmlparser', 'core/htmlparser/fragment' ],
+			'core/htmlparser/fragment'	: [ 'core/htmlparser', 'core/htmlparser/comment', 'core/htmlparser/text' ],
+			'core/htmlparser/text'		: [ 'core/htmlparser' ],
+			'core/imagecacher'		: [ 'core/dom/element' ],
+			'core/lang'				: [],
+			'core/plugins'			: [ 'core/resourcemanager' ],
+			'core/resourcemanager'	: [ 'core/scriptloader', 'core/tools' ],
+			'core/scriptloader'		: [ 'core/dom/element', 'core/env' ],
+			'core/skins'			: [ 'core/imagecacher', 'core/scriptloader' ],
+			'core/themes'			: [ 'core/resourcemanager' ],
+			'core/tools'			: [ 'core/env' ],
+			'core/ui'				: [],
+			'core/xml'				: [ 'core/env' ]
+		};
+
+		var basePath = (function()
+		{
+			// This is a copy of CKEDITOR.basePath, but requires the script having
+			// "_source/core/loader.js".
+			if ( CKEDITOR && CKEDITOR.basePath )
+				return CKEDITOR.basePath;
+
+			// Find out the editor directory path, based on its <script> tag.
+			var path = '';
+			var scripts = document.getElementsByTagName( 'script' );
+
+			for ( var i = 0 ; i < scripts.length ; i++ )
+			{
+				var match = scripts[i].src.match( /(^|.*[\\\/])core\/loader.js(?:\?.*)?$/i );
+
+				if ( match )
+				{
+					path = match[1];
+					break;
+				}
+			}
+
+			// In IE (only) the script.src string is the raw valued entered in the
+			// HTML. Other browsers return the full resolved URL instead.
+			if ( path.indexOf('://') == -1 )
+			{
+				// Absolute path.
+				if ( path.indexOf( '/' ) === 0 )
+					path = location.href.match( /^.*?:\/\/[^\/]*/ )[0] + path;
+				// Relative path.
+				else
+					path = location.href.match( /^[^\?]*\// )[0] + path;
+			}
+
+			return path;
+		})();
+
+		var timestamp = ( CKEDITOR && CKEDITOR.timestamp ) || ( new Date() ).valueOf();	// %REMOVE_LINE%
+		/*																				// %REMOVE_LINE%
+		 * The production implementation contains a fixed timestamp						// %REMOVE_LINE%
+		 * generated by the releaser													// %REMOVE_LINE%
+		var timestamp = '%TIMESTAMP%';
+		 */																				// %REMOVE_LINE%
+
+		var getUrl = function( resource )
+		{
+			if ( CKEDITOR && CKEDITOR.getUrl )
+				return CKEDITOR.getUrl( resource );
+
+			return basePath + resource +
+				( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) +
+				't=' + timestamp;
+		};
+
+		/** @lends CKEDITOR.loader */
+		return {
+			/**
+			 * The list of loaded scripts in their loading order.
+			 * @type Array
+			 * @example
+			 * // Alert the loaded script names.
+			 * alert( <b>CKEDITOR.loader.loadedScripts</b> );
+			 */
+			loadedScripts : [],
+
+			/**
+			 * Loads a specific script, including its dependencies. This is not a
+			 * synchronous loading, which means that the code the be loaded will
+			 * not necessarily be available after this call.
+			 * @example
+			 * CKEDITOR.loader.load( 'core/dom/element' );
+			 */
+			load : function( scriptName )
+			{
+				// Check if the script has already been loaded.
+				if ( scriptName in this.loadedScripts )
+					return;
+
+				// Get the script dependencies list.
+				var dependencies = scripts[ scriptName ];
+				if ( !dependencies )
+					throw 'The script name"' + scriptName + '" is not defined.';
+
+				// Mark the script as loaded, even before really loading it, to
+				// avoid cross references recursion.
+				this.loadedScripts[ scriptName ] = true;
+
+				// Load all dependencies first.
+				for ( var i = 0 ; i < dependencies.length ; i++ )
+					this.load( dependencies[ i ] );
+
+				// Append this script to the list of loaded scripts.
+				this.loadedScripts.push( scriptName );
+
+				var scriptSrc = getUrl( '_source/' + scriptName + '.js' );
+
+				// Append the <script> element to the DOM.
+				if ( document.body )
+				{
+					var script = document.createElement( 'script' );
+					script.type = 'text/javascript';
+					script.src = scriptSrc;
+
+					document.body.appendChild( script );
+				}
+				else
+					document.write( '<script src="' + scriptSrc + '" type="text/javascript"><\/script>' );
+			}
+		};
+	})();
+}
+
+// Check if any script has been defined for autoload.
+if ( CKEDITOR._autoLoad )
+{
+	CKEDITOR.loader.load( CKEDITOR._autoLoad );
+	delete CKEDITOR._autoLoad;
+}
Index: /CKEditor/trunk/_source/core/plugindefinition.js
===================================================================
--- /CKEditor/trunk/_source/core/plugindefinition.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/plugindefinition.js	(revision 2948)
@@ -0,0 +1,68 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the "virtual" {@link CKEDITOR.pluginDefinition} class, which
+ *		contains the defintion of a plugin. This file is for documentation
+ *		purposes only.
+ */
+
+/**
+ * (Virtual Class) Do not call this constructor. This class is not really part
+ *		of the API. It just illustrates the features of plugin objects to be
+ *		passed to the {@link CKEDITOR.plugins.add} function.
+ * @name CKEDITOR.pluginDefinition
+ * @constructor
+ * @example
+ */
+
+/**
+ * A list of plugins that are required by this plugin. Note that this property
+ * doesn't guarantee the loading order of the plugins.
+ * @name CKEDITOR.pluginDefinition.prototype.requires
+ * @type Array
+ * @example
+ * CKEDITOR.plugins.add( 'sample',
+ * {
+ *     requires : [ 'button', 'selection' ]
+ * });
+ */
+
+ /**
+ * Function called on initialization of every editor instance created in the
+ * page before the init() call task. The beforeInit function will be called for
+ * all plugins, after that the init function is called for all of them. This
+ * feature makes it possible to initialize things that could be used in the
+ * init function of other plugins.
+ * @name CKEDITOR.pluginDefinition.prototype.beforeInit
+ * @function
+ * @param {CKEDITOR.editor} editor The editor instance being initialized.
+ * @param {String} pluginPath The URL path for the plugin installation folder.
+ * @example
+ * CKEDITOR.plugins.add( 'sample',
+ * {
+ *     beforeInit : function( editor, pluginPath )
+ *     {
+ *         alert( 'Editor "' + editor.name + '" is to be initialized!' );
+ *     }
+ * });
+ */
+
+ /**
+ * Function called on initialization of every editor instance created in the
+ * page.
+ * @name CKEDITOR.pluginDefinition.prototype.init
+ * @function
+ * @param {CKEDITOR.editor} editor The editor instance being initialized.
+ * @param {String} pluginPath The URL path for the plugin installation folder.
+ * @example
+ * CKEDITOR.plugins.add( 'sample',
+ * {
+ *     init : function( editor, pluginPath )
+ *     {
+ *         alert( 'Editor "' + editor.name + '" is being initialized!' );
+ *     }
+ * });
+ */
Index: /CKEditor/trunk/_source/core/plugins.js
===================================================================
--- /CKEditor/trunk/_source/core/plugins.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/plugins.js	(revision 2948)
@@ -0,0 +1,66 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.plugins} object, which is used to
+ *		manage plugins registration and loading.
+ */
+
+/**
+ * Manages plugins registration and loading.
+ * @namespace
+ * @augments CKEDITOR.resourceManager
+ * @example
+ */
+CKEDITOR.plugins = new CKEDITOR.resourceManager(
+	'_source/' +	// %REMOVE_LINE%
+	'plugins/', 'plugin' );
+
+CKEDITOR.plugins.load = CKEDITOR.tools.override( CKEDITOR.plugins.load, function( originalLoad )
+	{
+		return function( name, callback, scope )
+		{
+			var allPlugins = {};
+
+			var loadPlugins = function( names )
+			{
+				originalLoad.call( this, names, function( plugins )
+					{
+						CKEDITOR.tools.extend( allPlugins, plugins );
+
+						var requiredPlugins = [];
+						for ( var pluginName in plugins )
+						{
+							var plugin = plugins[ pluginName ],
+								requires = plugin && plugin.requires;
+
+							if ( requires )
+							{
+								for ( var i = 0 ; i < requires.length ; i++ )
+								{
+									if ( !allPlugins[ requires[ i ] ] )
+										requiredPlugins.push( requires[ i ] );
+								}
+							}
+						}
+
+						if ( requiredPlugins.length )
+							loadPlugins.call( this, requiredPlugins );
+						else if ( callback )
+							callback.call( scope || window, allPlugins );
+					}
+					, this);
+
+			};
+
+			loadPlugins.call( this, name );
+		};
+	});
+
+CKEDITOR.plugins.setLang = function( pluginName, languageCode, languageEntries )
+{
+	var plugin = this.get( pluginName );
+	plugin.lang[ languageCode ] = languageEntries;
+};
Index: /CKEditor/trunk/_source/core/resourcemanager.js
===================================================================
--- /CKEditor/trunk/_source/core/resourcemanager.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/resourcemanager.js	(revision 2948)
@@ -0,0 +1,195 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.resourceManager} class, which is
+ *		the base for resource managers, like plugins and themes.
+ */
+
+ /**
+ * Base class for resource managers, like plugins and themes. This class is not
+ * intended to be used out of the CKEditor core code.
+ * @param {String} basePath The path for the resources folder.
+ * @param {String} fileName The name used for resource files.
+ * @namespace
+ * @example
+ */
+CKEDITOR.resourceManager = function( basePath, fileName )
+{
+	/**
+	 * The base directory containing all resources.
+	 * @name CKEDITOR.resourceManager.prototype.basePath
+	 * @type String
+	 * @example
+	 */
+	this.basePath = basePath;
+
+	/**
+	 * The name used for resource files.
+	 * @name CKEDITOR.resourceManager.prototype.fileName
+	 * @type String
+	 * @example
+	 */
+	this.fileName = fileName;
+
+	/**
+	 * Contains references to all resources that have already been registered
+	 * with {@link #add}.
+	 * @name CKEDITOR.resourceManager.prototype.registered
+	 * @type Object
+	 * @example
+	 */
+	this.registered = {};
+
+	/**
+	 * Contains references to all resources that have already been loaded
+	 * with {@link #load}.
+	 * @name CKEDITOR.resourceManager.prototype.loaded
+	 * @type Object
+	 * @example
+	 */
+	this.loaded = {};
+
+	/**
+	 * Contains references to all resources that have already been registered
+	 * with {@link #addExternal}.
+	 * @name CKEDITOR.resourceManager.prototype.externals
+	 * @type Object
+	 * @example
+	 */
+	this.externals = {};
+
+	/**
+	 * @private
+	 */
+	this._ =
+	{
+		// List of callbacks waiting for plugins to be loaded.
+		waitingList : {}
+	};
+};
+
+CKEDITOR.resourceManager.prototype =
+{
+	/**
+	 * Registers a resource.
+	 * @param {String} name The resource name.
+	 * @param {Object} [definition] The resource definition.
+	 * @example
+	 * CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
+	 * @see CKEDITOR.pluginDefinition
+	 */
+	add : function( name, definition )
+	{
+		if ( this.registered[ name ] )
+			throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
+
+		this.registered[ name ] = definition || {};
+	},
+
+	/**
+	 * Gets the definition of a specific resource.
+	 * @param {String} name The resource name.
+	 * @type Object
+	 * @example
+	 * var definition = <b>CKEDITOR.plugins.get( 'sample' )</b>;
+	 */
+	get : function( name )
+	{
+		return this.registered[ name ] || null;
+	},
+
+	/**
+	 * Get the full path for a specific loaded resource.
+	 * @param {String} name The resource name.
+	 * @type String
+	 * @example
+	 * alert( <b>CKEDITOR.plugins.getPath( 'sample' )</b> );  // "&lt;editor path&gt;/plugins/sample/"
+	 */
+	getPath : function( name )
+	{
+		return this.externals[ name ] || this.basePath + name + '/';
+	},
+
+	/**
+	 * Registers a resource to be loaded from an external path instead of the core base path.
+	 * @param {String} name The resource name.
+	 * @param {String} path The resource external path.
+	 * @example
+	 * // Loads a plugin from '/myplugin/samples/plugin.js'.
+	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
+	 */
+	addExternal : function( name, path )
+	{
+		if ( this.registered[ name ] || this.externals[ name ] )
+			throw '[CKEDITOR.resourceManager.import] The resource name "' + name + '" is already registered or imported.';
+
+		this.externals[ name ] = path;
+	},
+
+	/**
+	 * Loads one or more resources.
+	 * @param {String|Array} name The name of the resource to load. It may be a
+	 *		string with a single resource name, or an array with several names.
+	 * @param {Function} callback A function to be called when all resources
+	 *		are loaded. The callback will receive an array containing all
+	 *		loaded names.
+	 * @param {Object} [scope] The scope object to be used for the callback
+	 *		call.
+	 * @example
+	 * <b>CKEDITOR.plugins.load</b>( 'myplugin', function( plugins )
+	 *     {
+	 *         alert( plugins['myplugin'] );  // "object"
+	 *     });
+	 */
+	load : function( names, callback, scope )
+	{
+		// Ensure that we have an array of names.
+		if ( !CKEDITOR.tools.isArray( names ) )
+			names = names ? [ names ] : [];
+
+		var loaded = this.loaded,
+			registered = this.registered,
+			urls = [],
+			urlsNames = {},
+			resources = {};
+
+		// Loop through all names.
+		for ( var i = 0 ; i < names.length ; i++ )
+		{
+			var name = names[ i ];
+
+			if ( !name )
+				continue;
+
+			// If not available yet.
+			if ( !loaded[ name ] && !registered[ name ] )
+			{
+				var url = CKEDITOR.getUrl( this.getPath( name ) + this.fileName + '.js' );
+				urls.push( url );
+				urlsNames[ url ] = name;
+			}
+			else
+				resources[ name ] = this.get( name );
+		}
+
+		CKEDITOR.scriptLoader.load( urls, function( completed, failed )
+			{
+				if ( failed.length )
+					throw '[CKEDITOR.resourceManager.load] Resource name "' + urlsNames[ failed[ 0 ] ] + '" was not found at "' + failed[ 0 ] + '".';
+
+				for ( var i = 0 ; i < completed.length ; i++ )
+				{
+					var name = urlsNames[ completed[ i ] ];
+					resources[ name ] = this.get( name );
+
+					loaded[ name ] = 1;
+				}
+
+				callback.call( scope, resources );
+			}
+			, this);
+	}
+};
Index: /CKEditor/trunk/_source/core/scriptloader.js
===================================================================
--- /CKEditor/trunk/_source/core/scriptloader.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/scriptloader.js	(revision 2948)
@@ -0,0 +1,192 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.scriptLoader} object, used to load scripts
+ *		asynchronously.
+ */
+
+/**
+ * Load scripts asynchronously.
+ * @namespace
+ * @example
+ */
+CKEDITOR.scriptLoader = (function()
+{
+	var uniqueScripts = {};
+	var waitingList = {};
+
+	return /** @lends CKEDITOR.scriptLoader */ {
+		/**
+		 * Loads one or more external script checking if not already loaded
+		 * previously by this function.
+		 * @param {String|Array} scriptUrl One or more URLs pointing to the
+		 *		scripts to be loaded.
+		 * @param {Function} [callback] A function to be called when the script
+		 *		is loaded and executed. If a string is passed to "scriptUrl", a
+		 *		boolean parameter is passed to the callback, indicating the
+		 *		success of the load. If an array is passed instead, two array
+		 *		parameters are passed to the callback; the first contains the
+		 *		URLs that have been properly loaded, and the second the failed
+		 *		ones.
+		 * @param {Object} [scope] The scope ("this" reference) to be used for
+		 *		the callback call. Default to {@link CKEDITOR}.
+		 * @param {Boolean} [noCheck] Indicates that the script must be loaded
+		 *		anyway, not checking if it has already loaded.
+		 * @example
+		 * CKEDITOR.scriptLoader.load( '/myscript.js' );
+		 * @example
+		 * CKEDITOR.scriptLoader.load( '/myscript.js', function( success )
+		 *     {
+		 *         // Alerts "true" if the script has been properly loaded.
+		 *         // HTTP error 404 should return "false".
+		 *         alert( success );
+		 *     });
+		 * @example
+		 * CKEDITOR.scriptLoader.load( [ '/myscript1.js', '/myscript2.js' ], function( completed, failed )
+		 *     {
+		 *         alert( 'Number of scripts loaded: ' + completed.length );
+		 *         alert( 'Number of failures: ' + failed.length );
+		 *     });
+		 */
+		load : function( scriptUrl, callback, scope, noCheck )
+		{
+			var isString = ( typeof scriptUrl == 'string' );
+
+			if ( isString )
+				scriptUrl = [ scriptUrl ];
+
+			if ( !scope )
+				scope = CKEDITOR;
+
+			var scriptCount = scriptUrl.length,
+				completed = [],
+				failed = [];
+
+			var doCallback = function( success )
+			{
+				if ( callback )
+				{
+					if ( isString )
+						callback.call( scope, success );
+					else
+						callback.call( scope, completed, failed );
+				}
+			};
+
+			if ( scriptCount === 0 )
+			{
+				doCallback( true );
+				return;
+			}
+
+			var checkLoaded = function( url, success )
+			{
+				( success ? completed : failed).push( url );
+
+				if ( --scriptCount <= 0 )
+					doCallback( success );
+			};
+
+			var onLoad = function( url, success )
+			{
+				// Mark this script as loaded.
+				uniqueScripts[ url ] = 1;
+
+				// Get the list of callback checks waiting for this file.
+				var waitingInfo = waitingList[ url ];
+				delete waitingList[ url ];
+
+				// Check all callbacks waiting for this file.
+				for ( var i = 0 ; i < waitingInfo.length ; i++ )
+					waitingInfo[ i ]( url, success );
+			};
+
+			var loadScript = function( url )
+			{
+				if ( noCheck !== true && uniqueScripts[ url ] )
+				{
+					checkLoaded( url, true );
+					return;
+				}
+
+				var waitingInfo = waitingList[ url ] || ( waitingList[ url ] = [] );
+				waitingInfo.push( checkLoaded );
+
+				// Load it only for the first request.
+				if ( waitingInfo.length > 1 )
+					return;
+
+				// Create the <script> element.
+				var script = new CKEDITOR.dom.element( 'script' );
+				script.setAttributes( {
+					type : 'text/javascript',
+					src : url } );
+
+				if ( callback )
+				{
+					if ( CKEDITOR.env.ie )
+					{
+						// FIXME: For IE, we are not able to return false on error (like 404).
+
+						/** @ignore */
+						script.$.onreadystatechange = function ()
+						{
+							if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' )
+							{
+								script.$.onreadystatechange = null;
+								onLoad( url, true );
+							}
+						};
+					}
+					else
+					{
+						/** @ignore */
+						script.$.onload = function()
+						{
+							onLoad( url, true );
+						};
+
+						// FIXME: Opera and Safari will not fire onerror.
+
+						/** @ignore */
+						script.$.onerror = function()
+						{
+							onLoad( url, false );
+						};
+					}
+				}
+
+				// Append it to <head>.
+				script.appendTo( CKEDITOR.document.getHead() );
+
+				CKEDITOR.fire( 'download', url );		// @Packager.RemoveLine
+			};
+
+			for ( var i = 0 ; i < scriptCount ; i++ )
+			{
+				loadScript( scriptUrl[ i ] );
+			}
+		},
+
+		/**
+		 * Executes a JavaScript code into the current document.
+		 * @param {String} code The code to be executed.
+		 * @example
+		 * CKEDITOR.scriptLoader.loadCode( 'var x = 10;' );
+		 * alert( x );  // "10"
+		 */
+		loadCode : function( code )
+		{
+			// Create the <script> element.
+			var script = new CKEDITOR.dom.element( 'script' );
+			script.setAttribute( 'type', 'text/javascript' );
+			script.appendText( code );
+
+			// Append it to <head>.
+			script.appendTo( CKEDITOR.document.getHead() );
+		}
+	};
+})();
Index: /CKEditor/trunk/_source/core/skins.js
===================================================================
--- /CKEditor/trunk/_source/core/skins.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/skins.js	(revision 2948)
@@ -0,0 +1,167 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.skins} object, which is used to
+ *		manage skins loading.
+ */
+
+/**
+ * Manages skins loading.
+ * @namespace
+ * @example
+ */
+CKEDITOR.skins = (function()
+{
+	// Holds the list of loaded skins.
+	var loaded = {};
+	var preloaded = {};
+
+	var loadedPart = function( skinName, part, callback )
+	{
+		// Get the skin definition.
+		var skinDefinition = loaded[ skinName ];
+
+		var appendSkinPath = function( fileNames )
+		{
+			for ( var n = 0 ; n < fileNames.length ; n++ )
+			{
+				fileNames[ n ] = CKEDITOR.getUrl(
+					'_source/' +	// %REMOVE_LINE%
+					'skins/' + skinName + '/' + fileNames[ n ] );
+			}
+		};
+
+		// Check if we need to preload images from it.
+		if ( !preloaded[ skinName ] )
+		{
+			var preload = skinDefinition.preload;
+			if ( preload && preload.length > 0 )
+			{
+				appendSkinPath( preload );
+				CKEDITOR.imageCacher.load( preload, function()
+					{
+						preloaded[ skinName ] = 1;
+						loadedPart( skinName, part, callback );
+					} );
+				return;
+			}
+
+			// Mark it as preloaded.
+			preloaded[ skinName ] = 1;
+		}
+
+		// Get the part definition.
+		part = skinDefinition[ part ];
+		var partIsLoaded = !part || !!part._isLoaded;
+
+		// Call the callback immediately if already loaded.
+		if ( partIsLoaded )
+			callback && callback();
+		else
+		{
+			// Put the callback in a queue.
+			var pending = part._pending || ( part._pending = [] );
+			pending.push( callback );
+
+			// We may have more than one skin part load request. Just the first
+			// one must do the loading job.
+			if ( pending.length > 1 )
+				return;
+
+			// Check whether the "css" and "js" properties have been defined
+			// for that part.
+			var cssIsLoaded = !part.css || !part.css.length;
+			var jsIsLoaded = !part.js || !part.js.length;
+
+			// This is the function that will trigger the callback calls on
+			// load.
+			var checkIsLoaded = function()
+			{
+				if ( cssIsLoaded && jsIsLoaded )
+				{
+					// Mark the part as loaded.
+					part._isLoaded = 1;
+
+					// Call all pending callbacks.
+					for ( var i = 0 ; i < pending.length ; i++ )
+					{
+						if ( pending[ i ] )
+							pending[ i ]();
+					}
+				}
+			};
+
+			// Load the "css" pieces.
+			if ( !cssIsLoaded )
+			{
+				appendSkinPath( part.css );
+
+				for ( var c = 0 ; c < part.css.length ; c++ )
+					CKEDITOR.document.appendStyleSheet( part.css[ c ] );
+
+				cssIsLoaded = 1;
+			}
+
+			// Load the "js" pieces.
+			if ( !jsIsLoaded )
+			{
+				appendSkinPath( part.js );
+				CKEDITOR.scriptLoader.load( part.js, function()
+					{
+						jsIsLoaded = 1;
+						checkIsLoaded();
+					});
+			}
+
+			// We may have nothing to load, so check it immediately.
+			checkIsLoaded();
+		}
+	};
+
+	return /** @lends CKEDITOR.skins */ {
+
+		/**
+		 * Registers a skin definition.
+		 * @param {String} skinName The skin name.
+		 * @param {Object} skinDefinition The skin definition.
+		 * @example
+		 */
+		add : function( skinName, skinDefinition )
+		{
+			loaded[ skinName ] = skinDefinition;
+
+			skinDefinition.skinPath = CKEDITOR.getUrl(
+					'_source/' +	// %REMOVE_LINE%
+					'skins/' + skinName + '/' );
+		},
+
+		/**
+		 * Loads a skin part. Skins are defined in parts, which are basically
+		 * separated CSS files. This function is mainly used by the core code and
+		 * should not have much use out of it.
+		 * @param {String} skinName The name of the skin to be loaded.
+		 * @param {String} skinPart The skin part to be loaded. Common skin parts
+		 *		are "editor" and "dialog".
+		 * @param {Function} [callback] A function to be called once the skin
+		 *		part files are loaded.
+		 * @example
+		 */
+		load : function( skinName, skinPart, callback )
+		{
+			if ( loaded[ skinName ] )
+				loadedPart( skinName, skinPart, callback );
+			else
+			{
+				CKEDITOR.scriptLoader.load( CKEDITOR.getUrl(
+					'_source/' +	// %REMOVE_LINE%
+					'skins/' + skinName + '/skin.js' ), function()
+						{
+							loadedPart( skinName, skinPart, callback );
+						} );
+			}
+		}
+	 };
+})();
Index: /CKEditor/trunk/_source/core/test.js
===================================================================
--- /CKEditor/trunk/_source/core/test.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/test.js	(revision 2948)
@@ -0,0 +1,116 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.test} object, which contains
+ *		functions used at our testing environment.
+ */
+
+/*jsl:import ../tests/yuitest.js*/
+
+/**
+ * Contains functions used at our testing environment. Currently,
+ * our testing system is based on the
+ * <a href="http://developer.yahoo.com/yui/yuitest/">YUI Test</a>.
+ * @namespace
+ * @example
+ */
+CKEDITOR.test =
+{
+	/**
+	 * The assertion namespace, containing all assertion functions. Currently,
+	 * this is an alias for
+	 * <a href="http://developer.yahoo.com/yui/docs/YAHOO.util.Assert.html">YAHOO.util.Assert</a>.
+	 * @example
+	 * <b>CKEDITOR.test.assert</b>.areEqual( '10', 10 );        // "true"
+	 * <b>CKEDITOR.test.assert</b>.areSame( '10', 10 );         // "false"
+	 * <b>CKEDITOR.test.assert</b>.isUndefined( window.test );  // "true"
+	 */
+	assert : YAHOO.util.Assert,
+
+	/**
+	 * Adds a test case to the test runner.
+	 * @param {Object} testCase The test case object. See other tests for
+	 *		examples.
+	 * @example
+	 * <b>CKEDITOR.test.addTestCase</b>((function()
+	 * {
+	 *     // Local reference to the "assert" object.
+	 *     var assert = CKEDITOR.test.assert;
+	 *
+	 *     return {
+	 *         test_example : function()
+	 *         {
+	 *             assert.areSame( '10', 10 );  // FAIL
+	 *         }
+	 *      };
+	 * })());
+	 */
+	addTestCase : function( testCase )
+	{
+		YAHOO.tool.TestRunner.add( new YAHOO.tool.TestCase( testCase ) );
+	},
+
+	/**
+	 * Gets the inner HTML of an element, for testing purposes.
+	 */
+	getInnerHtml : function( elementOrId )
+	{
+		var html = ( elementOrId.nodeType ? elementOrId : document.getElementById( elementOrId ) ).innerHTML;
+		html = html.toLowerCase();
+		html = html.replace( /[\n\r]/g, '' );
+
+		html = html.replace( /<\w[^>]*/g, function( match )
+			{
+				var attribs = [];
+				var hasClass;
+
+				match = match.replace( /\s([^\s=]+)=((?:"[^"]*")|(?:'[^']*')|(?:[^\s]+))/g, function( match, attName, attValue )
+					{
+						if ( attName == 'style' )
+						{
+							// IE doesn't add the final ";"
+							attValue = attValue.replace( /([^"';\s])\s*(["']?)$/, '$1;$2' );
+
+							// Safari adds some extra space to the end.
+							attValue = attValue.replace( /\s+(["']?)$/, '$1' );
+						}
+
+						// IE may have 'class' more than once.
+						if ( attName == 'class' )
+						{
+							if ( hasClass )
+								return '';
+
+							hasClass = true;
+						}
+
+						if ( attName != '_cke_expando' )
+							attribs.push( [ attName, attValue ] );
+
+						return '';
+					} );
+
+				attribs.sort( function( a, b )
+					{
+						var nameA = a[ 0 ];
+						var nameB = b[ 0 ];
+						return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
+					} );
+
+				var ret = match.replace( /\s{2,}/g, ' ' );
+
+				for ( var i = 0 ; i < attribs.length ; i++ )
+				{
+					ret += ' ' + attribs[i][0] + '=';
+					ret += (/^["']/).test( attribs[i][1] ) ? attribs[i][1] : '"' + attribs[i][1] + '"';
+				}
+
+				return ret;
+			} );
+
+		return html;
+	}
+};
Index: /CKEditor/trunk/_source/core/themes.js
===================================================================
--- /CKEditor/trunk/_source/core/themes.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/themes.js	(revision 2948)
@@ -0,0 +1,19 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.themes} object, which is used to
+ *		manage themes registration and loading.
+ */
+
+/**
+ * Manages themes registration and loading.
+ * @namespace
+ * @augments CKEDITOR.resourceManager
+ * @example
+ */
+CKEDITOR.themes = new CKEDITOR.resourceManager(
+	'_source/'+		// %REMOVE_LINE%
+	'themes/', 'theme' );
Index: /CKEditor/trunk/_source/core/tools.js
===================================================================
--- /CKEditor/trunk/_source/core/tools.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/tools.js	(revision 2948)
@@ -0,0 +1,316 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.tools} object, which contains
+ *		utility functions.
+ */
+
+/**
+ * Utility functions.
+ * @namespace
+ * @example
+ */
+CKEDITOR.tools =
+{
+	/**
+	 * Copy the properties from one object to another. By default, properties
+	 * already present in the target object <strong>are not</strong> overwritten.
+	 * @param {Object} target The object to be extended.
+	 * @param {Object} source[,souce(n)] The objects from which copy
+	 *		properties. Any number of objects can be passed to this function.
+	 * @param {Boolean} [overwrite] Indicates that properties already present
+	 *		in the target object must be overwritten. This must be the last
+	 *		parameter in the function call.
+	 * @returns {Object} the extended object (target).
+	 * @example
+	 * // Create the sample object.
+	 * var myObject =
+	 * {
+	 *     prop1 : true
+	 * };
+	 *
+	 * // Extend the above object with two properties.
+	 * CKEDITOR.tools.extend( myObject,
+	 *     {
+	 *         prop2 : true,
+	 *         prop3 : true
+	 *     } );
+	 *
+	 * // Alert "prop1", "prop2" and "prop3".
+	 * for ( var p in myObject )
+	 *     alert( p );
+	 */
+	extend : function( target )
+	{
+		var argsLength = arguments.length,
+			overwrite = arguments[ argsLength - 1 ];
+
+		if ( typeof overwrite == 'boolean' )
+			argsLength--;
+		else
+			overwrite = false;
+
+		for ( var i = 1 ; i < argsLength ; i++ )
+		{
+			var source = arguments[ i ];
+
+			for ( var propertyName in source )
+			{
+				if ( overwrite || target[ propertyName ] == undefined )
+					target[ propertyName ] = source[ propertyName ];
+			}
+		}
+
+		return target;
+	},
+
+	/**
+	 * Creates an object which is an instance of a class which prototype is a
+	 * predefined object. All properties defined in the source object are
+	 * automatically inherited by the resulting object, including future
+	 * changes to it.
+	 * @param {Object} source The source object to be used as the prototype for
+	 *		the final object.
+	 * @returns {Object} The resulting copy.
+	 */
+	prototypedCopy : function( source )
+	{
+		var copy = function()
+		{};
+		copy.prototype = source;
+		return new copy();
+	},
+
+	/**
+	 * Checks if an object is an Array.
+	 * @param {Object} object The object to be checked.
+	 * @type Boolean
+	 * @returns <i>true</i> if the object is an Array, otherwise <i>false</i>.
+	 * @example
+	 * alert( CKEDITOR.tools.isArray( [] ) );      // "true"
+	 * alert( CKEDITOR.tools.isArray( 'Test' ) );  // "false"
+	 */
+	isArray : function( object )
+	{
+		return ( !!object && object instanceof Array );
+	},
+
+	/**
+	 * Transforms a CSS property name to its relative DOM style name.
+	 * @param {String} cssName The CSS property name.
+	 * @returns {String} The transformed name.
+	 * @example
+	 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) );  // "backgroundColor"
+	 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'float' ) );             // "cssFloat"
+	 */
+	cssStyleToDomStyle : function( cssName )
+	{
+		if ( cssName == 'float' )
+			return 'cssFloat';
+		else
+		{
+			return cssName.replace( /-./g, function( match )
+				{
+					return match.substr( 1 ).toUpperCase();
+				});
+		}
+	},
+
+	/**
+	 * Replace special HTML characters in a string with their relative HTML
+	 * entity values.
+	 * @param {String} text The string to be encoded.
+	 * @returns {String} The encode string.
+	 * @example
+	 * alert( CKEDITOR.tools.htmlEncode( 'A > B & C < D' ) );  // "A &amp;gt; B &amp;amp; C &amp;lt; D"
+	 */
+	htmlEncode : function( text )
+	{
+		var standard = function( text )
+		{
+			var span = new CKEDITOR.dom.element( 'span' );
+			span.setText( text );
+			return span.getHtml();
+		};
+
+		this.htmlEncode = ( standard( '>' ) == '>' ) ?
+			function( text )
+			{
+				// WebKit does't encode the ">" character, which makes sense, but
+				// it's different than other browsers.
+				return standard( text ).replace( />/g, '&gt;' );
+			} :
+			standard;
+
+		return this.htmlEncode( text );
+	},
+
+	/**
+	 * Gets a unique number for this CKEDITOR execution session. It returns
+	 * progressive numbers starting at 1.
+	 * @function
+	 * @returns {Number} A unique number.
+	 * @example
+	 * alert( CKEDITOR.tools.<b>getNextNumber()</b> );  // "1" (e.g.)
+	 * alert( CKEDITOR.tools.<b>getNextNumber()</b> );  // "2"
+	 */
+	getNextNumber : (function()
+	{
+		var last = 0;
+		return function()
+		{
+			return ++last;
+		};
+	})(),
+
+	/**
+	 * Creates a function override.
+	 * @param {Function} originalFunction The function to be overridden.
+	 * @param {Function} functionBuilder A function that returns the new
+	 *		function. The original function reference will be passed to this
+	 *		function.
+	 * @returns {Function} The new function.
+	 * @example
+	 * var example =
+	 * {
+	 *     myFunction : function( name )
+	 *     {
+	 *         alert( 'Name: ' + name );
+	 *     }
+	 * };
+	 *
+	 * example.myFunction = CKEDITOR.tools.override( example.myFunction, function( myFunctionOriginal )
+	 *     {
+	 *         return function( name )
+	 *             {
+	 *                 alert( 'Override Name: ' + name );
+	 *                 myFunctionOriginal.call( this, name );
+	 *             };
+	 *     });
+	 */
+	override : function( originalFunction, functionBuilder )
+	{
+		return functionBuilder( originalFunction );
+	},
+
+	/**
+	 * Executes a function after specified delay.
+	 * @param {Function} func The function to be executed.
+	 * @param {Number} [milliseconds] The amount of time (millisecods) to wait
+	 *		to fire the function execution. Defaults to zero.
+	 * @param {Object} [scope] The object to hold the function execution scope
+	 *		(the "this" object). By default the "window" object.
+	 * @param {Object|Array} [args] A single object, or an array of objects, to
+	 *		pass as arguments to the function.
+	 * @param {Object} [ownerWindow] The window that will be used to set the
+	 *		timeout. By default the current "window".
+	 * @returns {Object} A value that can be used to cancel the function execution.
+	 * @example
+	 * CKEDITOR.tools.<b>setTimeout(
+	 *     function()
+	 *     {
+	 *         alert( 'Executed after 2 seconds' );
+	 *     },
+	 *     2000 )</b>;
+	 */
+	setTimeout : function( func, milliseconds, scope, args, ownerWindow )
+	{
+		if ( !ownerWindow )
+			ownerWindow = window;
+
+		if ( !scope )
+			scope = ownerWindow;
+
+		return ownerWindow.setTimeout(
+			function()
+			{
+				if ( args )
+					func.apply( scope, [].concat( args ) ) ;
+				else
+					func.apply( scope ) ;
+			},
+			milliseconds || 0 );
+	},
+
+	/**
+	 * Remove spaces from the start and the end of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @function
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string without the boundary spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.trim( '  example ' );  // "example"
+	 */
+	trim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})(),
+
+	/**
+	 * Remove spaces from the start (left) of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @function
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string excluding the removed spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "example "
+	 */
+	ltrim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /^[ \t\n\r]+/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})(),
+
+	/**
+	 * Remove spaces from the end (right) of a string. The following
+	 * characters are removed: space, tab, line break, line feed.
+	 * @function
+	 * @param {String} str The text from which remove the spaces.
+	 * @returns {String} The modified string excluding the removed spaces.
+	 * @example
+	 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "  example"
+	 */
+	rtrim : (function()
+	{
+		// We are not using \s because we don't want "non-breaking spaces" to be caught.
+		var trimRegex = /[ \t\n\r]+$/g;
+		return function( str )
+		{
+			return str.replace( trimRegex, '' ) ;
+		};
+	})(),
+
+	/**
+	 * Returns the index of an element in an array.
+	 * @param {Array} array The array to be searched.
+	 * @param {Object} entry The element to be found.
+	 * @returns {Number} The (zero based) index of the first entry that matches
+	 *		the entry, or -1 if not found.
+	 * @example
+	 * var letters = [ 'a', 'b', 'c' ];
+	 * alert( CKEDITOR.tools.indexOf( letters, 'b' ) );  "1"
+	 */
+	indexOf : function( array, entry )
+	{
+		for ( var i = 0, len = array.length ; i < len ; i++ )
+		{
+			if ( array[ i ] == entry )
+				return i;
+		}
+		return -1;
+	}
+};
+
+// PACKAGER_RENAME( CKEDITOR.tools )
Index: /CKEditor/trunk/_source/core/ui.js
===================================================================
--- /CKEditor/trunk/_source/core/ui.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/ui.js	(revision 2948)
@@ -0,0 +1,101 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * Contains UI features related to an editor instance.
+ * @constructor
+ * @param {CKEDITOR.editor} editor The editor instance.
+ * @example
+ */
+CKEDITOR.ui = function( editor )
+{
+	if ( editor.ui )
+		return editor.ui;
+
+	/**
+	 * Object used to hold private stuff.
+	 * @private
+	 */
+	this._ =
+	{
+		handlers : {},
+		items : {}
+	};
+
+	return this;
+};
+
+// PACKAGER_RENAME( CKEDITOR.ui )
+
+CKEDITOR.ui.prototype =
+{
+	/**
+	 * Adds a UI item to the items collection. These items can be later used in
+	 * the interface.
+	 * @param {String} name The UI item name.
+	 * @param {Object} type The item type.
+	 * @param {Object} definition The item definition. The properties of this
+	 *		object depend on the item type.
+	 * @example
+	 * // Add a new button named "MyBold".
+	 * editorInstance.ui.add( 'MyBold', CKEDITOR.UI_BUTTON,
+	 *     {
+	 *         label : 'My Bold',
+	 *         command : 'bold'
+	 *     });
+	 */
+	add : function( name, type, definition )
+	{
+		var item = this._.handlers[ type ].create( definition );
+		item.name = name;
+		this._.items[ name ] = item;
+	},
+
+	/**
+	 * Gets a UI object.
+	 * @param {String} name The UI item hame.
+	 * @example
+	 */
+	get : function( name )
+	{
+		return this._.items[ name ] || null;
+	},
+
+	/**
+	 * Adds a handler for a UI item type. The handler is responsible for
+	 * transforming UI item definitions in UI objects.
+	 * @param {Object} type The item type.
+	 * @param {Object} handler The handler definition.
+	 * @example
+	 */
+	addHandler : function( type, handler )
+	{
+		this._.handlers[ type ] = handler;
+	}
+};
+
+/**
+ * (Virtual Class) Do not call this constructor. This class is not really part
+ *		of the API. It just illustrates the features of hanlder objects to be
+ *		passed to the {@link CKEDITOR.ui.prototype.addHandler} function.
+ * @name CKEDITOR.ui.handlerDefinition
+ * @constructor
+ * @example
+ */
+
+ /**
+ * Transforms an item definition into an UI item object.
+ * @name CKEDITOR.handlerDefinition.prototype.create
+ * @function
+ * @param {Object} definition The item definition.
+ * @example
+ * editorInstance.ui.addHandler( CKEDITOR.UI_BUTTON,
+ *     {
+ *         create : function( definition )
+ *         {
+ *             return new CKEDITOR.ui.button( definition );
+ *         }
+ *     });
+ */
Index: /CKEditor/trunk/_source/core/xml.js
===================================================================
--- /CKEditor/trunk/_source/core/xml.js	(revision 2948)
+++ /CKEditor/trunk/_source/core/xml.js	(revision 2948)
@@ -0,0 +1,164 @@
+﻿/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.xml} class, which represents a
+ *		loaded XML document.
+ */
+
+/**
+ * Represents a loaded XML document.
+ * @constructor
+ * @param {object|string} xmlObjectOrData A native XML (DOM document) object or
+ *		a string containing the XML definition to be loaded.
+ * @example
+ * var xml = <b>new CKEDITOR.xml( '<books><book title="My Book" /></books>' )</b>;
+ */
+CKEDITOR.xml = function( xmlObjectOrData )
+{
+	var baseXml = null;
+
+	if ( typeof xmlObjectOrData == 'object' )
+		baseXml = xmlObjectOrData;
+	else
+	{
+		if ( window.DOMParser )
+			baseXml = (new DOMParser()).parseFromString( xmlObjectOrData || '', 'text/xml' );
+		else if ( window.ActiveXObject )
+		{
+			try { baseXml = new ActiveXObject( 'MSXML2.DOMDocument' ); }
+			catch(e)
+			{
+				try { baseXml = new ActiveXObject( 'Microsoft.XmlDom' ); } catch(e) {}
+			}
+
+			if ( baseXml )
+			{
+				baseXml.async = false;
+				baseXml.resolveExternals = false;
+				baseXml.validateOnParse = false;
+				baseXml.loadXML( xmlObjectOrData || '' );
+			}
+		}
+	}
+
+	/**
+	 * The native XML (DOM document) used by the class instance.
+	 * @type object
+	 * @example
+	 */
+	this.baseXml = baseXml;
+};
+
+CKEDITOR.xml.prototype =
+{
+	/**
+	 * Get a single node from the XML document, based on a XPath query.
+	 * @param {String} xpath The XPath query to execute.
+	 * @param {Object} [contextNode] The XML DOM node to be used as the context
+	 *		for the XPath query. The document root is used by default.
+	 * @returns {Object} A XML node element or null if the query has no results.
+	 * @example
+	 * // Create the XML instance.
+	 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+	 * // Get the first <item> node.
+	 * var itemNode = <b>xml.selectSingleNode( 'list/item' )</b>;
+	 * // Alert "item".
+	 * alert( itemNode.nodeName );
+	 */
+	selectSingleNode : function( xpath, contextNode )
+	{
+		var baseXml = this.baseXml;
+
+		if ( contextNode || ( contextNode = baseXml ) )
+		{
+			if ( CKEDITOR.env.ie || contextNode.selectSingleNode )	// IE
+				return contextNode.selectSingleNode( xpath );
+			else if ( baseXml.evaluate )							// Others
+			{
+				var result = baseXml.evaluate( xpath, contextNode, null, 9, null);
+				return ( result && result.singleNodeValue ) || null;
+			}
+		}
+
+		return null;
+	},
+
+	/**
+	 * Gets a list node from the XML document, based on a XPath query.
+	 * @param {String} xpath The XPath query to execute.
+	 * @param {Object} [contextNode] The XML DOM node to be used as the context
+	 *		for the XPath query. The document root is used by default.
+	 * @returns {ArrayLike} An array containing all matched nodes. The array will
+	 *		be empty if the query has no results.
+	 * @example
+	 * // Create the XML instance.
+	 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+	 * // Get the first <item> node.
+	 * var itemNodes = xml.selectSingleNode( 'list/item' );
+	 * // Alert "item" twice, one for each <item>.
+	 * for ( var i = 0 ; i < itemNodes.length ; i++ )
+	 *     alert( itemNodes[i].nodeName );
+	 */
+	selectNodes : function( xpath, contextNode )
+	{
+		var baseXml = this.baseXml,
+			nodes = [];
+
+		if ( contextNode || ( contextNode = baseXml ) )
+		{
+			if ( CKEDITOR.env.ie || contextNode.selectNodes )		// IE
+				return contextNode.selectNodes( xpath );
+			else if ( baseXml.evaluate )							// Others
+			{
+				var result = baseXml.evaluate( xpath, contextNode, null, 5, null);
+
+				if ( result )
+				{
+					var node;
+					while( ( node = result.iterateNext() ) )
+						nodes.push( node );
+				}
+			}
+		}
+
+		return nodes;
+	},
+
+	/**
+	 * Gets the string representation of hte inner contents of a XML node,
+	 * based on a XPath query.
+	 * @param {String} xpath The XPath query to execute.
+	 * @param {Object} [contextNode] The XML DOM node to be used as the context
+	 *		for the XPath query. The document root is used by default.
+	 * @returns {String} The textual representation of the inner contents of
+	 *		the node or null if the query has no results.
+	 * @example
+	 * // Create the XML instance.
+	 * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+	 * // Alert "<item id="test1" /><item id="test2" />".
+	 * alert( xml.getInnerXml( 'list' ) );
+	 */
+	getInnerXml : function( xpath, contextNode )
+	{
+		var node = this.selectSingleNode( xpath, contextNode ),
+			xml = [];
+		if ( node )
+		{
+			node = node.firstChild;
+			while ( node )
+			{
+				if ( node.xml )				// IE
+					xml.push( node.xml );
+				else if ( window.XMLSerializer )	// Others
+					xml.push( ( new XMLSerializer() ).serializeToString( node ) );
+
+				node = node.nextSibling;
+			}
+		}
+
+		return xml.length ? xml.join( '' ) : null;
+	}
+};
