/*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
html = [],\r
fragment = new CKEDITOR.htmlParser.fragment(),\r
pendingInline = [],\r
+ pendingBRs = [],\r
currentNode = fragment,\r
// Indicate we're inside a <pre> element, spaces should be touched differently.\r
inPre = false,\r
\r
function checkPending( newTagName )\r
{\r
+ var pendingBRsSent;\r
+\r
if ( pendingInline.length > 0 )\r
{\r
for ( var i = 0 ; i < pendingInline.length ; i++ )\r
\r
if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) )\r
{\r
+ if ( !pendingBRsSent )\r
+ {\r
+ sendPendingBRs();\r
+ pendingBRsSent = 1;\r
+ }\r
+\r
// Get a clone for the pending element.\r
pendingElement = pendingElement.clone();\r
\r
}\r
}\r
\r
+ function sendPendingBRs( brsToIgnore )\r
+ {\r
+ while ( pendingBRs.length - ( brsToIgnore || 0 ) > 0 )\r
+ currentNode.add( pendingBRs.shift() );\r
+ }\r
+\r
function addElement( element, target, enforceCurrent )\r
{\r
target = target || currentNode || fragment;\r
\r
- // If the target is the fragment and this element can't go inside\r
+ // If the target is the fragment and this inline element can't go inside\r
// body (if fixForBody).\r
if ( fixForBody && !target.type )\r
{\r
var elementName, realElementName;\r
if ( element.attributes\r
&& ( realElementName =\r
- element.attributes[ '_cke_real_element_type' ] ) )\r
+ element.attributes[ 'data-cke-real-element-type' ] ) )\r
elementName = realElementName;\r
else\r
elementName = element.name;\r
- if ( !( elementName in CKEDITOR.dtd.$body ) )\r
+\r
+ if ( elementName && elementName in CKEDITOR.dtd.$inline )\r
{\r
var savedCurrent = currentNode;\r
\r
return;\r
}\r
\r
- var currentName = currentNode.name,\r
- currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );\r
+ if ( tagName == 'br' )\r
+ {\r
+ pendingBRs.push( element );\r
+ return;\r
+ }\r
+\r
+ var currentName = currentNode.name;\r
+\r
+ var currentDtd = currentName\r
+ && ( CKEDITOR.dtd[ currentName ]\r
+ || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );\r
\r
// If the element cannot be child of the current element.\r
- if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )\r
+ if ( currentDtd // Fragment could receive any elements.\r
+ && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )\r
{\r
- // If this is the fragment node, just ignore this tag and add\r
- // its children.\r
- if ( !currentName )\r
- return;\r
\r
var reApply = false,\r
addPoint; // New position to start adding nodes.\r
\r
- // Fixing malformed nested lists(#3828).\r
- if( tagName in listBlocks\r
+ // Fixing malformed nested lists by moving it into a previous list item. (#3828)\r
+ if ( tagName in listBlocks\r
&& currentName in listBlocks )\r
{\r
var children = currentNode.children,\r
lastChild = children[ children.length - 1 ];\r
- // Move inner list into to previous list item if any.\r
- if( lastChild && lastChild.name in listItems )\r
- returnPoint = currentNode, addPoint = lastChild;\r
- // Move inner list outside in the worst case.\r
- else\r
- addElement( currentNode, currentNode.parent );\r
+\r
+ // Establish the list item if it's not existed.\r
+ if ( !( lastChild && lastChild.name in listItems ) )\r
+ addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );\r
+\r
+ returnPoint = currentNode, addPoint = lastChild;\r
}\r
// If the element name is the same as the current element name,\r
// then just close the current one and append the new one to the\r
{\r
addElement( currentNode, currentNode.parent );\r
}\r
+ else if ( tagName in CKEDITOR.dtd.$listItem )\r
+ {\r
+ parser.onTagOpen( 'ul', {} );\r
+ addPoint = currentNode;\r
+ reApply = true;\r
+ }\r
else\r
{\r
if ( nonBreakingBlocks[ currentName ] )\r
reApply = true;\r
}\r
\r
- if( addPoint )\r
+ if ( addPoint )\r
currentNode = addPoint;\r
// Try adding it to the return point, or the parent element.\r
else\r
}\r
\r
checkPending( tagName );\r
+ sendPendingBRs();\r
\r
element.parent = currentNode;\r
element.returnPoint = returnPoint;\r
\r
parser.onTagClose = function( tagName )\r
{\r
- var index = 0,\r
- pendingAdd = [],\r
+ // Check if there is any pending tag to be closed.\r
+ for ( var i = pendingInline.length - 1 ; i >= 0 ; i-- )\r
+ {\r
+ // If found, just remove it from the list.\r
+ if ( tagName == pendingInline[ i ].name )\r
+ {\r
+ pendingInline.splice( i, 1 );\r
+ return;\r
+ }\r
+ }\r
+\r
+ var pendingAdd = [],\r
+ newPendingInline = [],\r
candidate = currentNode;\r
\r
while ( candidate.type && candidate.name != tagName )\r
{\r
- // If this is an inline element, add it to the pending list, so\r
- // it will continue after the closing tag.\r
+ // If this is an inline element, add it to the pending list, if we're\r
+ // really closing one of the parents element later, they will continue\r
+ // after it.\r
if ( !candidate._.isBlockLike )\r
- {\r
- pendingInline.unshift( candidate );\r
-\r
- // Increase the index, so it will not get checked again in\r
- // the pending list check that follows.\r
- index++;\r
- }\r
+ newPendingInline.unshift( candidate );\r
\r
// This node should be added to it's parent at this point. But,\r
// it should happen only if the closing tag is really closing\r
if ( candidate.type )\r
{\r
// Add all elements that have been found in the above loop.\r
- for ( var i = 0 ; i < pendingAdd.length ; i++ )\r
+ for ( i = 0 ; i < pendingAdd.length ; i++ )\r
{\r
var node = pendingAdd[ i ];\r
addElement( node, node.parent );\r
\r
currentNode = candidate;\r
\r
- if( currentNode.name == 'pre' )\r
+ if ( currentNode.name == 'pre' )\r
inPre = false;\r
\r
+ if ( candidate._.isBlockLike )\r
+ sendPendingBRs();\r
+\r
addElement( candidate, candidate.parent );\r
\r
// The parent should start receiving new nodes now, except if\r
// addElement changed the currentNode.\r
if ( candidate == currentNode )\r
currentNode = currentNode.parent;\r
- }\r
- // The tag is not actually closing anything, thus we need invalidate\r
- // the pending elements.(#3862)\r
- else\r
- {\r
- pendingInline.splice( 0, index );\r
- index = 0;\r
- }\r
-\r
- // Check if there is any pending tag to be closed.\r
- for ( ; index < pendingInline.length ; index++ )\r
- {\r
- // If found, just remove it from the list.\r
- if ( tagName == pendingInline[ index ].name )\r
- {\r
- pendingInline.splice( index, 1 );\r
\r
- // Decrease the index so we continue from the next one.\r
- index--;\r
- }\r
+ pendingInline = pendingInline.concat( newPendingInline );\r
}\r
+\r
+ if ( tagName == 'body' )\r
+ fixForBody = false;\r
};\r
\r
parser.onText = function( text )\r
return;\r
}\r
\r
+ sendPendingBRs();\r
checkPending();\r
\r
- if ( fixForBody && !currentNode.type )\r
+ if ( fixForBody\r
+ && ( !currentNode.type || currentNode.name == 'body' )\r
+ && CKEDITOR.tools.trim( text ) )\r
+ {\r
this.onTagOpen( fixForBody, {} );\r
+ }\r
\r
// Shrinking consequential spaces into one single for all elements\r
// text contents.\r
\r
parser.onComment = function( comment )\r
{\r
+ sendPendingBRs();\r
+ checkPending();\r
currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );\r
};\r
\r
// Parse it.\r
parser.parse( fragmentHtml );\r
\r
+ // Send all pending BRs except one, which we consider a unwanted bogus. (#5293)\r
+ sendPendingBRs( !CKEDITOR.env.ie && 1 );\r
+\r
// Close all pending nodes.\r
while ( currentNode.type )\r
{\r
var parent = currentNode.parent,\r
node = currentNode;\r
\r
- if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )\r
+ if ( fixForBody\r
+ && ( !parent.type || parent.name == 'body' )\r
+ && !CKEDITOR.dtd.$body[ node.name ] )\r
{\r
currentNode = parent;\r
parser.onTagOpen( fixForBody, {} );\r
*/\r
writeHtml : function( writer, filter )\r
{\r
- for ( var i = 0, len = this.children.length ; i < len ; i++ )\r
+ var isChildrenFiltered;\r
+ this.filterChildren = function()\r
+ {\r
+ var writer = new CKEDITOR.htmlParser.basicWriter();\r
+ this.writeChildrenHtml.call( this, writer, filter, true );\r
+ var html = writer.getHtml();\r
+ this.children = new CKEDITOR.htmlParser.fragment.fromHtml( html ).children;\r
+ isChildrenFiltered = 1;\r
+ };\r
+\r
+ // Filtering the root fragment before anything else.\r
+ !this.name && filter && filter.onFragment( this );\r
+\r
+ this.writeChildrenHtml( writer, isChildrenFiltered ? null : filter );\r
+ },\r
+\r
+ writeChildrenHtml : function( writer, filter )\r
+ {\r
+ for ( var i = 0 ; i < this.children.length ; i++ )\r
this.children[i].writeHtml( writer, filter );\r
}\r
};\r