Index: /CKEditor/branches/versions/3.6.x/CHANGES.html
===================================================================
--- /CKEditor/branches/versions/3.6.x/CHANGES.html	(revision 6786)
+++ /CKEditor/branches/versions/3.6.x/CHANGES.html	(revision 6787)
@@ -1,3 +1,3 @@
-﻿﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+﻿﻿﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
@@ -41,4 +41,5 @@
 	<ul>
 		<li><a href="http://dev.ckeditor.com/ticket/5647">#5647</a> : The kama skin now presents separators for the toolbar items, making it easier to group buttons, having a cleaner layout.</li>
+		<li><a href="http://dev.ckeditor.com/ticket/7044">#7044</a> : New "bbcode" plugin that make editor emitting (one dialect) of BBCode format.</li>
 		<li><a href="http://dev.ckeditor.com/ticket/5647">#5647</a> : Usability enhancements to the keyboard navigation on the toolbar. Not TAB is used to jump among toolbar groups, while the arrow can be used to cycle withing the group. The new <a href="http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html#.toolbarGroupCycling">toolbarGroupCycling</a> setting can be used to change the arrow keys behavior.</li>
 		<li><a href="http://dev.ckeditor.com/ticket/5647">#5647</a> : Accessibility enhancements to the structure of the toolbar.</li>
Index: /CKEditor/branches/versions/3.6.x/_samples/bbcode.html
===================================================================
--- /CKEditor/branches/versions/3.6.x/_samples/bbcode.html	(revision 6787)
+++ /CKEditor/branches/versions/3.6.x/_samples/bbcode.html	(revision 6787)
@@ -0,0 +1,140 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--
+Copyright (c) 2003-2011, 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>BBCode Plugin &mdash; 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 class="samples">
+		CKEditor Sample &mdash; BBCode Plugin
+	</h1>
+	<div class="description">
+	<p>
+		This sample shows how to configure CKEditor to output <a href="http://en.wikipedia.org/wiki/BBCode">BBCode</a> format instead of HTML.
+		Please note that the editor configuration was modify to reflect what is needed in a BBCode editing environment.
+		Smiley images, for example, were stripped to the emoticons that are commonly used in some BBCode dialects.
+	</p>
+	<p>
+		Please note that currently there is no standard for the BBCode markup language, so its implementation
+		for different platforms (message boards) can vary. This means that before using CKEditor to
+		output BBCode you may need to adjust the implementation to your own environment. 
+	</p>
+	<p>
+		A snippet of the configuration code can be seen below; check the source of this page for
+		a full definition:
+	</p>
+	<pre class="samples">
+CKEDITOR.replace( 'editor1',
+	{
+		extraPlugins : 'bbcode',
+		removePlugins : 'bidi,button,dialogadvtab,div,filebrowser,flash,format,forms,horizontalrule,iframe,indent,justify,liststyle,pagebreak,showborders,stylescombo,table,tabletools,templates',
+		toolbar :
+		[
+			['Source', '-', 'Save','NewPage','-','Undo','Redo'],
+			['Find','Replace','-','SelectAll','RemoveFormat'],
+			['Link', 'Image'],
+			'/',
+			['FontSize', 'Bold', 'Italic','Underline'],
+			['NumberedList','BulletedList','-','Blockquote'],
+			['TextColor', '-', 'Smiley','SpecialChar', '-', 'Maximize']
+		],
+		smiley_images :
+		[
+			'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','tounge_smile.gif',
+			'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angel_smile.gif','shades_smile.gif',
+			'cry_smile.gif','kiss.gif'
+		],
+		smiley_descriptions :
+		[
+			'smiley', 'sad', 'wink', 'laugh', 'cheeky', 'blush', 'surprise',
+			'indecision', 'angel', 'cool', 'crying', 'kiss'
+		]
+	});	</pre>
+	</div>
+
+	<!-- 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>
+			<label for="editor1">
+				Editor 1:</label>
+			<textarea cols="80" id="editor1" name="editor1" rows="10">This is some [b]sample text[/b]. You are using [url=http://ckeditor.com/]CKEditor[/url].</textarea>
+			<script type="text/javascript">
+			//<![CDATA[
+
+			// Replace the <textarea id="editor"> with an CKEditor
+			// instance, using the "bbcode" plugin, shaping some of the
+			// editor configuration to fit BBCode environment.
+			CKEDITOR.replace( 'editor1',
+				{
+					extraPlugins : 'bbcode',
+					// Remove unused plugins.
+					removePlugins : 'bidi,button,dialogadvtab,div,filebrowser,flash,format,forms,horizontalrule,iframe,indent,justify,liststyle,pagebreak,showborders,stylescombo,table,tabletools,templates',
+					// Width and height are not supported in the BBCode format, so object resizing is disabled.
+					disableObjectResizing : true,
+					// Define font sizes in percent values.
+					fontSize_sizes : "30/30%;50/50%;100/100%;120/120%;150/150%;200/200%;300/300%",
+					toolbar :
+					[
+						['Source', '-', 'Save','NewPage','-','Undo','Redo'],
+						['Find','Replace','-','SelectAll','RemoveFormat'],
+						['Link', 'Image', 'Smiley','SpecialChar'],
+						'/',
+						['Bold', 'Italic','Underline'],
+						['FontSize'],
+						['TextColor'],
+						['NumberedList','BulletedList','-','Blockquote'],
+						['Maximize']
+					],
+					// Strip CKEditor smileys to those commonly used in BBCode.
+					smiley_images :
+					[
+						'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','tounge_smile.gif',
+						'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angel_smile.gif','shades_smile.gif',
+						'cry_smile.gif','kiss.gif'
+					],
+					smiley_descriptions :
+					[
+						'smiley', 'sad', 'wink', 'laugh', 'cheeky', 'blush', 'surprise',
+						'indecision', 'angel', 'cool', 'crying', 'kiss'
+					]
+			} );
+
+			//]]>
+			</script>
+		</p>
+		<p>
+			<input type="submit" value="Submit" />
+		</p>
+	</form>
+	<div id="footer">
+		<hr />
+		<p>
+			CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
+		</p>
+		<p id="copy">
+			Copyright &copy; 2003-2011, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
+			Knabben. All rights reserved.
+		</p>
+	</div>
+</body>
+</html>
Index: /CKEditor/branches/versions/3.6.x/_samples/index.html
===================================================================
--- /CKEditor/branches/versions/3.6.x/_samples/index.html	(revision 6786)
+++ /CKEditor/branches/versions/3.6.x/_samples/index.html	(revision 6787)
@@ -88,4 +88,7 @@
 			Using the AutoGrow plugin in order to make the editor grow to fit the size of its content.
 		</li>
+		<li><a class="samples" href="bbcode.html">Output for BBCode</a><br />
+			Configuring CKEditor to produce BBCode tags instead of HTML.
+		</li>
 		<li><a class="samples" href="stylesheetparser.html">Stylesheet parser plugin</a><br />
 			The Stylesheet Parser plugin fills the Styles combo based on the css classes available in the document stylesheet.
Index: /CKEditor/branches/versions/3.6.x/_source/plugins/bbcode/plugin.js
===================================================================
--- /CKEditor/branches/versions/3.6.x/_source/plugins/bbcode/plugin.js	(revision 6787)
+++ /CKEditor/branches/versions/3.6.x/_source/plugins/bbcode/plugin.js	(revision 6787)
@@ -0,0 +1,923 @@
+﻿﻿/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+	CKEDITOR.on( 'dialogDefinition', function( ev )
+	{
+		var tab, name = ev.data.name,
+			definition = ev.data.definition;
+
+		if ( name == 'link' )
+		{
+			definition.removeContents( 'target' );
+			definition.removeContents( 'upload' );
+			definition.removeContents( 'advanced' );
+			tab = definition.getContents( 'info' );
+			tab.remove( 'emailSubject' );
+			tab.remove( 'emailBody' );
+		}
+		else if ( name == 'image' )
+		{
+			definition.removeContents( 'advanced' );
+			tab = definition.getContents( 'Link' );
+			tab.remove( 'cmbTarget' );
+			tab = definition.getContents( 'info' );
+			tab.remove( 'txtAlt' );
+			tab.remove( 'basic' );
+		}
+	});
+
+	var bbcodeMap = { 'b' : 'strong', 'u': 'u', 'i' : 'em', 'color' : 'span', 'size' : 'span', 'quote' : 'blockquote', 'code' : 'code', 'url' : 'a', 'email' : 'span', 'img' : 'span', '*' : 'li', 'list' : 'ol' },
+			convertMap = { 'strong' : 'b' , 'b' : 'b', 'u': 'u', 'em' : 'i', 'i': 'i', 'code' : 'code', 'li' : '*' },
+			tagnameMap = { 'strong' : 'b', 'em' : 'i', 'u' : 'u', 'li' : '*', 'ul' : 'list', 'ol' : 'list', 'code' : 'code', 'a' : 'link', 'img' : 'img', 'blockquote' : 'quote' },
+			stylesMap = { 'color' : 'color', 'size' : 'font-size' },
+			attributesMap = { 'url' : 'href', 'email' : 'mailhref', 'quote': 'cite', 'list' : 'listType' };
+
+	// List of block-like tags.
+	var dtd =  CKEDITOR.dtd,
+		blockLikeTags = CKEDITOR.tools.extend( { table:1 }, dtd.$block, dtd.$listItem, dtd.$tableContent, dtd.$list );
+
+	var semicolonFixRegex = /\s*(?:;\s*|$)/;
+	function serializeStyleText( stylesObject )
+	{
+		var styleText = '';
+		for ( var style in stylesObject )
+		{
+			var styleVal = stylesObject[ style ],
+				text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' );
+
+			styleText += text;
+		}
+		return styleText;
+	}
+
+	function parseStyleText( styleText )
+	{
+		var retval = {};
+		( styleText || '' )
+				.replace( /&quot;/g, '"' )
+				.replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )
+		{
+			retval[ name.toLowerCase() ] = value;
+		} );
+		return retval;
+	}
+
+	function RGBToHex( cssStyle )
+	{
+		return cssStyle.replace( /(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi, function( match, red, green, blue )
+			{
+				red = parseInt( red, 10 ).toString( 16 );
+				green = parseInt( green, 10 ).toString( 16 );
+				blue = parseInt( blue, 10 ).toString( 16 );
+				var color = [red, green, blue] ;
+
+				// Add padding zeros if the hex value is less than 0x10.
+				for ( var i = 0 ; i < color.length ; i++ )
+					color[i] = String( '0' + color[i] ).slice( -2 ) ;
+
+				return '#' + color.join( '' ) ;
+			 });
+	}
+
+	// Maintain the map of smiley-to-description.
+	var smileyMap = {"smiley":":)","sad":":(","wink":";)","laugh":":D","cheeky":":P","blush":":*)","surprise":":-o","indecision":":|","angry":">:(","angel":"o:)","cool":"8-)","devil":">:-)","crying":";(","kiss":":-*" },
+		smileyReverseMap = {},
+		smileyRegExp = [];
+
+	// Build regexp for the list of smiley text.
+	for ( var i in smileyMap )
+	{
+		smileyReverseMap[ smileyMap[ i ] ] = i;
+		smileyRegExp.push( smileyMap[ i ].replace( /\(|\)|\:|\/|\*|\-|\|/g, function( match ) { return '\\' + match; } ) );
+	}
+
+	smileyRegExp = new RegExp( smileyRegExp.join( '|' ), 'g' );
+
+	var decodeHtml = ( function ()
+	{
+		var regex = [],
+			entities =
+			{
+				nbsp	: '\u00A0',		// IE | FF
+				shy		: '\u00AD',		// IE
+				gt		: '\u003E',		// IE | FF |   --   | Opera
+				lt		: '\u003C'		// IE | FF | Safari | Opera
+			};
+
+		for ( var entity in entities )
+			regex.push( entity );
+
+		regex = new RegExp( '&(' + regex.join( '|' ) + ');', 'g' );
+
+		return function ( html )
+		{
+			return html.replace( regex, function( match, entity )
+			{
+				return entities[ entity ];
+			})
+		}
+	})();
+
+	CKEDITOR.BBCodeParser = function()
+	{
+		this._ =
+		{
+			bbcPartsRegex : /(?:\[([^\/\]=]*?)(?:=([^\]]*?))?\])|(?:\[\/([a-z]{1,16})\])/ig
+		};
+	};
+
+	CKEDITOR.BBCodeParser.prototype =
+	{
+		parse : function( bbcode )
+		{
+			var parts,
+					part,
+					lastIndex = 0;
+
+			while ( ( parts = this._.bbcPartsRegex.exec( bbcode ) ) )
+			{
+				var tagIndex = parts.index;
+				if ( tagIndex > lastIndex )
+				{
+					var text = bbcode.substring( lastIndex, tagIndex );
+					this.onText( text, 1 );
+				}
+
+				lastIndex = this._.bbcPartsRegex.lastIndex;
+
+				/*
+				 "parts" is an array with the following items:
+				 0 : The entire match for opening/closing tags and line-break;
+				 1 : line-break;
+				 2 : open of tag excludes option;
+				 3 : tag option;
+				 4 : close of tag;
+				 */
+
+				// Opening tag
+				if ( ( part = parts[ 1 ] ) )
+				{
+					part = part.toLowerCase();
+
+					var tagName = bbcodeMap[ part ],
+							attribs = {},
+							styles = {},
+							optionPart = parts[ 2 ];
+
+					if ( optionPart )
+					{
+						if ( part == 'list' )
+						{
+							if ( !isNaN( optionPart ) )
+								optionPart = 'decimal';
+							else if ( /^[a-z]+$/.test( optionPart ) )
+								optionPart = 'lower-alpha';
+							else if ( /^[A-Z]+$/.test( optionPart ) )
+								optionPart = 'upper-alpha';
+						}
+
+						if ( stylesMap[ part ] )
+						{
+							// Font size represents percentage.
+							if ( part == 'size' )
+								optionPart += '%';
+
+							styles[ stylesMap[ part ] ] = optionPart;
+							attribs.style = serializeStyleText( styles );
+						}
+						else if ( attributesMap[ part ] )
+							attribs[ attributesMap[ part ] ] = optionPart;
+					}
+
+					// Two special handling - image and email, protect them
+					// as "span" with an attribute marker.
+					if ( part == 'email' || part == 'img' )
+						attribs[ 'bbcode' ] = part;
+
+					this.onTagOpen( tagName, attribs, CKEDITOR.dtd.$empty[ tagName ] );
+				}
+				// Closing tag
+				else if ( ( part = parts[ 3 ] ) )
+					this.onTagClose( bbcodeMap[ part ] );
+			}
+
+			if ( bbcode.length > lastIndex )
+				this.onText( bbcode.substring( lastIndex, bbcode.length ), 1 );
+		}
+	};
+
+	/**
+	 * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
+	 * @param {String} source The HTML to be parsed, filling the fragment.
+	 * @param {Number} [fixForBody=false] Wrap body with specified element if needed.
+	 * @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.fromBBCode = function( source )
+	{
+		var parser = new CKEDITOR.BBCodeParser(),
+			fragment = new CKEDITOR.htmlParser.fragment(),
+			pendingInline = [],
+			pendingBrs = 0,
+			currentNode = fragment,
+			returnPoint;
+
+		function checkPending( 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.
+						pendingElement.parent = currentNode;
+						currentNode = pendingElement;
+
+						// Remove the pending element (back the index by one
+						// to properly process the next entry).
+						pendingInline.splice( i, 1 );
+						i--;
+					}
+				}
+			}
+		}
+
+		function checkPendingBrs( tagName, closing )
+		{
+			var len = currentNode.children.length,
+				previous = len > 0 && currentNode.children[ len - 1 ],
+				lineBreakParent = !previous && BBCodeWriter.getRule( tagnameMap[ currentNode.name ], 'breakAfterOpen' ),
+				lineBreakPrevious = previous && previous.type == CKEDITOR.NODE_ELEMENT && BBCodeWriter.getRule( tagnameMap[ previous.name ], 'breakAfterClose' ),
+				lineBreakCurrent = tagName && BBCodeWriter.getRule( tagnameMap[ tagName ], closing ? 'breakBeforeClose' : 'breakBeforeOpen' );
+
+			if ( pendingBrs && ( lineBreakParent || lineBreakPrevious || lineBreakCurrent ) )
+				pendingBrs--;
+
+			// 1. Either we're at the end of block, where it requires us to compensate the br filler
+			// removing logic (from htmldataprocessor).
+			// 2. Or we're at the end of pseudo block, where it requires us to compensate
+			// the bogus br effect.
+			if ( pendingBrs && tagName in blockLikeTags )
+				pendingBrs++;
+
+			while ( pendingBrs && pendingBrs-- )
+				currentNode.children.push( previous = new CKEDITOR.htmlParser.element( 'br' ) );
+		}
+
+		function addElement( node, target )
+		{
+			checkPendingBrs( node.name, 1 );
+
+			target = target || currentNode || fragment;
+
+			var len = target.children.length,
+				previous = len > 0 && target.children[ len - 1 ] || null;
+
+			node.previous = previous;
+			node.parent = target;
+
+			target.children.push( node );
+
+			if ( node.returnPoint )
+			{
+				currentNode = node.returnPoint;
+				delete node.returnPoint;
+			}
+		}
+
+		parser.onTagOpen = function( tagName, attributes, selfClosing )
+		{
+			var element = new CKEDITOR.htmlParser.element( tagName, attributes );
+
+			// 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;
+
+			var currentDtd = currentName
+				&& ( CKEDITOR.dtd[ currentName ]
+					|| ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
+
+			// If the element cannot be child of the current element.
+			if ( currentDtd && !currentDtd[ tagName ] )
+			{
+				var reApply = false,
+					addPoint;   // New position to start adding nodes.
+
+				// 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. Do not enter in this if block in this case.
+				if ( tagName == currentName )
+					addElement( currentNode, currentNode.parent );
+				else if ( tagName in CKEDITOR.dtd.$listItem )
+				{
+					parser.onTagOpen( 'ul', {} );
+					addPoint = currentNode;
+					reApply = true;
+				}
+				else
+				{
+					addElement( currentNode, currentNode.parent );
+
+					// 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;
+				}
+
+				if ( addPoint )
+					currentNode = addPoint;
+				// Try adding it to the return point, or the parent element.
+				else
+					currentNode = currentNode.returnPoint || currentNode.parent;
+
+				if ( reApply )
+				{
+					parser.onTagOpen.apply( this, arguments );
+					return;
+				}
+			}
+
+			checkPending( tagName );
+			checkPendingBrs( tagName );
+
+			element.parent = currentNode;
+			element.returnPoint = returnPoint;
+			returnPoint = 0;
+
+			if ( element.isEmpty )
+				addElement( element );
+			else
+				currentNode = element;
+		};
+
+		parser.onTagClose = function( tagName )
+		{
+			// Check if there is any pending tag to be closed.
+			for ( var i = pendingInline.length - 1 ; i >= 0 ; i-- )
+			{
+				// If found, just remove it from the list.
+				if ( tagName == pendingInline[ i ].name )
+				{
+					pendingInline.splice( i, 1 );
+					return;
+				}
+			}
+
+			var pendingAdd = [],
+				newPendingInline = [],
+				candidate = currentNode;
+
+			while ( candidate.type && candidate.name != tagName )
+			{
+				// If this is an inline element, add it to the pending list, if we're
+				// really closing one of the parents element later, they will continue
+				// after it.
+				if ( !candidate._.isBlockLike )
+					newPendingInline.unshift( candidate );
+
+				// This node should be added to it's parent at this point. But,
+				// it should happen only if the closing tag is really closing
+				// one of the nodes. So, for now, we just cache it.
+				pendingAdd.push( candidate );
+
+				candidate = candidate.parent;
+			}
+
+			if ( candidate.type )
+			{
+				// Add all elements that have been found in the above loop.
+				for ( i = 0 ; i < pendingAdd.length ; i++ )
+				{
+					var node = pendingAdd[ i ];
+					addElement( node, node.parent );
+				}
+
+				currentNode = candidate;
+
+
+				addElement( candidate, candidate.parent );
+
+				// The parent should start receiving new nodes now, except if
+				// addElement changed the currentNode.
+				if ( candidate == currentNode )
+					currentNode = currentNode.parent;
+
+				pendingInline = pendingInline.concat( newPendingInline );
+			}
+		};
+
+		parser.onText = function( text )
+		{
+			var currentDtd = CKEDITOR.dtd[ currentNode.name ];
+			if ( !currentDtd || currentDtd[ '#' ] )
+			{
+				checkPending();
+
+				text.replace(/([\r\n])|[^\r\n]*/g, function( piece, lineBreak )
+				{
+					if ( lineBreak !== undefined && lineBreak.length )
+						pendingBrs++;
+					else if ( piece.length )
+					{
+						var lastIndex = 0;
+
+						// Create smiley from text emotion.
+						piece.replace( smileyRegExp, function( match, index )
+						{
+							addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, index ) ), currentNode );
+							addElement( new CKEDITOR.htmlParser.element( 'smiley', { 'desc': smileyReverseMap[ match ] } ), currentNode );
+							lastIndex = index + match.length;
+						});
+
+						if ( lastIndex != piece.length )
+							addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, piece.length ) ), currentNode );
+					}
+				});
+			}
+		};
+
+		// Parse it.
+		parser.parse( CKEDITOR.tools.htmlEncode( source ) );
+
+		// Close all hanging nodes.
+		while ( currentNode.type )
+		{
+			var parent = currentNode.parent,
+				node = currentNode;
+
+			addElement( node, parent );
+			currentNode = parent;
+		}
+
+		return fragment;
+	};
+
+	CKEDITOR.htmlParser.BBCodeWriter = CKEDITOR.tools.createClass(
+	{
+		$ : function()
+		{
+			this._ =
+			{
+				output : [],
+				rules : []
+			};
+
+			// List and list item.
+			this.setRules( 'list',
+		   {
+			   breakBeforeOpen : 1,
+			   breakAfterOpen : 1,
+			   breakBeforeClose : 1,
+			   breakAfterClose : 1
+		   } );
+
+			this.setRules( '*',
+		   {
+			   breakBeforeOpen : 1,
+			   breakAfterOpen : 0,
+			   breakBeforeClose : 1,
+			   breakAfterClose : 0
+		   } );
+
+			this.setRules( 'quote',
+		   {
+			   breakBeforeOpen : 1,
+			   breakAfterOpen : 0,
+			   breakBeforeClose : 0,
+			   breakAfterClose : 1
+		   } );
+		},
+
+		proto :
+		{
+			/**
+			 * Sets formatting rules for a given tag. The possible rules are:
+			 * <ul>
+			 *	<li><b>breakBeforeOpen</b>: break line before the opener tag for this element.</li>
+			 *	<li><b>breakAfterOpen</b>: break line after the opener tag for this element.</li>
+			 *	<li><b>breakBeforeClose</b>: break line before the closer tag for this element.</li>
+			 *	<li><b>breakAfterClose</b>: break line after the closer tag for this element.</li>
+			 * </ul>
+			 *
+			 * All rules default to "false". Each call to the function overrides
+			 * already present rules, leaving the undefined untouched.
+			 *
+			 * @param {String} tagName The tag name to which set the rules.
+			 * @param {Object} rules An object containing the element rules.
+			 * @example
+			 * // Break line before and after "img" tags.
+			 * writer.setRules( 'list',
+			 *     {
+			 *         breakBeforeOpen : true
+			 *         breakAfterOpen : true
+			 *     });
+			 */
+			setRules : function( tagName, rules )
+			{
+				var currentRules = this._.rules[ tagName ];
+
+				if ( currentRules )
+					CKEDITOR.tools.extend( currentRules, rules, true );
+				else
+					this._.rules[ tagName ] = rules;
+			},
+
+			getRule : function( tagName, ruleName )
+			{
+				return this._.rules[ tagName ] && this._.rules[ tagName ][ ruleName ];
+			},
+
+			openTag : function( tag, attributes )
+			{
+				if ( tag in bbcodeMap )
+				{
+					if ( this.getRule( tag, 'breakBeforeOpen' ) )
+						this.lineBreak( 1 );
+
+					this.write( '[', tag );
+					var option = attributes.option;
+					option && this.write( '=', option );
+					this.write( ']' );
+
+					if ( this.getRule( tag, 'breakAfterOpen' ) )
+						this.lineBreak( 1 );
+				}
+				else if ( tag == 'br' )
+					this._.output.push( '\n' );
+			},
+
+			openTagClose : function() { },
+			attribute : function() { },
+
+			closeTag : function( tag )
+			{
+				if ( tag in bbcodeMap )
+				{
+					if ( this.getRule( tag, 'breakBeforeClose' ) )
+						this.lineBreak( 1 );
+
+					tag != '*' && this.write( '[/', tag, ']' );
+
+					if ( this.getRule( tag, 'breakAfterClose' ) )
+						this.lineBreak( 1 );
+				}
+			},
+
+			text : function( text )
+			{
+				this.write( text );
+			},
+
+			/**
+			 * Writes a comment.
+			 * @param {String} comment The comment text.
+			 * @example
+			 * // Writes "&lt;!-- My comment --&gt;".
+			 * writer.comment( ' My comment ' );
+			 */
+			comment : function() {},
+
+			/*
+			* Output line-break for formatting.
+			 */
+			lineBreak : function()
+			{
+				// Avoid line break when:
+				// 1) Previous tag already put one.
+				// 2) We're at output start.
+				if ( !this._.hasLineBreak && this._.output.length )
+				{
+					this.write( '\n' );
+					this._.hasLineBreak = 1;
+				}
+			},
+
+			write : function()
+			{
+				this._.hasLineBreak = 0;
+				var data = Array.prototype.join.call( arguments, '' );
+				this._.output.push( data );
+			},
+
+			reset : function()
+			{
+				this._.output = [];
+				this._.hasLineBreak = 0;
+			},
+
+			getHtml : function( reset )
+			{
+				var bbcode = this._.output.join( '' );
+
+				if ( reset )
+					this.reset();
+
+				return decodeHtml ( bbcode );
+			}
+		}
+	});
+
+	var BBCodeWriter = new CKEDITOR.htmlParser.BBCodeWriter();
+
+	CKEDITOR.plugins.add( 'bbcode',
+	  {
+		  requires : [ 'htmldataprocessor', 'entities' ],
+		  beforeInit : function( editor )
+		  {
+			  // Adapt some critical editor configuration for better support
+			  // of BBCode environment.
+			  var config = editor.config;
+			  CKEDITOR.tools.extend( config,
+			  {
+				  enterMode : CKEDITOR.ENTER_BR,
+				  basicEntities: false,
+				  entities : false,
+				  fillEmptyBlocks : false
+			  }, true );
+		  },
+		  init : function( editor )
+		  {
+			  var config = editor.config;
+
+			  function BBCodeToHtml( code )
+			  {
+				  var fragment = CKEDITOR.htmlParser.fragment.fromBBCode( code ),
+						  writer = new CKEDITOR.htmlParser.basicWriter();
+
+				  fragment.writeHtml( writer, dataFilter );
+				  return writer.getHtml( true );
+			  }
+
+			  var dataFilter = new CKEDITOR.htmlParser.filter();
+			  dataFilter.addRules(
+			  {
+				  elements :
+				  {
+					  'blockquote' : function( element )
+					  {
+						  var quoted = new CKEDITOR.htmlParser.element( 'div' );
+						  quoted.children = element.children;
+						  element.children = [ quoted ];
+						  var citeText = element.attributes.cite;
+						  if ( citeText )
+						  {
+							  var cite = new CKEDITOR.htmlParser.element( 'cite' );
+							  cite.add( new CKEDITOR.htmlParser.text( citeText.replace( /^"|"$/g, '' ) ) )
+							  delete element.attributes.cite;
+							  element.children.unshift( cite );
+						  }
+					  },
+					  'span' : function( element )
+					  {
+						  var bbcode;
+						  if ( ( bbcode = element.attributes.bbcode ) )
+						  {
+							  if ( bbcode == 'img' )
+							  {
+								  element.name = 'img';
+								  element.attributes.src = element.children[ 0 ].value;
+								  element.children = [];
+							  }
+							  else if ( bbcode == 'email' )
+							  {
+								  element.name = 'a';
+								  element.attributes.href = 'mailto:' + element.children[ 0 ].value;
+							  }
+							  
+							  delete element.attributes.bbcode;
+						  }
+					  },
+					  'ol' : function ( element )
+					  {
+						  if ( element.attributes.listType )
+						  {
+							  if ( element.attributes.listType != 'decimal' )
+								  element.attributes.style = 'list-style-type:' + element.attributes.listType;
+						  }
+						  else
+							  element.name = 'ul';
+
+						  delete element.attributes.listType;
+					  },
+					  a : function( element )
+					  {
+						  if ( !element.attributes.href )
+							  element.attributes.href = element.children[ 0 ].value
+					  },
+					  'smiley' : function( element )
+					  {
+							element.name = 'img';
+
+							var description = element.attributes.desc,
+								image = config.smiley_images[ CKEDITOR.tools.indexOf( config.smiley_descriptions, description ) ],
+								src = CKEDITOR.tools.htmlEncode( config.smiley_path + image );
+
+						  element.attributes =
+						  {
+							  src : src,
+							  'data-cke-saved-src' : src,
+							  title :  description,
+							  alt : description
+						  };
+					  }
+				  }
+			  } );
+
+			  editor.dataProcessor.htmlFilter.addRules(
+			  {
+				elements :
+				{
+					$ : function( element )
+					{
+						var attributes = element.attributes,
+								style = parseStyleText( attributes.style ),
+								value;
+
+						var tagName = element.name;
+						if ( tagName in convertMap )
+							tagName = convertMap[ tagName ];
+						else if ( tagName == 'span' )
+						{
+							if ( value = style.color )
+							{
+								tagName = 'color';
+								value = RGBToHex( value );
+							}
+							else if ( value = style[ 'font-size' ] )
+							{
+								var percentValue = value.match( /(\d+)%$/ );
+								if ( percentValue )
+								{
+									value = percentValue[ 1 ];
+									tagName = 'size';
+								}
+							}
+						}
+						else if ( tagName == 'ol' || tagName == 'ul' )
+						{
+							if ( value = style[ 'list-style-type'] )
+							{
+								switch ( value )
+								{
+									case 'lower-alpha':
+										value = 'a';
+										break;
+									case 'upper-alpha':
+										value = 'A';
+										break;
+								}
+							}
+							else if ( tagName == 'ol' )
+								value = 1;
+
+							tagName = 'list';
+						}
+						else if ( tagName == 'blockquote' )
+						{
+							try
+							{
+								var cite = element.children[ 0 ],
+										quoted = element.children[ 1 ],
+										citeText = cite.name == 'cite' && cite.children[ 0 ].value;
+
+								if ( citeText )
+								{
+									value = '"' + citeText + '"';
+									element.children = quoted.children;
+								}
+
+							}
+							catch( er )
+							{
+							}
+
+							tagName = 'quote';
+						}
+						else if ( tagName == 'a' )
+						{
+							if ( value = attributes.href )
+							{
+								if ( value.indexOf( 'mailto:' ) !== -1 )
+								{
+									tagName = 'email';
+									// [email] should have a single text child with email address.
+									element.children = [ new CKEDITOR.htmlParser.text( value.replace( 'mailto:', '' ) ) ];
+									value = '';
+								}
+								else
+								{
+									var singleton = element.children.length == 1 && element.children[ 0 ];
+									if ( singleton
+											&& singleton.type == CKEDITOR.NODE_TEXT
+											&& singleton.value == value )
+										value = '';
+
+									tagName = 'url';
+								}
+							}
+						}
+						else if ( tagName == 'img' )
+						{
+							element.isEmpty = 0;
+
+							// Translate smiley (image) to text emotion.
+							var src = attributes[ 'data-cke-saved-src' ];
+							if ( src && src.indexOf( editor.config.smiley_path ) != -1 )
+								return new CKEDITOR.htmlParser.text( smileyMap[ attributes.alt ] );
+							else
+								element.children = [ new CKEDITOR.htmlParser.text( src ) ];
+						}
+
+						element.name = tagName;
+						value && ( element.attributes.option = value );
+					},
+
+					// Remove any bogus br from the end of a pseudo block,
+					// e.g. <div>some text<br /><p>paragraph</p></div>
+					br : function( element )
+					{
+						var next = element.next;
+						if ( next && next.name in blockLikeTags )
+							return false;
+					}
+				}
+			  }, 1 );
+
+			  editor.dataProcessor.writer = BBCodeWriter;
+
+			  editor.on( 'editingBlockReady', function ()
+			  {
+				  var wysiwyg = editor._.modes[ 'wysiwyg' ];
+				  wysiwyg.loadData = CKEDITOR.tools.override( wysiwyg.loadData, function( org )
+				  {
+					  return function( data )
+					  {
+						  return ( org.call( this, BBCodeToHtml( data ) ) );
+					  };
+				  } );
+			  } );
+		  },
+		  
+		  afterInit : function( editor )
+		  {
+			  var filters;
+			  if ( editor._.elementsPath  )
+			  {
+				  // Eliminate irrelevant elements from displaying, e.g body and p.
+				  if ( filters = editor._.elementsPath.filters )
+					filters.push( function( element )
+						{
+							var htmlName = element.getName(),
+								name = tagnameMap[ htmlName ] || false;
+
+							// Specialized anchor presents as email.
+							if ( name == 'link' && element.getAttribute( 'href' ).indexOf( 'mailto:' ) == 0 )
+								name = 'email';
+							// Styled span could be either size or color.
+							else if ( htmlName == 'span' )
+							{
+								if ( element.getStyle( 'font-size' ) )
+									name = 'size';
+								else if ( element.getStyle( 'color' ) )
+									name = 'color';
+							}
+							else if ( name == 'img' )
+							{
+								var src = element.data( 'cke-saved-src' );
+								if ( src && src.indexOf( editor.config.smiley_path ) == 0 )
+									name = 'smiley';
+							}
+
+							return name;
+						});
+			  }
+		  }
+	  } );
+
+})();
+
+
Index: /CKEditor/branches/versions/3.6.x/_source/plugins/entities/plugin.js
===================================================================
--- /CKEditor/branches/versions/3.6.x/_source/plugins/entities/plugin.js	(revision 6786)
+++ /CKEditor/branches/versions/3.6.x/_source/plugins/entities/plugin.js	(revision 6787)
@@ -7,5 +7,5 @@
 {
 	// Base HTML entities.
-	var htmlbase = 'nbsp,gt,lt';
+	var htmlbase = 'nbsp,gt,lt,amp';
 
 	var entities =
@@ -62,5 +62,6 @@
 				shy		: '\u00AD',		// IE
 				gt		: '\u003E',		// IE | FF |   --   | Opera
-				lt		: '\u003C'		// IE | FF | Safari | Opera
+				lt		: '\u003C',		// IE | FF | Safari | Opera
+				amp : '\u0026'		// ALL
 			};
 
@@ -114,5 +115,8 @@
 			{
 				// Mandatory HTML base entities.
-				var selectedEntities = htmlbase;
+				var selectedEntities = '';
+
+				if ( config.basicEntities !== false )
+					selectedEntities += htmlbase;
 
 				if ( config.entities )
@@ -131,6 +135,6 @@
 				var entitiesTable = buildTable( selectedEntities );
 
-				// Create the Regex used to find entities in the text.
-				var entitiesRegex = '[' + entitiesTable.regex + ']';
+				// Create the Regex used to find entities in the text, leave it matches nothing if entities are empty.
+				var entitiesRegex = entitiesTable.regex ? '[' + entitiesTable.regex + ']' : 'a^';
 				delete entitiesTable.regex;
 
@@ -171,4 +175,20 @@
 
 /**
+ * Whether to escape HTML preserved entities in text, including:
+ * <ul>
+ * <li>nbsp</li>
+ * <li>gt</li>
+ * <li>lt</li>
+ * <li>amp</li>
+ * </ul>
+ * <strong>Note:</strong> It should not be subjected to change unless you're outputting non-HTML data format like BBCode.
+ * @type Boolean
+ * @default true
+ * @example
+ * config.basicEntities = false;
+ */
+CKEDITOR.config.basicEntities = true;
+
+/**
  * Whether to use HTML entities in the output.
  * @type Boolean
Index: /CKEditor/branches/versions/3.6.x/_source/plugins/image/dialogs/image.js
===================================================================
--- /CKEditor/branches/versions/3.6.x/_source/plugins/image/dialogs/image.js	(revision 6786)
+++ /CKEditor/branches/versions/3.6.x/_source/plugins/image/dialogs/image.js	(revision 6787)
@@ -116,4 +116,7 @@
 		var switchLockRatio = function( dialog, value )
 		{
+			if ( !dialog.getContentElement( 'info', 'ratioLock' ) )
+				return;
+
 			var oImageOriginal = dialog.originalElement;
 
@@ -170,6 +173,8 @@
 			if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
 			{
-				dialog.setValueOf( 'info', 'txtWidth', oImageOriginal.$.width );
-				dialog.setValueOf( 'info', 'txtHeight', oImageOriginal.$.height );
+				var widthField = dialog.getContentElement( 'info', 'txtWidth' ),
+					heightField = dialog.getContentElement( 'info', 'txtHeight' );
+				widthField && widthField.setValue( oImageOriginal.$.width );
+				heightField && heightField.setValue( oImageOriginal.$.height );
 			}
 			updatePreview( dialog );
@@ -452,6 +457,10 @@
 					this.hidePage( 'Link' );		//Hide Link tab.
 				var doc = this._.element.getDocument();
-				this.addFocusable( doc.getById( btnResetSizeId ), 5 );
-				this.addFocusable( doc.getById( btnLockSizesId ), 5 );
+
+				if ( this.getContentElement( 'info', 'ratioLock' ) )
+				{
+					this.addFocusable( doc.getById( btnResetSizeId ), 5 );
+					this.addFocusable( doc.getById( btnLockSizesId ), 5 );
+				}
 
 				this.commitContent = commitContent;
@@ -613,4 +622,5 @@
 							[
 								{
+									id : 'basic',
 									type : 'vbox',
 									children :
@@ -729,4 +739,5 @@
 												},
 												{
+													id : 'ratioLock',
 													type : 'html',
 													id : 'htmlButtons',
