Ticket #6017: 6017.patch

File 6017.patch, 18.8 KB (added by Frederico Caldeira Knabben, 10 years ago)
  • _source/plugins/tableresize/plugin.js

     
    33For licensing, see LICENSE.html or http://ckeditor.com/license
    44*/
    55
    6 ( function()
     6(function()
    77{
    8         var pxUnit = CKEDITOR.tools.cssLength;
     8        var pxUnit = CKEDITOR.tools.cssLength,
     9                needsIEHacks = CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks || CKEDITOR.env.version < 7 );
    910
    10         function getCellWidth( cell )
     11        function getWidth( el )
    1112        {
    12                 return CKEDITOR.env.ie ? cell.$.clientWidth : parseInt( cell.getComputedStyle( 'width' ), 10 );
     13                return CKEDITOR.env.ie ? el.$.clientWidth : parseInt( el.getComputedStyle( 'width' ), 10 );
    1314        }
    1415
    15         function getCellBorderWidth( cell, side )
     16        function getBorderWidth( element, side )
    1617        {
    17                 var computed = cell.getComputedStyle( 'border-' + side + '-width' ),
     18                var computed = element.getComputedStyle( 'border-' + side + '-width' ),
    1819                        borderMap =
    1920                        {
    20                                 thin: '2px',
    21                                 medium: '4px',
    22                                 thick: '6px'
     21                                thin: '0px',
     22                                medium: '1px',
     23                                thick: '2px'
    2324                        };
    2425
    2526                if ( computed.indexOf( 'px' ) < 0 )
    2627                {
    2728                        // look up keywords
    28                         if ( computed in borderMap && cell.getComputedStyle( 'border-style' ) != 'none' )
     29                        if ( computed in borderMap && element.getComputedStyle( 'border-style' ) != 'none' )
    2930                                computed = borderMap[ computed ];
    3031                        else
    3132                                computed = 0;
    3233                }
    3334
    34                 return computed;
     35                return parseInt( computed, 10 );
    3536        }
    36 
    37         function buildTableColumnPillars( tableElement )
     37       
     38        // Gets the table row that contains the most columns.
     39        function getMasterPillarRow( table )
    3840        {
    39                 var table = tableElement.$;
     41                var $rows = table.$.rows,
     42                        maxCells = 0, cellsCount,
     43                        $elected, $tr;
    4044
    41                 // Elect the table row that contains the most columns.
    42                 var maxCells = 0, elected;
    43                 for ( var i = 0, rowCount = table.rows.length ; i < rowCount; i++ )
     45                for ( var i = 0, len = $rows.length ; i < len; i++ )
    4446                {
    45                         var tr = table.rows[ i ], cellsCount = tr.cells.length;
     47                        $tr = $rows[ i ];
     48                        cellsCount = $tr.cells.length;
    4649
    4750                        if ( cellsCount > maxCells )
    4851                        {
    4952                                maxCells = cellsCount;
    50                                 elected = tr;
     53                                $elected = $tr;
    5154                        }
    5255                }
     56               
     57                return $elected;
     58        }
    5359
    54                 tr = elected;
    55                 var columnIndex = -1, pillars = [];
    56                 var tbody = new CKEDITOR.dom.element( table.tBodies[ 0 ] ),
    57                                 tbodyPosition = tbody.getDocumentPosition();
    58                 for ( var j = 0, colCount = tr.cells.length ; j < colCount ; j++ )
     60        function buildTableColumnPillars( table )
     61        {
     62                var pillars = [],
     63                        pillarIndex = -1,
     64                        rtl = ( table.getComputedStyle( 'direction' ) == 'rtl' );
     65               
     66                // Get the raw row element that cointains the most columns.
     67                var $tr = getMasterPillarRow( table );
     68
     69                // Get the tbody element and position, which will be used to set the
     70                // top and bottom boundaries.
     71                var tbody = new CKEDITOR.dom.element( table.$.tBodies[ 0 ] ),
     72                        tbodyPosition = tbody.getDocumentPosition();
     73
     74                // Loop thorugh all cells, building pillars after each one of them.
     75                for ( var i = 0, len = $tr.cells.length ; i < len ; i++ )
    5976                {
    60                         var td = new CKEDITOR.dom.element( tr.cells[ j ] ),
    61                                         nextTd = tr.cells[ j + 1 ] && new CKEDITOR.dom.element( tr.cells[ j + 1 ] );
     77                        // Both the current cell and the successive one will be used in the
     78                        // pillar size calculation.
     79                        var td = new CKEDITOR.dom.element( $tr.cells[ i ] ),
     80                                nextTd = $tr.cells[ i + 1 ] && new CKEDITOR.dom.element( $tr.cells[ i + 1 ] );
    6281
    63                         columnIndex += td.$.colSpan || 1;
    64                         var cellPosition =  td.getDocumentPosition(),
    65                                         rangeLeft = cellPosition.x + td.$.offsetWidth - parseInt( getCellBorderWidth ( td, 'right' ), 10 );
     82                        pillarIndex += td.$.colSpan || 1;
     83                       
     84                        // Calculate the pillar boundary positions.
     85                        var pillarLeft, pillarRight, pillarWidth, pillarPadding;
    6686
     87                        var x = td.getDocumentPosition().x;
     88
     89                        // Calculate positions based on the current cell.
     90                        rtl ?
     91                                pillarRight = x + getBorderWidth( td, 'left' ) :
     92                                pillarLeft  = x + td.$.offsetWidth - getBorderWidth( td, 'right' );
     93
     94                        // Calculate positions based on the next cell, if available.                   
    6795                        if ( nextTd )
    6896                        {
    69                                 cellPosition =  nextTd.getDocumentPosition();
    70                                 var rangeRight = cellPosition.x + parseInt( getCellBorderWidth( nextTd, 'left' ), 10 );
     97                                x =  nextTd.getDocumentPosition().x;
    7198
    72                                 // Compsensate for too "slim" line between columns, make the pillar shown easier.
    73                                 if ( rangeRight - rangeLeft < 8 )
    74                                 {
    75                                         rangeLeft-= 4;
    76                                         rangeRight += 4;
    77                                 }
     99                                rtl ?
     100                                        pillarLeft      = x + nextTd.$.offsetWidth - getBorderWidth( nextTd, 'right' ) :
     101                                        pillarRight     = x + getBorderWidth( nextTd, 'left' );
     102                        }
     103                        // Otherwise calculate positions based on the table (for last cell).
     104                        else
     105                        {
     106                                x =  table.getDocumentPosition().x;
    78107
    79                                 var columnWidth = rangeRight - rangeLeft;
     108                                rtl ?
     109                                        pillarLeft      = x :
     110                                        pillarRight     = x + table.$.offsetWidth;
     111                        }
    80112
    81                                 // The pillar should reflects exactly the shape of the hovered column border line.
    82                                 pillars.push({ table : tableElement, index : columnIndex, x : rangeLeft, y : tbodyPosition.y , width : columnWidth, height: tbody.$.offsetHeight });
    83                         }
     113                        pillarWidth = Math.max( pillarRight - pillarLeft, 3 );
     114                       
     115                        // Make the pillar touch area at least 14 pixels wide, for easy to use.
     116                        pillarPadding = Math.max( Math.round( 7 - ( pillarWidth / 2 ) ), 0 );
     117
     118                        // The pillar should reflects exactly the shape of the hovered
     119                        // column border line.
     120                        pillars.push( {
     121                                table : table,
     122                                index : pillarIndex,
     123                                x : pillarLeft,
     124                                y : tbodyPosition.y,
     125                                width : pillarWidth,
     126                                height: tbody.$.offsetHeight,
     127                                padding : pillarPadding,
     128                                rtl : rtl } );
    84129                }
    85130
    86131                return pillars;
    87132        }
    88133
    89         function getPillarAtPosition( pillars, position )
     134        function getPillarAtPosition( pillars, positionX )
    90135        {
    91                 for ( var i = 0, length = pillars.length; i < length; i++ )
     136                for ( var i = 0, len = pillars.length ; i < len ; i++ )
    92137                {
    93                         if ( position.x > pillars[ i ].x && position.x - pillars[ i ].x < pillars[ i ].width )
    94                                 return pillars[ i ];
     138                        var pillar = pillars[ i ],
     139                                pad = pillar.padding;
     140                       
     141                        if ( positionX >= pillar.x - pad && positionX <= ( pillar.x + pillar.width + pad ) )
     142                                return pillar;
    95143                }
     144
    96145                return null;
    97146        }
    98147
    99148        function cancel( evt )
    100149        {
    101                 evt.data.preventDefault();
     150                ( evt.data || evt ).preventDefault();
    102151        }
    103152
    104153        function columnResizer( editor )
    105154        {
    106                 var pillar, document, resizer, startOffset, currentShift;
     155                var pillar,
     156                        document,
     157                        resizer,
     158                        isResizing,
     159                        startOffset,
     160                        currentShift;
    107161
    108162                var leftSideCells, rightSideCells, leftShiftBoundary, rightShiftBoundary;
    109163
    110164                function detach()
    111165                {
    112166                        pillar = null;
    113                         currentShift = null;
    114                         document.removeListener( 'mousemove', onMouseMove );
     167                        currentShift = 0;
     168                        isResizing = 0;
     169
    115170                        document.removeListener( 'mouseup', onMouseUp );
    116171                        resizer.removeListener( 'mousedown', onMouseDown );
    117                         resizer.removeListener( 'mouseout', onMouseOut );
    118                         resizer.hide();
     172                        resizer.removeListener( 'mousemove', onMouseMove );
     173
     174                        document.getBody().setStyle( 'cursor', 'auto' );
     175
     176                        // Hide the resizer (remove it on IE7 - #5890).
     177                        needsIEHacks ? resizer.remove() : resizer.hide();                       
    119178                }
    120179
    121180                function resizeStart()
    122181                {
    123182                        // Before starting to resize, figure out which cells to change
    124183                        // and the boundaries of this resizing shift.
     184
    125185                        var columnIndex = pillar.index,
    126                                         map = CKEDITOR.tools.buildTableMap( pillar.table ),
    127                                         leftColumnCells = [],
    128                                         rightColumnCells= [],
    129                                         leftMinSize =  Number.MAX_VALUE,
    130                                         rightMinSize = leftMinSize;
     186                                map = CKEDITOR.tools.buildTableMap( pillar.table ),
     187                                leftColumnCells = [],
     188                                rightColumnCells = [],
     189                                leftMinSize = Number.MAX_VALUE,
     190                                rightMinSize = leftMinSize,
     191                                rtl = pillar.rtl;
    131192
    132                         for ( var i = 0, rowCount = map.length; i < rowCount; i++ )
     193                        for ( var i = 0, len = map.length ; i < len ; i++ )
    133194                        {
    134                                 var row = map[ i ],
    135                                                 leftCell = new CKEDITOR.dom.element( row[ columnIndex ] ),
    136                                                 rightCell = new CKEDITOR.dom.element( row[ columnIndex + 1 ] );
     195                                var row                 = map[ i ],
     196                                        leftCell        = row[ columnIndex + ( rtl ? 1 : 0 ) ],
     197                                        rightCell       = row[ columnIndex + ( rtl ? 0 : 1 ) ];
     198                                       
     199                                leftCell        = leftCell && new CKEDITOR.dom.element( leftCell );
     200                                rightCell       = rightCell && new CKEDITOR.dom.element( rightCell );
    137201
    138                                 if ( !leftCell.equals( rightCell ) )
     202                                if ( !leftCell || !rightCell || !leftCell.equals( rightCell ) )
    139203                                {
    140                                         leftMinSize = Math.min( leftMinSize, getCellWidth( leftCell ) );
    141                                         rightMinSize = Math.min( rightMinSize, getCellWidth( rightCell ) );
     204                                        leftCell && ( leftMinSize = Math.min( leftMinSize, getWidth( leftCell ) ) );
     205                                        rightCell && ( rightMinSize = Math.min( rightMinSize, getWidth( rightCell ) ) );
     206                                       
    142207                                        leftColumnCells.push( leftCell );
    143208                                        rightColumnCells.push( rightCell );
    144209                                }
    145210                        }
    146211
     212                        // Cache the list of cells to be resized.
    147213                        leftSideCells = leftColumnCells;
    148214                        rightSideCells = rightColumnCells;
     215
     216                        // Cache the resize limit boundaries.
    149217                        leftShiftBoundary =  pillar.x - leftMinSize;
    150218                        rightShiftBoundary = pillar.x + rightMinSize;
    151219
    152220                        resizer.setOpacity( 0.5 );
    153221                        startOffset = parseInt( resizer.getStyle( 'left' ), 10 );
    154222                        currentShift = 0;
    155                         document.on( 'mousemove', onMouseMove, this );
    156                         // Prevent the native drag behavior otherwise the above 'mousemove' won't fired.
    157                         document.on( 'dragstart', cancel, this );
     223                        isResizing = 1;
     224                       
     225                        resizer.on( 'mousemove', onMouseMove );
     226                       
     227                        // Prevent the native drag behavior otherwise 'mousemove' won't fire.
     228                        document.on( 'dragstart', cancel );
    158229                }
    159230
    160231                function resizeEnd()
    161232                {
     233                        isResizing = 0;
     234
    162235                        resizer.setOpacity( 0 );
     236                       
    163237                        currentShift && resizeColumn();
    164238
    165239                        var table = pillar.table;
    166240                        setTimeout( function () { table.removeCustomData( '_cke_table_pillars' ); }, 0 );
    167241
    168                         detach();
     242                        document.removeListener( 'dragstart', cancel );
    169243                }
    170244
    171245                function resizeColumn()
    172246                {
     247                        var rtl = pillar.rtl,
     248                                cellsCount = rtl ? rightSideCells.length : leftSideCells.length;
     249
    173250                        // Perform the actual resize to table cells, only for those by side of the pillar.
    174                         for ( var i = 0, count = leftSideCells.length; i < count; i++ )
     251                        for ( var i = 0 ; i < cellsCount ; i++ )
    175252                        {
    176253                                var leftCell = leftSideCells[ i ],
    177                                                 rightCell = rightSideCells[ i ];
    178 
     254                                        rightCell = rightSideCells[ i ],
     255                                        table = pillar.table;
     256                               
    179257                                // Defer the resizing to avoid any interference among cells.
    180                                 ( function( leftCell, leftOldWidth, rightCell, rightOldWidth, sizeShift )
    181                                 {
    182                                         CKEDITOR.tools.setTimeout( function()
     258                                CKEDITOR.tools.setTimeout(
     259                                        function( leftCell, leftOldWidth, rightCell, rightOldWidth, tableWidth, sizeShift )
    183260                                        {
    184                                                 leftCell.setStyle( 'width', pxUnit(  leftOldWidth + sizeShift ) );
    185                                                 rightCell.setStyle( 'width', pxUnit(  rightOldWidth - sizeShift ) );
    186 
    187                                         }, 0, this );
    188 
    189                                 }).call( this, leftCell, getCellWidth( leftCell ),
    190                                                 rightCell, getCellWidth( rightCell ), currentShift );
     261                                                leftCell && leftCell.setStyle( 'width', pxUnit( Math.max( leftOldWidth + sizeShift, 0 ) ) );
     262                                                rightCell && rightCell.setStyle( 'width', pxUnit( Math.max( rightOldWidth - sizeShift, 0 ) ) );
     263                                               
     264                                                // If we're in the last cell, we need to resize the table as well
     265                                                if ( tableWidth )
     266                                                        table.setStyle( 'width', pxUnit( tableWidth + sizeShift * ( rtl ? -1 : 1 ) ) );
     267                                        }
     268                                        , 0,
     269                                        this, [
     270                                                leftCell, leftCell && getWidth( leftCell ),
     271                                                rightCell, rightCell && getWidth( rightCell ),
     272                                                ( !leftCell || !rightCell ) && ( getWidth( table ) + getBorderWidth( table, 'left' ) + getBorderWidth( table, 'right' ) ),
     273                                                currentShift ] );
    191274                        }
    192275                }
    193276
    194                 function onMouseMove( evt )
     277                function onMouseDown( evt )
    195278                {
    196                         var mouseOffset = evt.data.$.clientX,
    197                                         resizerNewPosition = mouseOffset - Math.round( parseInt( resizer.getComputedStyle( 'width' ), 10 ) / 2 );
    198 
    199                         // Boundaries checking.
    200                         if ( resizerNewPosition > leftShiftBoundary && resizerNewPosition < rightShiftBoundary )
    201                         {
    202                                 resizer.setStyle( 'left', pxUnit( resizerNewPosition ) );
    203                                 currentShift = resizerNewPosition - startOffset;
    204                         }
    205 
    206279                        cancel( evt );
    207                 }
    208280
    209                 function onMouseDown( evt )
    210                 {
    211                         cancel( evt );
    212281                        resizeStart();
     282
    213283                        document.on( 'mouseup', onMouseUp, this );
    214284                }
    215285
    216                 function onMouseUp()
     286                function onMouseUp( evt )
    217287                {
     288                        evt.removeListener();
     289
    218290                        resizeEnd();
    219291                }
    220 
    221                 function onMouseOut()
     292               
     293                function onMouseMove( evt )
    222294                {
    223                         // Don't detach during resizing.
    224                         !currentShift && detach();
     295                        move( evt.data.$.clientX );
    225296                }
    226297
    227298                document = editor.document;
    228                 resizer = CKEDITOR.dom.element.createFromHtml( '<div style="position: absolute; cursor: col-resize; ' +
    229                         'filter:alpha(opacity=0);opacity:0;padding:0;background-color:#004;background-image:none;border: 0px none;"></div>' );
    230299
    231                 // Place the resizer after body to prevent it from being editable.
    232                 document.getDocumentElement().append( resizer );
     300                resizer = CKEDITOR.dom.element.createFromHtml(
     301                        '<div cke_temp=1 contenteditable=false unselectable=on '+
     302                        'style="position:absolute;cursor:col-resize;filter:alpha(opacity=0);opacity:0;' +
     303                                'padding:0;background-color:#004;background-image:none;border:0px none;z-index:10"></div>', document );
     304
     305                // Except on IE6/7 (#5890), place the resizer after body to prevent it
     306                // from being editable.
     307                if ( !needsIEHacks )
     308                        document.getDocumentElement().append( resizer );
     309
    233310                this.attachTo = function( targetPillar )
    234311                {
    235312                        // Accept only one pillar at a time.
    236                         if ( currentShift )
     313                        if ( isResizing )
    237314                                return;
    238315
     316                        // On IE6/7, we append the resizer everytime we need it. (#5890)
     317                        if ( needsIEHacks )
     318                        {
     319                                document.getBody().append( resizer );
     320                                currentShift = 0;
     321                        }
     322
    239323                        pillar = targetPillar;
     324
    240325                        resizer.setStyles(
    241                         {
    242                                 width: pxUnit( targetPillar.width ),
    243                                 height : pxUnit( targetPillar.height ),
    244                                 left : pxUnit( targetPillar.x ),
    245                                 top : pxUnit( targetPillar.y )
    246                         });
     326                                {
     327                                        width: pxUnit( targetPillar.width ),
     328                                        height : pxUnit( targetPillar.height ),
     329                                        left : pxUnit( targetPillar.x ),
     330                                        top : pxUnit( targetPillar.y )
     331                                });
    247332
     333                        // In IE6/7, it's not possible to have custom cursors for floating
     334                        // elements in an editable document. Show the resizer in that case,
     335                        // to give the user a visual clue.
     336                        needsIEHacks && resizer.setOpacity( 0.25 );
     337                       
    248338                        resizer.on( 'mousedown', onMouseDown, this );
    249                         resizer.on( 'mouseout', onMouseOut, this );
    250339
     340                        document.getBody().setStyle( 'cursor', 'col-resize' );
     341                       
    251342                        // Display the resizer to receive events but don't show it,
    252343                        // only change the cursor to resizable shape.
    253344                        resizer.show();
    254345                };
     346               
     347                var move = this.move = function( posX )
     348                {
     349                        if ( !pillar )
     350                                return 0;
     351                       
     352                        var pad = pillar.padding;
     353
     354                        if ( !isResizing && ( posX < pillar.x - pad || posX > ( pillar.x + pillar.width + pad ) ) )
     355                        {
     356                                detach();
     357                                return 0;
     358                        }
     359
     360                        var resizerNewPosition = posX - Math.round( resizer.$.offsetWidth / 2 );
     361                       
     362                        if ( isResizing )
     363                        {
     364                                if ( resizerNewPosition == leftShiftBoundary || resizerNewPosition == rightShiftBoundary )
     365                                        return 1;
     366                               
     367                                resizerNewPosition = Math.max( resizerNewPosition, leftShiftBoundary );
     368                                resizerNewPosition = Math.min( resizerNewPosition, rightShiftBoundary );
     369                       
     370                                currentShift = resizerNewPosition - startOffset;
     371                        }
     372
     373                        resizer.setStyle( 'left', pxUnit( resizerNewPosition ) );
     374
     375                        return 1;
     376                };
    255377        }
    256378
    257379        function clearPillarsCache( evt )
     
    265387                                return;
    266388
    267389                        var dest = new CKEDITOR.dom.element( evt.data.$.relatedTarget || evt.data.$.toElement );
    268                         while( dest && !dest.equals( target ) && !dest.is( 'body' ) )
     390                        while( dest && dest.$ && !dest.equals( target ) && !dest.is( 'body' ) )
    269391                                dest = dest.getParent();
    270392                        if ( !dest || dest.equals( target ) )
    271393                                return;
    272394                }
    273395
    274396                target.getAscendant( 'table', true ).removeCustomData( '_cke_table_pillars' );
    275                 evt && evt.removeListener();
     397                evt.removeListener();
    276398        }
    277399
    278400        CKEDITOR.plugins.add( 'tableresize',
     
    280402                requires : [ 'tabletools' ],
    281403                init : function( editor )
    282404                {
    283                         editor.on( 'contentDom', function ()
     405                        editor.on( 'contentDom', function()
    284406                        {
    285407                                var resizer;
     408
    286409                                editor.document.getBody().on( 'mousemove', function( evt )
    287                                 {
    288                                         evt = evt.data;
     410                                        {
     411                                                evt = evt.data;
     412                                               
     413                                                // If we're already attached to a pillar, simply move the
     414                                                // resizer.
     415                                                if ( resizer && resizer.move( evt.$.clientX ) )
     416                                                {
     417                                                        cancel( evt );
     418                                                        return;
     419                                                }
    289420
    290                                         // Considering table, tr, td, tbody but nothing else.
    291                                         var target = evt.getTarget();
    292                                         if ( !( target.is( 'table' ) || target.getAscendant( 'tbody', true ) ) )
    293                                                 return;
    294 
    295                                         var table = target.getAscendant( 'table', true ),
     421                                                // Considering table, tr, td, tbody but nothing else.
     422                                                var target = evt.getTarget(),
     423                                                        table,
    296424                                                        pillars;
    297425
    298                                         if ( !( pillars = table.getCustomData( '_cke_table_pillars' ) ) )
    299                                         {
    300                                                 // Cache table pillars caculation result.
    301                                                 table.setCustomData( '_cke_table_pillars', ( pillars = buildTableColumnPillars( table ) ) );
    302                                                 table.on( 'mouseout', clearPillarsCache );
    303                                                 table.on( 'mousedown', clearPillarsCache );
    304                                         }
     426                                                if ( !target.is( 'table' ) && !target.getAscendant( 'tbody', true ) )
     427                                                        return;
    305428
    306                                         var pillar = getPillarAtPosition( pillars, { x : evt.$.clientX, y : evt.$.clientY } );
    307                                         if ( pillar )
    308                                         {
    309                                                 !resizer && ( resizer = new columnResizer( editor ) );
    310                                                 resizer.attachTo( pillar );
    311                                         }
    312                                 });
     429                                                table = target.getAscendant( 'table', true );
     430
     431                                                if ( !( pillars = table.getCustomData( '_cke_table_pillars' ) ) )
     432                                                {
     433                                                        // Cache table pillars calculation result.
     434                                                        table.setCustomData( '_cke_table_pillars', ( pillars = buildTableColumnPillars( table ) ) );
     435                                                        table.on( 'mouseout', clearPillarsCache );
     436                                                        table.on( 'mousedown', clearPillarsCache );
     437                                                }
     438
     439                                                var pillar = getPillarAtPosition( pillars, evt.$.clientX );
     440                                                if ( pillar )
     441                                                {
     442                                                        !resizer && ( resizer = new columnResizer( editor ) );
     443                                                        resizer.attachTo( pillar );
     444                                                }
     445                                        });
    313446                        });
    314447                }
    315448        });
    316449
    317 } )( );
     450})();
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy