Ticket #4574: 4574_5.patch

File 4574_5.patch, 23.0 KB (added by Garry Yao, 14 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        // Remove filler at end and empty spaces around the cell content.
     268        function trimCell( cell )
     269        {
     270                var bogus = cell.getBogus();
     271                bogus && bogus.remove();
     272                cell.trim();
     273        }
     274
     275        function placeCursorInCell( cell, placeAtEnd )
     276        {
     277                var range = new CKEDITOR.dom.range( cell.getDocument() );
     278                if ( !range[ 'moveToElementEdit' + ( placeAtEnd ? 'End' : 'Start' ) ]( cell ) )
     279                {
     280                        range.selectNodeContents( cell );
     281                        range.collapse( placeAtEnd ? false : true );
     282                }
     283                range.select( true );
     284        }
     285
     286        function buildTableMap( table )
     287        {
     288
     289                var aRows = table.$.rows ;
     290
     291                // Row and Column counters.
     292                var r = -1 ;
     293
     294                var aMap = [];
     295
     296                for ( var i = 0 ; i < aRows.length ; i++ )
     297                {
     298                        r++ ;
     299                        !aMap[r] && ( aMap[r] = [] );
     300
     301                        var c = -1 ;
     302
     303                        for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
     304                        {
     305                                var oCell = aRows[i].cells[j] ;
     306
     307                                c++ ;
     308                                while ( aMap[r][c] )
     309                                        c++ ;
     310
     311                                var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
     312                                var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
     313
     314                                for ( var rs = 0 ; rs < iRowSpan ; rs++ )
     315                                {
     316                                        if ( !aMap[r + rs] )
     317                                                aMap[r + rs] = new Array() ;
     318
     319                                        for ( var cs = 0 ; cs < iColSpan ; cs++ )
     320                                        {
     321                                                aMap[r + rs][c + cs] = aRows[i].cells[j] ;
     322                                        }
     323                                }
     324
     325                                c += iColSpan - 1 ;
     326                        }
     327                }
     328                return aMap ;
     329        }
     330
     331        function cellInRow( tableMap, rowIndex, cell )
     332        {
     333                var oRow = tableMap[ rowIndex ];
     334                if( typeof cell == 'undefined' )
     335                        return oRow;
     336
     337                for ( var c = 0 ; oRow && c < oRow.length ; c++ )
     338                {
     339                        if ( cell.is && oRow[c] == cell.$ )
     340                                return c;
     341                        else if( c == cell )
     342                                return new CKEDITOR.dom.element( oRow[ c ] );
     343                }
     344                return cell.is ? -1 : null;
     345        }
     346
     347        function cellInCol( tableMap, colIndex, cell )
     348        {
     349                var oCol = [];
     350                for ( var r = 0; r < tableMap.length; r++ )
     351                {
     352                        var row = tableMap[ r ];
     353                        if( typeof cell == 'undefined' )
     354                                oCol.push( row[ colIndex ] );
     355                        else if( cell.is && row[ colIndex ] == cell.$ )
     356                                return r;
     357                        else if( r == cell )
     358                                return new CKEDITOR.dom.element( row[ colIndex ] );
     359                }
     360               
     361                return ( typeof cell == 'undefined' )? oCol : cell.is ? -1 :  null;
     362        }
     363
     364        function mergeCells( selection, mergeDirection, isDetect )
     365        {
     366                var cells = getSelectedCells( selection );
     367
     368                // Invalid merge request if:
     369                // 1. In batch mode despite that less than two selected.
     370                // 2. In solo mode while not exactly only one selected.   
     371                // 3. Cells distributed in different table groups (e.g. from both thead and tbody).
     372                if( ( mergeDirection ? cells.length != 1 : cells.length < 2 )
     373                         || selection.getCommonAncestor().is( 'table' ) )
     374                        return false;
     375
     376                var     cell,
     377                        firstCell = cells[ 0 ],
     378                        table = firstCell.getAscendant( 'table' ),
     379                        map = buildTableMap( table ),
     380                        mapHeight = map.length,
     381                        mapWidth = map[ 0 ].length,
     382                        startRow = firstCell.getParent().$.rowIndex,
     383                        startColumn = cellInRow( map, startRow, firstCell );
     384
     385                if( mergeDirection )
     386                {
     387                        var targetCell;
     388                        try
     389                        {
     390                                targetCell =
     391                                        map[ mergeDirection == 'up' ?
     392                                                        ( startRow - 1 ):
     393                                                        mergeDirection == 'down' ? ( startRow + 1 ) : startRow  ] [
     394                                                 mergeDirection == 'left' ?
     395                                                        ( startColumn - 1 ):
     396                                                 mergeDirection == 'right' ?  ( startColumn + 1 ) : startColumn ];
     397
     398                        }
     399                        catch( er )
     400                        {
     401                                return false;
     402                        }
     403
     404                        // 1. No cell could be merged.
     405                        // 2. Same cell actually.
     406                        if( !targetCell || firstCell.$ == targetCell  )
     407                                return false;
     408
     409                        // Sort in map order regardless of the DOM sequence.
     410                        cells[ ( mergeDirection == 'up' || mergeDirection == 'left' ) ?
     411                                 'unshift' : 'push' ]( new CKEDITOR.dom.element( targetCell ) );
     412                }
     413
     414                // Start from here are merging way ignorance (merge up/right, batch merge).
     415                var     doc = firstCell.getDocument(),
     416                        lastRowIndex = startRow,
     417                        totalRowSpan = 0,
     418                        totalColSpan = 0,
     419                        // Use a documentFragment as buffer when appending cell contents.
     420                        frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ),
     421                        dimension = 0;
     422
     423                for ( var i = 0; i < cells.length; i++ )
     424                {
     425                        cell = cells[ i ];
     426
     427                        var tr = cell.getParent(),
     428                                cellFirstChild = cell.getFirst(),
     429                                colSpan = cell.$.colSpan,
     430                                rowSpan = cell.$.rowSpan,
     431                                rowIndex = tr.$.rowIndex,
     432                                colIndex = cellInRow( map, rowIndex, cell );
     433
     434                        // Accumulated the actual places taken by all selected cells.
     435                        dimension += colSpan * rowSpan;
     436                        // Accumulated the maximum virtual spans from column and row.
     437                        totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ;
     438                        totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan );
     439
     440                        if ( !isDetect )
     441                        {
     442                                // Trim all cell fillers and check to remove empty cells.
     443                                if( trimCell( cell ), cell.getChildren().count() )
     444                                {
     445                                        // Merge vertically cells as two separated paragraphs.
     446                                        if( rowIndex != lastRowIndex
     447                                                && cellFirstChild
     448                                                && !( cellFirstChild.isBlockBoundary
     449                                                          && cellFirstChild.isBlockBoundary( { br : 1 } ) ) )
     450                                        {
     451                                                var last = frag.getLast( CKEDITOR.dom.walker.whitespaces( true ) );
     452                                                if( last && !( last.is && last.is( 'br' ) ) )
     453                                                        frag.append( new CKEDITOR.dom.element( 'br' ) );
     454                                        }
     455
     456                                        cell.moveChildren( frag );
     457                                }
     458                                i ? cell.remove() : cell.setHtml( '' );
     459                        }
     460                        lastRowIndex = rowIndex;
     461                }
     462
     463                if ( !isDetect )
     464                {
     465                        frag.moveChildren( firstCell );
     466
     467                        if( !CKEDITOR.env.ie )
     468                                firstCell.appendBogus();
     469
     470                        if( totalColSpan >= mapWidth )
     471                                firstCell.removeAttribute( 'rowSpan' );
     472                        else
     473                                firstCell.$.rowSpan = totalRowSpan;
     474
     475                        if( totalRowSpan >= mapHeight )
     476                                firstCell.removeAttribute( 'colSpan' );
     477                        else
     478                                firstCell.$.colSpan = totalColSpan;
     479
     480                        // Swip empty <tr> left at the end of table due to the merging.
     481                        var trs = new CKEDITOR.dom.nodeList( table.$.rows ),
     482                                count = trs.count();
     483
     484                        for ( var i = count - 1; i >= 0; i-- )
     485                        {
     486                                var tailTr = trs.getItem( i );
     487                                if( !tailTr.$.cells.length )
     488                                {
     489                                        tailTr.remove();
     490                                        count++;
     491                                        continue;
     492                                }
     493                        }
     494
     495                        return firstCell;
     496                }
     497                // Be able to merge cells only if actual dimension of selected
     498                // cells equals to the caculated rectangle.
     499                else
     500                        return ( totalRowSpan * totalColSpan ) == dimension;
     501        }
     502
     503        function verticalSplitCell ( selection, isDetect )
     504        {
     505                var cells = getSelectedCells( selection );
     506                if( cells.length > 1 )
     507                        return false;
     508                else if( isDetect )
     509                        return true;
     510               
     511                var cell = cells[ 0 ],
     512                        tr = cell.getParent(),
     513                        table = tr.getAscendant( 'table' ),
     514                        map = buildTableMap( table ),
     515                        rowIndex = tr.$.rowIndex,
     516                        colIndex = cellInRow( map, rowIndex, cell ),
     517                        rowSpan = cell.$.rowSpan,
     518                        newCell,
     519                        newRowSpan,
     520                        newCellRowSpan,
     521                        newRowIndex;
     522               
     523                if( rowSpan > 1 )
     524                {
     525                        newRowSpan = Math.ceil( rowSpan / 2 );
     526                        newCellRowSpan = Math.floor( rowSpan / 2 );
     527                        newRowIndex = rowIndex + newRowSpan;
     528                        var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ),
     529                                newCellRow = cellInRow( map, newRowIndex ),
     530                                candidateCell;
     531                       
     532                        newCell = cell.clone();
     533
     534                        // Figure out where to insert the new cell by checking the vitual row.
     535                        for ( var c = 0; c < newCellRow.length; c++ )
     536                        {
     537                                candidateCell = newCellRow[ c ];
     538                                // Catch first cell actually following the column.
     539                                if( candidateCell.parentNode == newCellTr.$
     540                                        && c > colIndex )
     541                                {
     542                                        newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) );
     543                                        break;
     544                                }
     545                                else
     546                                        candidateCell = null;
     547                        }
     548
     549                        // The destination row is empty, append at will.
     550                        if( !candidateCell )
     551                                newCellTr.append( newCell, true );
     552                }
     553                else
     554                {
     555                        newCellRowSpan = newRowSpan = 1;
     556                        var newCellTr = tr.clone();
     557                        newCellTr.insertAfter( tr );
     558                        newCellTr.append( newCell = cell.clone() );
     559                        var cellsInSameRow = cellInRow( map, rowIndex );
     560                        for ( var i = 0; i < cellsInSameRow.length; i++ )
     561                                cellsInSameRow[ i ].rowSpan++;
     562                }
     563
     564                if( !CKEDITOR.env.ie )
     565                        newCell.appendBogus();
     566
     567                cell.$.rowSpan = newRowSpan;
     568                newCell.$.rowSpan = newCellRowSpan;
     569                if( newRowSpan == 1 )
     570                        cell.removeAttribute( 'rowSpan' );
     571                if( newCellRowSpan == 1 )
     572                        newCell.removeAttribute( 'rowSpan' );
     573
     574                return newCell;
     575        }
     576
     577        function horizontalSplitCell( selection, isDetect )
     578        {
     579                var cells = getSelectedCells( selection );
     580                if( cells.length > 1 )
     581                        return false;
     582                else if( isDetect )
     583                        return true;
     584
     585                var cell = cells[ 0 ],
     586                        tr = cell.getParent(),
     587                        table = tr.getAscendant( 'table' ),
     588                        map = buildTableMap( table ),
     589                        rowIndex = tr.$.rowIndex,
     590                        colIndex = cellInRow( map, rowIndex, cell ),
     591                        colSpan = cell.$.colSpan,
     592                        newCell,
     593                        newColSpan,
     594                        newCellColSpan;
     595
     596                if( colSpan > 1 )
     597                {
     598                        newColSpan = Math.ceil( colSpan / 2 );
     599                        newCellColSpan = Math.floor( colSpan / 2 );
     600                }
     601                else
     602                {
     603                        newCellColSpan = newColSpan = 1;
     604                        var cellsInSameCol = cellInCol( map, colIndex );
     605                        for ( var i = 0; i < cellsInSameCol.length; i++ )
     606                                cellsInSameCol[ i ].colSpan++;
     607                }
     608                newCell = cell.clone();
     609                newCell.insertAfter( cell );
     610                if( !CKEDITOR.env.ie )
     611                        newCell.appendBogus();
     612
     613                cell.$.colSpan = newColSpan;
     614                newCell.$.colSpan = newCellColSpan;
     615                if( newColSpan == 1 )
     616                        cell.removeAttribute( 'colSpan' );
     617                if( newCellColSpan == 1 )
     618                        newCell.removeAttribute( 'colSpan' );
     619
     620                return newCell;
     621        }
    419622        // Context menu on table caption incorrect (#3834)
    420623        var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 };
    421624
     
    516719                                        }
    517720                                } );
    518721
     722                        editor.addCommand( 'cellMerge',
     723                                {
     724                                        exec : function( editor )
     725                                        {
     726                                                placeCursorInCell( mergeCells( editor.getSelection() ), true );
     727                                        }
     728                                } );
     729
     730                        editor.addCommand( 'cellMergeRight',
     731                                {
     732                                        exec : function( editor )
     733                                        {
     734                                                placeCursorInCell( mergeCells( editor.getSelection(), 'right' ), true );
     735                                        }
     736                                } );
     737
     738                        editor.addCommand( 'cellMergeDown',
     739                                {
     740                                        exec : function( editor )
     741                                        {
     742                                                placeCursorInCell( mergeCells( editor.getSelection(), 'down' ), true );
     743                                        }
     744                                } );
     745
     746                        editor.addCommand( 'cellVerticalSplit',
     747                                {
     748                                        exec : function( editor )
     749                                        {
     750                                                placeCursorInCell( verticalSplitCell( editor.getSelection() ) );
     751                                        }
     752                                } );
     753                       
     754                        editor.addCommand( 'cellHorizontalSplit',
     755                                {
     756                                        exec : function( editor )
     757                                        {
     758                                                placeCursorInCell( horizontalSplitCell( editor.getSelection() ) );
     759                                        }
     760                                } );
     761                       
    519762                        editor.addCommand( 'cellInsertBefore',
    520763                                {
    521764                                        exec : function( editor )
     
    546789                                                        order : 1,
    547790                                                        getItems : function()
    548791                                                        {
    549                                                                 var cells = getSelectedCells( editor.getSelection() );
     792                                                                var selection = editor.getSelection(),
     793                                                                        cells = getSelectedCells( selection );
    550794                                                                return {
    551795                                                                        tablecell_insertBefore : CKEDITOR.TRISTATE_OFF,
    552796                                                                        tablecell_insertAfter : CKEDITOR.TRISTATE_OFF,
    553797                                                                        tablecell_delete : CKEDITOR.TRISTATE_OFF,
     798                                                                        tablecell_merge : mergeCells( selection, null, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     799                                                                        tablecell_merge_right : mergeCells( selection, 'right', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     800                                                                        tablecell_merge_down : mergeCells( selection, 'down', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     801                                                                        tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
     802                                                                        tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
    554803                                                                        tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
    555804                                                                };
    556805                                                        }
     
    580829                                                        order : 15
    581830                                                },
    582831
     832                                                tablecell_merge :
     833                                                {
     834                                                        label : lang.cell.merge,
     835                                                        group : 'tablecell',
     836                                                        command : 'cellMerge',
     837                                                        order : 16
     838                                                },
     839
     840                                                tablecell_merge_right :
     841                                                {
     842                                                        label : lang.cell.mergeRight,
     843                                                        group : 'tablecell',
     844                                                        command : 'cellMergeRight',
     845                                                        order : 17
     846                                                },
     847
     848                                                tablecell_merge_down :
     849                                                {
     850                                                        label : lang.cell.mergeDown,
     851                                                        group : 'tablecell',
     852                                                        command : 'cellMergeDown',
     853                                                        order : 18
     854                                                },
     855
     856                                                tablecell_split_horizontal :
     857                                                {
     858                                                        label : lang.cell.splitHorizontal,
     859                                                        group : 'tablecell',
     860                                                        command : 'cellHorizontalSplit',
     861                                                        order : 19
     862                                                },
     863
     864                                                tablecell_split_vertical :
     865                                                {
     866                                                        label : lang.cell.splitVertical,
     867                                                        group : 'tablecell',
     868                                                        command : 'cellVerticalSplit',
     869                                                        order : 20
     870                                                },
     871
    583872                                                tablecell_properties :
    584873                                                {
    585874                                                        label : lang.cell.title,
    586875                                                        group : 'tablecellproperties',
    587876                                                        command : 'cellProperties',
    588                                                         order : 20
     877                                                        order : 21
    589878                                                },
    590879
    591880                                                tablerow :
  • _source/plugins/selection/plugin.js

     
    949949                        }
    950950                        this.selectRanges( ranges );
    951951                        return this;
    952                 }
     952                },
     953               
     954                getCommonAncestor : function()
     955                {
     956                        var ranges = this.getRanges(),
     957                                startNode = ranges[ 0 ].startContainer,
     958                                endNode = ranges[ ranges.length - 1 ].endContainer;
     959                        return startNode.getCommonAncestor( endNode );
     960                 }
    953961        };
    954962})();
    955963( function()
  • _source/core/dom/range.js

     
    15781578
    15791579                        return walker.checkForward();
    15801580                },
    1581 
    15821581                /**
    1583                  * Moves the range boundaries to the first editing point inside an
     1582                 * Moves the range boundaries to the first/end editing point inside an
    15841583                 * element. For example, in an element tree like
    15851584                 * "&lt;p&gt;&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt; Text&lt;/p&gt;", the start editing point is
    15861585                 * "&lt;p&gt;&lt;b&gt;&lt;i&gt;^&lt;/i&gt;&lt;/b&gt; Text&lt;/p&gt;" (inside &lt;i&gt;).
    15871586                 * @param {CKEDITOR.dom.element} targetElement The element into which
    1588                  *              look for the editing spot.
     1587                 *              look for the editing spot, it should be guaranteed to contains at least one editable position.
     1588                        @param {Boolean} isMoveToEnd Whether move to the end editable position.
    15891589                 */
    1590                 moveToElementEditStart : function( targetElement )
     1590                moveToElementEditablePosition: function( targetElement, isMoveToEnd )
    15911591                {
    15921592                        var editableElement;
    15931593
     
    15981598                                else if ( editableElement )
    15991599                                        break ;         // If we already found an editable element, stop the loop.
    16001600
    1601                                 targetElement = targetElement.getFirst();
     1601                                targetElement = targetElement[ isMoveToEnd? 'getLast' : 'getFirst' ].call( targetElement );
    16021602                        }
    16031603
    16041604                        if ( editableElement )
    16051605                        {
    1606                                 this.moveToPosition(editableElement, CKEDITOR.POSITION_AFTER_START);
     1606                                // Make sure carot anchor before filler when moving to editable end.
     1607                                var filler = editableElement.isBlockBoundary() && editableElement.getBogus();
     1608                                this.moveToPosition( isMoveToEnd && filler ? filler : editableElement,
     1609                                                isMoveToEnd && filler ? CKEDITOR.POSITION_BEFORE_START :
     1610                                                        isMoveToEnd ? CKEDITOR.POSITION_BEFORE_END :
     1611                                                        CKEDITOR.POSITION_AFTER_START );
    16071612                                return true;
    16081613                        }
    16091614                        else
     
    16111616                },
    16121617
    16131618                /**
    1614                  * Get the single node enclosed within the range if there's one.
     1619                 *@see {CKEDITOR.dom.range.moveToElementEditablePosition}
    16151620                 */
    1616                 getEnclosedNode : function()
     1621                moveToElementEditStart : function( target )
    16171622                {
    1618                         var walkerRange = this.clone(),
    1619                                 walker = new CKEDITOR.dom.walker( walkerRange ),
    1620                                 isNotBookmarks = CKEDITOR.dom.walker.bookmark( true ),
    1621                                 isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
    1622                                 evaluator = function( node )
    1623                                 {
    1624                                         return isNotWhitespaces( node ) && isNotBookmarks( node );
    1625                                 };
    1626                         walkerRange.evaluator = evaluator;
    1627                         var node = walker.next();
    1628                         walker.reset();
    1629                         return node && node.equals( walker.previous() ) ? node : null;
     1623                        return this.moveToElementEditablePosition( target );
     1624                },
     1625
     1626                /**
     1627                 *@see {CKEDITOR.dom.range.moveToElementEditablePosition}
     1628                 */
     1629                moveToElementEditEnd : function( target )
     1630                {
     1631                        return this.moveToElementEditablePosition( target, true );
    16301632                },
    16311633
    16321634                getTouchedStartNode : function()
  • _source/core/dom/walker.js

     
    428428                };
    429429        };
    430430
     431        var tailNbspRegex = /^[\t\r\n ]*(?:&nbsp;|\xa0)$/,
     432                isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
     433                isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ),
     434                fillerEvaluator = function( element )
     435                {
     436                        return isNotBookmark( element ) && isNotWhitespaces( element );
     437                };
     438
     439        // Check if there's a filler node at the end of an element, and return it.
     440        CKEDITOR.dom.element.prototype.getBogus = function ()
     441        {
     442                var tail = this.getLast( fillerEvaluator );
     443                if ( tail && ( !CKEDITOR.env.ie ? tail.is && tail.is( 'br' )
     444                                : tail.getText && tailNbspRegex.test( tail.getText() ) ) )
     445                {
     446                        return tail;
     447                }
     448                return false;
     449        };
     450
    431451})();
  • _source/plugins/contextmenu/plugin.js

     
    8383                        var selection = this.editor.getSelection(),
    8484                                element = selection && selection.getStartElement();
    8585
    86                         // Lock the selection in IE, so it can be restored when closing the
    87                         // menu.
    88                         if ( CKEDITOR.env.ie )
    89                                 selection.lock();
    90 
    9186                        menu.onHide = CKEDITOR.tools.bind( function()
    9287                                {
    9388                                        menu.onHide = null;
     
    179174                                } );
    180175                        }
    181176
     177                        // Certain forms of IE selection changes on 'contextmenu' event,
     178                        // lock the selection before that.(#4041)
     179                        if ( CKEDITOR.env.ie )
     180                        {
     181                                element.on( 'mousedown', function( event )
     182                                {
     183                                        if ( event.data.$.button == 2 )
     184                                                this.editor.getSelection().lock();
     185                                }, this );
     186                        }
     187
    182188                        element.on( 'contextmenu', function( event )
    183189                                {
    184190                                        var domEvent = event.data;
© 2003 – 2022, CKSource sp. z o.o. sp.k. All rights reserved. | Terms of use | Privacy policy