JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-4.0_full
[ckeditor.git] / _source / core / htmlparser / fragment.js
diff --git a/_source/core/htmlparser/fragment.js b/_source/core/htmlparser/fragment.js
deleted file mode 100644 (file)
index a00ac2e..0000000
+++ /dev/null
@@ -1,537 +0,0 @@
-/*\r
-Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.\r
-For licensing, see LICENSE.html or http://ckeditor.com/license\r
-*/\r
-\r
-/**\r
- * A lightweight representation of an HTML DOM structure.\r
- * @constructor\r
- * @example\r
- */\r
-CKEDITOR.htmlParser.fragment = function()\r
-{\r
-       /**\r
-        * The nodes contained in the root of this fragment.\r
-        * @type Array\r
-        * @example\r
-        * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );\r
-        * alert( fragment.children.length );  "2"\r
-        */\r
-       this.children = [];\r
-\r
-       /**\r
-        * Get the fragment parent. Should always be null.\r
-        * @type Object\r
-        * @default null\r
-        * @example\r
-        */\r
-       this.parent = null;\r
-\r
-       /** @private */\r
-       this._ =\r
-       {\r
-               isBlockLike : true,\r
-               hasInlineStarted : false\r
-       };\r
-};\r
-\r
-(function()\r
-{\r
-       // Block-level elements whose internal structure should be respected during\r
-       // 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
-        * @param {Number} [fixForBody=false] Wrap body with specified element if needed.\r
-        * @param {CKEDITOR.htmlParser.element} contextNode Parse the html as the content of this element.\r
-        * @returns CKEDITOR.htmlParser.fragment The fragment created.\r
-        * @example\r
-        * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );\r
-        * alert( fragment.children[0].name );  "b"\r
-        * alert( fragment.children[1].value );  " Text"\r
-        */\r
-       CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody, contextNode )\r
-       {\r
-               var parser = new CKEDITOR.htmlParser(),\r
-                       fragment = contextNode || new CKEDITOR.htmlParser.fragment(),\r
-                       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
-               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
-                                       var pendingElement = pendingInline[ i ],\r
-                                               pendingName = pendingElement.name,\r
-                                               pendingDtd = CKEDITOR.dtd[ pendingName ],\r
-                                               currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];\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
-                                               // Add it to the current node and make it the current,\r
-                                               // so the new element will be added inside of it.\r
-                                               pendingElement.parent = currentNode;\r
-                                               currentNode = pendingElement;\r
-\r
-                                               // Remove the pending element (back the index by one\r
-                                               // to properly process the next entry).\r
-                                               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
-\r
-               function sendPendingBRs()\r
-               {\r
-                       while ( pendingBRs.length )\r
-                               currentNode.add( pendingBRs.shift() );\r
-               }\r
-\r
-               /*\r
-               * Beside of simply append specified element to target, this function also takes\r
-               * care of other dirty lifts like forcing block in body, trimming spaces at\r
-               * the block boundaries etc.\r
-               *\r
-               * @param {Element} element  The element to be added as the last child of {@link target}.\r
-               * @param {Element} target The parent element to relieve the new node.\r
-               * @param {Boolean} [moveCurrent=false] Don't change the "currentNode" global unless\r
-               * there's a return point node specified on the element, otherwise move current onto {@link target} node.\r
-                */\r
-               function addElement( element, target, moveCurrent )\r
-               {\r
-                       // Ignore any element that has already been added.\r
-                       if ( element.previous !== undefined )\r
-                               return;\r
-\r
-                       target = target || currentNode || fragment;\r
-\r
-                       // Current element might be mangled by fix body below,\r
-                       // save it for restore later.\r
-                       var savedCurrent = currentNode;\r
-\r
-                       // If the target is the fragment and this inline element can't go inside\r
-                       // body (if fixForBody).\r
-                       if ( fixForBody && ( !target.type || target.name == 'body' ) )\r
-                       {\r
-                               var elementName, realElementName;\r
-                               if ( element.attributes\r
-                                        && ( realElementName =\r
-                                                 element.attributes[ 'data-cke-real-element-type' ] ) )\r
-                                       elementName = realElementName;\r
-                               else\r
-                                       elementName =  element.name;\r
-\r
-                               if ( elementName && !( elementName in CKEDITOR.dtd.$body || elementName == 'body' || element.isOrphan ) )\r
-                               {\r
-                                       // Create a <p> in the fragment.\r
-                                       currentNode = target;\r
-                                       parser.onTagOpen( fixForBody, {} );\r
-\r
-                                       // The new target now is the <p>.\r
-                                       element.returnPoint = target = currentNode;\r
-                               }\r
-                       }\r
-\r
-                       // Rtrim empty spaces on block end boundary. (#3585)\r
-                       if ( element._.isBlockLike\r
-                                && element.name != 'pre' && element.name != 'textarea' )\r
-                       {\r
-\r
-                               var length = element.children.length,\r
-                                       lastChild = element.children[ length - 1 ],\r
-                                       text;\r
-                               if ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT )\r
-                               {\r
-                                       if ( !( text = CKEDITOR.tools.rtrim( lastChild.value ) ) )\r
-                                               element.children.length = length -1;\r
-                                       else\r
-                                               lastChild.value = text;\r
-                               }\r
-                       }\r
-\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
-                               delete element.returnPoint;\r
-                       }\r
-                       else\r
-                               currentNode = moveCurrent ? target : savedCurrent;\r
-               }\r
-\r
-               parser.onTagOpen = function( tagName, attributes, selfClosing, optionalClose )\r
-               {\r
-                       var element = new CKEDITOR.htmlParser.element( tagName, attributes );\r
-\r
-                       // "isEmpty" will be always "false" for unknown elements, so we\r
-                       // must force it if the parser has identified it as a selfClosing tag.\r
-                       if ( element.isUnknown && selfClosing )\r
-                               element.isEmpty = true;\r
-\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 ( isRemoveEmpty( element ) )\r
-                       {\r
-                               pendingInline.push( element );\r
-                               return;\r
-                       }\r
-                       else if ( tagName == 'pre' )\r
-                               inPre = true;\r
-                       else if ( tagName == 'br' && inPre )\r
-                       {\r
-                               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
-                               pendingBRs.push( element );\r
-                               return;\r
-                       }\r
-\r
-                       while( 1 )\r
-                       {\r
-                               var currentName = currentNode.name;\r
-\r
-                               var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ]\r
-                                               || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) )\r
-                                               : rootDtd;\r
-\r
-                               // If the element cannot be child of the current element.\r
-                               if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )\r
-                               {\r
-                                       // Current node doesn't have a close tag, time for a close\r
-                                       // as this element isn't fit in. (#7497)\r
-                                       if ( currentNode.isOptionalClose )\r
-                                               parser.onTagClose( currentName );\r
-                                       // Fixing malformed nested lists by moving it into a previous list item. (#3828)\r
-                                       else if ( tagName in listBlocks\r
-                                               && currentName in listBlocks )\r
-                                       {\r
-                                               var children = currentNode.children,\r
-                                                       lastChild = children[ children.length - 1 ];\r
-\r
-                                               // Establish the list item if it's not existed.\r
-                                               if ( !( lastChild && lastChild.name == 'li' ) )\r
-                                                       addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );\r
-\r
-                                               !element.returnPoint && ( element.returnPoint = currentNode );\r
-                                               currentNode = lastChild;\r
-                                       }\r
-                                       // Establish new list root for orphan list items.\r
-                                       else if ( tagName in CKEDITOR.dtd.$listItem && currentName != tagName )\r
-                                               parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {}, 0, 1 );\r
-                                       // We're inside a structural block like table and list, AND the incoming element\r
-                                       // is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,\r
-                                       // and most importantly, return back to here once this element is added,\r
-                                       // e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>\r
-                                       else if ( currentName in nonBreakingBlocks && currentName != tagName )\r
-                                       {\r
-                                               !element.returnPoint && ( element.returnPoint = currentNode );\r
-                                               currentNode = currentNode.parent;\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               // The current element is an inline element, which\r
-                                               // need to be continued even after the close, so put\r
-                                               // it in the pending list.\r
-                                               if ( currentName in CKEDITOR.dtd.$inline )\r
-                                                       pendingInline.unshift( currentNode );\r
-\r
-                                               // The most common case where we just need to close the\r
-                                               // current one and append the new one to the parent.\r
-                                               if ( currentNode.parent )\r
-                                                       addElement( currentNode, currentNode.parent, 1 );\r
-                                               // We've tried our best to fix the embarrassment here, while\r
-                                               // this element still doesn't find it's parent, mark it as\r
-                                               // orphan and show our tolerance to it.\r
-                                               else\r
-                                               {\r
-                                                       element.isOrphan = 1;\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               else\r
-                                       break;\r
-                       }\r
-\r
-                       checkPending( tagName );\r
-                       sendPendingBRs();\r
-\r
-                       element.parent = currentNode;\r
-\r
-                       if ( element.isEmpty )\r
-                               addElement( element );\r
-                       else\r
-                               currentNode = element;\r
-               };\r
-\r
-               parser.onTagClose = function( tagName )\r
-               {\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 != fragment && candidate.name != tagName )\r
-                       {\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
-                                       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
-                               // one of the nodes. So, for now, we just cache it.\r
-                               pendingAdd.push( candidate );\r
-\r
-                               // Make sure return point is properly restored.\r
-                               candidate = candidate.returnPoint || candidate.parent;\r
-                       }\r
-\r
-                       if ( candidate != fragment )\r
-                       {\r
-                               // Add all elements that have been found in the above loop.\r
-                               for ( i = 0 ; i < pendingAdd.length ; i++ )\r
-                               {\r
-                                       var node = pendingAdd[ i ];\r
-                                       addElement( node, node.parent );\r
-                               }\r
-\r
-                               currentNode = candidate;\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
-                               pendingInline = pendingInline.concat( newPendingInline );\r
-                       }\r
-\r
-                       if ( tagName == 'body' )\r
-                               fixForBody = false;\r
-               };\r
-\r
-               parser.onText = function( text )\r
-               {\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
-                               if ( text.length === 0 )\r
-                                       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
-                       if ( fixForBody\r
-                                && ( !currentNode.type || currentNode.name == 'body' )\r
-                                && CKEDITOR.tools.trim( text ) )\r
-                       {\r
-                               this.onTagOpen( fixForBody, {}, 0, 1 );\r
-                       }\r
-\r
-                       // Shrinking consequential spaces into one single for all elements\r
-                       // text contents.\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
-               };\r
-\r
-               parser.onCDATA = function( cdata )\r
-               {\r
-                       currentNode.add( new CKEDITOR.htmlParser.cdata( cdata ) );\r
-               };\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, make sure return point is properly restored.\r
-               while ( currentNode != fragment )\r
-                       addElement( currentNode, currentNode.parent, 1 );\r
-\r
-               return fragment;\r
-       };\r
-\r
-       CKEDITOR.htmlParser.fragment.prototype =\r
-       {\r
-               /**\r
-                * Adds a node to this fragment.\r
-                * @param {Object} node The node to be added. It can be any of of the\r
-                *              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, index )\r
-               {\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
-                               // the right of it.\r
-                               if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT )\r
-                               {\r
-                                       previous.value = CKEDITOR.tools.rtrim( previous.value );\r
-\r
-                                       // If we have completely cleared the previous node.\r
-                                       if ( previous.value.length === 0 )\r
-                                       {\r
-                                               // Remove it from the list and add the node again.\r
-                                               this.children.pop();\r
-                                               this.add( node );\r
-                                               return;\r
-                                       }\r
-                               }\r
-\r
-                               previous.next = node;\r
-                       }\r
-\r
-                       node.previous = previous;\r
-                       node.parent = this;\r
-\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
-\r
-               /**\r
-                * Writes the fragment HTML to a CKEDITOR.htmlWriter.\r
-                * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.\r
-                * @example\r
-                * var writer = new CKEDITOR.htmlWriter();\r
-                * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '&lt;P&gt;&lt;B&gt;Example' );\r
-                * fragment.writeHtml( writer )\r
-                * alert( writer.getHtml() );  "&lt;p&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/p&gt;"\r
-                */\r
-               writeHtml : function( writer, filter )\r
-               {\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
-})();\r