Ticket #3195: 3195_3.patch
File 3195_3.patch, 10.2 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 } 90 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 } 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 ) 128 158 { 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 ] ) 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 ); 139 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 } 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 162 207 parser.onTagClose = function( tagName ) 163 208 { 164 var closingElement = currentNode,165 index = 0;209 var index = 0, 210 pendingAdd = []; 166 211 167 while ( c losingElement && closingElement.name != tagName )212 while ( currentNode.type && currentNode.name != tagName ) 168 213 { 169 214 // If this is an inline element, add it to the pending list, so 170 215 // it will continue after the closing tag. 171 if ( !c losingElement._.isBlockLike )216 if ( !currentNode._.isBlockLike ) 172 217 { 173 pendingInline.unshift( c losingElement);218 pendingInline.unshift( currentNode ); 174 219 175 220 // Increase the index, so it will not get checked again in 176 221 // the pending list check that follows. 177 222 index++; 178 223 } 179 224 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; 181 231 } 182 232 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 } 185 252 else if ( pendingInline.length > index ) 186 253 { 187 254 // If we didn't find any parent to be closed, let's check the … … 223 290 currentNode.add( new CKEDITOR.htmlParser.comment( comment ) ); 224 291 }; 225 292 293 // Parse it. 226 294 parser.parse( fragmentHtml ); 227 295 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 228 313 return fragment; 229 314 }; 230 315 -
_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"> 2 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 3 <head> 4 4 <title>CKEDITOR.htmlParser.fragment</title> … … 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; 17 19 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 18 30 return { 19 31 test_fromHtml_1 : function() 20 32 { … … 25 37 assert.areSame( 'p', fragment.children[0].name, 'Wrong child name' ); 26 38 }, 27 39 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 28 118 name : document.title 29 119 }; 30 120 })() ); 31 121 122 // Uncomment the following to run a single test. 123 // window.onload = tc.test_parser_7; 124 32 125 //]]> 33 126 </script> 34 127 </head>