Ticket #4574: 4574.patch

File 4574.patch, 14.9 KB (added by Garry Yao, 10 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 getChildren( element, tagNames )
     268        {
     269                var children = element.getChildren(),
     270                        count = children.count(),
     271                        child,
     272                        retval = [];
     273                for ( var i = count - 1; i >= 0; i-- )
     274                {
     275                        child = children.getItem( i );
     276                        // Remove tail empty <tr>.
     277                        if( child.is && child.is( 'tr' ) )
     278                        {
     279                                var tr = child;
     280                                if( !getChildren( tr, { td : 1, th : 1 } ).length
     281                                        && !tr.getNext( function( node ){ return node.is && node.is( 'tr' ) } ) )
     282                                {
     283                                        tr.remove();
     284                                        count++;
     285                                        continue;
     286                                }
     287                        }
     288                        if( child.is && ( child.getName() in tagNames ) )
     289                                retval.push( child );
     290                }
     291                return retval;
     292        }
     293
     294        function placeCursorInCell( cell )
     295        {
     296                var range = new CKEDITOR.dom.range( cell.getDocument() );
     297                range.selectNodeContents( cell );
     298                range.collapse();
     299                range.select( true );
     300        }
     301
     302        function buildTableMap( table )
     303        {
     304
     305                var aRows = table.$.rows ;
     306
     307                // Row and Column counters.
     308                var r = -1 ;
     309
     310                var aMap = [];
     311
     312                for ( var i = 0 ; i < aRows.length ; i++ )
     313                {
     314                        r++ ;
     315                        !aMap[r] && ( aMap[r] = [] );
     316
     317                        var c = -1 ;
     318
     319                        for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
     320                        {
     321                                var oCell = aRows[i].cells[j] ;
     322
     323                                c++ ;
     324                                while ( aMap[r][c] )
     325                                        c++ ;
     326
     327                                var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
     328                                var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
     329
     330                                for ( var rs = 0 ; rs < iRowSpan ; rs++ )
     331                                {
     332                                        if ( !aMap[r + rs] )
     333                                                aMap[r + rs] = new Array() ;
     334
     335                                        for ( var cs = 0 ; cs < iColSpan ; cs++ )
     336                                        {
     337                                                aMap[r + rs][c + cs] = aRows[i].cells[j] ;
     338                                        }
     339                                }
     340
     341                                c += iColSpan - 1 ;
     342                        }
     343                }
     344                return aMap ;
     345        }
     346
     347        function cellInRow( tableMap, rowIndex, cell )
     348        {
     349                var oRow = tableMap[ rowIndex ];
     350                if( typeof cell == 'undefined' )
     351                        return oRow;
     352
     353                for ( var c = 0 ; oRow && c < oRow.length ; c++ )
     354                {
     355                        if ( cell.is && oRow[c] == cell.$ )
     356                                return c;
     357                        else if( c == cell )
     358                                return new CKEDITOR.dom.element( oRow[ c ] );
     359                }
     360                return cell.is ? -1 : null;
     361        }
     362
     363        function cellInCol( tableMap, colIndex, cell )
     364        {
     365                var oCol = [];
     366                for ( var r = 0; r < tableMap.length; r++ )
     367                {
     368                        var row = tableMap[ r ];
     369                        if( typeof cell == 'undefined' )
     370                                oCol.push( row[ colIndex ] );
     371                        else if( cell.is && row[ colIndex ] == cell.$ )
     372                                return r;
     373                        else if( r == cell )
     374                                return new CKEDITOR.dom.element( row[ colIndex ] );
     375                }
     376               
     377                return ( typeof cell == 'undefined' )? oCol : cell.is ? -1 :  null;
     378        }
     379
     380        function mergeCells( selection, isDetect )
     381        {
     382                var cells = getSelectedCells( selection );
     383                if( cells.length < 2 )
     384                        return false;
     385
     386                var     cell,
     387                        firstCell = cells[ 0 ],
     388                        doc = firstCell.getDocument(),
     389                        map = buildTableMap( firstCell.getAscendant( 'table' ) ),
     390                        startRow = firstCell.getParent().$.rowIndex,
     391                        startColumn = cellInRow( map, startRow, firstCell ),
     392                        lastRowIndex = startRow,
     393                        totalRowSpan = 0,
     394                        totalColSpan = 0,
     395                        // Use a documentFragment as buffer when appending cell contents.
     396                        frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ),
     397                        dimension = 0;
     398
     399                for ( var i = 0; i < cells.length; i++ )
     400                {
     401                        cell = cells[ i ];
     402
     403                        var tr = cell.getParent(),
     404                                cellFirstChild = cell.getFirst(),
     405                                colSpan = cell.$.colSpan,
     406                                rowSpan = cell.$.rowSpan,
     407                                rowIndex = tr.$.rowIndex,
     408                                colIndex = cellInRow( map, rowIndex, cell );
     409
     410                        // Accumulated the actual places taken by all selected cells.
     411                        dimension += colSpan * rowSpan;
     412                        // Accumulated the maximum virtual spans from column and row.
     413                        totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ;
     414                        totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan );
     415
     416                        if ( !isDetect )
     417                        {
     418                                // Create line-break on new row.
     419                                if( rowIndex != lastRowIndex
     420                                        && cellFirstChild
     421                                        && !( cellFirstChild.isBlockBoundary
     422                                                  && cellFirstChild.isBlockBoundary( { br : 1 } ) ) )
     423                                        frag.append( new CKEDITOR.dom.element( 'br', doc ) );
     424
     425                                cell.moveChildren( frag );
     426                                i && cell.remove();
     427                        }
     428                        lastRowIndex = rowIndex;
     429                }
     430
     431                if ( !isDetect )
     432                {
     433                        frag.moveChildren( firstCell );
     434                        var resultTr = firstCell.getParent(),
     435                                        tbody = resultTr.getParent();
     436
     437                        // Apply/swip rowspan/colspan by futher checking rows and columns.
     438                        if ( getChildren(resultTr, { td : 1, th : 1 }).length > 1 )
     439                                firstCell.$.rowSpan = totalRowSpan;
     440                        else
     441                                firstCell.removeAttribute('rowSpan');
     442
     443                        if ( getChildren(tbody, { tr : 1 }).length > 1 )
     444                                firstCell.$.colSpan = totalColSpan;
     445                        else
     446                                firstCell.removeAttribute('colSpan');
     447
     448                        return firstCell;
     449                }
     450                // Be able to merge cells only if actual dimension of selected
     451                // cells equals to the caculated rectangle.
     452                else
     453                        return ( totalRowSpan * totalColSpan ) == dimension;
     454        }
     455
     456        function verticalSplitCell ( selection, isDetect )
     457        {
     458                var cells = getSelectedCells( selection );
     459                if( cells.length > 1 )
     460                        return false;
     461                else if( isDetect )
     462                        return true;
     463               
     464                var cell = cells[ 0 ],
     465                        tr = cell.getParent(),
     466                        table = tr.getAscendant( 'table' ),
     467                        map = buildTableMap( table ),
     468                        rowIndex = tr.$.rowIndex,
     469                        colIndex = cellInRow( map, rowIndex, cell ),
     470                        rowSpan = cell.$.rowSpan,
     471                        newCell,
     472                        newRowSpan,
     473                        newCellRowSpan,
     474                        newRowIndex;
     475               
     476                if( rowSpan > 1 )
     477                {
     478                        newRowSpan = Math.ceil( rowSpan / 2 );
     479                        newCellRowSpan = Math.floor( rowSpan / 2 );
     480                        newRowIndex = rowIndex + newRowSpan;
     481                        var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ),
     482                                newCellRow = cellInRow( map, newRowIndex ),
     483                                candidateCell;
     484                       
     485                        newCell = cell.clone();
     486
     487                        // Figure out where to insert the new cell by checking the vitual row.
     488                        for ( var c = 0; c < newCellRow.length; c++ )
     489                        {
     490                                candidateCell = newCellRow[ c ];
     491                                // Catch first cell actually following the column.
     492                                if( candidateCell.parentNode == newCellTr.$
     493                                        && c > colIndex )
     494                                {
     495                                        newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) );
     496                                        break;
     497                                }
     498                                else
     499                                        candidateCell = null;
     500                        }
     501
     502                        // The destination row is empty, append at will.
     503                        if( !candidateCell )
     504                                newCellTr.append( newCell, true );
     505                }
     506                else
     507                {
     508                        newCellRowSpan = newRowSpan = 1;
     509                        var newCellTr = tr.clone();
     510                        newCellTr.insertAfter( tr );
     511                        newCellTr.append( newCell = cell.clone() );
     512                        var cellsInSameRow = cellInRow( map, rowIndex );
     513                        for ( var i = 0; i < cellsInSameRow.length; i++ )
     514                                cellsInSameRow[ i ].rowSpan++;
     515                }
     516
     517                if( !CKEDITOR.env.ie )
     518                        newCell.appendBogus();
     519                cell.$.rowSpan = newRowSpan;
     520                newCell.$.rowSpan = newCellRowSpan;
     521                if( newRowSpan == 1 )
     522                        cell.removeAttribute( 'rowSpan' );
     523                if( newCellRowSpan == 1 )
     524                        newCell.removeAttribute( 'rowSpan' );
     525
     526                return newCell;
     527        }
     528
     529        function horizontalSplitCell( selection, isDetect )
     530        {
     531                var cells = getSelectedCells( selection );
     532                if( cells.length > 1 )
     533                        return false;
     534                else if( isDetect )
     535                        return true;
     536
     537                var cell = cells[ 0 ],
     538                        tr = cell.getParent(),
     539                        table = tr.getAscendant( 'table' ),
     540                        map = buildTableMap( table ),
     541                        rowIndex = tr.$.rowIndex,
     542                        colIndex = cellInRow( map, rowIndex, cell ),
     543                        colSpan = cell.$.colSpan,
     544                        newCell,
     545                        newColSpan,
     546                        newCellColSpan;
     547
     548                if( colSpan > 1 )
     549                {
     550                        newColSpan = Math.ceil( colSpan / 2 );
     551                        newCellColSpan = Math.floor( colSpan / 2 );
     552                }
     553                else
     554                {
     555                        newCellColSpan = newColSpan = 1;
     556                        var cellsInSameCol = cellInCol( map, colIndex );
     557                        for ( var i = 0; i < cellsInSameCol.length; i++ )
     558                                cellsInSameCol[ i ].colSpan++;
     559                }
     560                newCell = cell.clone();
     561                newCell.insertAfter( cell );
     562                if( !CKEDITOR.env.ie )
     563                        newCell.appendBogus();
     564
     565                cell.$.colSpan = newColSpan;
     566                newCell.$.colSpan = newCellColSpan;
     567                if( newColSpan == 1 )
     568                        cell.removeAttribute( 'colSpan' );
     569                if( newCellColSpan == 1 )
     570                        newCell.removeAttribute( 'colSpan' );
     571
     572                return newCell;
     573        }
    419574        // Context menu on table caption incorrect (#3834)
    420575        var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 };
    421576
     
    516671                                        }
    517672                                } );
    518673
     674                        editor.addCommand( 'cellMerge',
     675                                {
     676                                        exec : function( editor )
     677                                        {
     678                                                placeCursorInCell( mergeCells( editor.getSelection() ) );
     679                                        }
     680                                } );
     681
     682                        editor.addCommand( 'cellVerticalSplit',
     683                                {
     684                                        exec : function( editor )
     685                                        {
     686                                                placeCursorInCell( verticalSplitCell( editor.getSelection() ) );
     687                                        }
     688                                } );
     689                       
     690                        editor.addCommand( 'cellHorizontalSplit',
     691                                {
     692                                        exec : function( editor )
     693                                        {
     694                                                placeCursorInCell( horizontalSplitCell( editor.getSelection() ) );
     695                                        }
     696                                } );
     697                       
    519698                        editor.addCommand( 'cellInsertBefore',
    520699                                {
    521700                                        exec : function( editor )
     
    546725                                                        order : 1,
    547726                                                        getItems : function()
    548727                                                        {
    549                                                                 var cells = getSelectedCells( editor.getSelection() );
     728                                                                var selection = editor.getSelection(),
     729                                                                        cells = getSelectedCells( selection );
    550730                                                                return {
    551731                                                                        tablecell_insertBefore : CKEDITOR.TRISTATE_OFF,
    552732                                                                        tablecell_insertAfter : CKEDITOR.TRISTATE_OFF,
    553733                                                                        tablecell_delete : CKEDITOR.TRISTATE_OFF,
     734                                                                        tablecell_merge : mergeCells( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     735                                                                        tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     736                                                                        tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
    554737                                                                        tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
    555738                                                                };
    556739                                                        }
     
    580763                                                        order : 15
    581764                                                },
    582765
     766                                                tablecell_merge :
     767                                                {
     768                                                        label : lang.cell.merge,
     769                                                        group : 'tablecell',
     770                                                        command : 'cellMerge',
     771                                                        order : 16
     772                                                },
     773
     774                                                tablecell_split_horizontal :
     775                                                {
     776                                                        label : lang.cell.splitHorizontal,
     777                                                        group : 'tablecell',
     778                                                        command : 'cellHorizontalSplit',
     779                                                        order : 17
     780                                                },
     781
     782                                                tablecell_split_vertical :
     783                                                {
     784                                                        label : lang.cell.splitVertical,
     785                                                        group : 'tablecell',
     786                                                        command : 'cellVerticalSplit',
     787                                                        order : 18
     788                                                },
     789
    583790                                                tablecell_properties :
    584791                                                {
    585792                                                        label : lang.cell.title,
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy