PluginProbe
Wp-Insert / 1.7.2
Wp-Insert v1.7.2
trunk 1.0 1.1 1.2 1.2.1 1.2.2 1.3.0 1.3.1 1.3.3 1.4 1.5.0 1.5.1 1.5.2 1.5.3 1.6.0 1.6.1 1.6.2 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 2.0 All 63 releases
wp-insert / fckeditor / editor / _source / internals / fcktablehandler.js

fcktablehandler.js in Wp-Insert 1.7.2, at fckeditor/editor/_source/internals/fcktablehandler.js

864 lines 26.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2009 Frederico Caldeira Knabben
4 *
5 * == BEGIN LICENSE ==
6 *
7 * Licensed under the terms of any of the following licenses at your
8 * choice:
9 *
10 * - GNU General Public License Version 2 or later (the "GPL")
11 * http://www.gnu.org/licenses/gpl.html
12 *
13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14 * http://www.gnu.org/licenses/lgpl.html
15 *
16 * - Mozilla Public License Version 1.1 or later (the "MPL")
17 * http://www.mozilla.org/MPL/MPL-1.1.html
18 *
19 * == END LICENSE ==
20 *
21 * Manage table operations.
22 */
23
24 var FCKTableHandler = new Object() ;
25
26 FCKTableHandler.InsertRow = function( insertBefore )
27 {
28 // Get the row where the selection is placed in.
29 var oRow = FCKSelection.MoveToAncestorNode( 'TR' ) ;
30 if ( !oRow ) return ;
31
32 // Create a clone of the row.
33 var oNewRow = oRow.cloneNode( true ) ;
34
35 // Insert the new row (copy) before of it.
36 oRow.parentNode.insertBefore( oNewRow, oRow ) ;
37
38 // Clean one of the rows to produce the illusion of inserting an empty row before or after.
39 FCKTableHandler.ClearRow( insertBefore ? oNewRow : oRow ) ;
40 }
41
42 FCKTableHandler.DeleteRows = function( row )
43 {
44 // If no row has been passed as a parameter,
45 // then get the row( s ) containing the cells where the selection is placed in.
46 // If user selected multiple rows ( by selecting multiple cells ), walk
47 // the selected cell list and delete the rows containing the selected cells
48 if ( ! row )
49 {
50 var aCells = FCKTableHandler.GetSelectedCells() ;
51 var aRowsToDelete = new Array() ;
52 //queue up the rows -- it's possible ( and likely ) that we may get duplicates
53 for ( var i = 0; i < aCells.length; i++ )
54 {
55 var oRow = aCells[i].parentNode ;
56 aRowsToDelete[oRow.rowIndex] = oRow ;
57 }
58 for ( var i = aRowsToDelete.length; i >= 0; i-- )
59 {
60 if ( aRowsToDelete[i] )
61 FCKTableHandler.DeleteRows( aRowsToDelete[i] );
62 }
63 return ;
64 }
65
66 // Get the row's table.
67 var oTable = FCKTools.GetElementAscensor( row, 'TABLE' ) ;
68
69 // If just one row is available then delete the entire table.
70 if ( oTable.rows.length == 1 )
71 {
72 FCKTableHandler.DeleteTable( oTable ) ;
73 return ;
74 }
75
76 // Delete the row.
77 row.parentNode.removeChild( row ) ;
78 }
79
80 FCKTableHandler.DeleteTable = function( table )
81 {
82 // If no table has been passed as a parameter,
83 // then get the table where the selection is placed in.
84 if ( !table )
85 {
86 table = FCKSelection.GetSelectedElement() ;
87 if ( !table || table.tagName != 'TABLE' )
88 table = FCKSelection.MoveToAncestorNode( 'TABLE' ) ;
89 }
90 if ( !table ) return ;
91
92 // Delete the table.
93 FCKSelection.SelectNode( table ) ;
94 FCKSelection.Collapse();
95
96 // if the table is wrapped with a singleton <p> ( or something similar ), remove
97 // the surrounding tag -- which likely won't show after deletion anyway
98 if ( table.parentNode.childNodes.length == 1 )
99 table.parentNode.parentNode.removeChild( table.parentNode );
100 else
101 table.parentNode.removeChild( table ) ;
102 }
103
104 FCKTableHandler.InsertColumn = function( insertBefore )
105 {
106 // Get the cell where the selection is placed in.
107 var oCell = null ;
108 var nodes = this.GetSelectedCells() ;
109
110 if ( nodes && nodes.length )
111 oCell = nodes[ insertBefore ? 0 : ( nodes.length - 1 ) ] ;
112
113 if ( ! oCell )
114 return ;
115
116 // Get the cell's table.
117 var oTable = FCKTools.GetElementAscensor( oCell, 'TABLE' ) ;
118
119 var iIndex = oCell.cellIndex ;
120
121 // Loop through all rows available in the table.
122 for ( var i = 0 ; i < oTable.rows.length ; i++ )
123 {
124 // Get the row.
125 var oRow = oTable.rows[i] ;
126
127 // If the row doesn't have enough cells, ignore it.
128 if ( oRow.cells.length < ( iIndex + 1 ) )
129 continue ;
130
131 oCell = oRow.cells[iIndex].cloneNode(false) ;
132
133 if ( FCKBrowserInfo.IsGeckoLike )
134 FCKTools.AppendBogusBr( oCell ) ;
135
136 // Get back the currently selected cell.
137 var oBaseCell = oRow.cells[iIndex] ;
138
139 oRow.insertBefore( oCell, ( insertBefore ? oBaseCell : oBaseCell.nextSibling ) ) ;
140 }
141 }
142
143 FCKTableHandler.DeleteColumns = function( oCell )
144 {
145 // if user selected multiple cols ( by selecting multiple cells ), walk
146 // the selected cell list and delete the rows containing the selected cells
147 if ( !oCell )
148 {
149 var aColsToDelete = FCKTableHandler.GetSelectedCells();
150 for ( var i = aColsToDelete.length; i >= 0; i-- )
151 {
152 if ( aColsToDelete[i] )
153 FCKTableHandler.DeleteColumns( aColsToDelete[i] );
154 }
155 return;
156 }
157
158 if ( !oCell ) return ;
159
160 // Get the cell's table.
161 var oTable = FCKTools.GetElementAscensor( oCell, 'TABLE' ) ;
162
163 // Get the cell index.
164 var iIndex = oCell.cellIndex ;
165
166 // Loop throw all rows (from down to up, because it's possible that some
167 // rows will be deleted).
168 for ( var i = oTable.rows.length - 1 ; i >= 0 ; i-- )
169 {
170 // Get the row.
171 var oRow = oTable.rows[i] ;
172
173 // If the cell to be removed is the first one and the row has just one cell.
174 if ( iIndex == 0 && oRow.cells.length == 1 )
175 {
176 // Remove the entire row.
177 FCKTableHandler.DeleteRows( oRow ) ;
178 continue ;
179 }
180
181 // If the cell to be removed exists the delete it.
182 if ( oRow.cells[iIndex] )
183 oRow.removeChild( oRow.cells[iIndex] ) ;
184 }
185 }
186
187 FCKTableHandler.InsertCell = function( cell, insertBefore )
188 {
189 // Get the cell where the selection is placed in.
190 var oCell = null ;
191 var nodes = this.GetSelectedCells() ;
192 if ( nodes && nodes.length )
193 oCell = nodes[ insertBefore ? 0 : ( nodes.length - 1 ) ] ;
194 if ( ! oCell )
195 return null ;
196
197 // Create the new cell element to be added.
198 var oNewCell = FCK.EditorDocument.createElement( 'TD' ) ;
199 if ( FCKBrowserInfo.IsGeckoLike )
200 FCKTools.AppendBogusBr( oNewCell ) ;
201
202 if ( !insertBefore && oCell.cellIndex == oCell.parentNode.cells.length - 1 )
203 oCell.parentNode.appendChild( oNewCell ) ;
204 else
205 oCell.parentNode.insertBefore( oNewCell, insertBefore ? oCell : oCell.nextSibling ) ;
206
207 return oNewCell ;
208 }
209
210 FCKTableHandler.DeleteCell = function( cell )
211 {
212 // If this is the last cell in the row.
213 if ( cell.parentNode.cells.length == 1 )
214 {
215 // Delete the entire row.
216 FCKTableHandler.DeleteRows( cell.parentNode ) ;
217 return ;
218 }
219
220 // Delete the cell from the row.
221 cell.parentNode.removeChild( cell ) ;
222 }
223
224 FCKTableHandler.DeleteCells = function()
225 {
226 var aCells = FCKTableHandler.GetSelectedCells() ;
227
228 for ( var i = aCells.length - 1 ; i >= 0 ; i-- )
229 {
230 FCKTableHandler.DeleteCell( aCells[i] ) ;
231 }
232 }
233
234 FCKTableHandler._MarkCells = function( cells, label )
235 {
236 for ( var i = 0 ; i < cells.length ; i++ )
237 cells[i][label] = true ;
238 }
239
240 FCKTableHandler._UnmarkCells = function( cells, label )
241 {
242 for ( var i = 0 ; i < cells.length ; i++ )
243 {
244 FCKDomTools.ClearElementJSProperty(cells[i], label ) ;
245 }
246 }
247
248 FCKTableHandler._ReplaceCellsByMarker = function( tableMap, marker, substitute )
249 {
250 for ( var i = 0 ; i < tableMap.length ; i++ )
251 {
252 for ( var j = 0 ; j < tableMap[i].length ; j++ )
253 {
254 if ( tableMap[i][j][marker] )
255 tableMap[i][j] = substitute ;
256 }
257 }
258 }
259
260 FCKTableHandler._GetMarkerGeometry = function( tableMap, rowIdx, colIdx, markerName )
261 {
262 var selectionWidth = 0 ;
263 var selectionHeight = 0 ;
264 var cellsLeft = 0 ;
265 var cellsUp = 0 ;
266 for ( var i = colIdx ; tableMap[rowIdx][i] && tableMap[rowIdx][i][markerName] ; i++ )
267 selectionWidth++ ;
268 for ( var i = colIdx - 1 ; tableMap[rowIdx][i] && tableMap[rowIdx][i][markerName] ; i-- )
269 {
270 selectionWidth++ ;
271 cellsLeft++ ;
272 }
273 for ( var i = rowIdx ; tableMap[i] && tableMap[i][colIdx] && tableMap[i][colIdx][markerName] ; i++ )
274 selectionHeight++ ;
275 for ( var i = rowIdx - 1 ; tableMap[i] && tableMap[i][colIdx] && tableMap[i][colIdx][markerName] ; i-- )
276 {
277 selectionHeight++ ;
278 cellsUp++ ;
279 }
280 return { 'width' : selectionWidth, 'height' : selectionHeight, 'x' : cellsLeft, 'y' : cellsUp } ;
281 }
282
283 FCKTableHandler.CheckIsSelectionRectangular = function()
284 {
285 // If every row and column in an area on a plane are of the same width and height,
286 // Then the area is a rectangle.
287 var cells = FCKTableHandler.GetSelectedCells() ;
288 if ( cells.length < 1 )
289 return false ;
290
291 // Check if the selected cells are all in the same table section (thead, tfoot or tbody)
292 for (var i = 0; i < cells.length; i++)
293 {
294 if ( cells[i].parentNode.parentNode != cells[0].parentNode.parentNode )
295 return false ;
296 }
297
298 this._MarkCells( cells, '_CellSelected' ) ;
299
300 var tableMap = this._CreateTableMap( cells[0] ) ;
301 var rowIdx = cells[0].parentNode.rowIndex ;
302 var colIdx = this._GetCellIndexSpan( tableMap, rowIdx, cells[0] ) ;
303
304 var geometry = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_CellSelected' ) ;
305 var baseColIdx = colIdx - geometry.x ;
306 var baseRowIdx = rowIdx - geometry.y ;
307
308 if ( geometry.width >= geometry.height )
309 {
310 for ( colIdx = baseColIdx ; colIdx < baseColIdx + geometry.width ; colIdx++ )
311 {
312 rowIdx = baseRowIdx + ( colIdx - baseColIdx ) % geometry.height ;
313 if ( ! tableMap[rowIdx] || ! tableMap[rowIdx][colIdx] )
314 {
315 this._UnmarkCells( cells, '_CellSelected' ) ;
316 return false ;
317 }
318 var g = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_CellSelected' ) ;
319 if ( g.width != geometry.width || g.height != geometry.height )
320 {
321 this._UnmarkCells( cells, '_CellSelected' ) ;
322 return false ;
323 }
324 }
325 }
326 else
327 {
328 for ( rowIdx = baseRowIdx ; rowIdx < baseRowIdx + geometry.height ; rowIdx++ )
329 {
330 colIdx = baseColIdx + ( rowIdx - baseRowIdx ) % geometry.width ;
331 if ( ! tableMap[rowIdx] || ! tableMap[rowIdx][colIdx] )
332 {
333 this._UnmarkCells( cells, '_CellSelected' ) ;
334 return false ;
335 }
336 var g = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_CellSelected' ) ;
337 if ( g.width != geometry.width || g.height != geometry.height )
338 {
339 this._UnmarkCells( cells, '_CellSelected' ) ;
340 return false ;
341 }
342 }
343 }
344
345 this._UnmarkCells( cells, '_CellSelected' ) ;
346 return true ;
347 }
348
349 FCKTableHandler.MergeCells = function()
350 {
351 // Get all selected cells.
352 var cells = this.GetSelectedCells() ;
353 if ( cells.length < 2 )
354 return ;
355
356 // Assume the selected cells are already in a rectangular geometry.
357 // Because the checking is already done by FCKTableCommand.
358 var refCell = cells[0] ;
359 var tableMap = this._CreateTableMap( refCell ) ;
360 var rowIdx = refCell.parentNode.rowIndex ;
361 var colIdx = this._GetCellIndexSpan( tableMap, rowIdx, refCell ) ;
362
363 this._MarkCells( cells, '_SelectedCells' ) ;
364 var selectionGeometry = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_SelectedCells' ) ;
365
366 var baseColIdx = colIdx - selectionGeometry.x ;
367 var baseRowIdx = rowIdx - selectionGeometry.y ;
368 var cellContents = FCKTools.GetElementDocument( refCell ).createDocumentFragment() ;
369 for ( var i = 0 ; i < selectionGeometry.height ; i++ )
370 {
371 var rowChildNodesCount = 0 ;
372 for ( var j = 0 ; j < selectionGeometry.width ; j++ )
373 {
374 var currentCell = tableMap[baseRowIdx + i][baseColIdx + j] ;
375 while ( currentCell.childNodes.length > 0 )
376 {
377 var node = currentCell.removeChild( currentCell.firstChild ) ;
378 if ( node.nodeType != 1
379 || ( node.getAttribute( 'type', 2 ) != '_moz' && node.getAttribute( '_moz_dirty' ) != null ) )
380 {
381 cellContents.appendChild( node ) ;
382 rowChildNodesCount++ ;
383 }
384 }
385 }
386 if ( rowChildNodesCount > 0 )
387 cellContents.appendChild( FCK.EditorDocument.createElement( 'br' ) ) ;
388 }
389
390 this._ReplaceCellsByMarker( tableMap, '_SelectedCells', refCell ) ;
391 this._UnmarkCells( cells, '_SelectedCells' ) ;
392 this._InstallTableMap( tableMap, refCell.parentNode.parentNode.parentNode ) ;
393 refCell.appendChild( cellContents ) ;
394
395 if ( FCKBrowserInfo.IsGeckoLike && ( ! refCell.firstChild ) )
396 FCKTools.AppendBogusBr( refCell ) ;
397
398 this._MoveCaretToCell( refCell, false ) ;
399 }
400
401 FCKTableHandler.MergeRight = function()
402 {
403 var target = this.GetMergeRightTarget() ;
404 if ( target == null )
405 return ;
406 var refCell = target.refCell ;
407 var tableMap = target.tableMap ;
408 var nextCell = target.nextCell ;
409
410 var cellContents = FCK.EditorDocument.createDocumentFragment() ;
411 while ( nextCell && nextCell.childNodes && nextCell.childNodes.length > 0 )
412 cellContents.appendChild( nextCell.removeChild( nextCell.firstChild ) ) ;
413
414 nextCell.parentNode.removeChild( nextCell ) ;
415 refCell.appendChild( cellContents ) ;
416 this._MarkCells( [nextCell], '_Replace' ) ;
417 this._ReplaceCellsByMarker( tableMap, '_Replace', refCell ) ;
418 this._InstallTableMap( tableMap, refCell.parentNode.parentNode.parentNode ) ;
419
420 this._MoveCaretToCell( refCell, false ) ;
421 }
422
423 FCKTableHandler.MergeDown = function()
424 {
425 var target = this.GetMergeDownTarget() ;
426 if ( target == null )
427 return ;
428 var refCell = target.refCell ;
429 var tableMap = target.tableMap ;
430 var nextCell = target.nextCell ;
431
432 var cellContents = FCKTools.GetElementDocument( refCell ).createDocumentFragment() ;
433 while ( nextCell && nextCell.childNodes && nextCell.childNodes.length > 0 )
434 cellContents.appendChild( nextCell.removeChild( nextCell.firstChild ) ) ;
435 if ( cellContents.firstChild )
436 cellContents.insertBefore( FCK.EditorDocument.createElement( 'br' ), cellContents.firstChild ) ;
437 refCell.appendChild( cellContents ) ;
438 this._MarkCells( [nextCell], '_Replace' ) ;
439 this._ReplaceCellsByMarker( tableMap, '_Replace', refCell ) ;
440 this._InstallTableMap( tableMap, refCell.parentNode.parentNode.parentNode ) ;
441
442 this._MoveCaretToCell( refCell, false ) ;
443 }
444
445 FCKTableHandler.HorizontalSplitCell = function()
446 {
447 var cells = FCKTableHandler.GetSelectedCells() ;
448 if ( cells.length != 1 )
449 return ;
450
451 var refCell = cells[0] ;
452 var tableMap = this._CreateTableMap( refCell ) ;
453 var rowIdx = refCell.parentNode.rowIndex ;
454 var colIdx = FCKTableHandler._GetCellIndexSpan( tableMap, rowIdx, refCell ) ;
455 var cellSpan = isNaN( refCell.colSpan ) ? 1 : refCell.colSpan ;
456
457 if ( cellSpan > 1 )
458 {
459 // Splitting a multi-column cell - original cell gets ceil(colSpan/2) columns,
460 // new cell gets floor(colSpan/2).
461 var newCellSpan = Math.ceil( cellSpan / 2 ) ;
462 var newCell = FCK.EditorDocument.createElement( refCell.nodeName ) ;
463 if ( FCKBrowserInfo.IsGeckoLike )
464 FCKTools.AppendBogusBr( newCell ) ;
465 var startIdx = colIdx + newCellSpan ;
466 var endIdx = colIdx + cellSpan ;
467 var rowSpan = isNaN( refCell.rowSpan ) ? 1 : refCell.rowSpan ;
468 for ( var r = rowIdx ; r < rowIdx + rowSpan ; r++ )
469 {
470 for ( var i = startIdx ; i < endIdx ; i++ )
471 tableMap[r][i] = newCell ;
472 }
473 }
474 else
475 {
476 // Splitting a single-column cell - add a new cell, and expand
477 // cells crossing the same column.
478 var newTableMap = [] ;
479 for ( var i = 0 ; i < tableMap.length ; i++ )
480 {
481 var newRow = tableMap[i].slice( 0, colIdx ) ;
482 if ( tableMap[i].length <= colIdx )
483 {
484 newTableMap.push( newRow ) ;
485 continue ;
486 }
487 if ( tableMap[i][colIdx] == refCell )
488 {
489 newRow.push( refCell ) ;
490 newRow.push( FCK.EditorDocument.createElement( refCell.nodeName ) ) ;
491 if ( FCKBrowserInfo.IsGeckoLike )
492 FCKTools.AppendBogusBr( newRow[newRow.length - 1] ) ;
493 }
494 else
495 {
496 newRow.push( tableMap[i][colIdx] ) ;
497 newRow.push( tableMap[i][colIdx] ) ;
498 }
499 for ( var j = colIdx + 1 ; j < tableMap[i].length ; j++ )
500 newRow.push( tableMap[i][j] ) ;
501 newTableMap.push( newRow ) ;
502 }
503 tableMap = newTableMap ;
504 }
505
506 this._InstallTableMap( tableMap, refCell.parentNode.parentNode.parentNode ) ;
507 }
508
509 FCKTableHandler.VerticalSplitCell = function()
510 {
511 var cells = FCKTableHandler.GetSelectedCells() ;
512 if ( cells.length != 1 )
513 return ;
514
515 var currentCell = cells[0] ;
516 var tableMap = this._CreateTableMap( currentCell ) ;
517 var currentRowIndex = currentCell.parentNode.rowIndex ;
518 var cellIndex = FCKTableHandler._GetCellIndexSpan( tableMap, currentRowIndex, currentCell ) ;
519 // Save current cell colSpan
520 var currentColSpan = isNaN( currentCell.colSpan ) ? 1 : currentCell.colSpan ;
521 var currentRowSpan = currentCell.rowSpan ;
522 if ( isNaN( currentRowSpan ) )
523 currentRowSpan = 1 ;
524
525 if ( currentRowSpan > 1 )
526 {
527 // 1. Set the current cell's rowSpan to 1.
528 currentCell.rowSpan = Math.ceil( currentRowSpan / 2 ) ;
529
530 // 2. Find the appropriate place to insert a new cell at the next row.
531 var newCellRowIndex = currentRowIndex + Math.ceil( currentRowSpan / 2 ) ;
532 var oRow = tableMap[newCellRowIndex] ;
533 var insertMarker = null ;
534 for ( var i = cellIndex+1 ; i < oRow.length ; i++ )
535 {
536 if ( oRow[i].parentNode.rowIndex == newCellRowIndex )
537 {
538 insertMarker = oRow[i] ;
539 break ;
540 }
541 }
542
543 // 3. Insert the new cell to the indicated place, with the appropriate rowSpan and colSpan, next row.
544 var newCell = FCK.EditorDocument.createElement( currentCell.nodeName ) ;
545 newCell.rowSpan = Math.floor( currentRowSpan / 2 ) ;
546 if ( currentColSpan > 1 )
547 newCell.colSpan = currentColSpan ;
548 if ( FCKBrowserInfo.IsGeckoLike )
549 FCKTools.AppendBogusBr( newCell ) ;
550 currentCell.parentNode.parentNode.parentNode.rows[newCellRowIndex].insertBefore( newCell, insertMarker ) ;
551 }
552 else
553 {
554 // 1. Insert a new row.
555 var newSectionRowIdx = currentCell.parentNode.sectionRowIndex + 1 ;
556 var newRow = FCK.EditorDocument.createElement( 'tr' ) ;
557 var tSection = currentCell.parentNode.parentNode ;
558 if ( tSection.rows.length > newSectionRowIdx )
559 tSection.insertBefore( newRow, tSection.rows[newSectionRowIdx] ) ;
560 else
561 tSection.appendChild( newRow ) ;
562
563 // 2. +1 to rowSpan for all cells crossing currentCell's row.
564 for ( var i = 0 ; i < tableMap[currentRowIndex].length ; )
565 {
566 var colSpan = tableMap[currentRowIndex][i].colSpan ;
567 if ( isNaN( colSpan ) || colSpan < 1 )
568 colSpan = 1 ;
569 if ( i == cellIndex )
570 {
571 i += colSpan ;
572 continue ;
573 }
574 var rowSpan = tableMap[currentRowIndex][i].rowSpan ;
575 if ( isNaN( rowSpan ) )
576 rowSpan = 1 ;
577 tableMap[currentRowIndex][i].rowSpan = rowSpan + 1 ;
578 i += colSpan ;
579 }
580
581 // 3. Insert a new cell to new row. Set colSpan on the new cell.
582 var newCell = FCK.EditorDocument.createElement( currentCell.nodeName ) ;
583 if ( currentColSpan > 1 )
584 newCell.colSpan = currentColSpan ;
585 if ( FCKBrowserInfo.IsGeckoLike )
586 FCKTools.AppendBogusBr( newCell ) ;
587 newRow.appendChild( newCell ) ;
588 }
589 }
590
591 // Get the cell index from a TableMap.
592 FCKTableHandler._GetCellIndexSpan = function( tableMap, rowIndex, cell )
593 {
594 if ( tableMap.length < rowIndex + 1 )
595 return null ;
596
597 var oRow = tableMap[ rowIndex ] ;
598
599 for ( var c = 0 ; c < oRow.length ; c++ )
600 {
601 if ( oRow[c] == cell )
602 return c ;
603 }
604
605 return null ;
606 }
607
608 // Get the cell location from a TableMap. Returns an array with an [x,y] location
609 FCKTableHandler._GetCellLocation = function( tableMap, cell )
610 {
611 for ( var i = 0 ; i < tableMap.length; i++ )
612 {
613 for ( var c = 0 ; c < tableMap[i].length ; c++ )
614 {
615 if ( tableMap[i][c] == cell ) return [i,c];
616 }
617 }
618 return null ;
619 }
620
621 // This function is quite hard to explain. It creates a matrix representing all cells in a table.
622 // The difference here is that the "spanned" cells (colSpan and rowSpan) are duplicated on the matrix
623 // cells that are "spanned". For example, a row with 3 cells where the second cell has colSpan=2 and rowSpan=3
624 // will produce a bi-dimensional matrix with the following values (representing the cells):
625 // Cell1, Cell2, Cell2, Cell3
626 // Cell4, Cell2, Cell2, Cell5
627 // Cell6, Cell2, Cell2, Cell7
628 FCKTableHandler._CreateTableMap = function( refCell )
629 {
630 var table = (refCell.nodeName == 'TABLE' ? refCell : refCell.parentNode.parentNode.parentNode ) ;
631
632 var aRows = table.rows ;
633
634 // Row and Column counters.
635 var r = -1 ;
636
637 var aMap = new Array() ;
638
639 for ( var i = 0 ; i < aRows.length ; i++ )
640 {
641 r++ ;
642 if ( !aMap[r] )
643 aMap[r] = new Array() ;
644
645 var c = -1 ;
646
647 for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
648 {
649 var oCell = aRows[i].cells[j] ;
650
651 c++ ;
652 while ( aMap[r][c] )
653 c++ ;
654
655 var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
656 var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
657
658 for ( var rs = 0 ; rs < iRowSpan ; rs++ )
659 {
660 if ( !aMap[r + rs] )
661 aMap[r + rs] = new Array() ;
662
663 for ( var cs = 0 ; cs < iColSpan ; cs++ )
664 {
665 aMap[r + rs][c + cs] = aRows[i].cells[j] ;
666 }
667 }
668
669 c += iColSpan - 1 ;
670 }
671 }
672 return aMap ;
673 }
674
675 // This function is the inverse of _CreateTableMap - it takes in a table map and converts it to an HTML table.
676 FCKTableHandler._InstallTableMap = function( tableMap, table )
677 {
678 // Workaround for #1917 : MSIE will always report a cell's rowSpan as 1 as long
679 // as the cell is not attached to a row. So we'll need an alternative attribute
680 // for storing the calculated rowSpan in IE.
681 var rowSpanAttr = FCKBrowserInfo.IsIE ? "_fckrowspan" : "rowSpan" ;
682
683 // Disconnect all the cells in tableMap from their parents, set all colSpan and rowSpan attributes to 1.
684 for ( var i = 0 ; i < tableMap.length ; i++ )
685 {
686 for ( var j = 0 ; j < tableMap[i].length ; j++ )
687 {
688 var cell = tableMap[i][j] ;
689 if ( cell.parentNode )
690 cell.parentNode.removeChild( cell ) ;
691 cell.colSpan = cell[rowSpanAttr] = 1 ;
692 }
693 }
694
695 // Scan by rows and set colSpan.
696 var maxCol = 0 ;
697 for ( var i = 0 ; i < tableMap.length ; i++ )
698 {
699 for ( var j = 0 ; j < tableMap[i].length ; j++ )
700 {
701 var cell = tableMap[i][j] ;
702 if ( ! cell)
703 continue ;
704 if ( j > maxCol )
705 maxCol = j ;
706 if ( cell._colScanned === true )
707 continue ;
708 if ( tableMap[i][j-1] == cell )
709 cell.colSpan++ ;
710 if ( tableMap[i][j+1] != cell )
711 cell._colScanned = true ;
712 }
713 }
714
715 // Scan by columns and set rowSpan.
716 for ( var i = 0 ; i <= maxCol ; i++ )
717 {
718 for ( var j = 0 ; j < tableMap.length ; j++ )
719 {
720 if ( ! tableMap[j] )
721 continue ;
722 var cell = tableMap[j][i] ;
723 if ( ! cell || cell._rowScanned === true )
724 continue ;
725 if ( tableMap[j-1] && tableMap[j-1][i] == cell )
726 cell[rowSpanAttr]++ ;
727 if ( ! tableMap[j+1] || tableMap[j+1][i] != cell )
728 cell._rowScanned = true ;
729 }
730 }
731
732 // Clear all temporary flags.
733 for ( var i = 0 ; i < tableMap.length ; i++ )
734 {
735 for ( var j = 0 ; j < tableMap[i].length ; j++)
736 {
737 var cell = tableMap[i][j] ;
738 FCKDomTools.ClearElementJSProperty(cell, '_colScanned' ) ;
739 FCKDomTools.ClearElementJSProperty(cell, '_rowScanned' ) ;
740 }
741 }
742
743 // Insert physical rows and columns to the table.
744 for ( var i = 0 ; i < tableMap.length ; i++ )
745 {
746 var rowObj = FCK.EditorDocument.createElement( 'tr' ) ;
747 for ( var j = 0 ; j < tableMap[i].length ; )
748 {
749 var cell = tableMap[i][j] ;
750 if ( tableMap[i-1] && tableMap[i-1][j] == cell )
751 {
752 j += cell.colSpan ;
753 continue ;
754 }
755 rowObj.appendChild( cell ) ;
756 if ( rowSpanAttr != 'rowSpan' )
757 {
758 cell.rowSpan = cell[rowSpanAttr] ;
759 cell.removeAttribute( rowSpanAttr ) ;
760 }
761 j += cell.colSpan ;
762 if ( cell.colSpan == 1 )
763 cell.removeAttribute( 'colspan' ) ;
764 if ( cell.rowSpan == 1 )
765 cell.removeAttribute( 'rowspan' ) ;
766 }
767 if ( FCKBrowserInfo.IsIE )
768 {
769 table.rows[i].replaceNode( rowObj ) ;
770 }
771 else
772 {
773 table.rows[i].innerHTML = '' ;
774 FCKDomTools.MoveChildren( rowObj, table.rows[i] ) ;
775 }
776 }
777 }
778
779 FCKTableHandler._MoveCaretToCell = function ( refCell, toStart )
780 {
781 var range = new FCKDomRange( FCK.EditorWindow ) ;
782 range.MoveToNodeContents( refCell ) ;
783 range.Collapse( toStart ) ;
784 range.Select() ;
785 }
786
787 FCKTableHandler.ClearRow = function( tr )
788 {
789 // Get the array of row's cells.
790 var aCells = tr.cells ;
791
792 // Replace the contents of each cell with "nothing".
793 for ( var i = 0 ; i < aCells.length ; i++ )
794 {
795 aCells[i].innerHTML = '' ;
796
797 if ( FCKBrowserInfo.IsGeckoLike )
798 FCKTools.AppendBogusBr( aCells[i] ) ;
799 }
800 }
801
802 FCKTableHandler.GetMergeRightTarget = function()
803 {
804 var cells = this.GetSelectedCells() ;
805 if ( cells.length != 1 )
806 return null ;
807
808 var refCell = cells[0] ;
809 var tableMap = this._CreateTableMap( refCell ) ;
810 var rowIdx = refCell.parentNode.rowIndex ;
811 var colIdx = this._GetCellIndexSpan( tableMap, rowIdx, refCell ) ;
812 var nextColIdx = colIdx + ( isNaN( refCell.colSpan ) ? 1 : refCell.colSpan ) ;
813 var nextCell = tableMap[rowIdx][nextColIdx] ;
814
815 if ( ! nextCell )
816 return null ;
817
818 // The two cells must have the same vertical geometry, otherwise merging does not make sense.
819 this._MarkCells( [refCell, nextCell], '_SizeTest' ) ;
820 var refGeometry = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_SizeTest' ) ;
821 var nextGeometry = this._GetMarkerGeometry( tableMap, rowIdx, nextColIdx, '_SizeTest' ) ;
822 this._UnmarkCells( [refCell, nextCell], '_SizeTest' ) ;
823
824 if ( refGeometry.height != nextGeometry.height || refGeometry.y != nextGeometry.y )
825 return null ;
826
827 return { 'refCell' : refCell, 'nextCell' : nextCell, 'tableMap' : tableMap } ;
828 }
829
830 FCKTableHandler.GetMergeDownTarget = function()
831 {
832 var cells = this.GetSelectedCells() ;
833 if ( cells.length != 1 )
834 return null ;
835
836 var refCell = cells[0] ;
837 var tableMap = this._CreateTableMap( refCell ) ;
838 var rowIdx = refCell.parentNode.rowIndex ;
839 var colIdx = this._GetCellIndexSpan( tableMap, rowIdx, refCell ) ;
840 var newRowIdx = rowIdx + ( isNaN( refCell.rowSpan ) ? 1 : refCell.rowSpan ) ;
841 if ( ! tableMap[newRowIdx] )
842 return null ;
843
844 var nextCell = tableMap[newRowIdx][colIdx] ;
845
846 if ( ! nextCell )
847 return null ;
848
849 // Check if the selected cells are both in the same table section (thead, tfoot or tbody).
850 if ( refCell.parentNode.parentNode != nextCell.parentNode.parentNode )
851 return null ;
852
853 // The two cells must have the same horizontal geometry, otherwise merging does not makes sense.
854 this._MarkCells( [refCell, nextCell], '_SizeTest' ) ;
855 var refGeometry = this._GetMarkerGeometry( tableMap, rowIdx, colIdx, '_SizeTest' ) ;
856 var nextGeometry = this._GetMarkerGeometry( tableMap, newRowIdx, colIdx, '_SizeTest' ) ;
857 this._UnmarkCells( [refCell, nextCell], '_SizeTest' ) ;
858
859 if ( refGeometry.width != nextGeometry.width || refGeometry.x != nextGeometry.x )
860 return null ;
861
862 return { 'refCell' : refCell, 'nextCell' : nextCell, 'tableMap' : tableMap } ;
863 }
864