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 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' } ) ; }, "Reset" : function() { FCKDragTableHandler._LeftCell = FCKDragTableHandler._RightCell = FCKDragTableHandler._TableMap = null ; } }; FCK.Events.AttachEvent( "OnMouseMove", FCKDragTableHandler.MouseMoveListener ) ; FCK.Events.AttachEvent( "OnAfterSetHTML", FCKDragTableHandler.Reset ) ;