text: var FCKDragTableHandler =
  {
  	"_DragState" : 0,
  	"_LeftCell" : null,
  	"_RightCell" : null,
  	"_MouseMoveMode" : 0,	// 0 - find candidate cells for resizing, 1 - drag to resize
  	"_ResizeBar" : null,
  	"_OriginalX" : null,
  	"_MinimumX" : null,
  	"_MaximumX" : null,
  	"_LastX" : null,
  	"_TableMap" : null,
  	"_doc" : document,
  	"_IsInsideNode" : function( w, domNode, pos )
  	{
  		var myCoords = FCKTools.GetWindowPosition( w, domNode ) ;
  		var xMin = myCoords.x ;
  		var yMin = myCoords.y ;
  		var xMax = parseInt( xMin, 10 ) + parseInt( domNode.offsetWidth, 10 ) ;
  		var yMax = parseInt( yMin, 10 ) + parseInt( domNode.offsetHeight, 10 ) ;
  		if ( pos.x >= xMin && pos.x <= xMax && pos.y >= yMin && pos.y <= yMax )
  			return true;
  		return false;
  	},
  	"_GetBorderCells" : function( w, tableNode, tableMap, mouse )
  	{
  		// Enumerate all the cells in the table.
  		var cells = [] ;
  		for ( var i = 0 ; i < tableNode.rows.length ; i++ )
  		{
  			var r = tableNode.rows[i] ;
  			for ( var j = 0 ; j < r.cells.length ; j++ )
  				cells.push( r.cells[j] ) ;
  		}
  
  		if ( cells.length < 1 )
  			return null ;
  
  		// Get the cells whose right or left border is nearest to the mouse cursor's x coordinate.
  		var minRxDist = null ;
  		var lxDist = null ;
  		var minYDist = null ;
  		var rbCell = null ;
  		var lbCell = null ;
  		for ( var i = 0 ; i < cells.length ; i++ )
  		{
  			var pos = FCKTools.GetWindowPosition( w, cells[i] ) ;
  			var rightX = pos.x + parseInt( cells[i].clientWidth, 10 ) ;
  			var rxDist = mouse.x - rightX ;
  			var yDist = mouse.y - ( pos.y + ( cells[i].clientHeight / 2 ) ) ;
  			if ( minRxDist == null ||
  					( Math.abs( rxDist ) <= Math.abs( minRxDist ) &&
  					  ( minYDist == null || Math.abs( yDist ) <= Math.abs( minYDist ) ) ) )
  			{
  				minRxDist = rxDist ;
  				minYDist = yDist ;
  				rbCell = cells[i] ;
  			}
  		}
  		/*
  		var rowNode = FCKTools.GetElementAscensor( rbCell, "tr" ) ;
  		var cellIndex = rbCell.cellIndex + 1 ;
  		if ( cellIndex >= rowNode.cells.length )
  			return null ;
  		lbCell = rowNode.cells.item( cellIndex ) ;
  		*/
  		var rowIdx = rbCell.parentNode.rowIndex ;
  		var colIdx = FCKTableHandler._GetCellIndexSpan( tableMap, rowIdx, rbCell ) ;
  		var colSpan = isNaN( rbCell.colSpan ) ? 1 : rbCell.colSpan ;
  		lbCell = tableMap[rowIdx][colIdx + colSpan] ;
  
  		if ( ! lbCell )
  			return null ;
  
  		// Abort if too far from the border.
  		lxDist = mouse.x - FCKTools.GetWindowPosition( w, lbCell ).x ;
  		if ( lxDist < 0 && minRxDist < 0 && minRxDist < -2 )
  			return null ;
  		if ( lxDist > 0 && minRxDist > 0 && lxDist > 3 )
  			return null ;
  
  		return { "leftCell" : rbCell, "rightCell" : lbCell } ;
  	},
  	"_GetResizeBarPosition" : function()
  	{
  		var row = FCKTools.GetElementAscensor( this._RightCell, "tr" ) ;
  		return FCKTableHandler._GetCellIndexSpan( this._TableMap, row.rowIndex, this._RightCell ) ;
  	},
  	"_ResizeBarMouseDownListener" : function( evt )
  	{
  		if ( FCKDragTableHandler._LeftCell )
  			FCKDragTableHandler._MouseMoveMode = 1 ;
  		if ( FCKBrowserInfo.IsIE )
  			FCKDragTableHandler._ResizeBar.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 50 ;
  		else
  			FCKDragTableHandler._ResizeBar.style.opacity = 0.5 ;
  		FCKDragTableHandler._OriginalX = evt.clientX ;
  
  		// Calculate maximum and minimum x-coordinate delta.
  		var borderIndex = FCKDragTableHandler._GetResizeBarPosition() ;
  		var offset = FCKDragTableHandler._GetIframeOffset();
  		var table = FCKTools.GetElementAscensor( FCKDragTableHandler._LeftCell, "table" );
  		var minX = null ;
  		var maxX = null ;
  		for ( var r = 0 ; r < FCKDragTableHandler._TableMap.length ; r++ )
  		{
  			var leftCell = FCKDragTableHandler._TableMap[r][borderIndex - 1] ;
  			var rightCell = FCKDragTableHandler._TableMap[r][borderIndex] ;
  			var leftPosition = FCKTools.GetWindowPosition( FCK.EditorWindow, leftCell ) ;
  			var rightPosition = FCKTools.GetWindowPosition( FCK.EditorWindow, rightCell ) ;
  			var leftPadding = FCKDragTableHandler._GetCellPadding( table, leftCell ) ;
  			var rightPadding = FCKDragTableHandler._GetCellPadding( table, rightCell ) ;
  			if ( minX == null || leftPosition.x + leftPadding > minX )
  				minX = leftPosition.x + leftPadding ;
  			if ( maxX == null || rightPosition.x + rightCell.clientWidth - rightPadding < maxX )
  				maxX = rightPosition.x + rightCell.clientWidth - rightPadding ;
  		}
  
  		FCKDragTableHandler._MinimumX = minX + offset.x ;
  		FCKDragTableHandler._MaximumX = maxX + offset.x ;
  		FCKDragTableHandler._LastX = null ;
  
  		if (evt.preventDefault)
  			evt.preventDefault();
  		else
  			evt.returnValue = false;
  	},
  	"_ResizeBarMouseUpListener" : function( evt )
  	{
  		FCKDragTableHandler._MouseMoveMode = 0 ;
  		FCKDragTableHandler._HideResizeBar() ;
  
  		if ( FCKDragTableHandler._LastX == null )
  			return ;
  
  		// Calculate the delta value.
  		var deltaX = FCKDragTableHandler._LastX - FCKDragTableHandler._OriginalX ;
  
  		// Then, build an array of current column width values.
  		// This algorithm can be very slow if the cells have insane colSpan values. (e.g. colSpan=1000).
  		var table = FCKTools.GetElementAscensor( FCKDragTableHandler._LeftCell, "table" ) ;
  		var colArray = [] ;
  		var tableMap = FCKDragTableHandler._TableMap ;
  		for ( var i = 0 ; i < tableMap.length ; i++ )
  		{
  			for ( var j = 0 ; j < tableMap[i].length ; j++ )
  			{
  				var cell = tableMap[i][j] ;
  				var width = FCKDragTableHandler._GetCellWidth( table, cell ) ;
  				var colSpan = isNaN( cell.colSpan) ? 1 : cell.colSpan ;
  				if ( colArray.length <= j )
  					colArray.push( { width : width / colSpan, colSpan : colSpan } ) ;
  				else
  				{
  					var guessItem = colArray[j] ;
  					if ( guessItem.colSpan > colSpan )
  					{
  						guessItem.width = width / colSpan ;
  						guessItem.colSpan = colSpan ;
  					}
  				}
  			}
  		}
  
  		// Find out the equivalent column index of the two cells selected for resizing.
  		colIndex = FCKDragTableHandler._GetResizeBarPosition() ;
  
  		// Note that colIndex must be at least 1 here, so it's safe to subtract 1 from it.
  		colIndex-- ;
  
  		// Modify the widths in the colArray according to the mouse coordinate delta value.
  		colArray[colIndex].width += deltaX ;
  		colArray[colIndex + 1].width -= deltaX ;
  
  		// Clear all cell widths, delete all <col> elements from the table.
  		for ( var r = 0 ; r < table.rows.length ; r++ )
  		{
  			var row = table.rows.item( r ) ;
  			for ( var c = 0 ; c < row.cells.length ; c++ )
  			{
  				var cell = row.cells.item( c ) ;
  				cell.width = "" ;
  				cell.style.width = "" ;
  			}
  		}
  		var colElements = table.getElementsByTagName( "col" ) ;
  		for ( var i = colElements.length - 1 ; i >= 0 ; i-- )
  			colElements[i].parentNode.removeChild( colElements[i] ) ;
  
  		// Set new cell widths.
  		var processedCells = [] ;
  		for ( var i = 0 ; i < tableMap.length ; i++ )
  		{
  			for ( var j = 0 ; j < tableMap[i].length ; j++ )
  			{
  				var cell = tableMap[i][j] ;
  				if ( cell._Processed )
  					continue ;
  				if ( tableMap[i][j-1] != cell )
  					cell.width = colArray[j].width ;
  				else
  					cell.width = parseInt( cell.width, 10 ) + parseInt( colArray[j].width, 10 ) ;
  				if ( tableMap[i][j+1] != cell )
  				{
  					processedCells.push( cell ) ;
  					cell._Processed = true ;
  				}
  			}
  		}
  		for ( var i = 0 ; i < processedCells.length ; i++ )
  		{
  			if ( FCKBrowserInfo.IsIE )
  				processedCells[i].removeAttribute( '_Processed' ) ;
  			else
  				delete processedCells[i]._Processed ;
  		}
  
  		FCKDragTableHandler._LastX = null ;
  	},
  	"_ResizeBarMouseMoveListener" : function( evt )
  	{
  		if ( FCKDragTableHandler._MouseMoveMode == 0 )
  			return FCKDragTableHandler._MouseFindHandler( FCK, evt ) ;
  		else
  			return FCKDragTableHandler._MouseDragHandler( FCK, evt ) ;
  	},
  	// Calculate the padding of a table cell.
  	// It returns the value of paddingLeft + paddingRight of a table cell.
  	// This function is used, in part, to calculate the width parameter that should be used for setting cell widths.
  	// The equation in question is clientWidth = paddingLeft + paddingRight + width.
  	// So that width = clientWidth - paddingLeft - paddingRight.
  	// The return value of this function must be pixel accurate acorss all supported browsers, so be careful if you need to modify it.
  	"_GetCellPadding" : function( table, cell )
  	{
  		var attrGuess = parseInt( table.cellPadding, 10 ) * 2 ;
  		var cssGuess = null ;
  		if ( typeof( window.getComputedStyle ) == "function" )
  		{
  			var styleObj = window.getComputedStyle( cell, null ) ;
  			cssGuess = parseInt( styleObj.getPropertyValue( "padding-left" ), 10 ) +
  				parseInt( styleObj.getPropertyValue( "padding-right" ), 10 ) ;
  		}
  		else
  			cssGuess = parseInt( cell.currentStyle.paddingLeft, 10 ) + parseInt (cell.currentStyle.paddingRight, 10 ) ;
  
  		var cssRuntime = cell.style.padding ;
  		if ( isFinite( cssRuntime ) )
  			cssGuess = parseInt( cssRuntime, 10 ) * 2 ;
  		else
  		{
  			cssRuntime = cell.style.paddingLeft ;
  			if ( isFinite( cssRuntime ) )
  				cssGuess = parseInt( cssRuntime, 10 ) ;
  			cssRuntime = cell.style.paddingRight ;
  			if ( isFinite( cssRuntime ) )
  				cssGuess += parseInt( cssRuntime, 10 ) ;
  		}
  
  		attrGuess = parseInt( attrGuess, 10 ) ;
  		cssGuess = parseInt( cssGuess, 10 ) ;
  		if ( isNaN( attrGuess ) )
  			attrGuess = 0 ;
  		if ( isNaN( cssGuess ) )
  			cssGuess = 0 ;
  		return Math.max( attrGuess, cssGuess ) ;
  	},
  	// Calculate the real width of the table cell.
  	// The real width of the table cell is the pixel width that you can set to the width attribute of the table cell and after
  	// that, the table cell should be of exactly the same width as before.
  	// The real width of a table cell can be calculated as:
  	// width = clientWidth - paddingLeft - paddingRight.
  	"_GetCellWidth" : function( table, cell )
  	{
  		var clientWidth = cell.clientWidth ;
  		if ( isNaN( clientWidth ) )
  			clientWidth = 0 ;
  		return clientWidth - this._GetCellPadding( table, cell ) ;
  	},
  	"MouseMoveListener" : function( FCK, evt )
  	{
  		if ( FCKDragTableHandler._MouseMoveMode == 0 )
  			return FCKDragTableHandler._MouseFindHandler( FCK, evt ) ;
  		else
  			return FCKDragTableHandler._MouseDragHandler( FCK, evt ) ;
  	},
  	"_MouseFindHandler" : function( FCK, evt )
  	{
  		if ( FCK.MouseDownFlag )
  			return ;
  		var node = evt.srcElement || evt.target ;
  		try
  		{
  			if ( ! node || node.nodeType != 1 )
  			{
  				this._HideResizeBar() ;
  				return ;
  			}
  		}
  		catch ( e )
  		{
  			this._HideResizeBar() ;
  			return ;
  		}
  
  		// Since this function might be called from the editing area iframe or the outer fckeditor iframe,
  		// the mouse point coordinates from evt.clientX/Y can have different reference points.
  		// We need to resolve the mouse pointer position relative to the editing area iframe.
  		var mouseX = evt.clientX ;
  		var mouseY = evt.clientY ;
  		if ( FCKTools.GetElementDocument( node ) == document )
  		{
  			var offset = this._GetIframeOffset() ;
  			mouseX -= offset.x ;
  			mouseY -= offset.y ;
  		}
  
  
  		if ( this._ResizeBar && this._LeftCell )
  		{
  			var leftPos = FCKTools.GetWindowPosition( FCK.EditorWindow, this._LeftCell ) ;
  			var rightPos = FCKTools.GetWindowPosition( FCK.EditorWindow, this._RightCell ) ;
  			var rxDist = mouseX - ( leftPos.x + this._LeftCell.clientWidth ) ;
  			var lxDist = mouseX - rightPos.x ;
  			var inRangeFlag = false ;
  			if ( lxDist >= 0 && rxDist <= 0 )
  				inRangeFlag = true ;
  			else if ( rxDist > 0 && lxDist <= 3 )
  				inRangeFlag = true ;
  			else if ( lxDist < 0 && rxDist >= -2 )
  				inRangeFlag = true ;
  			if ( inRangeFlag )
  			{
  				this._ShowResizeBar( FCK.EditorWindow,
  					FCKTools.GetElementAscensor( this._LeftCell, "table" ),
  					{ "x" : mouseX, "y" : mouseY } ) ;
  				return ;
  			}
  		}
  
  		var tagName = node.tagName.toLowerCase() ;
  		if ( tagName != "table" && tagName != "td" && tagName != "th" )
  		{
  			if ( this._LeftCell )
  				this._LeftCell = this._RightCell = this._TableMap = null ;
  			this._HideResizeBar() ;
  			return ;
  		}
  		node = FCKTools.GetElementAscensor( node, "table" ) ;
  		var tableMap = FCKTableHandler._CreateTableMap( node ) ;
  		var cellTuple = this._GetBorderCells( FCK.EditorWindow, node, tableMap, { "x" : mouseX, "y" : mouseY } ) ;
  
  		if ( cellTuple == null )
  		{
  			if ( this._LeftCell )
  				this._LeftCell = this._RightCell = this._TableMap = null ;
  			this._HideResizeBar() ;
  		}
  		else
  		{
  			this._LeftCell = cellTuple["leftCell"] ;
  			this._RightCell = cellTuple["rightCell"] ;
  			this._TableMap = tableMap ;
  			this._ShowResizeBar( FCK.EditorWindow,
  					FCKTools.GetElementAscensor( this._LeftCell, "table" ),
  					{ "x" : mouseX, "y" : mouseY } ) ;
  		}
  	},
  	"_MouseDragHandler" : function( FCK, evt )
  	{
  		var mouse = { "x" : evt.clientX, "y" : evt.clientY } ;
  
  		// Convert mouse coordinates in reference to the outer iframe.
  		var node = evt.srcElement || evt.target ;
  		if ( FCKTools.GetElementDocument( node ) == FCK.EditorDocument )
  		{
  			var offset = this._GetIframeOffset() ;
  			mouse.x += offset.x ;
  			mouse.y += offset.y ;
  		}
  
  		// Calculate the mouse position delta and see if we've gone out of range.
  		if ( mouse.x >= this._MaximumX - 5 )
  			mouse.x = this._MaximumX - 5 ;
  		if ( mouse.x <= this._MinimumX + 5 )
  			mouse.x = this._MinimumX + 5 ;
  
  		var docX = mouse.x + FCKTools.GetScrollPosition( window ).X ;
  		this._ResizeBar.style.left = ( docX - this._ResizeBar.offsetWidth / 2 ) + "px" ;
  		this._LastX = mouse.x ;
  	},
  	"_ShowResizeBar" : function( w, table, mouse )
  	{
  		if ( this._ResizeBar == null )
  		{
  			this._ResizeBar = this._doc.createElement( "div" ) ;
  			var paddingBar = this._ResizeBar ;
  			var paddingStyles = { 'position' : 'absolute', 'cursor' : 'e-resize' } ;
  			if ( FCKBrowserInfo.IsIE )
  				paddingStyles.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=10,enabled=true)" ;
  			else
  				paddingStyles.opacity = 0.10 ;
  			FCKDomTools.SetElementStyles( paddingBar, paddingStyles ) ;
  			this._avoidStyles( paddingBar );
  			paddingBar.setAttribute('_fcktemp', true);
  			this._doc.body.appendChild( paddingBar ) ;
  			FCKTools.AddEventListener( paddingBar, "mousemove", this._ResizeBarMouseMoveListener ) ;
  			FCKTools.AddEventListener( paddingBar, "mousedown", this._ResizeBarMouseDownListener ) ;
  			FCKTools.AddEventListener( document, "mouseup", this._ResizeBarMouseUpListener ) ;
  			FCKTools.AddEventListener( FCK.EditorDocument, "mouseup", this._ResizeBarMouseUpListener ) ;
  
  			// IE doesn't let the tranparent part of the padding block to receive mouse events unless there's something inside.
  			// So we need to create a spacer image to fill the block up.
  			var filler = this._doc.createElement( "img" ) ;
  			filler.setAttribute('_fcktemp', true);
  			filler.border = 0 ;
  			filler.src = FCKConfig.BasePath + "images/spacer.gif" ;
  			filler.style.position = "absolute" ;
  			paddingBar.appendChild( filler ) ;
  
  			// Disable drag and drop, and selection for the filler image.
  			var disabledListener = function( evt )
  			{
  				if ( evt.preventDefault )
  					evt.preventDefault() ;
  				else
  					evt.returnValue = false ;
  			}
  			FCKTools.AddEventListener( filler, "dragstart", disabledListener ) ;
  			FCKTools.AddEventListener( filler, "selectstart", disabledListener ) ;
  		}
  
  		var paddingBar = this._ResizeBar ;
  		var offset = this._GetIframeOffset() ;
  		var tablePos = this._GetTablePosition( w, table ) ;
  		var barHeight = table.offsetHeight ;
  		var barTop = offset.y + tablePos.y ;
  		// Do not let the resize bar intrude into the toolbar area.
  		if ( tablePos.y < 0 )
  		{
  			barHeight += tablePos.y ;
  			barTop -= tablePos.y ;
  		}
  		var bw = parseInt( table.border, 10 ) ;
  		if ( isNaN( bw ) )
  			bw = 0 ;
  		var cs = parseInt( table.cellSpacing, 10 ) ;
  		if ( isNaN( cs ) )
  			cs = 0 ;
  		var barWidth = Math.max( bw+100, cs+100 ) ;
  		var paddingStyles =
  		{
  			'top'		: barTop + 'px',
  			'height'	: barHeight + 'px',
  			'width'		: barWidth + 'px',
  			'left'		: ( offset.x + mouse.x + FCKTools.GetScrollPosition( w ).X - barWidth / 2 ) + 'px'
  		} ;
  		if ( FCKBrowserInfo.IsIE )
  			paddingBar.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 10 ;
  		else
  			paddingStyles.opacity = 0.1 ;
  
  		FCKDomTools.SetElementStyles( paddingBar, paddingStyles ) ;
  		var filler = paddingBar.getElementsByTagName( "img" )[0] ;
  
  		FCKDomTools.SetElementStyles( filler,
  			{
  				width	: paddingBar.offsetWidth + 'px',
  				height	: barHeight + 'px'
  			} ) ;
  
  		barWidth = Math.max( bw, cs, 3 ) ;
  		var visibleBar = null ;
  		if ( paddingBar.getElementsByTagName( "div" ).length < 1 )
  		{
  			visibleBar = this._doc.createElement( "div" ) ;
  			this._avoidStyles( visibleBar );
  			visibleBar.setAttribute('_fcktemp', true);
  			paddingBar.appendChild( visibleBar ) ;
  		}
  		else
  			visibleBar = paddingBar.getElementsByTagName( "div" )[0] ;
  
  		FCKDomTools.SetElementStyles( visibleBar,
  			{
  				position		: 'absolute',
  				backgroundColor	: 'blue',
  				width			: barWidth + 'px',
  				height			: barHeight + 'px',
  				left			: '50px',
  				top				: '0px'
  			} ) ;
  	},
  	"_HideResizeBar" : function()
  	{
  		if ( this._ResizeBar )
  			// IE bug: display : none does not hide the resize bar for some reason.
  			// so set the position to somewhere invisible.
  			FCKDomTools.SetElementStyles( this._ResizeBar,
  				{
  					top		: '-100000px',
  					left	: '-100000px'
  				} ) ;
  	},
  	"_GetIframeOffset" : function ()
  	{
  		return FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ;
  	},
  	"_GetTablePosition" : function ( w, table )
  	{
  		return FCKTools.GetWindowPosition( w, table ) ;
  	},
  	"_avoidStyles" : function( element )
  	{
  		FCKDomTools.SetElementStyles( element,
  			{
  				padding		: '0',
  				backgroundImage	: 'none',
  				border		: '0'
  			} ) ;
  	}
  
  };
  
  FCK.Events.AttachEvent( "OnMouseMove", FCKDragTableHandler.MouseMoveListener ) ;
  