Ticket #3195: 3195_3.patch

File 3195_3.patch, 10.2 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,
     210                                pendingAdd = [];
    166211
    167                         while ( closingElement && closingElement.name != tagName )
     212                        while ( currentNode.type && currentNode.name != tagName )
    168213                        {
    169214                                // If this is an inline element, add it to the pending list, so
    170215                                // it will continue after the closing tag.
    171                                 if ( !closingElement._.isBlockLike )
     216                                if ( !currentNode._.isBlockLike )
    172217                                {
    173                                         pendingInline.unshift( closingElement );
     218                                        pendingInline.unshift( currentNode );
    174219
    175220                                        // Increase the index, so it will not get checked again in
    176221                                        // the pending list check that follows.
    177222                                        index++;
    178223                                }
    179224
    180                                 closingElement = closingElement.parent;
     225                                // This node should be added to it's parent at this point. But,
     226                                // it should happen only if the closing tag is really closing
     227                                // one of the nodes. So, for now, we just cache it.
     228                                pendingAdd.push( currentNode );
     229
     230                                currentNode = currentNode.parent;
    181231                        }
    182232
    183                         if ( closingElement )
    184                                 currentNode = closingElement.parent;
     233                        if ( currentNode.type )
     234                        {
     235                                var node;
     236
     237                                // Add all elements that have been found in the above loop.
     238                                for ( var i = 0 ; i < pendingAdd.length ; i++ )
     239                                {
     240                                        node = pendingAdd[ i ];
     241                                        addElement( node, node.parent );
     242                                }
     243
     244                                node = currentNode;
     245                                addElement( currentNode, currentNode.parent );
     246
     247                                // The parent should start receiving new nodes now, except if
     248                                // addElement changed the currentNode.
     249                                if ( node == currentNode )
     250                                        currentNode = currentNode.parent;
     251                        }
    185252                        else if ( pendingInline.length > index )
    186253                        {
    187254                                // If we didn't find any parent to be closed, let's check the
     
    223290                        currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
    224291                };
    225292
     293                // Parse it.
    226294                parser.parse( fragmentHtml );
    227295
     296                // Close all pending nodes.
     297                while ( currentNode.type )
     298                {
     299                        var parent = currentNode.parent,
     300                                node = currentNode;
     301
     302                        if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
     303                        {
     304                                currentNode = parent;
     305                                parser.onTagOpen( 'p', {} );
     306                                parent = currentNode;
     307                        }
     308
     309                        parent.add( node );
     310                        currentNode = parent;
     311                }
     312
    228313                return fragment;
    229314        };
    230315
  • _source/tests/core/htmlparser/fragment.html

     
    1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    22<html xmlns="http://www.w3.org/1999/xhtml">
    33<head>
    44        <title>CKEDITOR.htmlParser.fragment</title>
     
    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                /**
     89                 * Test remove empty inline element.
     90                 */
     91                test_parser_8 : function()
     92                {
     93                        testParser(     '<p><b></b>text</p>',
     94                                                '<p>text</p>' );
     95                },
     96               
     97                test_parser_8_2 : function()
     98                {
     99                        testParser(     '<p><b><i></b></i>text</p>',
     100                                                '<p>text</p>' );
     101                },
     102               
     103                /**
     104                 * Test fixing malformed inline element closing.
     105                 */
     106                test_parser_9 : function()
     107                {
     108                        testParser(     '<p><b>bold<i>ita</b>lic</i></p>',
     109                                                '<p><b>bold<i>ita</i></b><i>lic</i></p>' );
     110                },
     111
     112                test_ticket_3195 : function()
     113                {
     114                        testParser(     '<table><tr><td>1</td><p>2</p><td>3</td></tr></table>',
     115                                                '<p>2</p><table><tr><td>1</td><td>3</td></tr></table>' );
     116                },
     117
    28118                name : document.title
    29119        };
    30120})() );
    31121
     122// Uncomment the following to run a single test.
     123// window.onload = tc.test_parser_7;
     124
    32125        //]]>
    33126        </script>
    34127</head>
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy