Ticket #3195: 3195.patch

File 3195.patch, 8.9 KB (added by Frederico Caldeira Knabben, 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                }
    9093
     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                }
     123
    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 )
    128158                                {
    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 ] )
     159                                        addElement( currentNode, currentNode.parent );
     160                                }
     161                                else
     162                                {
     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 );
    139171
     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                                        }
     180
    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
    162207                parser.onTagClose = function( tagName )
    163208                {
    164                         var closingElement = currentNode,
    165                                 index = 0;
     209                        var index = 0;
    166210
    167                         while ( closingElement && closingElement.name != tagName )
     211                        while ( currentNode.type && currentNode.name != tagName )
    168212                        {
    169213                                // If this is an inline element, add it to the pending list, so
    170214                                // it will continue after the closing tag.
    171                                 if ( !closingElement._.isBlockLike )
     215                                if ( !currentNode._.isBlockLike )
    172216                                {
    173                                         pendingInline.unshift( closingElement );
     217                                        pendingInline.unshift( currentNode );
    174218
    175219                                        // Increase the index, so it will not get checked again in
    176220                                        // the pending list check that follows.
    177221                                        index++;
    178222                                }
    179223
    180                                 closingElement = closingElement.parent;
     224                                addElement( currentNode, currentNode.parent );
     225                                currentNode = currentNode.parent;
    181226                        }
    182227
    183                         if ( closingElement )
    184                                 currentNode = closingElement.parent;
     228                        if ( currentNode.type )
     229                        {
     230                                var node = currentNode;
     231                                addElement( currentNode, currentNode.parent );
     232
     233                                // The parent should start receiving new nodes now, except if
     234                                // addElement changed the currentNode.
     235                                if ( node == currentNode )
     236                                        currentNode = currentNode.parent;
     237                        }
    185238                        else if ( pendingInline.length > index )
    186239                        {
    187240                                // If we didn't find any parent to be closed, let's check the
     
    223276                        currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
    224277                };
    225278
     279                // Parse it.
    226280                parser.parse( fragmentHtml );
    227281
     282                // Close all pending nodes.
     283                while ( currentNode.type )
     284                {
     285                        var parent = currentNode.parent,
     286                                node = currentNode;
     287
     288                        if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
     289                        {
     290                                currentNode = parent;
     291                                parser.onTagOpen( 'p', {} );
     292                                parent = currentNode;
     293                        }
     294
     295                        parent.add( node );
     296                        currentNode = parent;
     297                }
     298
    228299                return fragment;
    229300        };
    230301
  • _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;
    1719
     20        function testParser( input, expected )
     21        {
     22                var fragment = CKEDITOR.htmlParser.fragment.fromHtml( input, true ),
     23                        writer = new CKEDITOR.htmlParser.basicWriter();
     24
     25                fragment.writeHtml( writer );
     26
     27                assert.areSame( expected, writer.getHtml( true ) );
     28        }
     29
    1830        return {
    1931                test_fromHtml_1 : function()
    2032                {
     
    2537                        assert.areSame( 'p', fragment.children[0].name, 'Wrong child name' );
    2638                },
    2739
     40                test_parser_1 : function()
     41                {
     42                        testParser(     '<table><tr><td>1</td><p><b>2</b> Test</p><td>3</td></tr></table>',
     43                                                '<p><b>2</b> Test</p><table><tr><td>1</td><td>3</td></tr></table>' );
     44                },
     45
     46                test_parser_2 : function()
     47                {
     48                        testParser(     '<b><table><tr><td>1</td><td>2</td></tr></table></b>',
     49                                                '<table><tr><td><b>1</b></td><td><b>2</b></td></tr></table>' );
     50                },
     51
     52                test_parser_3_1 : function()
     53                {
     54                        testParser(     '<b><i>Table:<table><tr><td>1</td><td>2</td></tr></table></i></b>',
     55                                                '<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>' );
     56                },
     57
     58                test_parser_3_2 : function()
     59                {
     60                        testParser(     '<b><i><table><tr><td>1</td><td>2</td></tr></table>Table</i></b>',
     61                                                '<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>' );
     62                },
     63
     64                test_parser_4 : function()
     65                {
     66                        testParser(     '<b><i>Test',
     67                                                '<p><b><i>Test</i></b></p>' );
     68                },
     69
     70                test_parser_5 : function()
     71                {
     72                        testParser(     '<p>Para 1<p>Para 2<p>Para 3',
     73                                                '<p>Para 1</p><p>Para 2</p><p>Para 3</p>' );
     74                },
     75
     76                test_parser_6 : function()
     77                {
     78                        testParser(     '<b>A</b><i>B</i>',
     79                                                '<p><b>A</b><i>B</i></p>' );
     80                },
     81
     82                test_parser_7 : function()
     83                {
     84                        testParser(     '<p>Para 1<hr>Para 2<h1>Para 3',
     85                                                '<p>Para 1</p><hr /><p>Para 2</p><h1>Para 3</h1>' );
     86                },
     87
     88                test_ticket_3195 : function()
     89                {
     90                        testParser(     '<table><tr><td>1</td><p>2</p><td>3</td></tr></table>',
     91                                                '<p>2</p><table><tr><td>1</td><td>3</td></tr></table>' );
     92                },
     93
    2894                name : document.title
    2995        };
    3096})() );
    3197
     98// Uncomment the following to run a single test.
     99// window.onload = tc.test_parser_7;
     100
    32101        //]]>
    33102        </script>
    34103</head>
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy