Ticket #3195: 3195_2.patch

File 3195_2.patch, 10.0 KB (added by Garry Yao, 11 years ago)
  • _source/core/htmlparser/fragment.js

     
    4141        // (expect empty elements).
    4242        var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
    4343
     44        var tableBlocks = {table:1,tbody:1,thead:1,tfoot:1,tr:1};
     45
    4446        /**
    4547         * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
    4648         * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
     
    5658                        html = [],
    5759                        fragment = new CKEDITOR.htmlParser.fragment(),
    5860                        pendingInline = [],
    59                         currentNode = fragment;
     61                        currentNode = fragment,
     62                        returnPoint;
    6063
    61                 var checkPending = function( newTagName )
     64                function checkPending( newTagName )
    6265                {
    6366                        if ( pendingInline.length > 0 )
    6467                        {
     
    7679
    7780                                                // Add it to the current node and make it the current,
    7881                                                // so the new element will be added inside of it.
    79                                                 currentNode.add( pendingElement );
     82                                                pendingElement.parent = currentNode;
    8083                                                currentNode = pendingElement;
    8184
    8285                                                // Remove the pending element (back the index by one
     
    8689                                        }
    8790                                }
    8891                        }
    89                 };
     92                }
     93
     94                function addElement( element, target, enforceCurrent )
     95                {
     96                        target = target || currentNode || fragment;
     97
     98                        // If the target is the fragment and this element can't go inside
     99                        // body (if fixForBody).
     100                        if ( fixForBody && !target.type && !CKEDITOR.dtd.$body[ element.name ] )
     101                        {
     102                                var savedCurrent = currentNode;
     103
     104                                // Create a <p> in the fragment.
     105                                currentNode = target;
     106                                parser.onTagOpen( 'p', {} );
     107
     108                                // The new target now is the <p>.
     109                                target = currentNode;
     110
     111                                if ( enforceCurrent )
     112                                        currentNode = savedCurrent;
     113                        }
     114
     115                        target.add( element );
     116
     117                        if ( element.returnPoint )
     118                        {
     119                                currentNode = element.returnPoint;
     120                                delete element.returnPoint;
     121                        }
     122                }
    90123
    91124                parser.onTagOpen = function( tagName, attributes, selfClosing )
    92125                {
    93                         if ( fixForBody && !currentNode.type && !CKEDITOR.dtd.$body[ tagName ] )
    94                                 this.onTagOpen( 'p', {} );
    95 
    96126                        var element = new CKEDITOR.htmlParser.element( tagName, attributes );
    97127
    98128                        // "isEmpty" will be always "false" for unknown elements, so we
     
    123153                                // If the element name is the same as the current element name,
    124154                                // then just close the current one and append the new one to the
    125155                                // parent. This situation usually happens with <p>, <li>, <dt> and
    126                                 // <dd>, specially in IE.
    127                                 if ( tagName != currentName )
     156                                // <dd>, specially in IE. Do not enter in this if block in this case.
     157                                if ( tagName == currentName )
     158                                {
     159                                        addElement( currentNode, currentNode.parent );
     160                                }
     161                                else
    128162                                {
    129                                         // If it is optional to close the current element, then
    130                                         // close it at this point and simply add the new
    131                                         // element after it.
    132                                         if ( !optionalClose[ currentName ] )
     163                                        if ( tableBlocks[ currentName ] )
    133164                                        {
    134                                                 // The current element is an inline element, which
    135                                                 // cannot hold the new one. Put it in the pending list,
    136                                                 // and try adding the new one after it.
    137                                                 pendingInline.unshift( currentNode );
     165                                                if ( !returnPoint )
     166                                                        returnPoint = currentNode;
    138167                                        }
     168                                        else
     169                                        {
     170                                                addElement( currentNode, currentNode.parent, true );
     171
     172                                                if ( !optionalClose[ currentName ] )
     173                                                {
     174                                                        // The current element is an inline element, which
     175                                                        // cannot hold the new one. Put it in the pending list,
     176                                                        // and try adding the new one after it.
     177                                                        pendingInline.unshift( currentNode );
     178                                                }
     179                                        }
    139180
    140181                                        reApply = true;
    141182                                }
     
    142183
    143184                                // In any of the above cases, we'll be adding, or trying to
    144185                                // add it to the parent.
    145                                 currentNode = currentNode.parent;
     186                                currentNode = currentNode.returnPoint || currentNode.parent;
    146187
    147188                                if ( reApply )
    148189                                {
     
    153194
    154195                        checkPending( tagName );
    155196
    156                         currentNode.add( element );
     197                        element.parent = currentNode;
     198                        element.returnPoint = returnPoint;
     199                        returnPoint = 0;
    157200
    158                         if ( !element.isEmpty )
     201                        if ( element.isEmpty )
     202                                addElement( element );
     203                        else
    159204                                currentNode = element;
    160205                };
    161206
     
    161206
    162207                parser.onTagClose = function( tagName )
    163208                {
    164                         var closingElement = currentNode,
    165                                 index = 0;
     209                        // Empty tag content, drop the pending element.
     210                        var pendingLength = pendingInline.length;
     211                        if (pendingLength && pendingInline[pendingLength - 1].name == tagName)
     212                        {
     213                                pendingInline.length = pendingLength - 1;
     214                                return;
     215                        }
     216                               
     217                        var index = 0;
    166218
    167                         while ( closingElement && closingElement.name != tagName )
     219                        while ( currentNode.type && currentNode.name != tagName )
    168220                        {
    169221                                // If this is an inline element, add it to the pending list, so
    170222                                // it will continue after the closing tag.
    171                                 if ( !closingElement._.isBlockLike )
     223                                if ( !currentNode._.isBlockLike )
    172224                                {
    173                                         pendingInline.unshift( closingElement );
     225                                        pendingInline.unshift( currentNode );
    174226
    175227                                        // Increase the index, so it will not get checked again in
    176228                                        // the pending list check that follows.
     
    177229                                        index++;
    178230                                }
    179231
    180                                 closingElement = closingElement.parent;
     232                                addElement( currentNode, currentNode.parent );
     233                                currentNode = currentNode.parent;
    181234                        }
    182235
    183                         if ( closingElement )
    184                                 currentNode = closingElement.parent;
     236                        if ( currentNode.type )
     237                        {
     238                                var node = currentNode;
     239                                addElement( currentNode, currentNode.parent );
     240
     241                                // The parent should start receiving new nodes now, except if
     242                                // addElement changed the currentNode.
     243                                if ( node == currentNode )
     244                                        currentNode = currentNode.parent;
     245                        }
    185246                        else if ( pendingInline.length > index )
    186247                        {
    187248                                // If we didn't find any parent to be closed, let's check the
     
    223284                        currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
    224285                };
    225286
     287                // Parse it.
    226288                parser.parse( fragmentHtml );
    227289
     290                // Close all pending nodes.
     291                while ( currentNode.type )
     292                {
     293                        var parent = currentNode.parent,
     294                                node = currentNode;
     295
     296                        if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
     297                        {
     298                                currentNode = parent;
     299                                parser.onTagOpen( 'p', {} );
     300                                parent = currentNode;
     301                        }
     302
     303                        parent.add( node );
     304                        currentNode = parent;
     305                }
     306
    228307                return fragment;
    229308        };
    230309
  • _source/tests/core/htmlparser/fragment.html

     
    1010        <script type="text/javascript">
    1111        //<![CDATA[
    1212
    13 CKEDITOR.test.addTestCase( (function()
     13var tc;
     14
     15CKEDITOR.test.addTestCase( tc = (function()
    1416{
    1517        // Local reference to the "assert" object.
    1618        var assert = CKEDITOR.test.assert;
     
    1517        // Local reference to the "assert" object.
    1618        var assert = CKEDITOR.test.assert;
    1719
     20        function testParser( input, expected , writerConfig )
     21        {
     22                var fragment = CKEDITOR.htmlParser.fragment.fromHtml( input, true ),
     23                        writer = new CKEDITOR.htmlParser.basicWriter();
     24
     25                if( writerConfig )
     26                        CKEDITOR.tools.extend( writer, writerConfig , true);
     27
     28                fragment.writeHtml( writer );
     29
     30                assert.areSame( expected, writer.getHtml( true ) );
     31        }
     32
    1833        return {
    1934                test_fromHtml_1 : function()
    2035                {
     
    2540                        assert.areSame( 'p', fragment.children[0].name, 'Wrong child name' );
    2641                },
    2742
     43                test_parser_1 : function()
     44                {
     45                        testParser(     '<table><tr><td>1</td><p><b>2</b> Test</p><td>3</td></tr></table>',
     46                                                '<p><b>2</b> Test</p><table><tr><td>1</td><td>3</td></tr></table>' );
     47                },
     48
     49                test_parser_2 : function()
     50                {
     51                        testParser(     '<b><table><tr><td>1</td><td>2</td></tr></table></b>',
     52                                                '<table><tr><td><b>1</b></td><td><b>2</b></td></tr></table>' );
     53                },
     54
     55                test_parser_3_1 : function()
     56                {
     57                        testParser(     '<b><i>Table:<table><tr><td>1</td><td>2</td></tr></table></i></b>',
     58                                                '<p><b><i>Table:</i></b></p><table><tr><td><b><i>1</i></b></td><td><b><i>2</i></b></td></tr></table>' );
     59                },
     60
     61                test_parser_3_2 : function()
     62                {
     63                        testParser(     '<b><i><table><tr><td>1</td><td>2</td></tr></table>Table</i></b>',
     64                                                '<table><tr><td><b><i>1</i></b></td><td><b><i>2</i></b></td></tr></table><p><b><i>Table</i></b></p>' );
     65                },
     66
     67                test_parser_4 : function()
     68                {
     69                        testParser(     '<b><i>Test',
     70                                                '<p><b><i>Test</i></b></p>' );
     71                },
     72
     73                test_parser_5 : function()
     74                {
     75                        testParser(     '<p>Para 1<p>Para 2<p>Para 3',
     76                                                '<p>Para 1</p><p>Para 2</p><p>Para 3</p>' );
     77                },
     78
     79                test_parser_6 : function()
     80                {
     81                        testParser(     '<b>A</b><i>B</i>',
     82                                                '<p><b>A</b><i>B</i></p>' );
     83                },
     84
     85                test_parser_7 : function()
     86                {
     87                        testParser(     '<p>Para 1<hr>Para 2<h1>Para 3',
     88                                                '<p>Para 1</p><hr /><p>Para 2</p><h1>Para 3</h1>' );
     89                },
     90               
     91                /**
     92                 * Test remove empty inline element.
     93                 */
     94                test_parser_8 : function()
     95                {
     96                        testParser(     '<p><b></b>text</p>',
     97                                                '<p>text</p>' );
     98                },
     99               
     100                /**
     101                 * Test fixing malformed inline element closing.
     102                 */
     103                test_parser_9 : function()
     104                {
     105                        testParser(     '<p><b>bold<i>ita</b>lic</i></p>',
     106                                                '<p><b>bold<i>ita</i></b><i>lic</i></p>' );
     107                },
     108
     109                /**
     110                 * Test force simple ampersand.
     111                 */
     112                test_parser_2867 : function()
     113                {
     114                        testParser(     '<a href="foo=&amp;bar="></a>',
     115                                                '<a href="foo=&bar="></a>' , { forceSimpleAmpersand : true } );
     116                },
     117
     118                test_ticket_3195 : function()
     119                {
     120                        testParser(     '<table><tr><td>1</td><p>2</p><td>3</td></tr></table>',
     121                                                '<p>2</p><table><tr><td>1</td><td>3</td></tr></table>' );
     122                },
     123
    28124                name : document.title
    29125        };
    30126})() );
     
    29125        };
    30126})() );
    31127
     128// Uncomment the following to run a single test.
     129// window.onload = tc.test_parser_7;
     130
    32131        //]]>
    33132        </script>
    34133</head>
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy