+++ /dev/null
-/*\r
- * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
- * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
- *\r
- * == BEGIN LICENSE ==\r
- *\r
- * Licensed under the terms of any of the following licenses at your\r
- * choice:\r
- *\r
- * - GNU General Public License Version 2 or later (the "GPL")\r
- * http://www.gnu.org/licenses/gpl.html\r
- *\r
- * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
- * http://www.gnu.org/licenses/lgpl.html\r
- *\r
- * - Mozilla Public License Version 1.1 or later (the "MPL")\r
- * http://www.mozilla.org/MPL/MPL-1.1.html\r
- *\r
- * == END LICENSE ==\r
- *\r
- * This class partially implements the W3C DOM Range for browser that don't\r
- * support the standards (like IE):\r
- * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html\r
- */\r
-\r
-var FCKW3CRange = function( parentDocument )\r
-{\r
- this._Document = parentDocument ;\r
-\r
- this.startContainer = null ;\r
- this.startOffset = null ;\r
- this.endContainer = null ;\r
- this.endOffset = null ;\r
- this.collapsed = true ;\r
-}\r
-\r
-FCKW3CRange.CreateRange = function( parentDocument )\r
-{\r
- // We could opt to use the Range implementation of the browsers. The problem\r
- // is that every browser have different bugs on their implementations,\r
- // mostly related to different interpretations of the W3C specifications.\r
- // So, for now, let's use our implementation and pray for browsers fixings\r
- // soon. Otherwise will go crazy on trying to find out workarounds.\r
- /*\r
- // Get the browser implementation of the range, if available.\r
- if ( parentDocument.createRange )\r
- {\r
- var range = parentDocument.createRange() ;\r
- if ( typeof( range.startContainer ) != 'undefined' )\r
- return range ;\r
- }\r
- */\r
- return new FCKW3CRange( parentDocument ) ;\r
-}\r
-\r
-FCKW3CRange.CreateFromRange = function( parentDocument, sourceRange )\r
-{\r
- var range = FCKW3CRange.CreateRange( parentDocument ) ;\r
- range.setStart( sourceRange.startContainer, sourceRange.startOffset ) ;\r
- range.setEnd( sourceRange.endContainer, sourceRange.endOffset ) ;\r
- return range ;\r
-}\r
-\r
-FCKW3CRange.prototype =\r
-{\r
-\r
- _UpdateCollapsed : function()\r
- {\r
- this.collapsed = ( this.startContainer == this.endContainer && this.startOffset == this.endOffset ) ;\r
- },\r
-\r
- // W3C requires a check for the new position. If it is after the end\r
- // boundary, the range should be collapsed to the new start. It seams we\r
- // will not need this check for our use of this class so we can ignore it for now.\r
- setStart : function( refNode, offset )\r
- {\r
- this.startContainer = refNode ;\r
- this.startOffset = offset ;\r
-\r
- if ( !this.endContainer )\r
- {\r
- this.endContainer = refNode ;\r
- this.endOffset = offset ;\r
- }\r
-\r
- this._UpdateCollapsed() ;\r
- },\r
-\r
- // W3C requires a check for the new position. If it is before the start\r
- // boundary, the range should be collapsed to the new end. It seams we\r
- // will not need this check for our use of this class so we can ignore it for now.\r
- setEnd : function( refNode, offset )\r
- {\r
- this.endContainer = refNode ;\r
- this.endOffset = offset ;\r
-\r
- if ( !this.startContainer )\r
- {\r
- this.startContainer = refNode ;\r
- this.startOffset = offset ;\r
- }\r
-\r
- this._UpdateCollapsed() ;\r
- },\r
-\r
- setStartAfter : function( refNode )\r
- {\r
- this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;\r
- },\r
-\r
- setStartBefore : function( refNode )\r
- {\r
- this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;\r
- },\r
-\r
- setEndAfter : function( refNode )\r
- {\r
- this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;\r
- },\r
-\r
- setEndBefore : function( refNode )\r
- {\r
- this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;\r
- },\r
-\r
- collapse : function( toStart )\r
- {\r
- if ( toStart )\r
- {\r
- this.endContainer = this.startContainer ;\r
- this.endOffset = this.startOffset ;\r
- }\r
- else\r
- {\r
- this.startContainer = this.endContainer ;\r
- this.startOffset = this.endOffset ;\r
- }\r
-\r
- this.collapsed = true ;\r
- },\r
-\r
- selectNodeContents : function( refNode )\r
- {\r
- this.setStart( refNode, 0 ) ;\r
- this.setEnd( refNode, refNode.nodeType == 3 ? refNode.data.length : refNode.childNodes.length ) ;\r
- },\r
-\r
- insertNode : function( newNode )\r
- {\r
- var startContainer = this.startContainer ;\r
- var startOffset = this.startOffset ;\r
-\r
- // If we are in a text node.\r
- if ( startContainer.nodeType == 3 )\r
- {\r
- startContainer.splitText( startOffset ) ;\r
-\r
- // Check if it is necessary to update the end boundary.\r
- if ( startContainer == this.endContainer )\r
- this.setEnd( startContainer.nextSibling, this.endOffset - this.startOffset ) ;\r
-\r
- // Insert the new node it after the text node.\r
- FCKDomTools.InsertAfterNode( startContainer, newNode ) ;\r
-\r
- return ;\r
- }\r
- else\r
- {\r
- // Simply insert the new node before the current start node.\r
- startContainer.insertBefore( newNode, startContainer.childNodes[ startOffset ] || null ) ;\r
-\r
- // Check if it is necessary to update the end boundary.\r
- if ( startContainer == this.endContainer )\r
- {\r
- this.endOffset++ ;\r
- this.collapsed = false ;\r
- }\r
- }\r
- },\r
-\r
- deleteContents : function()\r
- {\r
- if ( this.collapsed )\r
- return ;\r
-\r
- this._ExecContentsAction( 0 ) ;\r
- },\r
-\r
- extractContents : function()\r
- {\r
- var docFrag = new FCKDocumentFragment( this._Document ) ;\r
-\r
- if ( !this.collapsed )\r
- this._ExecContentsAction( 1, docFrag ) ;\r
-\r
- return docFrag ;\r
- },\r
-\r
- // The selection may be lost when cloning (due to the splitText() call).\r
- cloneContents : function()\r
- {\r
- var docFrag = new FCKDocumentFragment( this._Document ) ;\r
-\r
- if ( !this.collapsed )\r
- this._ExecContentsAction( 2, docFrag ) ;\r
-\r
- return docFrag ;\r
- },\r
-\r
- _ExecContentsAction : function( action, docFrag )\r
- {\r
- var startNode = this.startContainer ;\r
- var endNode = this.endContainer ;\r
-\r
- var startOffset = this.startOffset ;\r
- var endOffset = this.endOffset ;\r
-\r
- var removeStartNode = false ;\r
- var removeEndNode = false ;\r
-\r
- // Check the start and end nodes and make the necessary removals or changes.\r
-\r
- // Start from the end, otherwise DOM mutations (splitText) made in the\r
- // start boundary may interfere on the results here.\r
-\r
- // For text containers, we must simply split the node and point to the\r
- // second part. The removal will be handled by the rest of the code .\r
- if ( endNode.nodeType == 3 )\r
- endNode = endNode.splitText( endOffset ) ;\r
- else\r
- {\r
- // If the end container has children and the offset is pointing\r
- // to a child, then we should start from it.\r
- if ( endNode.childNodes.length > 0 )\r
- {\r
- // If the offset points after the last node.\r
- if ( endOffset > endNode.childNodes.length - 1 )\r
- {\r
- // Let's create a temporary node and mark it for removal.\r
- endNode = FCKDomTools.InsertAfterNode( endNode.lastChild, this._Document.createTextNode('') ) ;\r
- removeEndNode = true ;\r
- }\r
- else\r
- endNode = endNode.childNodes[ endOffset ] ;\r
- }\r
- }\r
-\r
- // For text containers, we must simply split the node. The removal will\r
- // be handled by the rest of the code .\r
- if ( startNode.nodeType == 3 )\r
- {\r
- startNode.splitText( startOffset ) ;\r
-\r
- // In cases the end node is the same as the start node, the above\r
- // splitting will also split the end, so me must move the end to\r
- // the second part of the split.\r
- if ( startNode == endNode )\r
- endNode = startNode.nextSibling ;\r
- }\r
- else\r
- {\r
- // If the start container has children and the offset is pointing\r
- // to a child, then we should start from its previous sibling.\r
-\r
- // If the offset points to the first node, we don't have a\r
- // sibling, so let's use the first one, but mark it for removal.\r
- if ( startOffset == 0 )\r
- {\r
- // Let's create a temporary node and mark it for removal.\r
- startNode = startNode.insertBefore( this._Document.createTextNode(''), startNode.firstChild ) ;\r
- removeStartNode = true ;\r
- }\r
- else if ( startOffset > startNode.childNodes.length - 1 )\r
- {\r
- // Let's create a temporary node and mark it for removal.\r
- startNode = startNode.appendChild( this._Document.createTextNode('') ) ;\r
- removeStartNode = true ;\r
- }\r
- else\r
- startNode = startNode.childNodes[ startOffset ].previousSibling ;\r
- }\r
-\r
- // Get the parent nodes tree for the start and end boundaries.\r
- var startParents = FCKDomTools.GetParents( startNode ) ;\r
- var endParents = FCKDomTools.GetParents( endNode ) ;\r
-\r
- // Compare them, to find the top most siblings.\r
- var i, topStart, topEnd ;\r
-\r
- for ( i = 0 ; i < startParents.length ; i++ )\r
- {\r
- topStart = startParents[i] ;\r
- topEnd = endParents[i] ;\r
-\r
- // The compared nodes will match until we find the top most\r
- // siblings (different nodes that have the same parent).\r
- // "i" will hold the index in the parents array for the top\r
- // most element.\r
- if ( topStart != topEnd )\r
- break ;\r
- }\r
-\r
- var clone, levelStartNode, levelClone, currentNode, currentSibling ;\r
-\r
- if ( docFrag )\r
- clone = docFrag.RootNode ;\r
-\r
- // Remove all successive sibling nodes for every node in the\r
- // startParents tree.\r
- for ( var j = i ; j < startParents.length ; j++ )\r
- {\r
- levelStartNode = startParents[j] ;\r
-\r
- // For Extract and Clone, we must clone this level.\r
- if ( clone && levelStartNode != startNode ) // action = 0 = Delete\r
- levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == startNode ) ) ;\r
-\r
- currentNode = levelStartNode.nextSibling ;\r
-\r
- while( currentNode )\r
- {\r
- // Stop processing when the current node matches a node in the\r
- // endParents tree or if it is the endNode.\r
- if ( currentNode == endParents[j] || currentNode == endNode )\r
- break ;\r
-\r
- // Cache the next sibling.\r
- currentSibling = currentNode.nextSibling ;\r
-\r
- // If cloning, just clone it.\r
- if ( action == 2 ) // 2 = Clone\r
- clone.appendChild( currentNode.cloneNode( true ) ) ;\r
- else\r
- {\r
- // Both Delete and Extract will remove the node.\r
- currentNode.parentNode.removeChild( currentNode ) ;\r
-\r
- // When Extracting, move the removed node to the docFrag.\r
- if ( action == 1 ) // 1 = Extract\r
- clone.appendChild( currentNode ) ;\r
- }\r
-\r
- currentNode = currentSibling ;\r
- }\r
-\r
- if ( clone )\r
- clone = levelClone ;\r
- }\r
-\r
- if ( docFrag )\r
- clone = docFrag.RootNode ;\r
-\r
- // Remove all previous sibling nodes for every node in the\r
- // endParents tree.\r
- for ( var k = i ; k < endParents.length ; k++ )\r
- {\r
- levelStartNode = endParents[k] ;\r
-\r
- // For Extract and Clone, we must clone this level.\r
- if ( action > 0 && levelStartNode != endNode ) // action = 0 = Delete\r
- levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == endNode ) ) ;\r
-\r
- // The processing of siblings may have already been done by the parent.\r
- if ( !startParents[k] || levelStartNode.parentNode != startParents[k].parentNode )\r
- {\r
- currentNode = levelStartNode.previousSibling ;\r
-\r
- while( currentNode )\r
- {\r
- // Stop processing when the current node matches a node in the\r
- // startParents tree or if it is the startNode.\r
- if ( currentNode == startParents[k] || currentNode == startNode )\r
- break ;\r
-\r
- // Cache the next sibling.\r
- currentSibling = currentNode.previousSibling ;\r
-\r
- // If cloning, just clone it.\r
- if ( action == 2 ) // 2 = Clone\r
- clone.insertBefore( currentNode.cloneNode( true ), clone.firstChild ) ;\r
- else\r
- {\r
- // Both Delete and Extract will remove the node.\r
- currentNode.parentNode.removeChild( currentNode ) ;\r
-\r
- // When Extracting, mode the removed node to the docFrag.\r
- if ( action == 1 ) // 1 = Extract\r
- clone.insertBefore( currentNode, clone.firstChild ) ;\r
- }\r
-\r
- currentNode = currentSibling ;\r
- }\r
- }\r
-\r
- if ( clone )\r
- clone = levelClone ;\r
- }\r
-\r
- if ( action == 2 ) // 2 = Clone.\r
- {\r
- // No changes in the DOM should be done, so fix the split text (if any).\r
-\r
- var startTextNode = this.startContainer ;\r
- if ( startTextNode.nodeType == 3 )\r
- {\r
- startTextNode.data += startTextNode.nextSibling.data ;\r
- startTextNode.parentNode.removeChild( startTextNode.nextSibling ) ;\r
- }\r
-\r
- var endTextNode = this.endContainer ;\r
- if ( endTextNode.nodeType == 3 && endTextNode.nextSibling )\r
- {\r
- endTextNode.data += endTextNode.nextSibling.data ;\r
- endTextNode.parentNode.removeChild( endTextNode.nextSibling ) ;\r
- }\r
- }\r
- else\r
- {\r
- // Collapse the range.\r
-\r
- // If a node has been partially selected, collapse the range between\r
- // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).\r
- if ( topStart && topEnd && ( startNode.parentNode != topStart.parentNode || endNode.parentNode != topEnd.parentNode ) )\r
- {\r
- var endIndex = FCKDomTools.GetIndexOf( topEnd ) ;\r
-\r
- // If the start node is to be removed, we must correct the\r
- // index to reflect the removal.\r
- if ( removeStartNode && topEnd.parentNode == startNode.parentNode )\r
- endIndex-- ;\r
-\r
- this.setStart( topEnd.parentNode, endIndex ) ;\r
- }\r
-\r
- // Collapse it to the start.\r
- this.collapse( true ) ;\r
- }\r
-\r
- // Cleanup any marked node.\r
- if( removeStartNode )\r
- startNode.parentNode.removeChild( startNode ) ;\r
-\r
- if( removeEndNode && endNode.parentNode )\r
- endNode.parentNode.removeChild( endNode ) ;\r
- },\r
-\r
- cloneRange : function()\r
- {\r
- return FCKW3CRange.CreateFromRange( this._Document, this ) ;\r
- }\r
-} ;\r