Index: _source/core/tools.js
===================================================================
--- _source/core/tools.js	(revision 5336)
+++ _source/core/tools.js	Tue Apr 20 17:02:11 CST 2010
@@ -52,6 +52,50 @@
 			return true;
 		},
 
+		buildTableMap : function ( table )
+		{
+			var aRows = table.$.rows ;
+
+			// Row and Column counters.
+			var r = -1 ;
+
+			var aMap = [];
+
+			for ( var i = 0 ; i < aRows.length ; i++ )
+			{
+				r++ ;
+				!aMap[r] && ( aMap[r] = [] );
+
+				var c = -1 ;
+
+				for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
+				{
+					var oCell = aRows[i].cells[j] ;
+
+					c++ ;
+					while ( aMap[r][c] )
+						c++ ;
+
+					var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
+					var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
+
+					for ( var rs = 0 ; rs < iRowSpan ; rs++ )
+					{
+						if ( !aMap[r + rs] )
+							aMap[r + rs] = [];
+
+						for ( var cs = 0 ; cs < iColSpan ; cs++ )
+						{
+							aMap[r + rs][c + cs] = aRows[i].cells[j] ;
+						}
+					}
+
+					c += iColSpan - 1 ;
+				}
+			}
+			return aMap ;
+	},
+
 		/**
 		 * Creates a deep copy of an object.
 		 * Attention: there is no support for recursive references.
Index: _source/plugins/tableresize/plugin.js
===================================================================
--- _source/plugins/tableresize/plugin.js	Wed Apr 21 01:08:56 CST 2010
+++ _source/plugins/tableresize/plugin.js	Wed Apr 21 01:08:56 CST 2010
@@ -0,0 +1,261 @@
+/*
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+( function()
+{
+	var pxUnit = CKEDITOR.tools.cssLength;
+
+	function getCellWidth( cell )
+	{
+		return CKEDITOR.env.ie ? cell.$.clientWidth : parseInt( cell.getComputedStyle( 'width' ) );
+	}
+
+	function getCellBorderWidth( cell, side )
+	{
+		var computed = cell.getComputedStyle( 'border-' + side + '-width' ),
+			borderMap =
+			{
+				thin: '2px',
+				medium: '4px',
+				thick: '6px'
+			};
+
+		if ( computed.indexOf( 'px' ) < 0 )
+		{
+			// look up keywords
+			if ( computed in borderMap && cell.getComputedStyle( 'border-style' ) != 'none' )
+				computed = borderMap[ computed ];
+			else
+				computed = 0;
+		}
+
+		return computed;
+	}
+
+	function getTableColumnPillarAtPosition( tableElement, position )
+	{
+		var table = tableElement.$;
+		for ( var i = 0, rowCount = table.rows.length ; i < rowCount; i++ )
+		{
+			var tr = table.rows[ i ],
+					columnIndex = -1;
+
+			for ( var j = 0, colCount = tr.cells.length ; j < colCount ; j++ )
+			{
+				var td = new CKEDITOR.dom.element( tr.cells[ j ] ),
+						nextTd = tr.cells[ j + 1 ] && new CKEDITOR.dom.element( tr.cells[ j + 1 ] );
+
+				columnIndex += td.$.colSpan || 1;
+				var cellPosition =  td.getDocumentPosition(),
+						rangeLeft = cellPosition.x + td.$.offsetWidth - parseInt( getCellBorderWidth ( td, 'right' ) );
+
+				if ( ( position.x > rangeLeft ) && nextTd )
+				{
+					cellPosition =  nextTd.getDocumentPosition();
+					var rangeRight = cellPosition.x + parseInt( getCellBorderWidth( nextTd, 'left' ) );
+
+					// Compsensate for too "slim" line between columns, make the pillar shown easier.
+					if ( rangeRight - rangeLeft < 8 )
+						rangeLeft-= 4, rangeRight += 4;
+
+					if ( position.x < rangeRight )
+					{
+						var columnWidth = rangeRight - rangeLeft;
+						var tbody = new CKEDITOR.dom.element( table.tBodies[ 0 ] );
+
+						// The pillar should reflects exactly the shape of the hovered column border line.
+						return { table : tableElement, index : columnIndex, x : rangeLeft, y :  tbody.getDocumentPosition().y, width : columnWidth, height: tbody.$.offsetHeight };
+					}
+				}
+			}
+		}
+	}
+	
+	function ColumnResizer( editor )
+	{
+		this.document = editor.document;
+		this.resizer = CKEDITOR.dom.element.createFromHtml( '<div style="position: absolute; cursor: col-resize; ' +
+			'filter:alpha(opacity=0);opacity:0;padding:0;background-color:#004;background-image:none;border: 0px none;"></div>' );
+
+		// Place the resizer after body to prevent it from being editable.
+		new CKEDITOR.dom.element( editor.document.$.documentElement ).append( this.resizer );
+	}
+
+	ColumnResizer.prototype =
+	{
+		attachTo : function( pillar )
+		{
+			// Accept only one pillar at a time.
+			if ( this.pillar )
+				return;
+
+			this.pillar = pillar;
+			this.table = pillar.table;
+			this.resizer.setStyles(
+			{
+				width: pxUnit( pillar.width ),
+				height : pxUnit( pillar.height ),
+				left : pxUnit( pillar.x ),
+				top : pxUnit( pillar.y )
+			});
+
+			this.resizer.on( 'mousedown', this._.onMouseDown, this );
+			this.resizer.on( 'mouseout', this._.onMouseOut, this );
+
+			// Display the resizer to receive events but don't show it,
+			// only change the cursor to resizable shape.
+			this.resizer.show();
+		},
+
+		detach: function()
+		{
+			delete this.pillar;
+			delete this.currentShift;
+			this.document.removeListener( 'mousemove', this._.onMouseMove );
+			this.document.removeListener( 'mouseup', this._.onMouseUp );
+			this.resizer.removeListener( 'mousedown', this._.onMouseDown );
+			this.resizer.removeListener( 'mouseout', this._.onMouseOut );
+			this.resizer.hide();
+		},
+
+		resizeStart : function()
+		{
+			// Before starting to resize, figure out which cells to change
+			// and the boundaries of this resizing shift. 
+			var columnIndex = this.pillar.index,
+					map = CKEDITOR.tools.buildTableMap( this.table ),
+					leftColumnCells = [],
+					rightColumnCells= [],
+					leftMinSize =  Number.MAX_VALUE,
+					rightMinSize = leftMinSize;
+
+			for ( var i = 0, rowCount = map.length; i < rowCount; i++ )
+			{
+				var row = map[ i ],
+						leftCell = new CKEDITOR.dom.element( row[ columnIndex ] ),
+						rightCell = new CKEDITOR.dom.element( row[ columnIndex + 1 ] );
+
+				if ( !leftCell.equals( rightCell ) )
+				{
+					leftMinSize = Math.min( leftMinSize, getCellWidth( leftCell ) );
+					rightMinSize = Math.min( rightMinSize, getCellWidth( rightCell ) );
+					leftColumnCells.push( leftCell );
+					rightColumnCells.push( rightCell );
+				}
+			}
+
+			this.leftSideCells = leftColumnCells;
+			this.rightSideCells = rightColumnCells;
+			this.leftShiftBoundary =  this.pillar.x - leftMinSize;
+			this.rightShiftBoundary = this.pillar.x + rightMinSize;
+			
+			this.resizer.setOpacity( 0.5 );
+			this.startOffset = parseInt( this.resizer.getStyle( 'left' ) );
+			this.document.on( 'mousemove', this._.onMouseMove, this );
+			// Prevent the native drag behavior otherwise the above 'mousemove' won't fired.
+			this.document.on( 'dragstart', this._.onDragStart, this );
+		},
+
+		resizeEnd : function()
+		{
+			this.resizer.setOpacity( 0 );
+			this.resizeColumn();
+			this.detach();
+		},
+
+		resizeColumn : function()
+		{
+			// Perform the actual resize to table cells, only for those by side of the pillar.
+			for ( var i = 0, count = this.leftSideCells.length; i < count; i++ )
+			{
+				var leftCell = this.leftSideCells[ i ],
+						rightCell = this.rightSideCells[ i ];
+
+				// Defer the resizing to avoid any interference among cells.
+				( function( leftCell, leftOldWidth, rightCell, rightOldWidth, sizeShift )
+				{
+					CKEDITOR.tools.setTimeout( function()
+					{
+						leftCell.setStyle( 'width', pxUnit(  leftOldWidth + sizeShift ) );
+						rightCell.setStyle( 'width', pxUnit(  rightOldWidth - sizeShift ) );
+
+					}, 0, this );
+
+				}).call( this, leftCell, getCellWidth( leftCell ),
+						rightCell, getCellWidth( rightCell ), this.currentShift );
+			}
+		},
+
+		_ :
+		{
+			onMouseMove : function( evt )
+			{
+				var mouseOffset = evt.data.$.clientX,
+						resizerNewPosition = mouseOffset - parseInt( this.resizer.getComputedStyle( 'width' ) ) / 2;
+
+				// Boundaries checking.
+				if ( resizerNewPosition > this.leftShiftBoundary
+						&& resizerNewPosition < this.rightShiftBoundary )
+				{
+					this.resizer.setStyle( 'left', pxUnit( resizerNewPosition ) );
+					this.currentShift = resizerNewPosition - this.startOffset;
+				}
+			},
+
+			onMouseDown : function( evt )
+			{
+				evt.data.preventDefault();
+				this.resizeStart();
+				this.document.on( 'mouseup', this._.onMouseUp, this );
+			},
+
+			onMouseUp : function()
+			{
+				this.resizeEnd();
+			},
+
+			onMouseOut : function()
+			{
+				// Don't detach during resizing.
+				!this.currentShift && this.detach();
+			},
+
+			onDragStart : function( evt )
+			{
+				evt.data.preventDefault();
+			}
+		}
+	};
+
+	CKEDITOR.plugins.add( 'tableresize',
+	{
+		requires : [ 'table' ],
+		init : function( editor )
+		{
+			editor.on( 'contentDom', function ()
+			{
+				var columnResizer;
+				editor.document.getBody().on( 'mousemove', function( evt )
+				{
+					evt = evt.data;
+
+					// Considering table, tr, td, tbody but nothing else.
+					var target = evt.getTarget();
+					if ( !( target.is( 'table' ) || target.getAscendant( 'tbody', true ) ) )
+						return;
+
+					var table = target.getAscendant( 'table', true );
+					var pillar = getTableColumnPillarAtPosition( table, { x : evt.$.clientX, y : evt.$.clientY } );
+					if ( pillar )
+					{
+						!columnResizer && ( columnResizer = new ColumnResizer( editor ) );
+						columnResizer.attachTo( pillar );
+					}
+				});
+			});
+		}
+	});
+
+} )( );
Index: _source/core/config.js
===================================================================
--- _source/core/config.js	(revision 5336)
+++ _source/core/config.js	Tue Apr 20 01:54:33 CST 2010
@@ -229,7 +229,7 @@
 	 * @type String
 	 * @example
 	 */
-	plugins : 'about,a11yhelp,basicstyles,blockquote,button,clipboard,colorbutton,colordialog,contextmenu,div,elementspath,enterkey,entities,filebrowser,find,flash,font,format,forms,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,maximize,newpage,pagebreak,pastefromword,pastetext,popup,preview,print,removeformat,resize,save,scayt,smiley,showblocks,showborders,sourcearea,stylescombo,table,tabletools,specialchar,tab,templates,toolbar,undo,wysiwygarea,wsc',
+	plugins : 'about,a11yhelp,basicstyles,blockquote,button,clipboard,colorbutton,colordialog,contextmenu,div,elementspath,enterkey,entities,filebrowser,find,flash,font,format,forms,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,maximize,newpage,pagebreak,pastefromword,pastetext,popup,preview,print,removeformat,resize,save,scayt,smiley,showblocks,showborders,sourcearea,stylescombo,table,tabletools,tableresize,specialchar,tab,templates,toolbar,undo,wysiwygarea,wsc',
 
 	/**
 	 * List of additional plugins to be loaded. This is a tool setting which
Index: _source/plugins/tabletools/plugin.js
===================================================================
--- _source/plugins/tabletools/plugin.js	(revision 5387)
+++ _source/plugins/tabletools/plugin.js	Wed Apr 21 01:08:26 CST 2010
@@ -354,51 +354,6 @@
 		range.select( true );
 	}
 
-	function buildTableMap( table )
-	{
-
-		var aRows = table.$.rows ;
-
-		// Row and Column counters.
-		var r = -1 ;
-
-		var aMap = [];
-
-		for ( var i = 0 ; i < aRows.length ; i++ )
-		{
-			r++ ;
-			!aMap[r] && ( aMap[r] = [] );
-
-			var c = -1 ;
-
-			for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
-			{
-				var oCell = aRows[i].cells[j] ;
-
-				c++ ;
-				while ( aMap[r][c] )
-					c++ ;
-
-				var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
-				var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
-
-				for ( var rs = 0 ; rs < iRowSpan ; rs++ )
-				{
-					if ( !aMap[r + rs] )
-						aMap[r + rs] = new Array() ;
-
-					for ( var cs = 0 ; cs < iColSpan ; cs++ )
-					{
-						aMap[r + rs][c + cs] = aRows[i].cells[j] ;
-					}
-				}
-
-				c += iColSpan - 1 ;
-			}
-		}
-		return aMap ;
-	}
-
 	function cellInRow( tableMap, rowIndex, cell )
 	{
 		var oRow = tableMap[ rowIndex ];
@@ -452,7 +407,7 @@
 		var	cell,
 			firstCell = cells[ 0 ],
 			table = firstCell.getAscendant( 'table' ),
-			map = buildTableMap( table ),
+			map = CKEDITOR.tools.buildTableMap( table ),
 			mapHeight = map.length,
 			mapWidth = map[ 0 ].length,
 			startRow = firstCell.getParent().$.rowIndex,
@@ -587,7 +542,7 @@
 		var cell = cells[ 0 ],
 			tr = cell.getParent(),
 			table = tr.getAscendant( 'table' ),
-			map = buildTableMap( table ),
+			map = CKEDITOR.tools.buildTableMap( table ),
 			rowIndex = tr.$.rowIndex,
 			colIndex = cellInRow( map, rowIndex, cell ),
 			rowSpan = cell.$.rowSpan,
@@ -663,7 +618,7 @@
 		var cell = cells[ 0 ],
 			tr = cell.getParent(),
 			table = tr.getAscendant( 'table' ),
-			map = buildTableMap( table ),
+			map = CKEDITOR.tools.buildTableMap( table ),
 			rowIndex = tr.$.rowIndex,
 			colIndex = cellInRow( map, rowIndex, cell ),
 			colSpan = cell.$.colSpan,
