Index: /FCKeditor/trunk/editor/_source/classes/fckdomrange.js
===================================================================
--- /FCKeditor/trunk/editor/_source/classes/fckdomrange.js (revision 1047)
+++ /FCKeditor/trunk/editor/_source/classes/fckdomrange.js (revision 1048)
@@ -175,91 +175,148 @@
},
+ /**
+ * 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 bIsStartOfBlock = this._Cache.IsStartOfBlock ;
+ var cache = this._Cache ;
+ var bIsStartOfBlock = cache.IsStartOfBlock ;
if ( bIsStartOfBlock != undefined )
return bIsStartOfBlock ;
- // Create a clone of the current range.
- var oTestRange = this.Clone() ;
-
- // Collapse it to its start point.
- oTestRange.Collapse( true ) ;
-
- // Move the start boundary to the start of the block.
- oTestRange.SetStart( oTestRange.StartBlock || oTestRange.StartBlockLimit, 1 ) ;
-
- if ( oTestRange.CheckIsCollapsed() )
- bIsStartOfBlock = true ;
+ // Take the block reference.
+ var block = this.StartBlock || this.StartBlockLimit ;
+
+ var container = this._Range.startContainer ;
+ var offset = this._Range.startOffset ;
+ var currentNode ;
+
+ if ( offset > 0 )
+ {
+ // 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 == 3 )
+ {
+ var textValue = container.nodeValue.substr( 0, offset ).Trim() ;
+
+ // If we have some text left in the container, we are not at
+ // the end for the block.
+ if ( textValue.length != 0 )
+ return cache.IsStartOfBlock = false ;
+ }
+ else
+ currentNode = container.childNodes[ offset - 1 ] ;
+ }
+
+ // We'll not have a currentNode if the container was a text node, or
+ // the offset is zero.
+ if ( !currentNode )
+ currentNode = FCKDomTools.GetPreviousSourceNode( container, true, null, block ) ;
+
+ while ( currentNode )
+ {
+ switch ( currentNode.nodeType )
+ {
+ case 1 :
+ // It's not an inline element.
+ if ( !FCKListsLib.InlineChildReqElements[ currentNode.nodeName.toLowerCase() ] )
+ return cache.IsStartOfBlock = false ;
+
+ break ;
+
+ case 3 :
+ // It's a text node with real text.
+ if ( currentNode.nodeValue.Trim().length > 0 )
+ return cache.IsStartOfBlock = false ;
+ }
+
+ currentNode = FCKDomTools.GetPreviousSourceNode( currentNode, false, null, block ) ;
+ }
+
+ 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
, 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 == 3 )
+ {
+ var textValue = container.nodeValue ;
+ if ( offset < textValue.length )
+ {
+ textValue = textValue.substr( offset ) ;
+
+ // If we have some text left in the container, we are not at
+ // the end for the block.
+ if ( textValue.Trim().length != 0 )
+ return this._Cache.IsEndOfBlock = false ;
+ }
+ }
else
- {
- // Inserts the contents of the range in a div tag.
- var eToolDiv = oTestRange.Window.document.createElement( 'div' ) ;
- oTestRange._Range.cloneContents().AppendTo( eToolDiv ) ;
-
- // This line is why we don't use CheckIsEmpty() here...
- // Because using RTrimNode() or TrimNode() would be incorrect -
- // TrimNode() and RTrimNode() would delete
nodes at the end of the div node,
- // but for checking start of block they are actually meaningful. (Bug #1350)
- FCKDomTools.LTrimNode( eToolDiv ) ;
-
- bIsStartOfBlock = ( eToolDiv.innerHTML.length == 0 ) ;
- }
-
- oTestRange.Release() ;
-
- return ( this._Cache.IsStartOfBlock = bIsStartOfBlock ) ;
- },
-
- CheckEndOfBlock : function( refreshSelection )
- {
- var bIsEndOfBlock = this._Cache.IsEndOfBlock ;
-
- if ( bIsEndOfBlock != undefined )
- return bIsEndOfBlock ;
-
- // Create a clone of the current range.
- var oTestRange = this.Clone() ;
-
- // Collapse it to its end point.
- oTestRange.Collapse( false ) ;
-
- // Move the end boundary to the end of the block.
- oTestRange.SetEnd( oTestRange.EndBlock || oTestRange.EndBlockLimit, 2 ) ;
-
- bIsEndOfBlock = oTestRange.CheckIsCollapsed() ;
-
- if ( !bIsEndOfBlock )
- {
- // Inserts the contents of the range in a div tag.
- var eToolDiv = this.Window.document.createElement( 'div' ) ;
- oTestRange._Range.cloneContents().AppendTo( eToolDiv ) ;
- FCKDomTools.TrimNode( eToolDiv ) ;
-
- // Find out if we are in an empty tree of inline elements, like
- bIsEndOfBlock = true ;
- var eLastChild = eToolDiv ;
- while ( ( eLastChild = eLastChild.lastChild ) )
+ currentNode = container.childNodes[ offset ] ;
+
+ // 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.GetNextSourceNode( container, true, null, block ) ;
+
+ var hadBr = false ;
+
+ while ( currentNode )
+ {
+ switch ( currentNode.nodeType )
{
- // Check the following:
- // 1. Is there more than one node in the parents children?
- // 2. Is the node not an element node?
- // 3. Is it not a inline element.
- if ( eLastChild.previousSibling || eLastChild.nodeType != 1 || FCKListsLib.InlineChildReqElements[ eLastChild.nodeName.toLowerCase() ] == null )
- {
- // So we are not in the end of the range.
- bIsEndOfBlock = false ;
- break ;
- }
+ case 1 :
+ var nodeName = currentNode.nodeName.toLowerCase() ;
+
+ // It's an inline element.
+ if ( FCKListsLib.InlineChildReqElements[ nodeName ] )
+ break ;
+
+ // It is the first
found.
+ if ( nodeName == 'br' && !hadBr )
+ {
+ hadBr = true ;
+ break ;
+ }
+
+ return this._Cache.IsEndOfBlock = false ;
+
+ case 3 :
+ // It's a text node with real text.
+ if ( currentNode.nodeValue.Trim().length > 0 )
+ return this._Cache.IsEndOfBlock = false ;
}
- }
-
- oTestRange.Release() ;
+
+ currentNode = FCKDomTools.GetNextSourceNode( currentNode, false, null, block ) ;
+ }
if ( refreshSelection )
this.Select() ;
- return this._Cache.IsEndOfBlock = bIsEndOfBlock ;
+ return this._Cache.IsEndOfBlock = true ;
},
@@ -322,5 +379,5 @@
oBookmark.EndNode = eEndSpan ;
}
-
+
// Update the range position.
if ( eEndSpan )
@@ -331,5 +388,5 @@
else
this.MoveToPosition( eStartSpan, 4 ) ;
-
+
return oBookmark ;
},
@@ -532,5 +589,5 @@
if ( oNode.nodeType != 1 )
oNode = oNode.previousSibling ? null : oNode.parentNode ;
-
+
if ( oNode )
{
@@ -671,4 +728,5 @@
var eStartBlock = this.StartBlock ;
var eEndBlock = this.EndBlock ;
+ var oElementPath = null ;
if ( FCKConfig.EnterMode != 'br' )
@@ -696,4 +754,5 @@
if ( bIsEndOfBlock )
{
+ oElementPath = new FCKElementPath( this.StartContainer ) ;
this.MoveToPosition( eEndBlock, 4 ) ;
eEndBlock = null ;
@@ -701,4 +760,5 @@
else if ( bIsStartOfBlock )
{
+ oElementPath = new FCKElementPath( this.StartContainer ) ;
this.MoveToPosition( eStartBlock, 3 ) ;
eStartBlock = null ;
@@ -733,5 +793,6 @@
NextBlock : eEndBlock,
WasStartOfBlock : bIsStartOfBlock,
- WasEndOfBlock : bIsEndOfBlock
+ WasEndOfBlock : bIsEndOfBlock,
+ ElementPath : oElementPath
} ;
}
@@ -789,24 +850,24 @@
return !!this._Range ;
},
-
+
GetTouchedStartNode : function()
{
var range = this._Range ;
var container = range.startContainer ;
-
+
if ( range.collapsed || container.nodeType != 1 )
return container ;
-
+
return container.childNodes[ range.startOffset ] || container ;
},
-
+
GetTouchedEndNode : function()
{
var range = this._Range ;
var container = range.endContainer ;
-
+
if ( range.collapsed || container.nodeType != 1 )
return container ;
-
+
return container.childNodes[ range.endOffset - 1 ] || container ;
}
Index: /FCKeditor/trunk/editor/_source/classes/fckenterkey.js
===================================================================
--- /FCKeditor/trunk/editor/_source/classes/fckenterkey.js (revision 1047)
+++ /FCKeditor/trunk/editor/_source/classes/fckenterkey.js (revision 1048)
@@ -116,5 +116,5 @@
var parentPath = new FCKElementPath( parentElement ) ;
var sMode = mode || this.EnterMode ;
-
+
if ( sMode == 'br' || parentPath.Block && parentPath.Block.tagName.toLowerCase() == 'pre' )
return this._ExecuteEnterBr() ;
@@ -342,5 +342,5 @@
var eCurrentCell = FCKTools.GetElementAscensor( oCurrentBlock, 'td' );
- var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ],
+ var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ],
['UL','OL','TR'] ) ;
@@ -401,5 +401,7 @@
var oSplitInfo = oRange.SplitBlock() ;
-
+
+ // FCKDebug.OutputObject( oSplitInfo ) ;
+
if ( oSplitInfo )
{
@@ -431,4 +433,5 @@
var eNewBlock ;
+ var eFocusElement ;
if ( ePreviousBlock )
@@ -437,21 +440,32 @@
// If is a header tag, or we are in a Shift+Enter (#77),
- // create a new block element.
- if ( this._HasShift || (/^H[1-6]$/).test( sPreviousBlockTag ) )
- eNewBlock = this.Window.document.createElement( blockTag ) ;
- else
+ // create a new block element (later in the code).
+ if ( !this._HasShift && !(/^H[1-6]$/).test( sPreviousBlockTag ) )
{
// Otherwise, duplicate the previous block.
eNewBlock = FCKDomTools.CloneElement( ePreviousBlock ) ;
-
- this._RecreateEndingTree( ePreviousBlock, eNewBlock ) ;
}
}
else if ( eNextBlock )
- {
eNewBlock = FCKDomTools.CloneElement( eNextBlock ) ;
- }
- else
+
+ if ( !eNewBlock )
eNewBlock = this.Window.document.createElement( blockTag ) ;
+
+ // Recreate the inline elements tree, which was available
+ // before the hitting enter, so the same styles will be
+ // available in the new block.
+ var elementPath = oSplitInfo.ElementPath ;
+ var eFocusElement = eNewBlock ;
+ for ( var i = 0, len = elementPath.Elements.length ; i < len ; i++ )
+ {
+ var element = elementPath.Elements[i] ;
+
+ if ( element == elementPath.Block || element == elementPath.BlockLimit )
+ break ;
+
+ if ( FCKListsLib.InlineChildReqElements[ element.nodeName.toLowerCase() ] )
+ eFocusElement = eFocusElement.appendChild( FCKDomTools.CloneElement( element ) ) ;
+ }
if ( FCKBrowserInfo.IsGeckoLike )
@@ -469,4 +483,5 @@
}
+ // Move the selection to the new block.
oRange.MoveToElementEditStart( bIsStartOfBlock && !bIsEndOfBlock ? eNextBlock : eNewBlock ) ;
@@ -569,14 +584,4 @@
}
-// Recreate the elements tree at the end of the source block, at the beginning
-// of the target block. Eg.:
-// If source =
Some sample text
then target =-// If source =
Some sample text
then target = -FCKEnterKey.prototype._RecreateEndingTree = function( source, target ) -{ - while ( ( source = source.lastChild ) && source.nodeType == 1 && FCKListsLib.InlineChildReqElements[ source.nodeName.toLowerCase() ] != null ) - target = target.insertBefore( FCKDomTools.CloneElement( source ), target.firstChild ) ; -} - // Outdents a LI, maintaining the selection defined on a range. FCKEnterKey.prototype._OutdentWithSelection = function( li, range ) @@ -595,5 +600,5 @@ var startOk = false ; var endOk = false ; - + /* FCKDebug.Output( 'sc='+range.StartContainer.nodeName+ Index: /FCKeditor/trunk/editor/_source/internals/fckdomtools.js =================================================================== --- /FCKeditor/trunk/editor/_source/internals/fckdomtools.js (revision 1047) +++ /FCKeditor/trunk/editor/_source/internals/fckdomtools.js (revision 1048) @@ -265,5 +265,5 @@ * Get the next DOM node available in source order. */ - GetNextSourceNode : function( currentNode, startFromSibling, nodeType, stopSearchElement ) + GetNextSourceNode : function( currentNode, startFromSibling, nodeType, stopSearchNode ) { if ( !currentNode ) @@ -276,12 +276,15 @@ else { + if ( stopSearchNode && currentNode == stopSearchNode ) + return null ; + node = currentNode.nextSibling ; - - if ( !node && ( !stopSearchElement || stopSearchElement != currentNode.parentNode ) ) - return this.GetNextSourceNode( currentNode.parentNode, true, nodeType, stopSearchElement ) ; + + if ( !node && ( !stopSearchNode || stopSearchNode != currentNode.parentNode ) ) + return this.GetNextSourceNode( currentNode.parentNode, true, nodeType, stopSearchNode ) ; } if ( nodeType && node && node.nodeType != nodeType ) - return this.GetNextSourceNode( node, false, nodeType, stopSearchElement ) ; + return this.GetNextSourceNode( node, false, nodeType, stopSearchNode ) ; return node ; @@ -291,5 +294,5 @@ * Get the next DOM node available in source order. */ - GetPreviousSourceNode : function( currentNode, startFromSibling, nodeType ) + GetPreviousSourceNode : function( currentNode, startFromSibling, nodeType, stopSearchNode ) { if ( !currentNode ) @@ -301,8 +304,16 @@ node = currentNode.lastChild ; else - node = ( currentNode.previousSibling || this.GetPreviousSourceNode( currentNode.parentNode, true, nodeType ) ) ; - + { + if ( stopSearchNode && currentNode == stopSearchNode ) + return null ; + + node = currentNode.previousSibling ; + + if ( !node && ( !stopSearchNode || stopSearchNode != currentNode.parentNode ) ) + return this.GetPreviousSourceNode( currentNode.parentNode, true, nodeType, stopSearchNode ) ; + } + if ( nodeType && node && node.nodeType != nodeType ) - return this.GetPreviousSourceNode( node, false, nodeType ) ; + return this.GetPreviousSourceNode( node, false, nodeType, stopSearchNode ) ; return node ; @@ -452,5 +463,5 @@ if ( FCKBrowserInfo.IsIE && attributes[i].nodeName == 'class' ) { - // IE has a strange bug. If calling removeAttribute('className'), + // IE has a strange bug. If calling removeAttribute('className'), // the attributes collection will still contain the "class" // attribute, which will be marked as "specified", even if the @@ -578,7 +589,7 @@ { var candidate = parentNode.childNodes[i] ; - if ( normalized === true && - candidate.nodeType == 3 && - candidate.previousSibling && + if ( normalized === true && + candidate.nodeType == 3 && + candidate.previousSibling && candidate.previousSibling.nodeType == 3 ) continue; @@ -773,5 +784,5 @@ if ( currentListItem.nodeType == 11 ) { - if ( currentListItem.lastChild && + if ( currentListItem.lastChild && currentListItem.lastChild.getAttribute && currentListItem.lastChild.getAttribute( 'type' ) == '_moz' ) @@ -847,5 +858,5 @@ return node ; }, - + /** * Checks if an element has no "useful" content inside of it @@ -859,5 +870,5 @@ var child = element.firstChild ; var elementChild ; - + while ( child ) { @@ -866,5 +877,5 @@ if ( elementChild || !FCKListsLib.InlineNonEmptyElements[ child.nodeName.toLowerCase() ] ) return false ; - + if ( !elementCheckCallback || elementCheckCallback( child ) === true ) elementChild = child ; @@ -872,8 +883,8 @@ else if ( child.nodeType == 3 && child.nodeValue.length > 0 ) return false ; - + child = child.nextSibling ; } - + return elementChild ? this.CheckIsEmptyElement( elementChild, elementCheckCallback ) : true ; },