2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
8 var pxUnit
= CKEDITOR
.tools
.cssLength
,
9 needsIEHacks
= CKEDITOR
.env
.ie
&& ( CKEDITOR
.env
.ie7Compat
|| CKEDITOR
.env
.quirks
|| CKEDITOR
.env
.version
< 7 );
11 function getWidth( el
)
13 return CKEDITOR
.env
.ie
? el
.$.clientWidth
: parseInt( el
.getComputedStyle( 'width' ), 10 );
16 function getBorderWidth( element
, side
)
18 var computed
= element
.getComputedStyle( 'border-' + side
+ '-width' ),
26 if ( computed
.indexOf( 'px' ) < 0 )
29 if ( computed
in borderMap
&& element
.getComputedStyle( 'border-style' ) != 'none' )
30 computed
= borderMap
[ computed
];
35 return parseInt( computed
, 10 );
38 // Gets the table row that contains the most columns.
39 function getMasterPillarRow( table
)
41 var $rows
= table
.$.rows
,
42 maxCells
= 0, cellsCount
,
45 for ( var i
= 0, len
= $rows
.length
; i
< len
; i
++ )
48 cellsCount
= $tr
.cells
.length
;
50 if ( cellsCount
> maxCells
)
52 maxCells
= cellsCount
;
60 function buildTableColumnPillars( table
)
64 rtl
= ( table
.getComputedStyle( 'direction' ) == 'rtl' );
66 // Get the raw row element that cointains the most columns.
67 var $tr
= getMasterPillarRow( table
);
69 // Get the tbody element and position, which will be used to set the
70 // top and bottom boundaries.
71 var tbody
= new CKEDITOR
.dom
.element( table
.$.tBodies
[ 0 ] ),
72 tbodyPosition
= tbody
.getDocumentPosition();
74 // Loop thorugh all cells, building pillars after each one of them.
75 for ( var i
= 0, len
= $tr
.cells
.length
; i
< len
; i
++ )
77 // Both the current cell and the successive one will be used in the
78 // pillar size calculation.
79 var td
= new CKEDITOR
.dom
.element( $tr
.cells
[ i
] ),
80 nextTd
= $tr
.cells
[ i
+ 1 ] && new CKEDITOR
.dom
.element( $tr
.cells
[ i
+ 1 ] );
82 pillarIndex
+= td
.$.colSpan
|| 1;
84 // Calculate the pillar boundary positions.
85 var pillarLeft
, pillarRight
, pillarWidth
;
87 var x
= td
.getDocumentPosition().x
;
89 // Calculate positions based on the current cell.
91 pillarRight
= x
+ getBorderWidth( td
, 'left' ) :
92 pillarLeft
= x
+ td
.$.offsetWidth
- getBorderWidth( td
, 'right' );
94 // Calculate positions based on the next cell, if available.
97 x
= nextTd
.getDocumentPosition().x
;
100 pillarLeft
= x
+ nextTd
.$.offsetWidth
- getBorderWidth( nextTd
, 'right' ) :
101 pillarRight
= x
+ getBorderWidth( nextTd
, 'left' );
103 // Otherwise calculate positions based on the table (for last cell).
106 x
= table
.getDocumentPosition().x
;
110 pillarRight
= x
+ table
.$.offsetWidth
;
113 pillarWidth
= Math
.max( pillarRight
- pillarLeft
, 3 );
115 // The pillar should reflects exactly the shape of the hovered
116 // column border line.
123 height
: tbody
.$.offsetHeight
,
130 function getPillarAtPosition( pillars
, positionX
)
132 for ( var i
= 0, len
= pillars
.length
; i
< len
; i
++ )
134 var pillar
= pillars
[ i
];
136 if ( positionX
>= pillar
.x
&& positionX
<= ( pillar
.x
+ pillar
.width
) )
143 function cancel( evt
)
145 ( evt
.data
|| evt
).preventDefault();
148 function columnResizer( editor
)
157 var leftSideCells
, rightSideCells
, leftShiftBoundary
, rightShiftBoundary
;
165 document
.removeListener( 'mouseup', onMouseUp
);
166 resizer
.removeListener( 'mousedown', onMouseDown
);
167 resizer
.removeListener( 'mousemove', onMouseMove
);
169 document
.getBody().setStyle( 'cursor', 'auto' );
171 // Hide the resizer (remove it on IE7 - #5890).
172 needsIEHacks
? resizer
.remove() : resizer
.hide();
175 function resizeStart()
177 // Before starting to resize, figure out which cells to change
178 // and the boundaries of this resizing shift.
180 var columnIndex
= pillar
.index
,
181 map
= CKEDITOR
.tools
.buildTableMap( pillar
.table
),
182 leftColumnCells
= [],
183 rightColumnCells
= [],
184 leftMinSize
= Number
.MAX_VALUE
,
185 rightMinSize
= leftMinSize
,
188 for ( var i
= 0, len
= map
.length
; i
< len
; i
++ )
191 leftCell
= row
[ columnIndex
+ ( rtl
? 1 : 0 ) ],
192 rightCell
= row
[ columnIndex
+ ( rtl
? 0 : 1 ) ];
194 leftCell
= leftCell
&& new CKEDITOR
.dom
.element( leftCell
);
195 rightCell
= rightCell
&& new CKEDITOR
.dom
.element( rightCell
);
197 if ( !leftCell
|| !rightCell
|| !leftCell
.equals( rightCell
) )
199 leftCell
&& ( leftMinSize
= Math
.min( leftMinSize
, getWidth( leftCell
) ) );
200 rightCell
&& ( rightMinSize
= Math
.min( rightMinSize
, getWidth( rightCell
) ) );
202 leftColumnCells
.push( leftCell
);
203 rightColumnCells
.push( rightCell
);
207 // Cache the list of cells to be resized.
208 leftSideCells
= leftColumnCells
;
209 rightSideCells
= rightColumnCells
;
211 // Cache the resize limit boundaries.
212 leftShiftBoundary
= pillar
.x
- leftMinSize
;
213 rightShiftBoundary
= pillar
.x
+ rightMinSize
;
215 resizer
.setOpacity( 0.5 );
216 startOffset
= parseInt( resizer
.getStyle( 'left' ), 10 );
220 resizer
.on( 'mousemove', onMouseMove
);
222 // Prevent the native drag behavior otherwise 'mousemove' won't fire.
223 document
.on( 'dragstart', cancel
);
230 resizer
.setOpacity( 0 );
232 currentShift
&& resizeColumn();
234 var table
= pillar
.table
;
235 setTimeout( function () { table
.removeCustomData( '_cke_table_pillars' ); }, 0 );
237 document
.removeListener( 'dragstart', cancel
);
240 function resizeColumn()
242 var rtl
= pillar
.rtl
,
243 cellsCount
= rtl
? rightSideCells
.length
: leftSideCells
.length
;
245 // Perform the actual resize to table cells, only for those by side of the pillar.
246 for ( var i
= 0 ; i
< cellsCount
; i
++ )
248 var leftCell
= leftSideCells
[ i
],
249 rightCell
= rightSideCells
[ i
],
250 table
= pillar
.table
;
252 // Defer the resizing to avoid any interference among cells.
253 CKEDITOR
.tools
.setTimeout(
254 function( leftCell
, leftOldWidth
, rightCell
, rightOldWidth
, tableWidth
, sizeShift
)
256 leftCell
&& leftCell
.setStyle( 'width', pxUnit( Math
.max( leftOldWidth
+ sizeShift
, 0 ) ) );
257 rightCell
&& rightCell
.setStyle( 'width', pxUnit( Math
.max( rightOldWidth
- sizeShift
, 0 ) ) );
259 // If we're in the last cell, we need to resize the table as well
261 table
.setStyle( 'width', pxUnit( tableWidth
+ sizeShift
* ( rtl
? -1 : 1 ) ) );
265 leftCell
, leftCell
&& getWidth( leftCell
),
266 rightCell
, rightCell
&& getWidth( rightCell
),
267 ( !leftCell
|| !rightCell
) && ( getWidth( table
) + getBorderWidth( table
, 'left' ) + getBorderWidth( table
, 'right' ) ),
272 function onMouseDown( evt
)
278 document
.on( 'mouseup', onMouseUp
, this );
281 function onMouseUp( evt
)
283 evt
.removeListener();
288 function onMouseMove( evt
)
290 move( evt
.data
.$.clientX
);
293 document
= editor
.document
;
295 resizer
= CKEDITOR
.dom
.element
.createFromHtml(
296 '<div data-cke-temp=1 contenteditable=false unselectable=on '+
297 'style="position:absolute;cursor:col-resize;filter:alpha(opacity=0);opacity:0;' +
298 'padding:0;background-color:#004;background-image:none;border:0px none;z-index:10"></div>', document
);
300 // Except on IE6/7 (#5890), place the resizer after body to prevent it
301 // from being editable.
303 document
.getDocumentElement().append( resizer
);
305 this.attachTo = function( targetPillar
)
307 // Accept only one pillar at a time.
311 // On IE6/7, we append the resizer everytime we need it. (#5890)
314 document
.getBody().append( resizer
);
318 pillar
= targetPillar
;
322 width
: pxUnit( targetPillar
.width
),
323 height
: pxUnit( targetPillar
.height
),
324 left
: pxUnit( targetPillar
.x
),
325 top
: pxUnit( targetPillar
.y
)
328 // In IE6/7, it's not possible to have custom cursors for floating
329 // elements in an editable document. Show the resizer in that case,
330 // to give the user a visual clue.
331 needsIEHacks
&& resizer
.setOpacity( 0.25 );
333 resizer
.on( 'mousedown', onMouseDown
, this );
335 document
.getBody().setStyle( 'cursor', 'col-resize' );
337 // Display the resizer to receive events but don't show it,
338 // only change the cursor to resizable shape.
342 var move = this.move = function( posX
)
347 if ( !isResizing
&& ( posX
< pillar
.x
|| posX
> ( pillar
.x
+ pillar
.width
) ) )
353 var resizerNewPosition
= posX
- Math
.round( resizer
.$.offsetWidth
/ 2 );
357 if ( resizerNewPosition
== leftShiftBoundary
|| resizerNewPosition
== rightShiftBoundary
)
360 resizerNewPosition
= Math
.max( resizerNewPosition
, leftShiftBoundary
);
361 resizerNewPosition
= Math
.min( resizerNewPosition
, rightShiftBoundary
);
363 currentShift
= resizerNewPosition
- startOffset
;
366 resizer
.setStyle( 'left', pxUnit( resizerNewPosition
) );
372 function clearPillarsCache( evt
)
374 var target
= evt
.data
.getTarget();
376 if ( evt
.name
== 'mouseout' )
378 // Bypass interal mouse move.
379 if ( !target
.is ( 'table' ) )
382 var dest
= new CKEDITOR
.dom
.element( evt
.data
.$.relatedTarget
|| evt
.data
.$.toElement
);
383 while( dest
&& dest
.$ && !dest
.equals( target
) && !dest
.is( 'body' ) )
384 dest
= dest
.getParent();
385 if ( !dest
|| dest
.equals( target
) )
389 target
.getAscendant( 'table', 1 ).removeCustomData( '_cke_table_pillars' );
390 evt
.removeListener();
393 CKEDITOR
.plugins
.add( 'tableresize',
395 requires
: [ 'tabletools' ],
396 init : function( editor
)
398 editor
.on( 'contentDom', function()
402 editor
.document
.getBody().on( 'mousemove', function( evt
)
406 // If we're already attached to a pillar, simply move the
408 if ( resizer
&& resizer
.move( evt
.$.clientX
) )
414 // Considering table, tr, td, tbody but nothing else.
415 var target
= evt
.getTarget(),
419 if ( !target
.is( 'table' ) && !target
.getAscendant( 'tbody', 1 ) )
422 table
= target
.getAscendant( 'table', 1 );
424 if ( !( pillars
= table
.getCustomData( '_cke_table_pillars' ) ) )
426 // Cache table pillars calculation result.
427 table
.setCustomData( '_cke_table_pillars', ( pillars
= buildTableColumnPillars( table
) ) );
428 table
.on( 'mouseout', clearPillarsCache
);
429 table
.on( 'mousedown', clearPillarsCache
);
432 var pillar
= getPillarAtPosition( pillars
, evt
.$.clientX
);
435 !resizer
&& ( resizer
= new columnResizer( editor
) );
436 resizer
.attachTo( pillar
);