Ticket #3195: 3195_2.patch
File 3195_2.patch, 10.0 KB (added by , 15 years ago) |
---|
-
_source/core/htmlparser/fragment.js
41 41 // (expect empty elements). 42 42 var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1}; 43 43 44 var tableBlocks = {table:1,tbody:1,thead:1,tfoot:1,tr:1}; 45 44 46 /** 45 47 * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string. 46 48 * @param {String} fragmentHtml The HTML to be parsed, filling the fragment. … … 56 58 html = [], 57 59 fragment = new CKEDITOR.htmlParser.fragment(), 58 60 pendingInline = [], 59 currentNode = fragment; 61 currentNode = fragment, 62 returnPoint; 60 63 61 var checkPending = function( newTagName )64 function checkPending( newTagName ) 62 65 { 63 66 if ( pendingInline.length > 0 ) 64 67 { … … 76 79 77 80 // Add it to the current node and make it the current, 78 81 // so the new element will be added inside of it. 79 currentNode.add( pendingElement );82 pendingElement.parent = currentNode; 80 83 currentNode = pendingElement; 81 84 82 85 // Remove the pending element (back the index by one … … 86 89 } 87 90 } 88 91 } 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 } 90 123 91 124 parser.onTagOpen = function( tagName, attributes, selfClosing ) 92 125 { 93 if ( fixForBody && !currentNode.type && !CKEDITOR.dtd.$body[ tagName ] )94 this.onTagOpen( 'p', {} );95 96 126 var element = new CKEDITOR.htmlParser.element( tagName, attributes ); 97 127 98 128 // "isEmpty" will be always "false" for unknown elements, so we … … 123 153 // If the element name is the same as the current element name, 124 154 // then just close the current one and append the new one to the 125 155 // 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 128 162 { 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 ] ) 133 164 { 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; 138 167 } 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 } 139 180 140 181 reApply = true; 141 182 } … … 142 183 143 184 // In any of the above cases, we'll be adding, or trying to 144 185 // add it to the parent. 145 currentNode = currentNode. parent;186 currentNode = currentNode.returnPoint || currentNode.parent; 146 187 147 188 if ( reApply ) 148 189 { … … 153 194 154 195 checkPending( tagName ); 155 196 156 currentNode.add( element ); 197 element.parent = currentNode; 198 element.returnPoint = returnPoint; 199 returnPoint = 0; 157 200 158 if ( !element.isEmpty ) 201 if ( element.isEmpty ) 202 addElement( element ); 203 else 159 204 currentNode = element; 160 205 }; 161 206 … … 161 206 162 207 parser.onTagClose = function( tagName ) 163 208 { 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; 166 218 167 while ( c losingElement && closingElement.name != tagName )219 while ( currentNode.type && currentNode.name != tagName ) 168 220 { 169 221 // If this is an inline element, add it to the pending list, so 170 222 // it will continue after the closing tag. 171 if ( !c losingElement._.isBlockLike )223 if ( !currentNode._.isBlockLike ) 172 224 { 173 pendingInline.unshift( c losingElement);225 pendingInline.unshift( currentNode ); 174 226 175 227 // Increase the index, so it will not get checked again in 176 228 // the pending list check that follows. … … 177 229 index++; 178 230 } 179 231 180 closingElement = closingElement.parent; 232 addElement( currentNode, currentNode.parent ); 233 currentNode = currentNode.parent; 181 234 } 182 235 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 } 185 246 else if ( pendingInline.length > index ) 186 247 { 187 248 // If we didn't find any parent to be closed, let's check the … … 223 284 currentNode.add( new CKEDITOR.htmlParser.comment( comment ) ); 224 285 }; 225 286 287 // Parse it. 226 288 parser.parse( fragmentHtml ); 227 289 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 228 307 return fragment; 229 308 }; 230 309 -
_source/tests/core/htmlparser/fragment.html
10 10 <script type="text/javascript"> 11 11 //<![CDATA[ 12 12 13 CKEDITOR.test.addTestCase( (function() 13 var tc; 14 15 CKEDITOR.test.addTestCase( tc = (function() 14 16 { 15 17 // Local reference to the "assert" object. 16 18 var assert = CKEDITOR.test.assert; … … 15 17 // Local reference to the "assert" object. 16 18 var assert = CKEDITOR.test.assert; 17 19 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 18 33 return { 19 34 test_fromHtml_1 : function() 20 35 { … … 25 40 assert.areSame( 'p', fragment.children[0].name, 'Wrong child name' ); 26 41 }, 27 42 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=&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 28 124 name : document.title 29 125 }; 30 126 })() ); … … 29 125 }; 30 126 })() ); 31 127 128 // Uncomment the following to run a single test. 129 // window.onload = tc.test_parser_7; 130 32 131 //]]> 33 132 </script> 34 133 </head>