Index: _samples/index.html
===================================================================
--- _samples/index.html	(revision 5923)
+++ _samples/index.html	(working copy)
@@ -42,6 +42,7 @@
 		<li><a href="output_xhtml.html">Output XHTML</a></li>
 		<li><a href="output_html.html">Output HTML</a></li>
 		<li><a href="autogrow.html">AutoGrow plugin</a></li>
+		<li><a href="placeholder.html">Placeholder plugin</a></li>
 	</ul>
 	<div id="footer">
 		<hr />
Index: _samples/placeholder.html
===================================================================
--- _samples/placeholder.html	(revision 0)
+++ _samples/placeholder.html	(revision 0)
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<title>Placeholder Plugin - CKEditor Sample</title>
+	<meta content="text/html; charset=utf-8" http-equiv="content-type" />
+	<!-- CKReleaser %REMOVE_LINE%
+	<script type="text/javascript" src="../ckeditor.js"></script>
+	CKReleaser %REMOVE_START% -->
+	<script type="text/javascript" src="../ckeditor_source.js"></script>
+	<!-- CKReleaser %REMOVE_END% -->
+	<script src="sample.js" type="text/javascript"></script>
+	<link href="sample.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+	<h1>
+		CKEditor Sample
+	</h1>
+	<!-- This <div> holds alert messages to be display in the sample page. -->
+	<div id="alerts">
+		<noscript>
+			<p>
+				<strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript
+				support, like yours, you should still see the contents (HTML data) and you should
+				be able to edit it normally, without a rich editor interface.
+			</p>
+		</noscript>
+	</div>
+	<form action="sample_posteddata.php" method="post">
+		<p>
+			In this sample the Placeholder plugin is available.<br />
+			It replaces text in the format of <code>[[text]]</code> to uneditable sections, and lets the user edit them and create new ones using a dialog.</p>
+		<p>
+			<label for="editor1">
+				With default configuration:</label><br />
+			<textarea cols="80" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href="http://ckeditor.com/"&gt;CKEditor&lt;/a&gt;. [[This is a placeholder]]&lt;/p&gt;</textarea>
+			<script type="text/javascript">
+			//<![CDATA[
+
+				CKEDITOR.replace( 'editor1', {
+					extraPlugins : 'placeholder',
+					toolbar : [ [ 'Source', 'CreatePlaceholder' ] ]
+				});
+
+			//]]>
+			</script>
+		</p>
+		<p>
+			<input type="submit" value="Submit" />
+		</p>
+	</form>
+	<div id="footer">
+		<hr />
+		<p>
+			CKEditor - The text editor for Internet - <a href="http://ckeditor.com/">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright &copy; 2003-2010, <a href="http://cksource.com/">CKSource</a> - Frederico
+			Knabben. All rights reserved.
+		</p>
+	</div>
+</body>
+</html>
\ No newline at end of file
Index: _source/plugins/placeholder/dialogs/placeholder.js
===================================================================
--- _source/plugins/placeholder/dialogs/placeholder.js	(revision 0)
+++ _source/plugins/placeholder/dialogs/placeholder.js	(revision 0)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.html or http://ckeditor.com/license
+ */
+
+(function()
+{
+	function placeholderDialog( editor, isEdit )
+	{
+
+		var lang = editor.lang.placeholder,
+			generalLabel = editor.lang.common.generalTab;
+		return {
+			title : lang.title,
+			minWidth : 300,
+			minHeight : 80,
+			contents :
+			[
+				{
+					id : 'info',
+					label : generalLabel,
+					title : generalLabel,
+					elements :
+					[
+						{
+							id : 'text',
+							type : 'text',
+							style : 'width: 100%;',
+							label : lang.text,
+							'default' : '',
+							required : true,
+							validate : CKEDITOR.dialog.validate.notEmpty( lang.textMissing ),
+							setup : function( element )
+							{
+								if ( isEdit )
+									this.setValue( element.getText().slice( 2, -2 ) );
+							},
+							commit : function( element )
+							{
+								var text = '[[' + this.getValue() + ']]';
+								// The placeholder must be recreated.
+								CKEDITOR.plugins.placeholder.createPlaceholder( editor, element, text );
+							}
+						}
+					]
+				}
+			],
+			onShow : function()
+			{
+				this._element = isEdit && editor.getSelection().getRanges()[0].getTouchedStartNode();
+				this.setupContent( this._element );
+			},
+			onOk : function()
+			{
+				this.commitContent( this._element );
+				delete this._element;
+			}
+		};
+	}
+
+	CKEDITOR.dialog.add( 'createplaceholder', function( editor )
+		{
+			return placeholderDialog( editor );
+		});
+	CKEDITOR.dialog.add( 'editplaceholder', function( editor )
+		{
+			return placeholderDialog( editor, 1 );
+		});
+} )();
Index: _source/plugins/placeholder/lang/en.js
===================================================================
--- _source/plugins/placeholder/lang/en.js	(revision 0)
+++ _source/plugins/placeholder/lang/en.js	(revision 0)
@@ -0,0 +1,16 @@
+﻿/*
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'en',
+{
+	placeholder :
+	{
+		title		: 'Placeholder Properties',
+		toolbar		: 'Create Placeholder',
+		text		: 'Placeholder Text',
+		edit		: 'Edit Placeholder',
+		textMissing	: 'The placeholder must contain text.'
+	}
+});
Index: _source/plugins/placeholder/plugin.js
===================================================================
--- _source/plugins/placeholder/plugin.js	(revision 0)
+++ _source/plugins/placeholder/plugin.js	(revision 0)
@@ -0,0 +1,163 @@
+/*
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The "placeholder" plugin.
+ *
+ */
+
+(function()
+{
+	var placeholderReplaceRegex = /\[\[[^\]]+\]\]/g;
+	CKEDITOR.plugins.add( 'placeholder',
+	{
+		requires : [ 'dialog' ],
+
+		availableLangs : { en:1 },
+		init : function( editor )
+		{
+			var langCode = editor.langCode,
+				plugin = this;
+			langCode = plugin.availableLangs[ langCode ] ? langCode : 'en';
+
+			CKEDITOR.scriptLoader.load(
+				CKEDITOR.getUrl( plugin.path + 'lang/' + langCode + '.js' ),
+				function()
+				{
+					var lang = CKEDITOR.tools.extend( editor.lang, plugin.lang[ langCode ] ).placeholder;
+
+					editor.addCommand( 'createplaceholder', new CKEDITOR.dialogCommand( 'createplaceholder' ) );
+					editor.addCommand( 'editplaceholder', new CKEDITOR.dialogCommand( 'editplaceholder' ) );
+
+					editor.ui.addButton( 'CreatePlaceholder',
+					{
+						label : lang.toolbar,
+						command :'createplaceholder',
+						icon : this.path + 'placeholder.gif'
+					});
+
+					if ( editor.addMenuItems )
+					{
+						editor.addMenuGroup( 'placeholder', 20 );
+						editor.addMenuItems(
+							{
+								editplaceholder :
+								{
+									label : lang.edit,
+									command : 'editplaceholder',
+									group : 'placeholder',
+									order : 1,
+									icon : this.path + 'placeholder.gif'
+								}
+							});
+
+						if ( editor.contextMenu )
+						{
+							editor.contextMenu.addListener( function( element, selection )
+								{
+									if ( !element || !element.hasAttribute( '_cke_placeholder' ) )
+										return null;
+
+									return { editplaceholder : CKEDITOR.TRISTATE_OFF };
+								});
+						}
+					}
+				});
+
+			editor.addCss(
+				'.cke_placeholder' + 
+				'{' + 
+					'background-color: #ffff00;' + 
+					( CKEDITOR.env.gecko ? 'cursor: default;' : '' ) +
+				'}'
+			);
+
+			editor.on( 'contentDom', function()
+				{
+					editor.document.getBody().on( 'resizestart', function( evt )
+						{
+							if ( editor.getSelection().getSelectedElement().hasAttribute( '_cke_placeholder' ) )
+								evt.data.preventDefault();
+						});
+				});
+
+			CKEDITOR.dialog.add( 'createplaceholder', this.path + 'dialogs/placeholder.js' );
+			CKEDITOR.dialog.add( 'editplaceholder', this.path + 'dialogs/placeholder.js' );
+		},
+		afterInit : function( editor )
+		{
+			var dataProcessor = editor.dataProcessor,
+				dataFilter = dataProcessor && dataProcessor.dataFilter;
+				htmlFilter = dataProcessor && dataProcessor.htmlFilter;
+
+			if ( dataFilter )
+			{
+				dataFilter.addRules(
+				{
+					text : function( text )
+					{
+						return text.replace( placeholderReplaceRegex, function( match )
+							{
+								return CKEDITOR.plugins.placeholder.createPlaceholder( editor, null, match, 1 );
+							});
+					}
+				});
+			}
+
+			if ( htmlFilter )
+			{
+				htmlFilter.addRules(
+				{
+					elements :
+					{
+						'span' : function( element )
+						{
+							if ( element.attributes && element.attributes._cke_placeholder )
+								delete element.name;
+						}
+					}
+				});
+			}
+		}
+	});
+})();
+
+CKEDITOR.plugins.placeholder = 
+{
+	createPlaceholder : function( editor, oldElement, text, isGet )
+	{
+		var element = new CKEDITOR.dom.element( 'span', editor.document );
+		element.setAttributes(
+			{
+				contentEditable	: 'false',
+				_cke_placeholder	: 1,
+				'class'		: 'cke_placeholder'
+			}
+		);
+
+		text && element.setText( text );
+
+		if ( isGet )
+			return element.getOuterHtml();
+
+		if ( oldElement )
+		{
+			if ( CKEDITOR.env.ie )
+			{
+				element.insertAfter( oldElement );
+				// Some time is required for IE before the element is removed.
+				setTimeout( function()
+					{
+						oldElement.remove();
+						element.focus();
+					}, 10 );
+			}
+			else
+				element.replace( oldElement );
+		}
+		else
+			editor.insertElement( element );
+	}
+};
