Вход Регистрация
Файл: fckeditor/editor/_source/classes/fckdomrange.js
Строк: 1312
<?php

/*
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 * Copyright (C) 2003-2008 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 ==
 *
 * Class for working with a selection range, much like the W3C DOM Range, but
 * it is not intended to be an implementation of the W3C interface.
 */

var FCKDomRange = function( sourceWindow )
{
    
this.Window sourceWindow ;
    
this._Cache = {} ;
}

FCKDomRange.prototype =
{

    
_UpdateElementInfo : function()
    {
        var 
innerRange this._Range ;

        if ( !
innerRange )
            
this.Releasetrue ) ;
        else
        {
            
// For text nodes, the node itself is the StartNode.
            
var eStart    innerRange.startContainer ;

            var 
oElementPath = new FCKElementPatheStart ) ;
            
this.StartNode            eStart.nodeType == eStart eStart.childNodesinnerRange.startOffset ] ;
            
this.StartContainer        eStart ;
            
this.StartBlock            oElementPath.Block ;
            
this.StartBlockLimit    oElementPath.BlockLimit ;

            if ( 
innerRange.collapsed )
            {
                
this.EndNode        this.StartNode ;
                
this.EndContainer    this.StartContainer ;
                
this.EndBlock        this.StartBlock ;
                
this.EndBlockLimit    this.StartBlockLimit ;
            }
            else
            {
                var 
eEnd    innerRange.endContainer ;

                if ( 
eStart != eEnd )
                    
oElementPath = new FCKElementPatheEnd ) ;

                
// The innerRange.endContainer[ innerRange.endOffset ] is not
                // usually part of the range, but the marker for the range end. So,
                // let's get the previous available node as the real end.
                
var eEndNode eEnd ;
                if ( 
innerRange.endOffset == )
                {
                    while ( 
eEndNode && !eEndNode.previousSibling )
                        
eEndNode eEndNode.parentNode ;

                    if ( 
eEndNode )
                        
eEndNode eEndNode.previousSibling ;
                }
                else if ( 
eEndNode.nodeType == )
                    
eEndNode eEndNode.childNodesinnerRange.endOffset ] ;

                
this.EndNode            eEndNode ;
                
this.EndContainer        eEnd ;
                
this.EndBlock            oElementPath.Block ;
                
this.EndBlockLimit        oElementPath.BlockLimit ;
            }
        }

        
this._Cache = {} ;
    },

    
CreateRange : function()
    {
        return new 
FCKW3CRangethis.Window.document ) ;
    },

    
DeleteContents : function()
    {
        if ( 
this._Range )
        {
            
this._Range.deleteContents() ;
            
this._UpdateElementInfo() ;
        }
    },

    
ExtractContents : function()
    {
        if ( 
this._Range )
        {
            var 
docFrag this._Range.extractContents() ;
            
this._UpdateElementInfo() ;
            return 
docFrag ;
        }
        return 
null ;
    },

    
CheckIsCollapsed : function()
    {
        if ( 
this._Range )
            return 
this._Range.collapsed ;

        return 
false ;
    },

    
Collapse : function( toStart )
    {
        if ( 
this._Range )
            
this._Range.collapsetoStart ) ;

        
this._UpdateElementInfo() ;
    },

    Clone : function()
    {
        var 
oClone FCKTools.CloneObjectthis ) ;

        if ( 
this._Range )
            
oClone._Range this._Range.cloneRange() ;

        return 
oClone ;
    },

    
MoveToNodeContents : function( targetNode )
    {
        if ( !
this._Range )
            
this._Range this.CreateRange() ;

        
this._Range.selectNodeContentstargetNode ) ;

        
this._UpdateElementInfo() ;
    },

    
MoveToElementStart : function( targetElement )
    {
        
this.SetStart(targetElement,1) ;
        
this.SetEnd(targetElement,1) ;
    },

    
// Moves to the first editing point inside a element. For example, in a
    // element tree like "<p><b><i></i></b> Text</p>", the start editing point
    // is "<p><b><i>^</i></b> Text</p>" (inside <i>).
    
MoveToElementEditStart : function( targetElement )
    {
        var 
editableElement ;

        while ( 
targetElement && targetElement.nodeType == )
        {
            if ( 
FCKDomTools.CheckIsEditabletargetElement ) )
                
editableElement targetElement ;
            else if ( 
editableElement )
                break ;        
// If we already found an editable element, stop the loop.

            
targetElement targetElement.firstChild ;
        }

        if ( 
editableElement )
            
this.MoveToElementStarteditableElement ) ;
    },

    
InsertNode : function( node )
    {
        if ( 
this._Range )
            
this._Range.insertNodenode ) ;
    },

    
CheckIsEmpty : function()
    {
        if ( 
this.CheckIsCollapsed() )
            return 
true ;

        
// Inserts the contents of the range in a div tag.
        
var eToolDiv this.Window.document.createElement'div' ) ;
        
this._Range.cloneContents().AppendToeToolDiv ) ;

        
FCKDomTools.TrimNodeeToolDiv ) ;

        return ( 
eToolDiv.innerHTML.length == ) ;
    },

    
/**
     * Checks if the start boundary of the current range is "visually" (like a
     * selection caret) at the beginning of the block. It means that some
     * things could be brefore the range, like spaces or empty inline elements,
     * but it would still be considered at the beginning of the block.
     */
    
CheckStartOfBlock : function()
    {
        var 
cache this._Cache ;
        var 
bIsStartOfBlock cache.IsStartOfBlock ;

        if ( 
bIsStartOfBlock != undefined )
            return 
bIsStartOfBlock ;

        
// Take the block reference.
        
var block this.StartBlock || this.StartBlockLimit ;

        var 
container    this._Range.startContainer ;
        var 
offset        this._Range.startOffset ;
        var 
currentNode ;

        if ( 
offset )
        {
            
// First, check the start container. If it is a text node, get the
            // substring of the node value before the range offset.
            
if ( container.nodeType == )
            {
                var 
textValue container.nodeValue.substr0offset ).Trim() ;

                
// If we have some text left in the container, we are not at
                // the end for the block.
                
if ( textValue.length != )
                    return 
cache.IsStartOfBlock false ;
            }
            else
                
currentNode container.childNodesoffset ] ;
        }

        
// We'll not have a currentNode if the container was a text node, or
        // the offset is zero.
        
if ( !currentNode )
            
currentNode FCKDomTools.GetPreviousSourceNodecontainertruenullblock ) ;

        while ( 
currentNode )
        {
            switch ( 
currentNode.nodeType )
            {
                case 
:
                    
// It's not an inline element.
                    
if ( !FCKListsLib.InlineChildReqElementscurrentNode.nodeName.toLowerCase() ] )
                        return 
cache.IsStartOfBlock false ;

                    break ;

                case 
:
                    
// It's a text node with real text.
                    
if ( currentNode.nodeValue.Trim().length )
                        return 
cache.IsStartOfBlock false ;
            }

            
currentNode FCKDomTools.GetPreviousSourceNodecurrentNodefalsenullblock ) ;
        }

        return 
cache.IsStartOfBlock true ;
    },

    
/**
     * Checks if the end boundary of the current range is "visually" (like a
     * selection caret) at the end of the block. It means that some things
     * could be after the range, like spaces, empty inline elements, or a
     * single <br>, but it would still be considered at the end of the block.
     */
    
CheckEndOfBlock : function( refreshSelection )
    {
        var 
isEndOfBlock this._Cache.IsEndOfBlock ;

        if ( 
isEndOfBlock != undefined )
            return 
isEndOfBlock ;

        
// Take the block reference.
        
var block this.EndBlock || this.EndBlockLimit ;

        var 
container    this._Range.endContainer ;
        var 
offset            this._Range.endOffset ;
        var 
currentNode ;

        
// First, check the end container. If it is a text node, get the
        // substring of the node value after the range offset.
        
if ( container.nodeType == )
        {
            var 
textValue container.nodeValue ;
            if ( 
offset textValue.length )
            {
                
textValue textValue.substroffset ) ;

                
// If we have some text left in the container, we are not at
                // the end for the block.
                
if ( textValue.Trim().length != )
                    return 
this._Cache.IsEndOfBlock false ;
            }
        }
        else
            
currentNode container.childNodesoffset ] ;

        
// We'll not have a currentNode if the container was a text node, of
        // the offset is out the container children limits (after it probably).
        
if ( !currentNode )
            
currentNode FCKDomTools.GetNextSourceNodecontainertruenullblock ) ;

        var 
hadBr false ;

        while ( 
currentNode )
        {
            switch ( 
currentNode.nodeType )
            {
                case 
:
                    var 
nodeName currentNode.nodeName.toLowerCase() ;

                    
// It's an inline element.
                    
if ( FCKListsLib.InlineChildReqElementsnodeName ] )
                        break ;

                    
// It is the first <br> found.
                    
if ( nodeName == 'br' && !hadBr )
                    {
                        
hadBr true ;
                        break ;
                    }

                    return 
this._Cache.IsEndOfBlock false ;

                case 
:
                    
// It's a text node with real text.
                    
if ( currentNode.nodeValue.Trim().length )
                        return 
this._Cache.IsEndOfBlock false ;
            }

            
currentNode FCKDomTools.GetNextSourceNodecurrentNodefalsenullblock ) ;
        }

        if ( 
refreshSelection )
            
this.Select() ;

        return 
this._Cache.IsEndOfBlock true ;
    },

    
// This is an "intrusive" way to create a bookmark. It includes <span> tags
    // in the range boundaries. The advantage of it is that it is possible to
    // handle DOM mutations when moving back to the bookmark.
    // Attention: the inclusion of nodes in the DOM is a design choice and
    // should not be changed as there are other points in the code that may be
    // using those nodes to perform operations. See GetBookmarkNode.
    // For performance, includeNodes=true if intended to SelectBookmark.
    
CreateBookmark : function( includeNodes )
    {
        
// Create the bookmark info (random IDs).
        
var oBookmark =
        {
            
StartId    : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'S',
            
EndId    : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'E'
        
} ;

        var 
oDoc this.Window.document ;
        var 
eStartSpan ;
        var 
eEndSpan ;
        var 
oClone ;

        
// For collapsed ranges, add just the start marker.
        
if ( !this.CheckIsCollapsed() )
        {
            
eEndSpan oDoc.createElement'span' ) ;
            
eEndSpan.style.display 'none' ;
            
eEndSpan.id oBookmark.EndId ;
            
eEndSpan.setAttribute'_fck_bookmark'true ) ;

            
// For IE, it must have something inside, otherwise it may be
            // removed during DOM operations.
//            if ( FCKBrowserInfo.IsIE )
                
eEndSpan.innerHTML '&nbsp;' ;

            
oClone this.Clone() ;
            
oClone.Collapsefalse ) ;
            
oClone.InsertNodeeEndSpan ) ;
        }

        
eStartSpan oDoc.createElement'span' ) ;
        
eStartSpan.style.display 'none' ;
        
eStartSpan.id oBookmark.StartId ;
        
eStartSpan.setAttribute'_fck_bookmark'true ) ;

        
// For IE, it must have something inside, otherwise it may be removed
        // during DOM operations.
//        if ( FCKBrowserInfo.IsIE )
            
eStartSpan.innerHTML '&nbsp;' ;

        
oClone this.Clone() ;
        
oClone.Collapsetrue ) ;
        
oClone.InsertNodeeStartSpan ) ;

        if ( 
includeNodes )
        {
            
oBookmark.StartNode eStartSpan ;
            
oBookmark.EndNode eEndSpan ;
        }

        
// Update the range position.
        
if ( eEndSpan )
        {
            
this.SetStarteStartSpan) ;
            
this.SetEndeEndSpan) ;
        }
        else
            
this.MoveToPositioneStartSpan) ;

        return 
oBookmark ;
    },

    
// This one should be a part of a hypothetic "bookmark" object.
    
GetBookmarkNode : function( bookmarkstart )
    {
        var 
doc this.Window.document ;

        if ( 
start )
            return 
bookmark.StartNode || doc.getElementByIdbookmark.StartId ) ;
        else
            return 
bookmark.EndNode || doc.getElementByIdbookmark.EndId ) ;
    },

    
MoveToBookmark : function( bookmarkpreserveBookmark )
    {
        var 
eStartSpan    this.GetBookmarkNodebookmarktrue ) ;
        var 
eEndSpan    this.GetBookmarkNodebookmarkfalse ) ;

        
this.SetStarteStartSpan) ;

        if ( !
preserveBookmark )
            
FCKDomTools.RemoveNodeeStartSpan ) ;

        
// If collapsed, the end span will not be available.
        
if ( eEndSpan )
        {
            
this.SetEndeEndSpan) ;

            if ( !
preserveBookmark )
                
FCKDomTools.RemoveNodeeEndSpan ) ;
        }
        else
            
this.Collapsetrue ) ;

        
this._UpdateElementInfo() ;
    },

    
// Non-intrusive bookmark algorithm
    
CreateBookmark2 : function()
    {
        
// If there is no range then get out of here.
        // It happens on initial load in Safari #962 and if the editor it's hidden also in Firefox
        
if ( ! this._Range )
            return { 
"Start" 0"End" } ;

        
// First, we record down the offset values
        
var bookmark =
        {
            
"Start" : [ this._Range.startOffset ],
            
"End" : [ this._Range.endOffset ]
        } ;
        
// Since we're treating the document tree as normalized, we need to backtrack the text lengths
        // of previous text nodes into the offset value.
        
var curStart this._Range.startContainer.previousSibling ;
        var 
curEnd this._Range.endContainer.previousSibling ;

        
// Also note that the node that we use for "address base" would change during backtracking.
        
var addrStart this._Range.startContainer ;
        var 
addrEnd this._Range.endContainer ;
        while ( 
curStart && addrStart.nodeType == )
        {
            
bookmark.Start[0] += curStart.length ;
            
addrStart curStart ;
            
curStart curStart.previousSibling ;
        }
        while ( 
curEnd && addrEnd.nodeType == )
        {
            
bookmark.End[0] += curEnd.length ;
            
addrEnd curEnd ;
            
curEnd curEnd.previousSibling ;
        }

        
// If the object pointed to by the startOffset and endOffset are text nodes, we need
        // to backtrack and add in the text offset to the bookmark addresses.
        
if ( addrStart.nodeType == && addrStart.childNodes[bookmark.Start[0]] && addrStart.childNodes[bookmark.Start[0]].nodeType == )
        {
            var 
curNode addrStart.childNodes[bookmark.Start[0]] ;
            var 
offset ;
            while ( 
curNode.previousSibling && curNode.previousSibling.nodeType == )
            {
                
curNode curNode.previousSibling ;
                
offset += curNode.length ;
            }
            
addrStart curNode ;
            
bookmark.Start[0] = offset ;
        }
        if ( 
addrEnd.nodeType == && addrEnd.childNodes[bookmark.End[0]] && addrEnd.childNodes[bookmark.End[0]].nodeType == )
        {
            var 
curNode addrEnd.childNodes[bookmark.End[0]] ;
            var 
offset ;
            while ( 
curNode.previousSibling && curNode.previousSibling.nodeType == )
            {
                
curNode curNode.previousSibling ;
                
offset += curNode.length ;
            }
            
addrEnd curNode ;
            
bookmark.End[0] = offset ;
        }

        
// Then, we record down the precise position of the container nodes
        // by walking up the DOM tree and counting their childNode index
        
bookmark.Start FCKDomTools.GetNodeAddressaddrStarttrue ).concatbookmark.Start ) ;
        
bookmark.End FCKDomTools.GetNodeAddressaddrEndtrue ).concatbookmark.End ) ;
        return 
bookmark;
    },

    
MoveToBookmark2 : function( bookmark )
    {
        
// Reverse the childNode counting algorithm in CreateBookmark2()
        
var curStart FCKDomTools.GetNodeFromAddressthis.Window.documentbookmark.Start.slice0, -), true ) ;
        var 
curEnd FCKDomTools.GetNodeFromAddressthis.Window.documentbookmark.End.slice0, -), true ) ;

        
// Generate the W3C Range object and update relevant data
        
this.Releasetrue ) ;
        
this._Range = new FCKW3CRangethis.Window.document ) ;
        var 
startOffset bookmark.Startbookmark.Start.length ] ;
        var 
endOffset bookmark.Endbookmark.End.length ] ;
        while ( 
curStart.nodeType == && startOffset curStart.length )
        {
            if ( ! 
curStart.nextSibling || curStart.nextSibling.nodeType != )
                break ;
            
startOffset -= curStart.length ;
            
curStart curStart.nextSibling ;
        }
        while ( 
curEnd.nodeType == && endOffset curEnd.length )
        {
            if ( ! 
curEnd.nextSibling || curEnd.nextSibling.nodeType != )
                break ;
            
endOffset -= curEnd.length ;
            
curEnd curEnd.nextSibling ;
        }
        
this._Range.setStartcurStartstartOffset ) ;
        
this._Range.setEndcurEndendOffset ) ;
        
this._UpdateElementInfo() ;
    },

    
MoveToPosition : function( targetElementposition )
    {
        
this.SetStarttargetElementposition ) ;
        
this.Collapsetrue ) ;
    },

    
/*
     * Moves the position of the start boundary of the range to a specific position
     * relatively to a element.
     *        @position:
     *            1 = After Start        <target>^contents</target>
     *            2 = Before End        <target>contents^</target>
     *            3 = Before Start    ^<target>contents</target>
     *            4 = After End        <target>contents</target>^
     */
    
SetStart : function( targetElementpositionnoInfoUpdate )
    {
        var 
oRange this._Range ;
        if ( !
oRange )
            
oRange this._Range this.CreateRange() ;

        switch( 
position )
        {
            case 
:        // After Start        <target>^contents</target>
                
oRange.setStarttargetElement) ;
                break ;

            case 
:        // Before End        <target>contents^</target>
                
oRange.setStarttargetElementtargetElement.childNodes.length ) ;
                break ;

            case 
:        // Before Start        ^<target>contents</target>
                
oRange.setStartBeforetargetElement ) ;
                break ;

            case 
:        // After End        <target>contents</target>^
                
oRange.setStartAftertargetElement ) ;
        }

        if ( !
noInfoUpdate )
            
this._UpdateElementInfo() ;
    },

    
/*
     * Moves the position of the start boundary of the range to a specific position
     * relatively to a element.
     *        @position:
     *            1 = After Start        <target>^contents</target>
     *            2 = Before End        <target>contents^</target>
     *            3 = Before Start    ^<target>contents</target>
     *            4 = After End        <target>contents</target>^
     */
    
SetEnd : function( targetElementpositionnoInfoUpdate )
    {
        var 
oRange this._Range ;
        if ( !
oRange )
            
oRange this._Range this.CreateRange() ;

        switch( 
position )
        {
            case 
:        // After Start        <target>^contents</target>
                
oRange.setEndtargetElement) ;
                break ;

            case 
:        // Before End        <target>contents^</target>
                
oRange.setEndtargetElementtargetElement.childNodes.length ) ;
                break ;

            case 
:        // Before Start        ^<target>contents</target>
                
oRange.setEndBeforetargetElement ) ;
                break ;

            case 
:        // After End        <target>contents</target>^
                
oRange.setEndAftertargetElement ) ;
        }

        if ( !
noInfoUpdate )
            
this._UpdateElementInfo() ;
    },

    
Expand : function( unit )
    {
        var 
oNodeoSibling ;

        switch ( 
unit )
        {
            
// Expand the range to include all inline parent elements if we are
            // are in their boundary limits.
            // For example (where [ ] are the range limits):
            //    Before =>        Some <b>[<i>Some sample text]</i></b>.
            //    After =>        Some [<b><i>Some sample text</i></b>].
            
case 'inline_elements' :
                
// Expand the start boundary.
                
if ( this._Range.startOffset == )
                {
                    
oNode this._Range.startContainer ;

                    if ( 
oNode.nodeType != )
                        
oNode oNode.previousSibling null oNode.parentNode ;

                    if ( 
oNode )
                    {
                        while ( 
FCKListsLib.InlineNonEmptyElementsoNode.nodeName.toLowerCase() ] )
                        {
                            
this._Range.setStartBeforeoNode ) ;

                            if ( 
oNode != oNode.parentNode.firstChild )
                                break ;

                            
oNode oNode.parentNode ;
                        }
                    }
                }

                
// Expand the end boundary.
                
oNode this._Range.endContainer ;
                var 
offset this._Range.endOffset ;

                if ( ( 
oNode.nodeType == && offset >= oNode.nodeValue.length ) || ( oNode.nodeType == && offset >= oNode.childNodes.length ) || ( oNode.nodeType != && oNode.nodeType != ) )
                {
                    if ( 
oNode.nodeType != )
                        
oNode oNode.nextSibling null oNode.parentNode ;

                    if ( 
oNode )
                    {
                        while ( 
FCKListsLib.InlineNonEmptyElementsoNode.nodeName.toLowerCase() ] )
                        {
                            
this._Range.setEndAfteroNode ) ;

                            if ( 
oNode != oNode.parentNode.lastChild )
                                break ;

                            
oNode oNode.parentNode ;
                        }
                    }
                }

                break ;

            case 
'block_contents' :
            case 
'list_contents' :
                var 
boundarySet FCKListsLib.BlockBoundaries ;
                if ( 
unit == 'list_contents' || FCKConfig.EnterMode == 'br' )
                    
boundarySet FCKListsLib.ListBoundaries ;

                if ( 
this.StartBlock && FCKConfig.EnterMode != 'br' && unit == 'block_contents' )
                    
this.SetStartthis.StartBlock) ;
                else
                {
                    
// Get the start node for the current range.
                    
oNode this._Range.startContainer ;

                    
// If it is an element, get the node right before of it (in source order).
                    
if ( oNode.nodeType == )
                    {
                        var 
lastNode oNode.childNodesthis._Range.startOffset ] ;
                        if ( 
lastNode )
                            
oNode FCKDomTools.GetPreviousSourceNodelastNodetrue ) ;
                        else
                            
oNode oNode.lastChild || oNode ;
                    }

                    
// We must look for the left boundary, relative to the range
                    // start, which is limited by a block element.
                    
while ( oNode
                            
&& ( oNode.nodeType != 1
                                
|| ( oNode != this.StartBlockLimit
                                    
&& !boundarySetoNode.nodeName.toLowerCase() ] ) ) )
                    {
                        
this._Range.setStartBeforeoNode ) ;
                        
oNode oNode.previousSibling || oNode.parentNode ;
                    }
                }

                if ( 
this.EndBlock && FCKConfig.EnterMode != 'br' && unit == 'block_contents' && this.EndBlock.nodeName.toLowerCase() != 'li' )
                    
this.SetEndthis.EndBlock) ;
                else
                {
                    
oNode this._Range.endContainer ;
                    if ( 
oNode.nodeType == )
                        
oNode oNode.childNodesthis._Range.endOffset ] || oNode.lastChild ;

                    
// We must look for the right boundary, relative to the range
                    // end, which is limited by a block element.
                    
while ( oNode
                            
&& ( oNode.nodeType != 1
                                
|| ( oNode != this.StartBlockLimit
                                    
&& !boundarySetoNode.nodeName.toLowerCase() ] ) ) )
                    {
                        
this._Range.setEndAfteroNode ) ;
                        
oNode oNode.nextSibling || oNode.parentNode ;
                    }

                    
// In EnterMode='br', the end <br> boundary element must
                    // be included in the expanded range.
                    
if ( oNode && oNode.nodeName.toLowerCase() == 'br' )
                        
this._Range.setEndAfteroNode ) ;
                }

                
this._UpdateElementInfo() ;
        }
    },

    
/**
     * Split the block element for the current range. It deletes the contents
     * of the range and splits the block in the collapsed position, resulting
     * in two sucessive blocks. The range is then positioned in the middle of
     * them.
     *
     * It returns and object with the following properties:
     *        - PreviousBlock    : a reference to the block element that preceeds
     *          the range after the split.
     *        - NextBlock : a reference to the block element that follows the
     *          range after the split.
     *        - WasStartOfBlock : a boolean indicating that the range was
     *          originaly at the start of the block.
     *        - WasEndOfBlock : a boolean indicating that the range was originaly
     *          at the end of the block.
     *
     * If the range was originaly at the start of the block, no split will happen
     * and the PreviousBlock value will be null. The same is valid for the
     * NextBlock value if the range was at the end of the block.
     */
    
SplitBlock : function( forceBlockTag )
    {
        var 
blockTag forceBlockTag || FCKConfig.EnterMode ;

        if ( !
this._Range )
            
this.MoveToSelection() ;

        
// The range boundaries must be in the same "block limit" element.
        
if ( this.StartBlockLimit == this.EndBlockLimit )
        {
            
// Get the current blocks.
            
var eStartBlock        this.StartBlock ;
            var 
eEndBlock        this.EndBlock ;
            var 
oElementPath    null ;

            if ( 
blockTag != 'br' )
            {
                if ( !
eStartBlock )
                {
                    
eStartBlock this.FixBlocktrueblockTag ) ;
                    
eEndBlock    this.EndBlock ;    // FixBlock may have fixed the EndBlock too.
                
}

                if ( !
eEndBlock )
                    
eEndBlock this.FixBlockfalseblockTag ) ;
            }

            
// Get the range position.
            
var bIsStartOfBlock    = ( eStartBlock != null && this.CheckStartOfBlock() ) ;
            var 
bIsEndOfBlock    = ( eEndBlock != null && this.CheckEndOfBlock() ) ;

            
// Delete the current contents.
            
if ( !this.CheckIsEmpty() )
                
this.DeleteContents() ;

            if ( 
eStartBlock && eEndBlock && eStartBlock == eEndBlock )
            {
                if ( 
bIsEndOfBlock )
                {
                    
oElementPath = new FCKElementPaththis.StartContainer ) ;
                    
this.MoveToPositioneEndBlock) ;
                    
eEndBlock null ;
                }
                else if ( 
bIsStartOfBlock )
                {
                    
oElementPath = new FCKElementPaththis.StartContainer ) ;
                    
this.MoveToPositioneStartBlock) ;
                    
eStartBlock null ;
                }
                else
                {
                    
// Extract the contents of the block from the selection point to the end of its contents.
                    
this.SetEndeStartBlock) ;
                    var 
eDocFrag this.ExtractContents() ;

                    
// Duplicate the block element after it.
                    
eEndBlock eStartBlock.cloneNodefalse ) ;
                    
eEndBlock.removeAttribute'id'false ) ;

                    
// Place the extracted contents in the duplicated block.
                    
eDocFrag.AppendToeEndBlock ) ;

                    
FCKDomTools.InsertAfterNodeeStartBlockeEndBlock ) ;

                    
this.MoveToPositioneStartBlock) ;

                    
// In Gecko, the last child node must be a bogus <br>.
                    // Note: bogus <br> added under <ul> or <ol> would cause lists to be incorrectly rendered.
                    
if ( FCKBrowserInfo.IsGecko &&
                            ! 
eStartBlock.nodeName.IEquals( ['ul''ol'] ) )
                        
FCKTools.AppendBogusBreStartBlock ) ;
                }
            }

            return {
                
PreviousBlock    eStartBlock,
                
NextBlock        eEndBlock,
                
WasStartOfBlock bIsStartOfBlock,
                
WasEndOfBlock    bIsEndOfBlock,
                
ElementPath        oElementPath
            
} ;
        }

        return 
null ;
    },

    
// Transform a block without a block tag in a valid block (orphan text in the body or td, usually).
    
FixBlock : function( isStartblockTag )
    {
        
// Bookmark the range so we can restore it later.
        
var oBookmark this.CreateBookmark() ;

        
// Collapse the range to the requested ending boundary.
        
this.CollapseisStart ) ;

        
// Expands it to the block contents.
        
this.Expand'block_contents' ) ;

        
// Create the fixed block.
        
var oFixedBlock this.Window.document.createElementblockTag ) ;

        
// Move the contents of the temporary range to the fixed block.
        
this.ExtractContents().AppendTooFixedBlock ) ;
        
FCKDomTools.TrimNodeoFixedBlock ) ;

        
// If the fixed block is empty (not counting bookmark nodes)
        // Add a <br /> inside to expand it.
        
if ( FCKDomTools.CheckIsEmptyElement(oFixedBlock, function( element ) { return element.getAttribute('_fck_bookmark') != 'true' ; } )
                && 
FCKBrowserInfo.IsGeckoLike )
                
FCKTools.AppendBogusBroFixedBlock ) ;

        
// Insert the fixed block into the DOM.
        
this.InsertNodeoFixedBlock ) ;

        
// Move the range back to the bookmarked place.
        
this.MoveToBookmarkoBookmark ) ;

        return 
oFixedBlock ;
    },

    
Release : function( preserveWindow )
    {
        if ( !
preserveWindow )
            
this.Window null ;

        
this.StartNode null ;
        
this.StartContainer null ;
        
this.StartBlock null ;
        
this.StartBlockLimit null ;
        
this.EndNode null ;
        
this.EndContainer null ;
        
this.EndBlock null ;
        
this.EndBlockLimit null ;
        
this._Range null ;
        
this._Cache null ;
    },

    
CheckHasRange : function()
    {
        return !!
this._Range ;
    },

    
GetTouchedStartNode : function()
    {
        var 
range this._Range ;
        var 
container range.startContainer ;

        if ( 
range.collapsed || container.nodeType != )
            return 
container ;

        return 
container.childNodesrange.startOffset ] || container ;
    },

    
GetTouchedEndNode : function()
    {
        var 
range this._Range ;
        var 
container range.endContainer ;

        if ( 
range.collapsed || container.nodeType != )
            return 
container ;

        return 
container.childNodesrange.endOffset ] || container ;
    }
} ;
?>
Онлайн: 1
Реклама