Ticket #3188: 3188_5.patch

File 3188_5.patch, 13.3 KB (added by Garry Yao, 10 years ago)
  • _source/core/tools.js

     
    519519                        {
    520520                                return length + ( decimalRegex.test( length ) ? 'px' : '' );
    521521                        };
    522                 })()
     522                })(),
     523
     524                multiple : function( str, times )
     525                {
     526                        return new Array( times + 1 ).join( str );
     527                }
    523528        };
    524529})();
    525530
  • _source/plugins/styles/plugin.js

     
    662662                }
    663663
    664664                range.moveToBookmark( bookmark );
    665         }
     665}
    666666
    667667        function applyBlockStyle( range )
    668668        {
    669                 // Bookmark the range so we can re-select it after processing.
    670                 var bookmark = range.createBookmark();
     669                // Serializible bookmarks is needed here since
     670                // elements may be merged.
     671                var bookmark = range.createBookmark( true );
    671672
    672673                var iterator = range.createIterator();
    673674                iterator.enforceRealBlocks = true;
     
    678679
    679680                while( ( block = iterator.getNextParagraph() ) )                // Only one =
    680681                {
    681                         // Create the new node right before the current one.
    682682                        var newBlock = getElement( this, doc );
     683                        replaceBlock( block, newBlock );
     684                }
    683685
    684                         // Check if we are changing from/to <pre>.
    685 //                      var newBlockIsPre       = newBlock.nodeName.IEquals( 'pre' );
    686 //                      var blockIsPre          = block.nodeName.IEquals( 'pre' );
     686                range.moveToBookmark( bookmark );
     687        }
    687688
    688 //                      var toPre       = newBlockIsPre && !blockIsPre;
    689 //                      var fromPre     = !newBlockIsPre && blockIsPre;
     689        // Replace the original block with new one, with special treatment
     690        // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent
     691        // when necessary.(#3188)
     692        function replaceBlock( block, newBlock )
     693        {
     694                var newBlockIsPre       = newBlock.is( 'pre' );
     695                var blockIsPre          = block.is( 'pre' );
    690696
    691                         // Move everything from the current node to the new one.
    692 //                      if ( toPre )
    693 //                              newBlock = this._ToPre( doc, block, newBlock );
    694 //                      else if ( fromPre )
    695 //                              newBlock = this._FromPre( doc, block, newBlock );
    696 //                      else    // Convering from a regular block to another regular block.
    697                                 block.moveChildren( newBlock );
     697                var isToPre     = newBlockIsPre && !blockIsPre;
     698                var isFromPre   = !newBlockIsPre && blockIsPre;
     699
     700                if ( isToPre )
     701                        newBlock = toPre( block, newBlock );
     702                else if ( isFromPre )
     703                        // Split big <pre> into pieces before start to convert.
     704                        newBlock = fromPres( splitIntoPres( block ), newBlock );
     705                else
     706                        block.moveChildren( newBlock );
    698707
    699                         // Replace the current block.
    700                         newBlock.insertBefore( block );
    701                         block.remove();
     708                newBlock.replace( block );
    702709
    703                         // Complete other tasks after inserting the node in the DOM.
    704 //                      if ( newBlockIsPre )
    705 //                      {
    706 //                              if ( previousPreBlock )
    707 //                                      this._CheckAndMergePre( previousPreBlock, newBlock ) ;  // Merge successive <pre> blocks.
    708 //                              previousPreBlock = newBlock;
    709 //                      }
    710 //                      else if ( fromPre )
    711 //                              this._CheckAndSplitPre( newBlock ) ;    // Split <br><br> in successive <pre>s.
    712                 }
     710                if ( newBlockIsPre )
     711                        // Merge previous <pre> blocks.
     712                        mergePre( newBlock );
     713        };
     714
     715        /**
     716         * Merge a <pre> block with a previous sibling if available.
     717         */
     718        function mergePre( preBlock )
     719        {
     720                var previousBlock;
     721                if ( !( ( previousBlock = preBlock.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) )
     722                                 && previousBlock.is
     723                                 && previousBlock.is( 'pre') ) )
     724                        return;
     725
     726                // Merge the previous <pre> block contents into the current <pre>
     727                // block.
     728                //
     729                // Another thing to be careful here is that currentBlock might contain
     730                // a '\n' at the beginning, and previousBlock might contain a '\n'
     731                // towards the end. These new lines are not normally displayed but they
     732                // become visible after merging.
     733                var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' +
     734                                replace( preBlock.getHtml(), /^\n/, '' ) ;
     735
     736                // Krugle: IE normalizes innerHTML from <pre>, breaking whitespaces.
     737                if ( CKEDITOR.env.ie )
     738                        preBlock.$.outerHTML = '<pre>' + mergedHtml + '</pre>';
     739                else
     740                        preBlock.setHtml( mergedHtml );
     741
     742                previousBlock.remove();
     743        }
    713744
    714                 range.moveToBookmark( bookmark );
     745        /**
     746         * Split into multiple <pre> blocks separated by double line-break.
     747         * @param preBlock
     748         */
     749        function splitIntoPres( preBlock )
     750        {
     751                // Exclude the ones at header OR at tail,
     752                // and ignore bookmark content between them.
     753                var duoBrRegex = /(\S\s*)\n(?:\s|(<span[^>]+_fck_bookmark.*?\/span>))*\n(?!$)/gi,
     754                        blockName = preBlock.getName(),
     755                        splitedHtml = replace( preBlock.getOuterHtml(),
     756                                duoBrRegex,
     757                                function( match, charBefore, bookmark )
     758                                {
     759                                  return charBefore + '</pre>' + bookmark + '<pre>';
     760                                } );
     761
     762                var pres = [];
     763                splitedHtml.replace( /<pre>([\s\S]*?)<\/pre>/gi, function( match, preContent ){
     764                        pres.push( preContent );
     765                } );
     766                return pres;
    715767        }
    716768
     769        // Wrapper function of String::replace without considering of head/tail bookmarks nodes.
     770        function replace( str, regexp, replacement )
     771        {
     772                var headBookmark = '',
     773                        tailBookmark = '';
     774
     775                str = str.replace( /(^<span[^>]+_fck_bookmark.*?\/span>)|(<span[^>]+_fck_bookmark.*?\/span>$)/gi,
     776                        function( str, m1, m2 ){
     777                                        m1 && ( headBookmark = m1 );
     778                                        m2 && ( tailBookmark = m2 );
     779                                return '';
     780                        } );
     781                return headBookmark + str.replace( regexp, replacement ) + tailBookmark;
     782        }
     783        /**
     784         * Converting a list of <pre> into blocks with format well preserved.
     785         */
     786        function fromPres( preHtmls, newBlock )
     787        {
     788                var docFrag = new CKEDITOR.dom.documentFragment( newBlock.getDocument() );
     789                for ( var i = 0 ; i < preHtmls.length ; i++ )
     790                {
     791                        var blockHtml = preHtmls[ i ];
     792
     793                        // 1. Trim the first and last line-breaks immediately after and before <pre>,
     794                        // they're not visible.
     795                         blockHtml =  blockHtml.replace( /(\r\n|\r)/g, '\n' ) ;
     796                         blockHtml = replace(  blockHtml, /^[ \t]*\n/, '' ) ;
     797                         blockHtml = replace(  blockHtml, /\n$/, '' ) ;
     798                        // 2. Convert spaces or tabs at the beginning or at the end to &nbsp;
     799                         blockHtml = replace(  blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset, s )
     800                                        {
     801                                                if ( match.length == 1 )        // one space, preserve it
     802                                                        return '&nbsp;' ;
     803                                                else if ( offset == 0 )         // beginning of block
     804                                                        return CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 ) + ' ';
     805                                                else                            // end of block
     806                                                        return ' ' + CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 );
     807                                        } ) ;
     808
     809                        // 3. Convert \n to <BR>.
     810                        // 4. Convert contiguous (i.e. non-singular) spaces or tabs to &nbsp;
     811                         blockHtml =  blockHtml.replace( /\n/g, '<br>' ) ;
     812                         blockHtml =  blockHtml.replace( /[ \t]{2,}/g,
     813                                        function ( match )
     814                                        {
     815                                                return CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 ) + ' ' ;
     816                                        } ) ;
     817
     818                        var newBlockClone = newBlock.clone();
     819                        newBlockClone.setHtml(  blockHtml );
     820                        docFrag.append( newBlockClone );
     821                }
     822                return docFrag;
     823        }
     824
     825        /**
     826         * Converting from a non-PRE block to a PRE block in formatting operations.
     827         */
     828        function toPre( block, newBlock )
     829        {
     830                // First trim the block content.
     831                var preHtml = block.getHtml();
     832
     833                // 1. Trim head/tail spaces, they're not visible.
     834                preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' );
     835                // 2. Delete ANSI whitespaces immediately before and after <BR> because
     836                //    they are not visible.
     837                preHtml = preHtml.replace( /[ \t\r\n]*(<br[^>]*>)[ \t\r\n]*/gi, '$1' );
     838                // 3. Compress other ANSI whitespaces since they're only visible as one
     839                //    single space previously.
     840                // 4. Convert &nbsp; to spaces since &nbsp; is no longer needed in <PRE>.
     841                preHtml = preHtml.replace( /([ \t\n\r]+|&nbsp;)/g, ' ' );
     842                // 5. Convert any <BR /> to \n. This must not be done earlier because
     843                //    the \n would then get compressed.
     844                preHtml = preHtml.replace( /<br\b[^>]*>/gi, '\n' );
     845
     846                // Krugle: IE normalizes innerHTML to <pre>, breaking whitespaces.
     847                if ( CKEDITOR.env.ie )
     848                {
     849                        var temp = block.getDocument().createElement( 'div' );
     850                        temp.append( newBlock );
     851                        newBlock.$.outerHTML =  '<pre>' + preHtml + '</pre>';
     852                        newBlock = temp.getFirst().remove();
     853                }
     854                else
     855                        newBlock.setHtml( preHtml );
     856
     857                return newBlock;
     858        }
     859
    717860        // Removes a style from an element itself, don't care about its subtree.
    718861        function removeFromElement( style, element )
    719862        {
  • _source/core/test.js

     
    5555
    5656        /**
    5757         * Gets the inner HTML of an element, for testing purposes.
     58         * @param {Boolean} stripLineBreaks Assign 'false' to avoid trimming line-breaks.
    5859         */
    59         getInnerHtml : function( elementOrId )
     60        getInnerHtml : function( elementOrId , stripLineBreaks )
    6061        {
    6162                var html;
    6263
     
    6566                else if ( elementOrId.getHtml )
    6667                        html = elementOrId.getHtml();
    6768                else
    68                         html = elementOrId.innerHTML || '';
     69                        html = elementOrId.innerHTML    // retrieve from innerHTML
     70                                   || elementOrId.value;    // retrieve from value
    6971
    7072                html = html.toLowerCase();
    71                 html = html.replace( /[\n\r]/g, '' );
     73                if ( stripLineBreaks !== false )
     74                        html = html.replace( /[\n\r]/g, '' );
     75                else
     76                        html = html.replace( /\r/g, '' );    // Normalize CRLF.
    7277
    7378                html = html.replace( /<\w[^>]*/g, function( match )
    7479                        {
  • _source/tests/plugins/styles/styles.html

     
    77        <script type="text/javascript" src="../../test.js"></script>
    88        <script type="text/javascript">
    99
    10 CKEDITOR.plugins.load( 'styles' );
     10CKEDITOR.plugins.load( [ 'styles', 'domiterator', 'htmldataprocessor' ] );
    1111
    1212        </script>
    1313        <script type="text/javascript">
     
    2121        var assert                      = CKEDITOR.test.assert;
    2222        var getInnerHtml        = CKEDITOR.test.getInnerHtml;
    2323
     24        function getInnerHtmlParsed( element )
     25        {
     26                var dataProcessor = new CKEDITOR.htmlDataProcessor();
     27                dataProcessor.writer = new CKEDITOR.htmlParser.basicWriter();
     28                return dataProcessor.toDataFormat( getInnerHtml( element, false ) );
     29        }
     30
    2431        var doc = new CKEDITOR.dom.document( document );
    2532
    2633        return {
     
    537544                        // text <span><i>^</i></span><bold><span><b><i>styles</i></b></span></bold>
    538545                        assert.areSame( 'text <span><i></i></span><strong><bold><span><b><i id="_i1">styles</i></b></span></bold></strong>', getInnerHtml( element ) );
    539546                },
     547
     548                // Test convert multiple paragraphs to one <pre>.
     549                test_ticket_3188 : function()
     550                {
     551                        var element = doc.getById( '_P1' );
     552                        element.setHtml( '<p id="_P2">\nparagraph1<br /><br />para\t\ngraph2</p><p id="_P3">\nparagraph3\n</p>' );
     553
     554                        // <p id="_P2">[paragraph1</p><p id="_P3">paragraph2]</p>
     555                        var range = new CKEDITOR.dom.range( doc );
     556                        range.setStartAt( doc.getById( '_P2' ), CKEDITOR.POSITION_AFTER_START );
     557                        range.setEndAt( doc.getById( '_P3' ), CKEDITOR.POSITION_BEFORE_END );
     558
     559                        var style = new CKEDITOR.style( { element : 'pre' } );
     560                        style.applyToRange( range );
     561
     562                        var result = getInnerHtmlParsed( element );
     563                        assert.areSame( '<pre>paragraph1\n\npara graph2\n\nparagraph3</pre>', result );
     564                },
     565
     566                // Test convert one <pre> to multiple paragraphs.
     567                test_ticket_3188_2 : function()
     568                {
     569                        var element = doc.getById( '_P1' );
     570                        element.setHtml( '<pre>\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n</pre>' );
     571
     572                        //<pre>[\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n]</pre>
     573                        var range = new CKEDITOR.dom.range( doc );
     574                        range.selectNodeContents( doc.getById( '_P1' ).getFirst() );
     575                        var style = new CKEDITOR.style( { element : 'p' } );
     576                        style.applyToRange( range );
     577
     578                        var result = getInnerHtmlParsed( element );
     579                        assert.areSame( '<p>&nbsp;paragraph1&nbsp; paragraph1<br />paragraph2</p><p>&nbsp;para&nbsp;&nbsp; graph3</p>',
     580                                         result );
     581                },
    540582                name : document.title
    541583        };
    542584})() );
    543 //window.onload = testCase.test_ticket_3309_3;
     585//window.onload = testCase.test_ticket_3188;
    544586        //]]>
    545587        </script>
    546588</head>
  • _source/tests/core/tools.html

     
    154154                        assert.areSame( 'silver', clone.cars.Porsche.color );
    155155                },
    156156
     157                test_multiple : function()
     158                {
     159                        assert.areSame( '&nbsp;&nbsp;&nbsp;', CKEDITOR.tools.multiple( '&nbsp;', 3 ) );
     160                },
     161
    157162                name : document.title
    158163        };
    159164})() );
  • CHANGES.html

     
    3838                CKEditor 3.0 (SVN)</h3>
    3939        <p>
    4040                New features:</p>
     41        <li><a href="http://dev.fckeditor.net/ticket/3188">#3188</a> : Introduce
     42                &lt;pre&gt; formatting feature when converting from other blocks.</li>
    4143        <ul>
    4244                <li>&nbsp;</li>
    4345        </ul>
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy