1var FCKDragTableHandler = 2{ 3 "_DragState" : 0, 4 "_LeftCell" : null, 5 "_RightCell" : null, 6 "_MouseMoveMode" : 0, // 0 - find candidate cells for resizing, 1 - drag to resize 7 "_ResizeBar" : null, 8 "_OriginalX" : null, 9 "_MinimumX" : null, 10 "_MaximumX" : null, 11 "_LastX" : null, 12 "_TableMap" : null, 13 "_doc" : document, 14 "_IsInsideNode" : function( w, domNode, pos ) 15 { 16 var myCoords = FCKTools.GetWindowPosition( w, domNode ) ; 17 var xMin = myCoords.x ; 18 var yMin = myCoords.y ; 19 var xMax = parseInt( xMin, 10 ) + parseInt( domNode.offsetWidth, 10 ) ; 20 var yMax = parseInt( yMin, 10 ) + parseInt( domNode.offsetHeight, 10 ) ; 21 if ( pos.x >= xMin && pos.x <= xMax && pos.y >= yMin && pos.y <= yMax ) 22 return true; 23 return false; 24 }, 25 "_GetBorderCells" : function( w, tableNode, tableMap, mouse ) 26 { 27 // Enumerate all the cells in the table. 28 var cells = [] ; 29 for ( var i = 0 ; i < tableNode.rows.length ; i++ ) 30 { 31 var r = tableNode.rows[i] ; 32 for ( var j = 0 ; j < r.cells.length ; j++ ) 33 cells.push( r.cells[j] ) ; 34 } 35 36 if ( cells.length < 1 ) 37 return null ; 38 39 // Get the cells whose right or left border is nearest to the mouse cursor's x coordinate. 40 var minRxDist = null ; 41 var lxDist = null ; 42 var minYDist = null ; 43 var rbCell = null ; 44 var lbCell = null ; 45 for ( var i = 0 ; i < cells.length ; i++ ) 46 { 47 var pos = FCKTools.GetWindowPosition( w, cells[i] ) ; 48 var rightX = pos.x + parseInt( cells[i].clientWidth, 10 ) ; 49 var rxDist = mouse.x - rightX ; 50 var yDist = mouse.y - ( pos.y + ( cells[i].clientHeight / 2 ) ) ; 51 if ( minRxDist == null || 52 ( Math.abs( rxDist ) <= Math.abs( minRxDist ) && 53 ( minYDist == null || Math.abs( yDist ) <= Math.abs( minYDist ) ) ) ) 54 { 55 minRxDist = rxDist ; 56 minYDist = yDist ; 57 rbCell = cells[i] ; 58 } 59 } 60 /* 61 var rowNode = FCKTools.GetElementAscensor( rbCell, "tr" ) ; 62 var cellIndex = rbCell.cellIndex + 1 ; 63 if ( cellIndex >= rowNode.cells.length ) 64 return null ; 65 lbCell = rowNode.cells.item( cellIndex ) ; 66 */ 67 var rowIdx = rbCell.parentNode.rowIndex ; 68 var colIdx = FCKTableHandler._GetCellIndexSpan( tableMap, rowIdx, rbCell ) ; 69 var colSpan = isNaN( rbCell.colSpan ) ? 1 : rbCell.colSpan ; 70 lbCell = tableMap[rowIdx][colIdx + colSpan] ; 71 72 if ( ! lbCell ) 73 return null ; 74 75 // Abort if too far from the border. 76 lxDist = mouse.x - FCKTools.GetWindowPosition( w, lbCell ).x ; 77 if ( lxDist < 0 && minRxDist < 0 && minRxDist < -2 ) 78 return null ; 79 if ( lxDist > 0 && minRxDist > 0 && lxDist > 3 ) 80 return null ; 81 82 return { "leftCell" : rbCell, "rightCell" : lbCell } ; 83 }, 84 "_GetResizeBarPosition" : function() 85 { 86 var row = FCKTools.GetElementAscensor( this._RightCell, "tr" ) ; 87 return FCKTableHandler._GetCellIndexSpan( this._TableMap, row.rowIndex, this._RightCell ) ; 88 }, 89 "_ResizeBarMouseDownListener" : function( evt ) 90 { 91 if ( FCKDragTableHandler._LeftCell ) 92 FCKDragTableHandler._MouseMoveMode = 1 ; 93 if ( FCKBrowserInfo.IsIE ) 94 FCKDragTableHandler._ResizeBar.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 50 ; 95 else 96 FCKDragTableHandler._ResizeBar.style.opacity = 0.5 ; 97 FCKDragTableHandler._OriginalX = evt.clientX ; 98 99 // Calculate maximum and minimum x-coordinate delta. 100 var borderIndex = FCKDragTableHandler._GetResizeBarPosition() ; 101 var offset = FCKDragTableHandler._GetIframeOffset(); 102 var table = FCKTools.GetElementAscensor( FCKDragTableHandler._LeftCell, "table" ); 103 var minX = null ; 104 var maxX = null ; 105 for ( var r = 0 ; r < FCKDragTableHandler._TableMap.length ; r++ ) 106 { 107 var leftCell = FCKDragTableHandler._TableMap[r][borderIndex - 1] ; 108 var rightCell = FCKDragTableHandler._TableMap[r][borderIndex] ; 109 var leftPosition = FCKTools.GetWindowPosition( FCK.EditorWindow, leftCell ) ; 110 var rightPosition = FCKTools.GetWindowPosition( FCK.EditorWindow, rightCell ) ; 111 var leftPadding = FCKDragTableHandler._GetCellPadding( table, leftCell ) ; 112 var rightPadding = FCKDragTableHandler._GetCellPadding( table, rightCell ) ; 113 if ( minX == null || leftPosition.x + leftPadding > minX ) 114 minX = leftPosition.x + leftPadding ; 115 if ( maxX == null || rightPosition.x + rightCell.clientWidth - rightPadding < maxX ) 116 maxX = rightPosition.x + rightCell.clientWidth - rightPadding ; 117 } 118 119 FCKDragTableHandler._MinimumX = minX + offset.x ; 120 FCKDragTableHandler._MaximumX = maxX + offset.x ; 121 FCKDragTableHandler._LastX = null ; 122 123 if (evt.preventDefault) 124 evt.preventDefault(); 125 else 126 evt.returnValue = false; 127 }, 128 "_ResizeBarMouseUpListener" : function( evt ) 129 { 130 FCKDragTableHandler._MouseMoveMode = 0 ; 131 FCKDragTableHandler._HideResizeBar() ; 132 133 if ( FCKDragTableHandler._LastX == null ) 134 return ; 135 136 // Calculate the delta value. 137 var deltaX = FCKDragTableHandler._LastX - FCKDragTableHandler._OriginalX ; 138 139 // Then, build an array of current column width values. 140 // This algorithm can be very slow if the cells have insane colSpan values. (e.g. colSpan=1000). 141 var table = FCKTools.GetElementAscensor( FCKDragTableHandler._LeftCell, "table" ) ; 142 var colArray = [] ; 143 var tableMap = FCKDragTableHandler._TableMap ; 144 for ( var i = 0 ; i < tableMap.length ; i++ ) 145 { 146 for ( var j = 0 ; j < tableMap[i].length ; j++ ) 147 { 148 var cell = tableMap[i][j] ; 149 var width = FCKDragTableHandler._GetCellWidth( table, cell ) ; 150 var colSpan = isNaN( cell.colSpan) ? 1 : cell.colSpan ; 151 if ( colArray.length <= j ) 152 colArray.push( { width : width / colSpan, colSpan : colSpan } ) ; 153 else 154 { 155 var guessItem = colArray[j] ; 156 if ( guessItem.colSpan > colSpan ) 157 { 158 guessItem.width = width / colSpan ; 159 guessItem.colSpan = colSpan ; 160 } 161 } 162 } 163 } 164 165 // Find out the equivalent column index of the two cells selected for resizing. 166 colIndex = FCKDragTableHandler._GetResizeBarPosition() ; 167 168 // Note that colIndex must be at least 1 here, so it's safe to subtract 1 from it. 169 colIndex-- ; 170 171 // Modify the widths in the colArray according to the mouse coordinate delta value. 172 colArray[colIndex].width += deltaX ; 173 colArray[colIndex + 1].width -= deltaX ; 174 175 // Clear all cell widths, delete all <col> elements from the table. 176 for ( var r = 0 ; r < table.rows.length ; r++ ) 177 { 178 var row = table.rows.item( r ) ; 179 for ( var c = 0 ; c < row.cells.length ; c++ ) 180 { 181 var cell = row.cells.item( c ) ; 182 cell.width = "" ; 183 cell.style.width = "" ; 184 } 185 } 186 var colElements = table.getElementsByTagName( "col" ) ; 187 for ( var i = colElements.length - 1 ; i >= 0 ; i-- ) 188 colElements[i].parentNode.removeChild( colElements[i] ) ; 189 190 // Set new cell widths. 191 var processedCells = [] ; 192 for ( var i = 0 ; i < tableMap.length ; i++ ) 193 { 194 for ( var j = 0 ; j < tableMap[i].length ; j++ ) 195 { 196 var cell = tableMap[i][j] ; 197 if ( cell._Processed ) 198 continue ; 199 if ( tableMap[i][j-1] != cell ) 200 cell.width = colArray[j].width ; 201 else 202 cell.width = parseInt( cell.width, 10 ) + parseInt( colArray[j].width, 10 ) ; 203 if ( tableMap[i][j+1] != cell ) 204 { 205 processedCells.push( cell ) ; 206 cell._Processed = true ; 207 } 208 } 209 } 210 for ( var i = 0 ; i < processedCells.length ; i++ ) 211 { 212 if ( FCKBrowserInfo.IsIE ) 213 processedCells[i].removeAttribute( '_Processed' ) ; 214 else 215 delete processedCells[i]._Processed ; 216 } 217 218 FCKDragTableHandler._LastX = null ; 219 }, 220 "_ResizeBarMouseMoveListener" : function( evt ) 221 { 222 if ( FCKDragTableHandler._MouseMoveMode == 0 ) 223 return FCKDragTableHandler._MouseFindHandler( FCK, evt ) ; 224 else 225 return FCKDragTableHandler._MouseDragHandler( FCK, evt ) ; 226 }, 227 // Calculate the padding of a table cell. 228 // It returns the value of paddingLeft + paddingRight of a table cell. 229 // This function is used, in part, to calculate the width parameter that should be used for setting cell widths. 230 // The equation in question is clientWidth = paddingLeft + paddingRight + width. 231 // So that width = clientWidth - paddingLeft - paddingRight. 232 // The return value of this function must be pixel accurate acorss all supported browsers, so be careful if you need to modify it. 233 "_GetCellPadding" : function( table, cell ) 234 { 235 var attrGuess = parseInt( table.cellPadding, 10 ) * 2 ; 236 var cssGuess = null ; 237 if ( typeof( window.getComputedStyle ) == "function" ) 238 { 239 var styleObj = window.getComputedStyle( cell, null ) ; 240 cssGuess = parseInt( styleObj.getPropertyValue( "padding-left" ), 10 ) + 241 parseInt( styleObj.getPropertyValue( "padding-right" ), 10 ) ; 242 } 243 else 244 cssGuess = parseInt( cell.currentStyle.paddingLeft, 10 ) + parseInt (cell.currentStyle.paddingRight, 10 ) ; 245 246 var cssRuntime = cell.style.padding ; 247 if ( isFinite( cssRuntime ) ) 248 cssGuess = parseInt( cssRuntime, 10 ) * 2 ; 249 else 250 { 251 cssRuntime = cell.style.paddingLeft ; 252 if ( isFinite( cssRuntime ) ) 253 cssGuess = parseInt( cssRuntime, 10 ) ; 254 cssRuntime = cell.style.paddingRight ; 255 if ( isFinite( cssRuntime ) ) 256 cssGuess += parseInt( cssRuntime, 10 ) ; 257 } 258 259 attrGuess = parseInt( attrGuess, 10 ) ; 260 cssGuess = parseInt( cssGuess, 10 ) ; 261 if ( isNaN( attrGuess ) ) 262 attrGuess = 0 ; 263 if ( isNaN( cssGuess ) ) 264 cssGuess = 0 ; 265 return Math.max( attrGuess, cssGuess ) ; 266 }, 267 // Calculate the real width of the table cell. 268 // 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 269 // that, the table cell should be of exactly the same width as before. 270 // The real width of a table cell can be calculated as: 271 // width = clientWidth - paddingLeft - paddingRight. 272 "_GetCellWidth" : function( table, cell ) 273 { 274 var clientWidth = cell.clientWidth ; 275 if ( isNaN( clientWidth ) ) 276 clientWidth = 0 ; 277 return clientWidth - this._GetCellPadding( table, cell ) ; 278 }, 279 "MouseMoveListener" : function( FCK, evt ) 280 { 281 if ( FCKDragTableHandler._MouseMoveMode == 0 ) 282 return FCKDragTableHandler._MouseFindHandler( FCK, evt ) ; 283 else 284 return FCKDragTableHandler._MouseDragHandler( FCK, evt ) ; 285 }, 286 "_MouseFindHandler" : function( FCK, evt ) 287 { 288 if ( FCK.MouseDownFlag ) 289 return ; 290 var node = evt.srcElement || evt.target ; 291 try 292 { 293 if ( ! node || node.nodeType != 1 ) 294 { 295 this._HideResizeBar() ; 296 return ; 297 } 298 } 299 catch ( e ) 300 { 301 this._HideResizeBar() ; 302 return ; 303 } 304 305 // Since this function might be called from the editing area iframe or the outer fckeditor iframe, 306 // the mouse point coordinates from evt.clientX/Y can have different reference points. 307 // We need to resolve the mouse pointer position relative to the editing area iframe. 308 var mouseX = evt.clientX ; 309 var mouseY = evt.clientY ; 310 if ( FCKTools.GetElementDocument( node ) == document ) 311 { 312 var offset = this._GetIframeOffset() ; 313 mouseX -= offset.x ; 314 mouseY -= offset.y ; 315 } 316 317 318 if ( this._ResizeBar && this._LeftCell ) 319 { 320 var leftPos = FCKTools.GetWindowPosition( FCK.EditorWindow, this._LeftCell ) ; 321 var rightPos = FCKTools.GetWindowPosition( FCK.EditorWindow, this._RightCell ) ; 322 var rxDist = mouseX - ( leftPos.x + this._LeftCell.clientWidth ) ; 323 var lxDist = mouseX - rightPos.x ; 324 var inRangeFlag = false ; 325 if ( lxDist >= 0 && rxDist <= 0 ) 326 inRangeFlag = true ; 327 else if ( rxDist > 0 && lxDist <= 3 ) 328 inRangeFlag = true ; 329 else if ( lxDist < 0 && rxDist >= -2 ) 330 inRangeFlag = true ; 331 if ( inRangeFlag ) 332 { 333 this._ShowResizeBar( FCK.EditorWindow, 334 FCKTools.GetElementAscensor( this._LeftCell, "table" ), 335 { "x" : mouseX, "y" : mouseY } ) ; 336 return ; 337 } 338 } 339 340 var tagName = node.tagName.toLowerCase() ; 341 if ( tagName != "table" && tagName != "td" && tagName != "th" ) 342 { 343 if ( this._LeftCell ) 344 this._LeftCell = this._RightCell = this._TableMap = null ; 345 this._HideResizeBar() ; 346 return ; 347 } 348 node = FCKTools.GetElementAscensor( node, "table" ) ; 349 var tableMap = FCKTableHandler._CreateTableMap( node ) ; 350 var cellTuple = this._GetBorderCells( FCK.EditorWindow, node, tableMap, { "x" : mouseX, "y" : mouseY } ) ; 351 352 if ( cellTuple == null ) 353 { 354 if ( this._LeftCell ) 355 this._LeftCell = this._RightCell = this._TableMap = null ; 356 this._HideResizeBar() ; 357 } 358 else 359 { 360 this._LeftCell = cellTuple["leftCell"] ; 361 this._RightCell = cellTuple["rightCell"] ; 362 this._TableMap = tableMap ; 363 this._ShowResizeBar( FCK.EditorWindow, 364 FCKTools.GetElementAscensor( this._LeftCell, "table" ), 365 { "x" : mouseX, "y" : mouseY } ) ; 366 } 367 }, 368 "_MouseDragHandler" : function( FCK, evt ) 369 { 370 var mouse = { "x" : evt.clientX, "y" : evt.clientY } ; 371 372 // Convert mouse coordinates in reference to the outer iframe. 373 var node = evt.srcElement || evt.target ; 374 if ( FCKTools.GetElementDocument( node ) == FCK.EditorDocument ) 375 { 376 var offset = this._GetIframeOffset() ; 377 mouse.x += offset.x ; 378 mouse.y += offset.y ; 379 } 380 381 // Calculate the mouse position delta and see if we've gone out of range. 382 if ( mouse.x >= this._MaximumX - 5 ) 383 mouse.x = this._MaximumX - 5 ; 384 if ( mouse.x <= this._MinimumX + 5 ) 385 mouse.x = this._MinimumX + 5 ; 386 387 var docX = mouse.x + FCKTools.GetScrollPosition( window ).X ; 388 this._ResizeBar.style.left = ( docX - this._ResizeBar.offsetWidth / 2 ) + "px" ; 389 this._LastX = mouse.x ; 390 }, 391 "_ShowResizeBar" : function( w, table, mouse ) 392 { 393 if ( this._ResizeBar == null ) 394 { 395 this._ResizeBar = this._doc.createElement( "div" ) ; 396 var paddingBar = this._ResizeBar ; 397 var paddingStyles = { 'position' : 'absolute', 'cursor' : 'e-resize' } ; 398 if ( FCKBrowserInfo.IsIE ) 399 paddingStyles.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=10,enabled=true)" ; 400 else 401 paddingStyles.opacity = 0.10 ; 402 FCKDomTools.SetElementStyles( paddingBar, paddingStyles ) ; 403 this._avoidStyles( paddingBar ); 404 paddingBar.setAttribute('_fcktemp', true); 405 this._doc.body.appendChild( paddingBar ) ; 406 FCKTools.AddEventListener( paddingBar, "mousemove", this._ResizeBarMouseMoveListener ) ; 407 FCKTools.AddEventListener( paddingBar, "mousedown", this._ResizeBarMouseDownListener ) ; 408 FCKTools.AddEventListener( document, "mouseup", this._ResizeBarMouseUpListener ) ; 409 FCKTools.AddEventListener( FCK.EditorDocument, "mouseup", this._ResizeBarMouseUpListener ) ; 410 411 // IE doesn't let the tranparent part of the padding block to receive mouse events unless there's something inside. 412 // So we need to create a spacer image to fill the block up. 413 var filler = this._doc.createElement( "img" ) ; 414 filler.setAttribute('_fcktemp', true); 415 filler.border = 0 ; 416 filler.src = FCKConfig.BasePath + "images/spacer.gif" ; 417 filler.style.position = "absolute" ; 418 paddingBar.appendChild( filler ) ; 419 420 // Disable drag and drop, and selection for the filler image. 421 var disabledListener = function( evt ) 422 { 423 if ( evt.preventDefault ) 424 evt.preventDefault() ; 425 else 426 evt.returnValue = false ; 427 } 428 FCKTools.AddEventListener( filler, "dragstart", disabledListener ) ; 429 FCKTools.AddEventListener( filler, "selectstart", disabledListener ) ; 430 } 431 432 var paddingBar = this._ResizeBar ; 433 var offset = this._GetIframeOffset() ; 434 var tablePos = this._GetTablePosition( w, table ) ; 435 var barHeight = table.offsetHeight ; 436 var barTop = offset.y + tablePos.y ; 437 // Do not let the resize bar intrude into the toolbar area. 438 if ( tablePos.y < 0 ) 439 { 440 barHeight += tablePos.y ; 441 barTop -= tablePos.y ; 442 } 443 var bw = parseInt( table.border, 10 ) ; 444 if ( isNaN( bw ) ) 445 bw = 0 ; 446 var cs = parseInt( table.cellSpacing, 10 ) ; 447 if ( isNaN( cs ) ) 448 cs = 0 ; 449 var barWidth = Math.max( bw+100, cs+100 ) ; 450 var paddingStyles = 451 { 452 'top' : barTop + 'px', 453 'height' : barHeight + 'px', 454 'width' : barWidth + 'px', 455 'left' : ( offset.x + mouse.x + FCKTools.GetScrollPosition( w ).X - barWidth / 2 ) + 'px' 456 } ; 457 if ( FCKBrowserInfo.IsIE ) 458 paddingBar.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 10 ; 459 else 460 paddingStyles.opacity = 0.1 ; 461 462 FCKDomTools.SetElementStyles( paddingBar, paddingStyles ) ; 463 var filler = paddingBar.getElementsByTagName( "img" )[0] ; 464 465 FCKDomTools.SetElementStyles( filler, 466 { 467 width : paddingBar.offsetWidth + 'px', 468 height : barHeight + 'px' 469 } ) ; 470 471 barWidth = Math.max( bw, cs, 3 ) ; 472 var visibleBar = null ; 473 if ( paddingBar.getElementsByTagName( "div" ).length < 1 ) 474 { 475 visibleBar = this._doc.createElement( "div" ) ; 476 this._avoidStyles( visibleBar ); 477 visibleBar.setAttribute('_fcktemp', true); 478 paddingBar.appendChild( visibleBar ) ; 479 } 480 else 481 visibleBar = paddingBar.getElementsByTagName( "div" )[0] ; 482 483 FCKDomTools.SetElementStyles( visibleBar, 484 { 485 position : 'absolute', 486 backgroundColor : 'blue', 487 width : barWidth + 'px', 488 height : barHeight + 'px', 489 left : '50px', 490 top : '0px' 491 } ) ; 492 }, 493 "_HideResizeBar" : function() 494 { 495 if ( this._ResizeBar ) 496 // IE bug: display : none does not hide the resize bar for some reason. 497 // so set the position to somewhere invisible. 498 FCKDomTools.SetElementStyles( this._ResizeBar, 499 { 500 top : '-100000px', 501 left : '-100000px' 502 } ) ; 503 }, 504 "_GetIframeOffset" : function () 505 { 506 return FCKTools.GetDocumentPosition( window, FCK.EditingArea.IFrame ) ; 507 }, 508 "_GetTablePosition" : function ( w, table ) 509 { 510 return FCKTools.GetWindowPosition( w, table ) ; 511 }, 512 "_avoidStyles" : function( element ) 513 { 514 FCKDomTools.SetElementStyles( element, 515 { 516 padding : '0', 517 backgroundImage : 'none', 518 border : '0' 519 } ) ; 520 }, 521 "Reset" : function() 522 { 523 FCKDragTableHandler._LeftCell = FCKDragTableHandler._RightCell = FCKDragTableHandler._TableMap = null ; 524 } 525 526}; 527 528FCK.Events.AttachEvent( "OnMouseMove", FCKDragTableHandler.MouseMoveListener ) ; 529FCK.Events.AttachEvent( "OnAfterSetHTML", FCKDragTableHandler.Reset ) ; 530