Ticket #3188: 3188_3.patch

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

     
    501501                        {
    502502                                return length + ( decimalRegex.test( length ) ? 'px' : '' );
    503503                        };
    504                 })()
     504                })(),
     505
     506                multiple : function( str, times )
     507                {
     508                        return new Array( times + 1 ).join( str );
     509                }
    505510        };
    506511})();
    507512
  • _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, doc );
     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, document )
     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;
    698699
    699                         // Replace the current block.
    700                         newBlock.insertBefore( block );
    701                         block.remove();
     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 );
    702705
    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                 }
     706                newBlock.replace( block );
     707
     708                if ( newBlockIsPre )
     709                        // Merge previous <pre> blocks.
     710                        mergePre( newBlock );
     711        };
     712
     713        /**
     714         * Merge a <pre> block with a previous sibling if available.
     715         */
     716        function mergePre( preBlock )
     717        {
     718                var previousBlock;
     719                if ( !( ( previousBlock = preBlock.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) )
     720                                 && previousBlock.is
     721                                 && previousBlock.is( 'pre') ) )
     722                        return;
     723
     724                // Merge the previous <pre> block contents into the current <pre>
     725                // block.
     726                //
     727                // Another thing to be careful here is that currentBlock might contain
     728                // a '\n' at the beginning, and previousBlock might contain a '\n'
     729                // towards the end. These new lines are not normally displayed but they
     730                // become visible after merging.
     731                var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' +
     732                                replace( preBlock.getHtml(), /^\n/, '' ) ;
     733
     734                // Krugle: IE normalizes innerHTML from <pre>, breaking whitespaces.
     735                if ( CKEDITOR.env.ie )
     736                        preBlock.$.outerHTML = '<pre>' + mergedHtml + '</pre>';
     737                else
     738                        preBlock.setHtml( mergedHtml );
     739
     740                previousBlock.remove();
     741        }
    713742
    714                 range.moveToBookmark( bookmark );
     743        /**
     744         * Split into multiple <pre> blocks separated by double line-break.
     745         * @param preBlock
     746         */
     747        function splitIntoPres( preBlock )
     748        {
     749                // Exclude the ones at header OR at tail,
     750                // and ignore bookmark content between them.
     751                var duoBrRegex = /(\S\s*)\n(?:\s|(<span[^>]+_fck_bookmark.*?\/span>))*\n(?!$)/gi,
     752                        blockName = preBlock.getName(),
     753                        splitedHtml = replace( preBlock.getOuterHtml(),
     754                                duoBrRegex,
     755                                function( match, charBefore, bookmark )
     756                                {
     757                                  return charBefore + '</pre>' + bookmark + '<pre>';
     758                                } );
     759
     760                var pres = [];
     761                splitedHtml.replace( /<pre>([\s\S]*?)<\/pre>/gi, function( match, preContent ){
     762                        pres.push( preContent );
     763                } );
     764                return pres;
    715765        }
    716766
     767        // Wrapper function of String::replace without considering of head/tail bookmarks nodes.
     768        function replace( str, regexp, replacement )
     769        {
     770                var headBookmark = '',
     771                        tailBookmark = '';
     772
     773                str = str.replace( /(^<span[^>]+_fck_bookmark.*?\/span>)|(<span[^>]+_fck_bookmark.*?\/span>$)/gi,
     774                        function( str, m1, m2 ){
     775                                        m1 && ( headBookmark = m1 );
     776                                        m2 && ( tailBookmark = m2 );
     777                                return '';
     778                        } );
     779                return headBookmark + str.replace( regexp, replacement ) + tailBookmark;
     780        }
     781        /**
     782         * Converting a list of <pre> into blocks with format well preserved.
     783         */
     784        function fromPres( preHtmls, newBlock )
     785        {
     786                var docFrag = new CKEDITOR.dom.documentFragment();
     787                for ( var i = 0 ; i < preHtmls.length ; i++ )
     788                {
     789                        var blockHtml = preHtmls[ i ];
     790
     791                        // 1. Trim the first and last line-breaks immediately after and before <pre>,
     792                        // they're not visible.
     793                         blockHtml =  blockHtml.replace( /(\r\n|\r)/g, '\n' ) ;
     794                         blockHtml = replace(  blockHtml, /^[ \t]*\n/, '' ) ;
     795                         blockHtml = replace(  blockHtml, /\n$/, '' ) ;
     796                        // 2. Convert spaces or tabs at the beginning or at the end to &nbsp;
     797                         blockHtml = replace(  blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset, s )
     798                                        {
     799                                                if ( match.length == 1 )        // one space, preserve it
     800                                                        return '&nbsp;' ;
     801                                                else if ( offset == 0 )         // beginning of block
     802                                                        return CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 ) + ' ';
     803                                                else                            // end of block
     804                                                        return ' ' + CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 );
     805                                        } ) ;
     806
     807                        // 3. Convert \n to <BR>.
     808                        // 4. Convert contiguous (i.e. non-singular) spaces or tabs to &nbsp;
     809                         blockHtml =  blockHtml.replace( /\n/g, '<br>' ) ;
     810                         blockHtml =  blockHtml.replace( /[ \t]{2,}/g,
     811                                        function ( match )
     812                                        {
     813                                                return CKEDITOR.tools.multiple( '&nbsp;', match.length - 1 ) + ' ' ;
     814                                        } ) ;
     815
     816                        var newBlockClone = newBlock.clone();
     817                        newBlockClone.setHtml(  blockHtml );
     818                        docFrag.append( newBlockClone );
     819                }
     820                return docFrag;
     821        }
     822
     823        /**
     824         * Converting from a non-PRE block to a PRE block in formatting operations.
     825         */
     826        function toPre( block, newBlock )
     827        {
     828                // First trim the block content.
     829                var preHtml = block.getHtml();
     830
     831                // 1. Trim head/tail spaces, they're not visible.
     832                preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' );
     833                // 2. Delete ANSI whitespaces immediately before and after <BR> because
     834                //    they are not visible.
     835                preHtml = preHtml.replace( /[ \t\r\n]*(<br[^>]*>)[ \t\r\n]*/gi, '$1' );
     836                // 3. Compress other ANSI whitespaces since they're only visible as one
     837                //    single space previously.
     838                // 4. Convert &nbsp; to spaces since &nbsp; is no longer needed in <PRE>.
     839                preHtml = preHtml.replace( /([ \t\n\r]+|&nbsp;)/g, ' ' );
     840                // 5. Convert any <BR /> to \n. This must not be done earlier because
     841                //    the \n would then get compressed.
     842                preHtml = preHtml.replace( /<br\b[^>]*>/gi, '\n' );
     843
     844                // Krugle: IE normalizes innerHTML to <pre>, breaking whitespaces.
     845                if ( CKEDITOR.env.ie )
     846                {
     847                        var temp = block.getDocument().createElement( 'div' );
     848                        temp.append( newBlock );
     849                        newBlock.$.outerHTML =  '<pre>' + preHtml + '</pre>';
     850                        newBlock = temp.getFirst().remove();
     851                }
     852                else
     853                        newBlock.setHtml( preHtml );
     854
     855                return newBlock;
     856        }
     857
    717858        // Removes a style from an element itself, don't care about its subtree.
    718859        function removeFromElement( style, element )
    719860        {
  • _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 {
     
    494501                        element.setHtml( 'this is some <b><i id="_i1">styles</i></b> text' );
    495502
    496503                        // This is some <b><i>styles^</i></b> text
    497                         var range = new CKEDITOR.dom.range( doc );
     504                        var range = new CKEDdITOR.dom.range( doc );
    498505                        range.setStartAt( doc.getById( '_i1' ), CKEDITOR.POSITION_BEFORE_END );
    499506
    500507                        var style = new CKEDITOR.style( { element : 'b' } );
     
    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                        console.log( result );
     564                        assert.areSame( '<pre>paragraph1\n\npara graph2\n\nparagraph3</pre>', result );
     565                },
     566
     567                // Test convert one <pre> to multiple paragraphs.
     568                test_ticket_3188_2 : function()
     569                {
     570                        var element = doc.getById( '_P1' );
     571                        element.setHtml( '<pre>\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n</pre>' );
     572
     573                        //<pre>[\n\tparagraph1\t\tparagraph1\nparagraph2\n\t\n\tpara   graph3\n]</pre>
     574                        var range = new CKEDITOR.dom.range( doc );
     575                        range.selectNodeContents( doc.getById( '_P1' ).getFirst() );
     576                        var style = new CKEDITOR.style( { element : 'p' } );
     577                        style.applyToRange( range );
     578
     579                        var result = getInnerHtmlParsed( element );
     580                        assert.areSame( '<p>&nbsp;paragraph1&nbsp; paragraph1<br />paragraph2</p><p>&nbsp;para&nbsp;&nbsp; graph3</p>',
     581                                         result );
     582                },
    540583                name : document.title
    541584        };
    542585})() );
    543 //window.onload = testCase.test_ticket_3309_3;
     586//window.onload = testCase.test_ticket_3188;
    544587        //]]>
    545588        </script>
    546589</head>
  • _source/tests/core/tools.html

     
    149149                        assert.areSame( 'silver', clone.cars.Porsche.color );
    150150                },
    151151
     152                test_multiple : function()
     153                {
     154                        assert.areSame( '&nbsp;&nbsp;&nbsp;', CKEDITOR.tools.multiple( '&nbsp;', 3 ) );
     155                },
     156
    152157                name : document.title
    153158        };
    154159})() );
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy