Index: /CKEditor/trunk/_source/plugins/blockquote/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/blockquote/plugin.js	(revision 3066)
+++ /CKEditor/trunk/_source/plugins/blockquote/plugin.js	(revision 3066)
@@ -0,0 +1,300 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file Blockquote.
+ */
+
+(function()
+{
+	function getState( editor, path )
+	{
+		var firstBlock = path.block || path.blockLimit;
+
+		if ( !firstBlock || firstBlock.getName() == 'body' )
+			return CKEDITOR.TRISTATE_OFF;
+
+		// See if the first block has a blockquote parent.
+		if ( firstBlock.getAscendant( 'blockquote', true ) )
+			return CKEDITOR.TRISTATE_ON;
+
+		return CKEDITOR.TRISTATE_OFF;
+	}
+
+	function onSelectionChange( evt )
+	{
+		var editor = evt.editor,
+			command = editor.getCommand( 'blockquote' );
+		command.state = getState( editor, evt.data.path );
+		command.fire( 'state' );
+	}
+
+	function noBlockLeft( bqBlock )
+	{
+		for ( var i = 0, length = bqBlock.getChildCount(), child ; i < length && ( child = bqBlock.getChild( i ) ) ; i++ )
+		{
+			if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
+				return false;
+		}
+		return true;
+	}
+
+	var commandObject =
+	{
+		exec : function( editor )
+		{
+			var state = editor.getCommand( 'blockquote' ).state,
+				selection = editor.getSelection(),
+				range = selection && selection.getRanges()[0];
+
+			if ( !range )
+				return;
+
+			var bookmarks = selection.createBookmarks();
+
+			// Kludge for #1592: if the bookmark nodes are in the beginning of
+			// blockquote, then move them to the nearest block element in the
+			// blockquote.
+			if ( CKEDITOR.env.ie )
+			{
+				var bookmarkStart = bookmarks[0].startNode,
+					bookmarkEnd = bookmarks[0].endNode,
+					cursor;
+
+				if ( bookmarkStart && bookmarkStart.getParent().getName() == 'blockquote' )
+				{
+					cursor = bookmarkStart;
+					while ( ( cursor = cursor.getNext() ) )
+					{
+						if ( cursor.type == CKEDITOR.NODE_ELEMENT &&
+								cursor.isBlockBoundary() )
+						{
+							bookmarkStart.move( cursor, true );
+							break;
+						}
+					}
+				}
+
+				if ( bookmarkEnd
+						&& bookmarkEnd.getParent().getName() == 'blockquote' )
+				{
+					cursor = bookmarkEnd;
+					while ( ( cursor = cursor.getPrevious() ) )
+					{
+						if ( cursor.type == CKEDITOR.NODE_ELEMENT &&
+								cursor.isBlockBoundary() )
+						{
+							bookmarkEnd.move( cursor );
+							break;
+						}
+					}
+				}
+			}	
+
+			var iterator = range.createIterator(),
+				block;
+
+			if ( state == CKEDITOR.TRISTATE_OFF )
+			{
+				var paragraphs = [];
+				while ( ( block = iterator.getNextParagraph() ) )
+					paragraphs.push( block );
+
+				// If no paragraphs, create one from the current selection position.
+				if ( paragraphs.length < 1 )
+				{
+					var para = editor.document.createElement( editor.config.enterMode ),
+						firstBookmark = bookmarks.shift();
+					range.insertNode( para );
+					para.append( new CKEDITOR.dom.text( '\ufeff', editor.document ) );
+					range.moveToBookmark( firstBookmark );
+					range.selectNodeContents( para );
+					range.collapse( true );
+					firstBookmark = range.createBookmark();
+					paragraphs.push( para );
+					bookmarks.unshift( firstBookmark );
+				}
+
+				// Make sure all paragraphs have the same parent.
+				var commonParent = paragraphs[0].getParent(),
+					tmp = [];
+				for ( var i = 0 ; i < paragraphs.length ; i++ )
+				{
+					block = paragraphs[i];
+					commonParent = commonParent.getCommonAncestor( block.getParent() );
+				}
+
+				// The common parent must not be the following tags: table, tbody, tr, ol, ul.
+				var denyTags = { table : 1, tbody : 1, tr : 1, ol : 1, ul : 1 };
+				while ( denyTags[ commonParent.getName() ] )
+					commonParent = commonParent.getParent();
+
+				// Reconstruct the block list to be processed such that all resulting blocks
+				// satisfy parentNode.equals( commonParent ).
+				var lastBlock = null;
+				while ( paragraphs.length > 0 )
+				{
+					block = paragraphs.shift();
+					while ( !block.getParent().equals( commonParent ) )
+						block = block.getParent();
+					if ( !block.equals( lastBlock ) )
+						tmp.push( block );
+					lastBlock = block;
+				}
+
+				// If any of the selected blocks is a blockquote, remove it to prevent
+				// nested blockquotes.
+				while ( tmp.length > 0 )
+				{
+					block = tmp.shift();
+					if ( block.getName() == 'blockquote' )
+					{
+						var docFrag = new CKEDITOR.dom.documentFragment( editor.document );
+						while ( block.getFirst() )
+						{
+							docFrag.append( block.getFirst().remove() );
+							paragraphs.push( docFrag.getLast() );
+						}
+
+						docFrag.replace( block );		
+					}
+					else
+						paragraphs.push( block );
+				}
+
+				// Now we have all the blocks to be included in a new blockquote node.
+				var bqBlock = editor.document.createElement( 'blockquote' );
+				bqBlock.insertBefore( paragraphs[0] );
+				while ( paragraphs.length > 0 )
+				{
+					block = paragraphs.shift();
+					bqBlock.append( block );
+				}
+			}
+			else if ( state == CKEDITOR.TRISTATE_ON )
+			{
+				var moveOutNodes = [],
+					database = {};
+				
+				while ( ( block = iterator.getNextParagraph() ) )
+				{
+					var bqParent = null,
+						bqChild = null;
+					while ( block.getParent() )
+					{
+						if ( block.getParent().getName() == 'blockquote' )
+						{
+							bqParent = block.getParent();
+							bqChild = block;
+							break;
+						}
+						block = block.getParent();
+					}
+
+					// Remember the blocks that were recorded down in the moveOutNodes array
+					// to prevent duplicates.
+					if ( bqParent && bqChild && !bqChild.getCustomData( 'blockquote_moveout' ) )
+					{
+						moveOutNodes.push( bqChild );
+						CKEDITOR.dom.element.setMarker( database, bqChild, 'blockquote_moveout', true );
+					}
+				}
+
+				CKEDITOR.dom.element.clearAllMarkers( database );
+
+				var movedNodes = [],
+					processedBlockquoteBlocks = [],
+					database = {};
+				while ( moveOutNodes.length > 0 )
+				{
+					var node = moveOutNodes.shift(),
+						bqBlock = node.getParent();
+
+					// If the node is located at the beginning or the end, just take it out
+					// without splitting. Otherwise, split the blockquote node and move the
+					// paragraph in between the two blockquote nodes.
+					if ( !node.getPrevious() )
+						node.remove().insertBefore( bqBlock );
+					else if ( !node.getNext() )
+						node.remove().insertAfter( bqBlock );
+					else
+					{
+						node.breakParent( node.getParent() );
+						processedBlockquoteBlocks.push( node.getNext() );
+					}
+
+					// Remember the blockquote node so we can clear it later (if it becomes empty).
+					if ( !bqBlock.getCustomData( 'blockquote_processed' ) )
+					{
+						processedBlockquoteBlocks.push( bqBlock );
+						CKEDITOR.dom.element.setMarker( database, bqBlock, 'blockquote_processed', true );
+					}
+
+					movedNodes.push( node );
+				}
+
+				CKEDITOR.dom.element.clearAllMarkers( database );
+
+				// Clear blockquote nodes that have become empty.
+				for ( var i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )
+				{
+					var bqBlock = processedBlockquoteBlocks[i];
+					if ( noBlockLeft( bqBlock ) )
+						bqBlock.remove();
+				}
+
+				if ( editor.config.enterMode == 'br' )
+				{
+					var firstTime = true;
+					while ( movedNodes.length )
+					{
+						var node = movedNodes.shift();
+
+						if ( node.getName() == 'div' )
+						{
+							var docFrag = new CKEDITOR.dom.documentFragment( editor.document ),
+								needBeginBr = firstTime && node.getPrevious() &&
+									!( node.getPrevious().type == CKEDITOR.NODE_ELEMENT && node.getPrevious().isBlockBoundary() );
+							if ( needBeginBr )
+								docFrag.append( editor.document.createElement( 'br' ) );
+
+							var needEngBr = node.getNext() &&
+								!( node.getNext().type == CKEDITOR.NODE_ELEMENT && node.getNext().isBlockBoundary() );
+							while ( node.getFirst() )
+								node.getFirst().remove().appendTo( docFrag );
+
+							if ( needEndBr )
+								docFrag.append( editor.document.createElement( 'br' ) );
+
+							docFrag.replace( node );
+							firstTime = false;
+						}
+					}
+				}
+			}
+
+			selection.selectBookmarks( bookmarks );
+			editor.focus();
+		}
+	};
+
+	CKEDITOR.plugins.add( 'blockquote',
+	{
+		init : function( editor )
+		{
+			editor.addCommand( 'blockquote', commandObject );
+
+			editor.ui.addButton( 'Blockquote',
+				{
+					label : editor.lang.blockquote,
+					command : 'blockquote'
+				} );
+
+			editor.on( 'selectionChange', onSelectionChange );
+		},
+
+		requires : [ 'domiterator' ]
+	} );
+})();
Index: /CKEditor/trunk/_source/plugins/justify/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/justify/plugin.js	(revision 3066)
+++ /CKEditor/trunk/_source/plugins/justify/plugin.js	(revision 3066)
@@ -0,0 +1,159 @@
+/*
+Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file Justify commands.
+ */
+
+(function()
+{
+	var alignRemoveRegex = /(-moz-|-webkit-|start|auto)/i;
+
+	function getState( editor, path )
+	{
+		var firstBlock = path.block || path.blockLimit;
+
+		if ( !firstBlock || firstBlock.getName() == 'body' )
+			return CKEDITOR.TRISTATE_OFF;
+
+		var currentAlign = firstBlock.getComputedStyle( 'text-align' ).replace( alignRemoveRegex, '' );
+		if ( ( !currentAlign && this.isDefaultAlign ) || currentAlign == this.value )
+			return CKEDITOR.TRISTATE_ON;
+		return CKEDITOR.TRISTATE_OFF;
+	}
+
+	function onSelectionChange( evt )
+	{
+		var command = evt.editor.getCommand( this.name );
+		command.state = getState.call( this, evt.editor, evt.data.path );
+		command.fire( 'state' );
+	}
+
+	function justifyCommand( editor, name, value )
+	{
+		this.name = name;
+		this.value = value;
+
+		var contentDir = editor.config.contentsLangDirection;
+		this.isDefaultAlign = ( value == 'left' && contentDir == 'ltr' ) ||
+			( value == 'right' && contentDir == 'rtl' );
+
+		var classes = editor.config.justifyClasses;
+		if ( classes )
+		{
+			switch ( value )
+			{
+				case 'left' :
+					this.cssClassName = classes[0];
+					break;
+				case 'center' :
+					this.cssClassName = classes[1];
+					break;
+				case 'right' :
+					this.cssClassName = classes[2];
+					break;
+				case 'justify' :
+					this.cssClassName = classes[3];
+					break;
+			}
+
+			this.cssClassRegex = new RegExp( '(?:^|\\s+)(?:' + classes.join( '|' ) + ')(?=$|\\s)' );
+		}
+	}
+
+	justifyCommand.prototype = {
+		exec : function( editor )
+		{
+			var selection = editor.getSelection(),
+				range = selection && selection.getRanges()[0];
+
+			if ( !range )
+				return;
+
+			var bookmarks = selection.createBookmarks(),
+				cssClassName = this.cssClassName,
+				iterator = range.createIterator(),
+				block;
+
+			while ( ( block = iterator.getNextParagraph() ) )
+			{
+				block.removeAttribute( 'align' );
+
+				if ( cssClassName )
+				{
+					// Remove any of the alignment classes from the className.
+					var className = block.$.className =
+						CKEDITOR.tools.ltrim( block.$.className.replace( this.cssClassRegex, '' ) );
+
+					// Append the desired class name.
+					if ( this.state == CKEDITOR.TRISTATE_OFF && !this.isDefaultAlign )
+						block.addClass( cssClassName );
+					else if ( className.length == 0 )
+						block.removeAttribute( 'class' );
+				}
+				else
+				{
+					if ( this.state == CKEDITOR.TRISTATE_OFF && !this.isDefaultAlign )
+						block.setStyle( 'text-align', this.value );
+					else
+						block.removeStyle( 'text-align' );
+				}
+			}
+
+			editor.focus();
+			editor.forceNextSelectionCheck();
+			selection.selectBookmarks( bookmarks );
+		}
+	};
+
+	CKEDITOR.plugins.add( 'justify',
+	{
+		init : function( editor )
+		{
+			var left = new justifyCommand( editor, 'justifyleft', 'left' ),
+				center = new justifyCommand( editor, 'justifycenter', 'center' ),
+				right = new justifyCommand( editor, 'justifyright', 'right' ),
+				justify = new justifyCommand( editor, 'justifyblock', 'justify' );
+
+			editor.addCommand( 'justifyleft', left );
+			editor.addCommand( 'justifycenter', center );
+			editor.addCommand( 'justifyright', right );
+			editor.addCommand( 'justifyblock', justify );
+
+			editor.ui.addButton( 'JustifyLeft',
+				{
+					label : editor.lang.justify.left,
+					command : 'justifyleft' 
+				} );
+			editor.ui.addButton( 'JustifyCenter',
+				{
+					label : editor.lang.justify.center,
+					command : 'justifycenter' 
+				} );
+			editor.ui.addButton( 'JustifyRight',
+				{
+					label : editor.lang.justify.right,
+					command : 'justifyright' 
+				} );
+			editor.ui.addButton( 'JustifyBlock',
+				{
+					label : editor.lang.justify.block,
+					command : 'justifyblock' 
+				} );
+
+			editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, left ) );
+			editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, right ) );
+			editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, center ) );
+			editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, justify ) );
+		},
+
+		requires : [ 'domiterator' ]
+	});
+})();
+
+CKEDITOR.tools.extend( CKEDITOR.config,
+	{
+		justifyClasses : null
+	} );
