fckw3crange.js

来自「国外很不错的一个开源OA系统Group-Office」· JavaScript 代码 · 共 449 行

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

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?