JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.6.3
[ckeditor.git] / _source / core / htmlparser / fragment.js
index 3dfc788..a80fd1d 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
-Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
 For licensing, see LICENSE.html or http://ckeditor.com/license\r
 */\r
 \r
@@ -41,11 +41,21 @@ CKEDITOR.htmlParser.fragment = function()
        // parser fixing.\r
        var nonBreakingBlocks = CKEDITOR.tools.extend( { table:1,ul:1,ol:1,dl:1 }, CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl );\r
 \r
+       // IE < 8 don't output the close tag on definition list items. (#6975)\r
+       var optionalCloseTags = CKEDITOR.env.ie && CKEDITOR.env.version < 8 ? { dd : 1, dt :1 } : {};\r
+\r
        var listBlocks = { ol:1, ul:1 };\r
 \r
        // Dtd of the fragment element, basically it accept anything except for intermediate structure, e.g. orphan <li>.\r
        var rootDtd = CKEDITOR.tools.extend( {}, { html: 1 }, CKEDITOR.dtd.html, CKEDITOR.dtd.body, CKEDITOR.dtd.head, { style:1,script:1 } );\r
 \r
+       function isRemoveEmpty( node )\r
+       {\r
+               // Empty link is to be removed when empty but not anchor. (#7894)\r
+               return node.name == 'a' && node.attributes.href\r
+                       || CKEDITOR.dtd.$removeEmpty[ node.name ];\r
+       }\r
+\r
        /**\r
         * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.\r
         * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.\r
@@ -64,6 +74,8 @@ CKEDITOR.htmlParser.fragment = function()
                        pendingInline = [],\r
                        pendingBRs = [],\r
                        currentNode = fragment,\r
+                   // Indicate we're inside a <textarea> element, spaces should be touched differently.\r
+                       inTextarea = false,\r
                    // Indicate we're inside a <pre> element, spaces should be touched differently.\r
                        inPre = false;\r
 \r
@@ -101,6 +113,13 @@ CKEDITOR.htmlParser.fragment = function()
                                                pendingInline.splice( i, 1 );\r
                                                i--;\r
                                        }\r
+                                       else\r
+                                       {\r
+                                               // Some element of the same type cannot be nested, flat them,\r
+                                               // e.g. <a href="#">foo<a href="#">bar</a></a>. (#7894)\r
+                                               if ( pendingName == currentNode.name )\r
+                                                       addElement( currentNode, currentNode.parent, 1 ), i--;\r
+                                       }\r
                                }\r
                        }\r
                }\r
@@ -158,7 +177,7 @@ CKEDITOR.htmlParser.fragment = function()
 \r
                        // Rtrim empty spaces on block end boundary. (#3585)\r
                        if ( element._.isBlockLike\r
-                                && element.name != 'pre' )\r
+                                && element.name != 'pre' && element.name != 'textarea' )\r
                        {\r
 \r
                                var length = element.children.length,\r
@@ -175,6 +194,13 @@ CKEDITOR.htmlParser.fragment = function()
 \r
                        target.add( element );\r
 \r
+                       if ( element.name == 'pre' )\r
+                               inPre = false;\r
+\r
+                       if ( element.name == 'textarea' )\r
+                               inTextarea = false;\r
+\r
+\r
                        if ( element.returnPoint )\r
                        {\r
                                currentNode = element.returnPoint;\r
@@ -193,10 +219,11 @@ CKEDITOR.htmlParser.fragment = function()
                        if ( element.isUnknown && selfClosing )\r
                                element.isEmpty = true;\r
 \r
-                       element.isOptionalClose = optionalClose;\r
+                       // Check for optional closed elements, including browser quirks and manually opened blocks.\r
+                       element.isOptionalClose = tagName in optionalCloseTags || optionalClose;\r
 \r
                        // This is a tag to be removed if empty, so do not add it immediately.\r
-                       if ( CKEDITOR.dtd.$removeEmpty[ tagName ] )\r
+                       if ( isRemoveEmpty( element ) )\r
                        {\r
                                pendingInline.push( element );\r
                                return;\r
@@ -208,6 +235,8 @@ CKEDITOR.htmlParser.fragment = function()
                                currentNode.add( new CKEDITOR.htmlParser.text( '\n' ) );\r
                                return;\r
                        }\r
+                       else if ( tagName == 'textarea' )\r
+                               inTextarea = true;\r
 \r
                        if ( tagName == 'br' )\r
                        {\r
@@ -338,9 +367,6 @@ CKEDITOR.htmlParser.fragment = function()
 \r
                                currentNode = candidate;\r
 \r
-                               if ( currentNode.name == 'pre' )\r
-                                       inPre = false;\r
-\r
                                if ( candidate._.isBlockLike )\r
                                        sendPendingBRs();\r
 \r
@@ -360,8 +386,8 @@ CKEDITOR.htmlParser.fragment = function()
 \r
                parser.onText = function( text )\r
                {\r
-                       // Trim empty spaces at beginning of text contents except <pre>.\r
-                       if ( ( !currentNode._.hasInlineStarted || pendingBRs.length ) && !inPre )\r
+                       // Trim empty spaces at beginning of text contents except <pre> and <textarea>.\r
+                       if ( ( !currentNode._.hasInlineStarted || pendingBRs.length ) && !inPre && !inTextarea )\r
                        {\r
                                text = CKEDITOR.tools.ltrim( text );\r
 \r
@@ -369,6 +395,24 @@ CKEDITOR.htmlParser.fragment = function()
                                        return;\r
                        }\r
 \r
+                       var currentName = currentNode.name,\r
+                       currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ]\r
+                                                       || ( currentNode._.isBlockLike ?\r
+                                                                CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd;\r
+\r
+                       // Fix orphan text in list/table. (#8540) (#8870)\r
+                       if ( !inTextarea &&\r
+                                !currentDtd [ '#' ] &&\r
+                                currentName in nonBreakingBlocks )\r
+                       {\r
+                               parser.onTagOpen( currentName in listBlocks ? 'li' :\r
+                                                                 currentName == 'dl' ? 'dd' :\r
+                                                                 currentName == 'table' ? 'tr' :\r
+                                                                 currentName == 'tr' ? 'td' : '' );\r
+                               parser.onText( text );\r
+                               return;\r
+                       }\r
+\r
                        sendPendingBRs();\r
                        checkPending();\r
 \r
@@ -381,7 +425,7 @@ CKEDITOR.htmlParser.fragment = function()
 \r
                        // Shrinking consequential spaces into one single for all elements\r
                        // text contents.\r
-                       if ( !inPre )\r
+                       if ( !inPre && !inTextarea )\r
                                text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' );\r
 \r
                        currentNode.add( new CKEDITOR.htmlParser.text( text ) );\r
@@ -420,13 +464,14 @@ CKEDITOR.htmlParser.fragment = function()
                 *              following types: {@link CKEDITOR.htmlParser.element},\r
                 *              {@link CKEDITOR.htmlParser.text} and\r
                 *              {@link CKEDITOR.htmlParser.comment}.\r
+                *      @param {Number} [index] From where the insertion happens.\r
                 * @example\r
                 */\r
-               add : function( node )\r
+               add : function( node, index )\r
                {\r
-                       var len = this.children.length,\r
-                               previous = len > 0 && this.children[ len - 1 ] || null;\r
+                       isNaN( index ) && ( index = this.children.length );\r
 \r
+                       var previous = index > 0 ? this.children[ index - 1 ] : null;\r
                        if ( previous )\r
                        {\r
                                // If the block to be appended is following text, trim spaces at\r
@@ -451,7 +496,7 @@ CKEDITOR.htmlParser.fragment = function()
                        node.previous = previous;\r
                        node.parent = this;\r
 \r
-                       this.children.push( node );\r
+                       this.children.splice( index, 0, node );\r
 \r
                        this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );\r
                },\r