import rt 3.8.7
[freeside.git] / rt / share / html / NoAuth / RichText / FCKeditor / editor / _source / classes / fckenterkey.js
1 /*\r
2  * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
3  * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
4  *\r
5  * == BEGIN LICENSE ==\r
6  *\r
7  * Licensed under the terms of any of the following licenses at your\r
8  * choice:\r
9  *\r
10  *  - GNU General Public License Version 2 or later (the "GPL")\r
11  *    http://www.gnu.org/licenses/gpl.html\r
12  *\r
13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
14  *    http://www.gnu.org/licenses/lgpl.html\r
15  *\r
16  *  - Mozilla Public License Version 1.1 or later (the "MPL")\r
17  *    http://www.mozilla.org/MPL/MPL-1.1.html\r
18  *\r
19  * == END LICENSE ==\r
20  *\r
21  * Controls the [Enter] keystroke behavior in a document.\r
22  */\r
23 \r
24 /*\r
25  *      Constructor.\r
26  *              @targetDocument : the target document.\r
27  *              @enterMode : the behavior for the <Enter> keystroke.\r
28  *                      May be "p", "div", "br". Default is "p".\r
29  *              @shiftEnterMode : the behavior for the <Shift>+<Enter> keystroke.\r
30  *                      May be "p", "div", "br". Defaults to "br".\r
31  */\r
32 var FCKEnterKey = function( targetWindow, enterMode, shiftEnterMode, tabSpaces )\r
33 {\r
34         this.Window                     = targetWindow ;\r
35         this.EnterMode          = enterMode || 'p' ;\r
36         this.ShiftEnterMode     = shiftEnterMode || 'br' ;\r
37 \r
38         // Setup the Keystroke Handler.\r
39         var oKeystrokeHandler = new FCKKeystrokeHandler( false ) ;\r
40         oKeystrokeHandler._EnterKey = this ;\r
41         oKeystrokeHandler.OnKeystroke = FCKEnterKey_OnKeystroke ;\r
42 \r
43         oKeystrokeHandler.SetKeystrokes( [\r
44                 [ 13            , 'Enter' ],\r
45                 [ SHIFT + 13, 'ShiftEnter' ],\r
46                 [ 8                     , 'Backspace' ],\r
47                 [ CTRL + 8      , 'CtrlBackspace' ],\r
48                 [ 46            , 'Delete' ]\r
49         ] ) ;\r
50 \r
51         this.TabText = '' ;\r
52 \r
53         // Safari by default inserts 4 spaces on TAB, while others make the editor\r
54         // loose focus. So, we need to handle it here to not include those spaces.\r
55         if ( tabSpaces > 0 || FCKBrowserInfo.IsSafari )\r
56         {\r
57                 while ( tabSpaces-- )\r
58                         this.TabText += '\xa0' ;\r
59 \r
60                 oKeystrokeHandler.SetKeystrokes( [ 9, 'Tab' ] );\r
61         }\r
62 \r
63         oKeystrokeHandler.AttachToElement( targetWindow.document ) ;\r
64 }\r
65 \r
66 \r
67 function FCKEnterKey_OnKeystroke(  keyCombination, keystrokeValue )\r
68 {\r
69         var oEnterKey = this._EnterKey ;\r
70 \r
71         try\r
72         {\r
73                 switch ( keystrokeValue )\r
74                 {\r
75                         case 'Enter' :\r
76                                 return oEnterKey.DoEnter() ;\r
77                                 break ;\r
78                         case 'ShiftEnter' :\r
79                                 return oEnterKey.DoShiftEnter() ;\r
80                                 break ;\r
81                         case 'Backspace' :\r
82                                 return oEnterKey.DoBackspace() ;\r
83                                 break ;\r
84                         case 'Delete' :\r
85                                 return oEnterKey.DoDelete() ;\r
86                                 break ;\r
87                         case 'Tab' :\r
88                                 return oEnterKey.DoTab() ;\r
89                                 break ;\r
90                         case 'CtrlBackspace' :\r
91                                 return oEnterKey.DoCtrlBackspace() ;\r
92                                 break ;\r
93                 }\r
94         }\r
95         catch (e)\r
96         {\r
97                 // If for any reason we are not able to handle it, go\r
98                 // ahead with the browser default behavior.\r
99         }\r
100 \r
101         return false ;\r
102 }\r
103 \r
104 /*\r
105  * Executes the <Enter> key behavior.\r
106  */\r
107 FCKEnterKey.prototype.DoEnter = function( mode, hasShift )\r
108 {\r
109         // Save an undo snapshot before doing anything\r
110         FCKUndo.SaveUndoStep() ;\r
111 \r
112         this._HasShift = ( hasShift === true ) ;\r
113 \r
114         var parentElement = FCKSelection.GetParentElement() ;\r
115         var parentPath = new FCKElementPath( parentElement ) ;\r
116         var sMode = mode || this.EnterMode ;\r
117 \r
118         if ( sMode == 'br' || parentPath.Block && parentPath.Block.tagName.toLowerCase() == 'pre' )\r
119                 return this._ExecuteEnterBr() ;\r
120         else\r
121                 return this._ExecuteEnterBlock( sMode ) ;\r
122 }\r
123 \r
124 /*\r
125  * Executes the <Shift>+<Enter> key behavior.\r
126  */\r
127 FCKEnterKey.prototype.DoShiftEnter = function()\r
128 {\r
129         return this.DoEnter( this.ShiftEnterMode, true ) ;\r
130 }\r
131 \r
132 /*\r
133  * Executes the <Backspace> key behavior.\r
134  */\r
135 FCKEnterKey.prototype.DoBackspace = function()\r
136 {\r
137         var bCustom = false ;\r
138 \r
139         // Get the current selection.\r
140         var oRange = new FCKDomRange( this.Window ) ;\r
141         oRange.MoveToSelection() ;\r
142 \r
143         // Kludge for #247\r
144         if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )\r
145         {\r
146                 this._FixIESelectAllBug( oRange ) ;\r
147                 return true ;\r
148         }\r
149 \r
150         var isCollapsed = oRange.CheckIsCollapsed() ;\r
151 \r
152         if ( !isCollapsed )\r
153         {\r
154                 // Bug #327, Backspace with an img selection would activate the default action in IE.\r
155                 // Let's override that with our logic here.\r
156                 if ( FCKBrowserInfo.IsIE && this.Window.document.selection.type.toLowerCase() == "control" )\r
157                 {\r
158                         var controls = this.Window.document.selection.createRange() ;\r
159                         for ( var i = controls.length - 1 ; i >= 0 ; i-- )\r
160                         {\r
161                                 var el = controls.item( i ) ;\r
162                                 el.parentNode.removeChild( el ) ;\r
163                         }\r
164                         return true ;\r
165                 }\r
166 \r
167                 return false ;\r
168         }\r
169 \r
170         // On IE, it is better for us handle the deletion if the caret is preceeded\r
171         // by a <br> (#1383).\r
172         if ( FCKBrowserInfo.IsIE )\r
173         {\r
174                 var previousElement = FCKDomTools.GetPreviousSourceElement( oRange.StartNode, true ) ;\r
175 \r
176                 if ( previousElement && previousElement.nodeName.toLowerCase() == 'br' )\r
177                 {\r
178                         // Create a range that starts after the <br> and ends at the\r
179                         // current range position.\r
180                         var testRange = oRange.Clone() ;\r
181                         testRange.SetStart( previousElement, 4 ) ;\r
182 \r
183                         // If that range is empty, we can proceed cleaning that <br> manually.\r
184                         if ( testRange.CheckIsEmpty() )\r
185                         {\r
186                                 previousElement.parentNode.removeChild( previousElement ) ;\r
187                                 return true ;\r
188                         }\r
189                 }\r
190         }\r
191 \r
192         var oStartBlock = oRange.StartBlock ;\r
193         var oEndBlock = oRange.EndBlock ;\r
194 \r
195         // The selection boundaries must be in the same "block limit" element\r
196         if ( oRange.StartBlockLimit == oRange.EndBlockLimit && oStartBlock && oEndBlock )\r
197         {\r
198                 if ( !isCollapsed )\r
199                 {\r
200                         var bEndOfBlock = oRange.CheckEndOfBlock() ;\r
201 \r
202                         oRange.DeleteContents() ;\r
203 \r
204                         if ( oStartBlock != oEndBlock )\r
205                         {\r
206                                 oRange.SetStart(oEndBlock,1) ;\r
207                                 oRange.SetEnd(oEndBlock,1) ;\r
208 \r
209 //                              if ( bEndOfBlock )\r
210 //                                      oEndBlock.parentNode.removeChild( oEndBlock ) ;\r
211                         }\r
212 \r
213                         oRange.Select() ;\r
214 \r
215                         bCustom = ( oStartBlock == oEndBlock ) ;\r
216                 }\r
217 \r
218                 if ( oRange.CheckStartOfBlock() )\r
219                 {\r
220                         var oCurrentBlock = oRange.StartBlock ;\r
221 \r
222                         var ePrevious = FCKDomTools.GetPreviousSourceElement( oCurrentBlock, true, [ 'BODY', oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ;\r
223 \r
224                         bCustom = this._ExecuteBackspace( oRange, ePrevious, oCurrentBlock ) ;\r
225                 }\r
226                 else if ( FCKBrowserInfo.IsGeckoLike )\r
227                 {\r
228                         // Firefox and Opera (#1095) loose the selection when executing\r
229                         // CheckStartOfBlock, so we must reselect.\r
230                         oRange.Select() ;\r
231                 }\r
232         }\r
233 \r
234         oRange.Release() ;\r
235         return bCustom ;\r
236 }\r
237 \r
238 FCKEnterKey.prototype.DoCtrlBackspace = function()\r
239 {\r
240         FCKUndo.SaveUndoStep() ;\r
241         var oRange = new FCKDomRange( this.Window ) ;\r
242         oRange.MoveToSelection() ;\r
243         if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )\r
244         {\r
245                 this._FixIESelectAllBug( oRange ) ;\r
246                 return true ;\r
247         }\r
248         return false ;\r
249 }\r
250 \r
251 FCKEnterKey.prototype._ExecuteBackspace = function( range, previous, currentBlock )\r
252 {\r
253         var bCustom = false ;\r
254 \r
255         // We could be in a nested LI.\r
256         if ( !previous && currentBlock && currentBlock.nodeName.IEquals( 'LI' ) && currentBlock.parentNode.parentNode.nodeName.IEquals( 'LI' ) )\r
257         {\r
258                 this._OutdentWithSelection( currentBlock, range ) ;\r
259                 return true ;\r
260         }\r
261 \r
262         if ( previous && previous.nodeName.IEquals( 'LI' ) )\r
263         {\r
264                 var oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;\r
265 \r
266                 while ( oNestedList )\r
267                 {\r
268                         previous = FCKDomTools.GetLastChild( oNestedList, 'LI' ) ;\r
269                         oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;\r
270                 }\r
271         }\r
272 \r
273         if ( previous && currentBlock )\r
274         {\r
275                 // If we are in a LI, and the previous block is not an LI, we must outdent it.\r
276                 if ( currentBlock.nodeName.IEquals( 'LI' ) && !previous.nodeName.IEquals( 'LI' ) )\r
277                 {\r
278                         this._OutdentWithSelection( currentBlock, range ) ;\r
279                         return true ;\r
280                 }\r
281 \r
282                 // Take a reference to the parent for post processing cleanup.\r
283                 var oCurrentParent = currentBlock.parentNode ;\r
284 \r
285                 var sPreviousName = previous.nodeName.toLowerCase() ;\r
286                 if ( FCKListsLib.EmptyElements[ sPreviousName ] != null || sPreviousName == 'table' )\r
287                 {\r
288                         FCKDomTools.RemoveNode( previous ) ;\r
289                         bCustom = true ;\r
290                 }\r
291                 else\r
292                 {\r
293                         // Remove the current block.\r
294                         FCKDomTools.RemoveNode( currentBlock ) ;\r
295 \r
296                         // Remove any empty tag left by the block removal.\r
297                         while ( oCurrentParent.innerHTML.Trim().length == 0 )\r
298                         {\r
299                                 var oParent = oCurrentParent.parentNode ;\r
300                                 oParent.removeChild( oCurrentParent ) ;\r
301                                 oCurrentParent = oParent ;\r
302                         }\r
303 \r
304                         // Cleanup the previous and the current elements.\r
305                         FCKDomTools.LTrimNode( currentBlock ) ;\r
306                         FCKDomTools.RTrimNode( previous ) ;\r
307 \r
308                         // Append a space to the previous.\r
309                         // Maybe it is not always desirable...\r
310                         // previous.appendChild( this.Window.document.createTextNode( ' ' ) ) ;\r
311 \r
312                         // Set the range to the end of the previous element and bookmark it.\r
313                         range.SetStart( previous, 2, true ) ;\r
314                         range.Collapse( true ) ;\r
315                         var oBookmark = range.CreateBookmark( true ) ;\r
316 \r
317                         // Move the contents of the block to the previous element and delete it.\r
318                         // But for some block types (e.g. table), moving the children to the previous block makes no sense.\r
319                         // So a check is needed. (See #1081)\r
320                         if ( ! currentBlock.tagName.IEquals( [ 'TABLE' ] ) )\r
321                                 FCKDomTools.MoveChildren( currentBlock, previous ) ;\r
322 \r
323                         // Place the selection at the bookmark.\r
324                         range.SelectBookmark( oBookmark ) ;\r
325 \r
326                         bCustom = true ;\r
327                 }\r
328         }\r
329 \r
330         return bCustom ;\r
331 }\r
332 \r
333 /*\r
334  * Executes the <Delete> key behavior.\r
335  */\r
336 FCKEnterKey.prototype.DoDelete = function()\r
337 {\r
338         // Save an undo snapshot before doing anything\r
339         // This is to conform with the behavior seen in MS Word\r
340         FCKUndo.SaveUndoStep() ;\r
341 \r
342         // The <Delete> has the same effect as the <Backspace>, so we have the same\r
343         // results if we just move to the next block and apply the same <Backspace> logic.\r
344 \r
345         var bCustom = false ;\r
346 \r
347         // Get the current selection.\r
348         var oRange = new FCKDomRange( this.Window ) ;\r
349         oRange.MoveToSelection() ;\r
350 \r
351         // Kludge for #247\r
352         if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )\r
353         {\r
354                 this._FixIESelectAllBug( oRange ) ;\r
355                 return true ;\r
356         }\r
357 \r
358         // There is just one special case for collapsed selections at the end of a block.\r
359         if ( oRange.CheckIsCollapsed() && oRange.CheckEndOfBlock( FCKBrowserInfo.IsGeckoLike ) )\r
360         {\r
361                 var oCurrentBlock = oRange.StartBlock ;\r
362                 var eCurrentCell = FCKTools.GetElementAscensor( oCurrentBlock, 'td' );\r
363 \r
364                 var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ],\r
365                                 ['UL','OL','TR'], true ) ;\r
366 \r
367                 // Bug #1323 : if we're in a table cell, and the next node belongs to a different cell, then don't\r
368                 // delete anything.\r
369                 if ( eCurrentCell )\r
370                 {\r
371                         var eNextCell = FCKTools.GetElementAscensor( eNext, 'td' );\r
372                         if ( eNextCell != eCurrentCell )\r
373                                 return true ;\r
374                 }\r
375 \r
376                 bCustom = this._ExecuteBackspace( oRange, oCurrentBlock, eNext ) ;\r
377         }\r
378 \r
379         oRange.Release() ;\r
380         return bCustom ;\r
381 }\r
382 \r
383 /*\r
384  * Executes the <Tab> key behavior.\r
385  */\r
386 FCKEnterKey.prototype.DoTab = function()\r
387 {\r
388         var oRange = new FCKDomRange( this.Window );\r
389         oRange.MoveToSelection() ;\r
390 \r
391         // If the user pressed <tab> inside a table, we should give him the default behavior ( moving between cells )\r
392         // instead of giving him more non-breaking spaces. (Bug #973)\r
393         var node = oRange._Range.startContainer ;\r
394         while ( node )\r
395         {\r
396                 if ( node.nodeType == 1 )\r
397                 {\r
398                         var tagName = node.tagName.toLowerCase() ;\r
399                         if ( tagName == "tr" || tagName == "td" || tagName == "th" || tagName == "tbody" || tagName == "table" )\r
400                                 return false ;\r
401                         else\r
402                                 break ;\r
403                 }\r
404                 node = node.parentNode ;\r
405         }\r
406 \r
407         if ( this.TabText )\r
408         {\r
409                 oRange.DeleteContents() ;\r
410                 oRange.InsertNode( this.Window.document.createTextNode( this.TabText ) ) ;\r
411                 oRange.Collapse( false ) ;\r
412                 oRange.Select() ;\r
413         }\r
414         return true ;\r
415 }\r
416 \r
417 FCKEnterKey.prototype._ExecuteEnterBlock = function( blockTag, range )\r
418 {\r
419         // Get the current selection.\r
420         var oRange = range || new FCKDomRange( this.Window ) ;\r
421 \r
422         var oSplitInfo = oRange.SplitBlock( blockTag ) ;\r
423 \r
424         if ( oSplitInfo )\r
425         {\r
426                 // Get the current blocks.\r
427                 var ePreviousBlock      = oSplitInfo.PreviousBlock ;\r
428                 var eNextBlock          = oSplitInfo.NextBlock ;\r
429 \r
430                 var bIsStartOfBlock     = oSplitInfo.WasStartOfBlock ;\r
431                 var bIsEndOfBlock       = oSplitInfo.WasEndOfBlock ;\r
432 \r
433                 // If there is one block under a list item, modify the split so that the list item gets split as well. (Bug #1647)\r
434                 if ( eNextBlock )\r
435                 {\r
436                         if ( eNextBlock.parentNode.nodeName.IEquals( 'li' ) )\r
437                         {\r
438                                 FCKDomTools.BreakParent( eNextBlock, eNextBlock.parentNode ) ;\r
439                                 FCKDomTools.MoveNode( eNextBlock, eNextBlock.nextSibling, true ) ;\r
440                         }\r
441                 }\r
442                 else if ( ePreviousBlock && ePreviousBlock.parentNode.nodeName.IEquals( 'li' ) )\r
443                 {\r
444                         FCKDomTools.BreakParent( ePreviousBlock, ePreviousBlock.parentNode ) ;\r
445                         oRange.MoveToElementEditStart( ePreviousBlock.nextSibling );\r
446                         FCKDomTools.MoveNode( ePreviousBlock, ePreviousBlock.previousSibling ) ;\r
447                 }\r
448 \r
449                 // If we have both the previous and next blocks, it means that the\r
450                 // boundaries were on separated blocks, or none of them where on the\r
451                 // block limits (start/end).\r
452                 if ( !bIsStartOfBlock && !bIsEndOfBlock )\r
453                 {\r
454                         // If the next block is an <li> with another list tree as the first child\r
455                         // We'll need to append a placeholder or the list item wouldn't be editable. (Bug #1420)\r
456                         if ( eNextBlock.nodeName.IEquals( 'li' ) && eNextBlock.firstChild\r
457                                         && eNextBlock.firstChild.nodeName.IEquals( ['ul', 'ol'] ) )\r
458                                 eNextBlock.insertBefore( FCKTools.GetElementDocument( eNextBlock ).createTextNode( '\xa0' ), eNextBlock.firstChild ) ;\r
459                         // Move the selection to the end block.\r
460                         if ( eNextBlock )\r
461                                 oRange.MoveToElementEditStart( eNextBlock ) ;\r
462                 }\r
463                 else\r
464                 {\r
465                         if ( bIsStartOfBlock && bIsEndOfBlock && ePreviousBlock.tagName.toUpperCase() == 'LI' )\r
466                         {\r
467                                 oRange.MoveToElementStart( ePreviousBlock ) ;\r
468                                 this._OutdentWithSelection( ePreviousBlock, oRange ) ;\r
469                                 oRange.Release() ;\r
470                                 return true ;\r
471                         }\r
472 \r
473                         var eNewBlock ;\r
474 \r
475                         if ( ePreviousBlock )\r
476                         {\r
477                                 var sPreviousBlockTag = ePreviousBlock.tagName.toUpperCase() ;\r
478 \r
479                                 // If is a header tag, or we are in a Shift+Enter (#77),\r
480                                 // create a new block element (later in the code).\r
481                                 if ( !this._HasShift && !(/^H[1-6]$/).test( sPreviousBlockTag ) )\r
482                                 {\r
483                                         // Otherwise, duplicate the previous block.\r
484                                         eNewBlock = FCKDomTools.CloneElement( ePreviousBlock ) ;\r
485                                 }\r
486                         }\r
487                         else if ( eNextBlock )\r
488                                 eNewBlock = FCKDomTools.CloneElement( eNextBlock ) ;\r
489 \r
490                         if ( !eNewBlock )\r
491                                 eNewBlock = this.Window.document.createElement( blockTag ) ;\r
492 \r
493                         // Recreate the inline elements tree, which was available\r
494                         // before the hitting enter, so the same styles will be\r
495                         // available in the new block.\r
496                         var elementPath = oSplitInfo.ElementPath ;\r
497                         if ( elementPath )\r
498                         {\r
499                                 for ( var i = 0, len = elementPath.Elements.length ; i < len ; i++ )\r
500                                 {\r
501                                         var element = elementPath.Elements[i] ;\r
502 \r
503                                         if ( element == elementPath.Block || element == elementPath.BlockLimit )\r
504                                                 break ;\r
505 \r
506                                         if ( FCKListsLib.InlineChildReqElements[ element.nodeName.toLowerCase() ] )\r
507                                         {\r
508                                                 element = FCKDomTools.CloneElement( element ) ;\r
509                                                 FCKDomTools.MoveChildren( eNewBlock, element ) ;\r
510                                                 eNewBlock.appendChild( element ) ;\r
511                                         }\r
512                                 }\r
513                         }\r
514 \r
515                         if ( FCKBrowserInfo.IsGeckoLike )\r
516                                 FCKTools.AppendBogusBr( eNewBlock ) ;\r
517 \r
518                         oRange.InsertNode( eNewBlock ) ;\r
519 \r
520                         // This is tricky, but to make the new block visible correctly\r
521                         // we must select it.\r
522                         if ( FCKBrowserInfo.IsIE )\r
523                         {\r
524                                 // Move the selection to the new block.\r
525                                 oRange.MoveToElementEditStart( eNewBlock ) ;\r
526                                 oRange.Select() ;\r
527                         }\r
528 \r
529                         // Move the selection to the new block.\r
530                         oRange.MoveToElementEditStart( bIsStartOfBlock && !bIsEndOfBlock ? eNextBlock : eNewBlock ) ;\r
531                 }\r
532 \r
533                 if ( FCKBrowserInfo.IsGeckoLike )\r
534                 {\r
535                         if ( eNextBlock )\r
536                         {\r
537                                 // If we have split the block, adds a temporary span at the\r
538                                 // range position and scroll relatively to it.\r
539                                 var tmpNode = this.Window.document.createElement( 'span' ) ;\r
540 \r
541                                 // We need some content for Safari.\r
542                                 tmpNode.innerHTML = '&nbsp;';\r
543 \r
544                                 oRange.InsertNode( tmpNode ) ;\r
545                                 FCKDomTools.ScrollIntoView( tmpNode, false ) ;\r
546                                 oRange.DeleteContents() ;\r
547                         }\r
548                         else\r
549                         {\r
550                                 // We may use the above scroll logic for the new block case\r
551                                 // too, but it gives some weird result with Opera.\r
552                                 FCKDomTools.ScrollIntoView( eNextBlock || eNewBlock, false ) ;\r
553                         }\r
554                 }\r
555 \r
556                 oRange.Select() ;\r
557         }\r
558 \r
559         // Release the resources used by the range.\r
560         oRange.Release() ;\r
561 \r
562         return true ;\r
563 }\r
564 \r
565 FCKEnterKey.prototype._ExecuteEnterBr = function( blockTag )\r
566 {\r
567         // Get the current selection.\r
568         var oRange = new FCKDomRange( this.Window ) ;\r
569         oRange.MoveToSelection() ;\r
570 \r
571         // The selection boundaries must be in the same "block limit" element.\r
572         if ( oRange.StartBlockLimit == oRange.EndBlockLimit )\r
573         {\r
574                 oRange.DeleteContents() ;\r
575 \r
576                 // Get the new selection (it is collapsed at this point).\r
577                 oRange.MoveToSelection() ;\r
578 \r
579                 var bIsStartOfBlock     = oRange.CheckStartOfBlock() ;\r
580                 var bIsEndOfBlock       = oRange.CheckEndOfBlock() ;\r
581 \r
582                 var sStartBlockTag = oRange.StartBlock ? oRange.StartBlock.tagName.toUpperCase() : '' ;\r
583 \r
584                 var bHasShift = this._HasShift ;\r
585                 var bIsPre = false ;\r
586 \r
587                 if ( !bHasShift && sStartBlockTag == 'LI' )\r
588                         return this._ExecuteEnterBlock( null, oRange ) ;\r
589 \r
590                 // If we are at the end of a header block.\r
591                 if ( !bHasShift && bIsEndOfBlock && (/^H[1-6]$/).test( sStartBlockTag ) )\r
592                 {\r
593                         // Insert a BR after the current paragraph.\r
594                         FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createElement( 'br' ) ) ;\r
595 \r
596                         // The space is required by Gecko only to make the cursor blink.\r
597                         if ( FCKBrowserInfo.IsGecko )\r
598                                 FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createTextNode( '' ) ) ;\r
599 \r
600                         // IE and Gecko have different behaviors regarding the position.\r
601                         oRange.SetStart( oRange.StartBlock.nextSibling, FCKBrowserInfo.IsIE ? 3 : 1 ) ;\r
602                 }\r
603                 else\r
604                 {\r
605                         var eLineBreak ;\r
606                         bIsPre = sStartBlockTag.IEquals( 'pre' ) ;\r
607                         if ( bIsPre )\r
608                                 eLineBreak = this.Window.document.createTextNode( FCKBrowserInfo.IsIE ? '\r' : '\n' ) ;\r
609                         else\r
610                                 eLineBreak = this.Window.document.createElement( 'br' ) ;\r
611 \r
612                         oRange.InsertNode( eLineBreak ) ;\r
613 \r
614                         // The space is required by Gecko only to make the cursor blink.\r
615                         if ( FCKBrowserInfo.IsGecko )\r
616                                 FCKDomTools.InsertAfterNode( eLineBreak, this.Window.document.createTextNode( '' ) ) ;\r
617 \r
618                         // If we are at the end of a block, we must be sure the bogus node is available in that block.\r
619                         if ( bIsEndOfBlock && FCKBrowserInfo.IsGeckoLike )\r
620                                 FCKTools.AppendBogusBr( eLineBreak.parentNode ) ;\r
621 \r
622                         if ( FCKBrowserInfo.IsIE )\r
623                                 oRange.SetStart( eLineBreak, 4 ) ;\r
624                         else\r
625                                 oRange.SetStart( eLineBreak.nextSibling, 1 ) ;\r
626 \r
627                         if ( ! FCKBrowserInfo.IsIE )\r
628                         {\r
629                                 var dummy = null ;\r
630                                 if ( FCKBrowserInfo.IsOpera )\r
631                                         dummy = this.Window.document.createElement( 'span' ) ;\r
632                                 else\r
633                                         dummy = this.Window.document.createElement( 'br' ) ;\r
634 \r
635                                 eLineBreak.parentNode.insertBefore( dummy, eLineBreak.nextSibling ) ;\r
636 \r
637                                 FCKDomTools.ScrollIntoView( dummy, false ) ;\r
638 \r
639                                 dummy.parentNode.removeChild( dummy ) ;\r
640                         }\r
641                 }\r
642 \r
643                 // This collapse guarantees the cursor will be blinking.\r
644                 oRange.Collapse( true ) ;\r
645 \r
646                 oRange.Select( bIsPre ) ;\r
647         }\r
648 \r
649         // Release the resources used by the range.\r
650         oRange.Release() ;\r
651 \r
652         return true ;\r
653 }\r
654 \r
655 // Outdents a LI, maintaining the selection defined on a range.\r
656 FCKEnterKey.prototype._OutdentWithSelection = function( li, range )\r
657 {\r
658         var oBookmark = range.CreateBookmark() ;\r
659 \r
660         FCKListHandler.OutdentListItem( li ) ;\r
661 \r
662         range.MoveToBookmark( oBookmark ) ;\r
663         range.Select() ;\r
664 }\r
665 \r
666 // Is all the contents under a node included by a range?\r
667 FCKEnterKey.prototype._CheckIsAllContentsIncluded = function( range, node )\r
668 {\r
669         var startOk = false ;\r
670         var endOk = false ;\r
671 \r
672         /*\r
673         FCKDebug.Output( 'sc='+range.StartContainer.nodeName+\r
674                         ',so='+range._Range.startOffset+\r
675                         ',ec='+range.EndContainer.nodeName+\r
676                         ',eo='+range._Range.endOffset ) ;\r
677         */\r
678         if ( range.StartContainer == node || range.StartContainer == node.firstChild )\r
679                 startOk = ( range._Range.startOffset == 0 ) ;\r
680 \r
681         if ( range.EndContainer == node || range.EndContainer == node.lastChild )\r
682         {\r
683                 var nodeLength = range.EndContainer.nodeType == 3 ? range.EndContainer.length : range.EndContainer.childNodes.length ;\r
684                 endOk = ( range._Range.endOffset == nodeLength ) ;\r
685         }\r
686 \r
687         return startOk && endOk ;\r
688 }\r
689 \r
690 // Kludge for #247\r
691 FCKEnterKey.prototype._FixIESelectAllBug = function( range )\r
692 {\r
693         var doc = this.Window.document ;\r
694         doc.body.innerHTML = '' ;\r
695         var editBlock ;\r
696         if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) )\r
697         {\r
698                 editBlock = doc.createElement( FCKConfig.EnterMode ) ;\r
699                 doc.body.appendChild( editBlock ) ;\r
700         }\r
701         else\r
702                 editBlock = doc.body ;\r
703 \r
704         range.MoveToNodeContents( editBlock ) ;\r
705         range.Collapse( true ) ;\r
706         range.Select() ;\r
707         range.Release() ;\r
708 }\r