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,

	"_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 ( ! evt )

			evt = window.event ;

		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 = FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ;

		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 ;

	},

	"_ResizeBarMouseUpListener" : function( evt )

	{

		if ( ! evt )

			evt = window.event ;

		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 ( ! evt )

			evt = window.event ;

		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 ( node.ownerDocument == document )

		{

			var offset = FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ;

			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 ( node.ownerDocument == FCK.EditorDocument )

		{

			var offset = FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ;

			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 = document.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 ) ;

			document.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 = document.createElement( "img" ) ;

			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 )

					evt = window.event ;

				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 = FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ;

		var tablePos = FCKTools.GetWindowPosition( 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 = document.createElement( "div" ) ;

			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'

				} ) ;

	}

};



FCK.Events.AttachEvent( "OnMouseMove", FCKDragTableHandler.MouseMoveListener ) ;

