Ticket #4574: 4574_2.patch

File 4574_2.patch, 14.5 KB (added by Garry Yao, 15 years ago)
  • _source/plugins/tabletools/plugin.js

     
    8686                return retval;
    8787        }
    8888
    89         function createTableMap( $refCell )
    90         {
    91                 var refCell = new CKEDITOR.dom.element( $refCell );
    92                 var $table = ( refCell.getName() == 'table' ? $refCell : refCell.getAscendant( 'table' ) ).$;
    93                 var $rows = $table.rows;
    94 
    95                 // Row and column counters.
    96                 var r = -1;
    97                 var map = [];
    98                 for ( var i = 0 ; i < $rows.length ; i++ )
    99                 {
    100                         r++;
    101                         if ( !map[ r ] )
    102                                 map[ r ] = [];
    103 
    104                         var c = -1;
    105 
    106                         for ( var j = 0 ; j < $rows[ i ].cells.length ; j++ )
    107                         {
    108                                 var $cell = $rows[ i ].cells[ j ];
    109 
    110                                 c++;
    111                                 while ( map[ r ][ c ] )
    112                                         c++;
    113 
    114                                 var colSpan = isNaN( $cell.colSpan ) ? 1 : $cell.colSpan;
    115                                 var rowSpan = isNaN( $cell.rowSpan ) ? 1 : $cell.rowSpan;
    116 
    117                                 for ( var rs = 0 ; rs < rowSpan ; rs++ )
    118                                 {
    119                                         if ( !map[ r + rs ] )
    120                                                 map[ r + rs ] = [];
    121 
    122                                         for ( var cs = 0 ; cs < colSpan ; cs++ )
    123                                                 map [ r + rs ][ c + cs ] = $rows[ i ].cells[ j ];
    124                                 }
    125 
    126                                 c += colSpan - 1;
    127                         }
    128                 }
    129 
    130                 return map;
    131         }
    132 
    133         function installTableMap( tableMap, $table )
    134         {
    135                 /*
    136                  * IE BUG: rowSpan is always 1 in IE if the cell isn't attached to a row. So
    137                  * store is separately in another attribute. (#1917)
    138                  */
    139                 var rowSpanAttr = CKEDITOR.env.ie ? '_cke_rowspan' : 'rowSpan';
    140 
    141                 /*
    142                  * Disconnect all the cells in tableMap from their parents, set all colSpan
    143                  * and rowSpan attributes to 1.
    144                  */
    145                 for ( var i = 0 ; i < tableMap.length ; i++ )
    146                 {
    147                         for ( var j = 0 ; j < tableMap[ i ].length ; j++ )
    148                         {
    149                                 var $cell = tableMap[ i ][ j ];
    150                                 if ( $cell.parentNode )
    151                                         $cell.parentNode.removeChild( $cell );
    152                                 $cell.colSpan = $cell[ rowSpanAttr ] = 1;
    153                         }
    154                 }
    155 
    156                 // Scan by rows and set colSpan.
    157                 var maxCol = 0;
    158                 for ( i = 0 ; i < tableMap.length ; i++ )
    159                 {
    160                         for ( j = 0 ; j < tableMap[ i ].length ; j++ )
    161                         {
    162                                 $cell = tableMap[ i ][ j ];
    163                                 if ( !$cell )
    164                                         continue;
    165                                 if ( j > maxCol )
    166                                         maxCol = j;
    167                                 if ( $cell[ '_cke_colScanned' ] )
    168                                         continue;
    169                                 if ( tableMap[ i ][ j - 1 ] == $cell )
    170                                         $cell.colSpan++;
    171                                 if ( tableMap[ i ][ j + 1 ] != $cell )
    172                                         $cell[ '_cke_colScanned' ] = 1;
    173                         }
    174                 }
    175 
    176                 // Scan by columns and set rowSpan.
    177                 for ( i = 0 ; i <= maxCol ; i++ )
    178                 {
    179                         for ( j = 0 ; j < tableMap.length ; j++ )
    180                         {
    181                                 if ( !tableMap[ j ] )
    182                                         continue;
    183                                 $cell = tableMap[ j ][ i ];
    184                                 if ( !$cell || $cell[ '_cke_rowScanned' ] )
    185                                         continue;
    186                                 if ( tableMap[ j - 1 ] && tableMap[ j - 1 ][ i ] == $cell )
    187                                         $cell[ rowSpanAttr ]++;
    188                                 if ( !tableMap[ j + 1 ] || tableMap[ j + 1 ][ i ] != $cell  )
    189                                         $cell[ '_cke_rowScanned' ] = 1;
    190                         }
    191                 }
    192 
    193                 // Clear all temporary flags.
    194                 for ( i = 0 ; i < tableMap.length ; i++ )
    195                 {
    196                         for ( j = 0 ; j < tableMap[ i ].length ; j++ )
    197                         {
    198                                 $cell = tableMap[ i ][ j ];
    199                                 removeRawAttribute( $cell, '_cke_colScanned' );
    200                                 removeRawAttribute( $cell, '_cke_rowScanned' );
    201                         }
    202                 }
    203 
    204                 // Insert physical rows and columns to table.
    205                 for ( i = 0 ; i < tableMap.length ; i++ )
    206                 {
    207                         var $row = $table.ownerDocument.createElement( 'tr' );
    208                         for ( j = 0 ; j < tableMap[ i ].length ; )
    209                         {
    210                                 $cell = tableMap[ i ][ j ];
    211                                 if ( tableMap[ i - 1 ] && tableMap[ i - 1 ][ j ] == $cell )
    212                                 {
    213                                         j += $cell.colSpan;
    214                                         continue;
    215                                 }
    216                                 $row.appendChild( $cell );
    217                                 if ( rowSpanAttr != 'rowSpan' )
    218                                 {
    219                                         $cell.rowSpan = $cell[ rowSpanAttr ];
    220                                         $cell.removeAttribute( rowSpanAttr );
    221                                 }
    222                                 j += $cell.colSpan;
    223                                 if ( $cell.colSpan == 1 )
    224                                         $cell.removeAttribute( 'colSpan' );
    225                                 if ( $cell.rowSpan == 1 )
    226                                         $cell.removeAttribute( 'rowSpan' );
    227                         }
    228 
    229                         if ( CKEDITOR.env.ie )
    230                                 $table.rows[ i ].replaceNode( $row );
    231                         else
    232                         {
    233                                 var dest = new CKEDITOR.dom.element( $table.rows[ i ] );
    234                                 var src = new CKEDITOR.dom.element( $row );
    235                                 dest.setHtml( '' );
    236                                 src.moveChildren( dest );
    237                         }
    238                 }
    239         }
    240 
    24189        function clearRow( $tr )
    24290        {
    24391                // Get the array of row's cells.
     
    416264                }
    417265        }
    418266
     267        function placeCursorInCell( cell )
     268        {
     269                var range = new CKEDITOR.dom.range( cell.getDocument() );
     270                range.selectNodeContents( cell );
     271                range.collapse();
     272                range.select( true );
     273        }
     274
     275        function buildTableMap( table )
     276        {
     277
     278                var aRows = table.$.rows ;
     279
     280                // Row and Column counters.
     281                var r = -1 ;
     282
     283                var aMap = [];
     284
     285                for ( var i = 0 ; i < aRows.length ; i++ )
     286                {
     287                        r++ ;
     288                        !aMap[r] && ( aMap[r] = [] );
     289
     290                        var c = -1 ;
     291
     292                        for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
     293                        {
     294                                var oCell = aRows[i].cells[j] ;
     295
     296                                c++ ;
     297                                while ( aMap[r][c] )
     298                                        c++ ;
     299
     300                                var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
     301                                var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
     302
     303                                for ( var rs = 0 ; rs < iRowSpan ; rs++ )
     304                                {
     305                                        if ( !aMap[r + rs] )
     306                                                aMap[r + rs] = new Array() ;
     307
     308                                        for ( var cs = 0 ; cs < iColSpan ; cs++ )
     309                                        {
     310                                                aMap[r + rs][c + cs] = aRows[i].cells[j] ;
     311                                        }
     312                                }
     313
     314                                c += iColSpan - 1 ;
     315                        }
     316                }
     317                return aMap ;
     318        }
     319
     320        function cellInRow( tableMap, rowIndex, cell )
     321        {
     322                var oRow = tableMap[ rowIndex ];
     323                if( typeof cell == 'undefined' )
     324                        return oRow;
     325
     326                for ( var c = 0 ; oRow && c < oRow.length ; c++ )
     327                {
     328                        if ( cell.is && oRow[c] == cell.$ )
     329                                return c;
     330                        else if( c == cell )
     331                                return new CKEDITOR.dom.element( oRow[ c ] );
     332                }
     333                return cell.is ? -1 : null;
     334        }
     335
     336        function cellInCol( tableMap, colIndex, cell )
     337        {
     338                var oCol = [];
     339                for ( var r = 0; r < tableMap.length; r++ )
     340                {
     341                        var row = tableMap[ r ];
     342                        if( typeof cell == 'undefined' )
     343                                oCol.push( row[ colIndex ] );
     344                        else if( cell.is && row[ colIndex ] == cell.$ )
     345                                return r;
     346                        else if( r == cell )
     347                                return new CKEDITOR.dom.element( row[ colIndex ] );
     348                }
     349               
     350                return ( typeof cell == 'undefined' )? oCol : cell.is ? -1 :  null;
     351        }
     352
     353        function mergeCells( selection, isDetect )
     354        {
     355                var cells = getSelectedCells( selection );
     356                if( cells.length < 2 )
     357                        return false;
     358
     359                var     cell,
     360                        firstCell = cells[ 0 ],
     361                        table = firstCell.getAscendant( 'table' ),
     362                        doc = firstCell.getDocument(),
     363                        map = buildTableMap( table ),
     364                        mapHeight = map.length,
     365                        mapWidth = map[ 0 ].length,
     366                        startRow = firstCell.getParent().$.rowIndex,
     367                        startColumn = cellInRow( map, startRow, firstCell ),
     368                        lastRowIndex = startRow,
     369                        totalRowSpan = 0,
     370                        totalColSpan = 0,
     371                        // Use a documentFragment as buffer when appending cell contents.
     372                        frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ),
     373                        dimension = 0;
     374
     375                for ( var i = 0; i < cells.length; i++ )
     376                {
     377                        cell = cells[ i ];
     378
     379                        var tr = cell.getParent(),
     380                                cellFirstChild = cell.getFirst(),
     381                                colSpan = cell.$.colSpan,
     382                                rowSpan = cell.$.rowSpan,
     383                                rowIndex = tr.$.rowIndex,
     384                                colIndex = cellInRow( map, rowIndex, cell );
     385
     386                        // Accumulated the actual places taken by all selected cells.
     387                        dimension += colSpan * rowSpan;
     388                        // Accumulated the maximum virtual spans from column and row.
     389                        totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ;
     390                        totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan );
     391
     392                        if ( !isDetect )
     393                        {
     394                                // Create line-break on new row.
     395                                if( rowIndex != lastRowIndex
     396                                        && cellFirstChild
     397                                        && !( cellFirstChild.isBlockBoundary
     398                                                  && cellFirstChild.isBlockBoundary( { br : 1 } ) ) )
     399                                        frag.append( new CKEDITOR.dom.element( 'br', doc ) );
     400
     401                                cell.moveChildren( frag );
     402                                i && cell.remove();
     403                        }
     404                        lastRowIndex = rowIndex;
     405                }
     406
     407                if ( !isDetect )
     408                {
     409                        frag.moveChildren( firstCell );
     410
     411                        if( totalColSpan >= mapWidth )
     412                                firstCell.removeAttribute( 'rowSpan' );
     413                        else
     414                                firstCell.$.rowSpan = totalRowSpan;
     415
     416                        if( totalRowSpan >= mapHeight )
     417                                firstCell.removeAttribute( 'colSpan' );
     418                        else
     419                                firstCell.$.colSpan = totalColSpan;
     420
     421                        // Swip empty <tr> left at the end of table due to the merging.
     422                        var trs = new CKEDITOR.dom.nodeList( table.$.rows ),
     423                                count = trs.count();
     424
     425                        for ( var i = count - 1; i >= 0; i-- )
     426                        {
     427                                var tailTr = trs.getItem( i );
     428                                if( !tailTr.$.cells.length )
     429                                {
     430                                        tailTr.remove();
     431                                        count++;
     432                                        continue;
     433                                }
     434                                else
     435                                        break;
     436                        }
     437
     438                        return firstCell;
     439                }
     440                // Be able to merge cells only if actual dimension of selected
     441                // cells equals to the caculated rectangle.
     442                else
     443                        return ( totalRowSpan * totalColSpan ) == dimension;
     444        }
     445
     446        function verticalSplitCell ( selection, isDetect )
     447        {
     448                var cells = getSelectedCells( selection );
     449                if( cells.length > 1 )
     450                        return false;
     451                else if( isDetect )
     452                        return true;
     453               
     454                var cell = cells[ 0 ],
     455                        tr = cell.getParent(),
     456                        table = tr.getAscendant( 'table' ),
     457                        map = buildTableMap( table ),
     458                        rowIndex = tr.$.rowIndex,
     459                        colIndex = cellInRow( map, rowIndex, cell ),
     460                        rowSpan = cell.$.rowSpan,
     461                        newCell,
     462                        newRowSpan,
     463                        newCellRowSpan,
     464                        newRowIndex;
     465               
     466                if( rowSpan > 1 )
     467                {
     468                        newRowSpan = Math.ceil( rowSpan / 2 );
     469                        newCellRowSpan = Math.floor( rowSpan / 2 );
     470                        newRowIndex = rowIndex + newRowSpan;
     471                        var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ),
     472                                newCellRow = cellInRow( map, newRowIndex ),
     473                                candidateCell;
     474                       
     475                        newCell = cell.clone();
     476
     477                        // Figure out where to insert the new cell by checking the vitual row.
     478                        for ( var c = 0; c < newCellRow.length; c++ )
     479                        {
     480                                candidateCell = newCellRow[ c ];
     481                                // Catch first cell actually following the column.
     482                                if( candidateCell.parentNode == newCellTr.$
     483                                        && c > colIndex )
     484                                {
     485                                        newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) );
     486                                        break;
     487                                }
     488                                else
     489                                        candidateCell = null;
     490                        }
     491
     492                        // The destination row is empty, append at will.
     493                        if( !candidateCell )
     494                                newCellTr.append( newCell, true );
     495                }
     496                else
     497                {
     498                        newCellRowSpan = newRowSpan = 1;
     499                        var newCellTr = tr.clone();
     500                        newCellTr.insertAfter( tr );
     501                        newCellTr.append( newCell = cell.clone() );
     502                        var cellsInSameRow = cellInRow( map, rowIndex );
     503                        for ( var i = 0; i < cellsInSameRow.length; i++ )
     504                                cellsInSameRow[ i ].rowSpan++;
     505                }
     506
     507                if( !CKEDITOR.env.ie )
     508                        newCell.appendBogus();
     509                cell.$.rowSpan = newRowSpan;
     510                newCell.$.rowSpan = newCellRowSpan;
     511                if( newRowSpan == 1 )
     512                        cell.removeAttribute( 'rowSpan' );
     513                if( newCellRowSpan == 1 )
     514                        newCell.removeAttribute( 'rowSpan' );
     515
     516                return newCell;
     517        }
     518
     519        function horizontalSplitCell( selection, isDetect )
     520        {
     521                var cells = getSelectedCells( selection );
     522                if( cells.length > 1 )
     523                        return false;
     524                else if( isDetect )
     525                        return true;
     526
     527                var cell = cells[ 0 ],
     528                        tr = cell.getParent(),
     529                        table = tr.getAscendant( 'table' ),
     530                        map = buildTableMap( table ),
     531                        rowIndex = tr.$.rowIndex,
     532                        colIndex = cellInRow( map, rowIndex, cell ),
     533                        colSpan = cell.$.colSpan,
     534                        newCell,
     535                        newColSpan,
     536                        newCellColSpan;
     537
     538                if( colSpan > 1 )
     539                {
     540                        newColSpan = Math.ceil( colSpan / 2 );
     541                        newCellColSpan = Math.floor( colSpan / 2 );
     542                }
     543                else
     544                {
     545                        newCellColSpan = newColSpan = 1;
     546                        var cellsInSameCol = cellInCol( map, colIndex );
     547                        for ( var i = 0; i < cellsInSameCol.length; i++ )
     548                                cellsInSameCol[ i ].colSpan++;
     549                }
     550                newCell = cell.clone();
     551                newCell.insertAfter( cell );
     552                if( !CKEDITOR.env.ie )
     553                        newCell.appendBogus();
     554
     555                cell.$.colSpan = newColSpan;
     556                newCell.$.colSpan = newCellColSpan;
     557                if( newColSpan == 1 )
     558                        cell.removeAttribute( 'colSpan' );
     559                if( newCellColSpan == 1 )
     560                        newCell.removeAttribute( 'colSpan' );
     561
     562                return newCell;
     563        }
    419564        // Context menu on table caption incorrect (#3834)
    420565        var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 };
    421566
     
    516661                                        }
    517662                                } );
    518663
     664                        editor.addCommand( 'cellMerge',
     665                                {
     666                                        exec : function( editor )
     667                                        {
     668                                                placeCursorInCell( mergeCells( editor.getSelection() ) );
     669                                        }
     670                                } );
     671
     672                        editor.addCommand( 'cellVerticalSplit',
     673                                {
     674                                        exec : function( editor )
     675                                        {
     676                                                placeCursorInCell( verticalSplitCell( editor.getSelection() ) );
     677                                        }
     678                                } );
     679                       
     680                        editor.addCommand( 'cellHorizontalSplit',
     681                                {
     682                                        exec : function( editor )
     683                                        {
     684                                                placeCursorInCell( horizontalSplitCell( editor.getSelection() ) );
     685                                        }
     686                                } );
     687                       
    519688                        editor.addCommand( 'cellInsertBefore',
    520689                                {
    521690                                        exec : function( editor )
     
    546715                                                        order : 1,
    547716                                                        getItems : function()
    548717                                                        {
    549                                                                 var cells = getSelectedCells( editor.getSelection() );
     718                                                                var selection = editor.getSelection(),
     719                                                                        cells = getSelectedCells( selection );
    550720                                                                return {
    551721                                                                        tablecell_insertBefore : CKEDITOR.TRISTATE_OFF,
    552722                                                                        tablecell_insertAfter : CKEDITOR.TRISTATE_OFF,
    553723                                                                        tablecell_delete : CKEDITOR.TRISTATE_OFF,
     724                                                                        tablecell_merge : mergeCells( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     725                                                                        tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     726                                                                        tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
    554727                                                                        tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
    555728                                                                };
    556729                                                        }
     
    580753                                                        order : 15
    581754                                                },
    582755
     756                                                tablecell_merge :
     757                                                {
     758                                                        label : lang.cell.merge,
     759                                                        group : 'tablecell',
     760                                                        command : 'cellMerge',
     761                                                        order : 16
     762                                                },
     763
     764                                                tablecell_split_horizontal :
     765                                                {
     766                                                        label : lang.cell.splitHorizontal,
     767                                                        group : 'tablecell',
     768                                                        command : 'cellHorizontalSplit',
     769                                                        order : 17
     770                                                },
     771
     772                                                tablecell_split_vertical :
     773                                                {
     774                                                        label : lang.cell.splitVertical,
     775                                                        group : 'tablecell',
     776                                                        command : 'cellVerticalSplit',
     777                                                        order : 18
     778                                                },
     779
    583780                                                tablecell_properties :
    584781                                                {
    585782                                                        label : lang.cell.title,
© 2003 – 2022, CKSource sp. z o.o. sp.k. All rights reserved. | Terms of use | Privacy policy