Ticket #4574: 4574.patch

File 4574.patch, 14.9 KB (added by garry.yao, 3 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 – 2011 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy