source: branches/1.2/workflow/js/fckeditor/editor/_source/classes/fckenterkey.js @ 1349

Revision 1349, 15.4 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

  • Property svn:executable set to *
Line 
1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2007 Frederico Caldeira Knabben
4 *
5 * == BEGIN LICENSE ==
6 *
7 * Licensed under the terms of any of the following licenses at your
8 * choice:
9 *
10 *  - GNU General Public License Version 2 or later (the "GPL")
11 *    http://www.gnu.org/licenses/gpl.html
12 *
13 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14 *    http://www.gnu.org/licenses/lgpl.html
15 *
16 *  - Mozilla Public License Version 1.1 or later (the "MPL")
17 *    http://www.mozilla.org/MPL/MPL-1.1.html
18 *
19 * == END LICENSE ==
20 *
21 * Controls the [Enter] keystroke behavior in a document.
22 */
23
24/*
25 *      Constructor.
26 *              @targetDocument : the target document.
27 *              @enterMode : the behavior for the <Enter> keystroke.
28 *                      May be "p", "div", "br". Default is "p".
29 *              @shiftEnterMode : the behavior for the <Shift>+<Enter> keystroke.
30 *                      May be "p", "div", "br". Defaults to "br".
31 */
32var FCKEnterKey = function( targetWindow, enterMode, shiftEnterMode )
33{
34        this.Window                     = targetWindow ;
35        this.EnterMode          = enterMode || 'p' ;
36        this.ShiftEnterMode     = shiftEnterMode || 'br' ;
37
38        // Setup the Keystroke Handler.
39        var oKeystrokeHandler = new FCKKeystrokeHandler( false ) ;
40        oKeystrokeHandler._EnterKey = this ;
41        oKeystrokeHandler.OnKeystroke = FCKEnterKey_OnKeystroke ;
42
43        oKeystrokeHandler.SetKeystrokes( [
44                [ 13            , 'Enter' ],
45                [ SHIFT + 13, 'ShiftEnter' ],
46                [ 8                     , 'Backspace' ],
47                [ 46            , 'Delete' ]
48        ] ) ;
49
50        oKeystrokeHandler.AttachToElement( targetWindow.document ) ;
51}
52
53
54function FCKEnterKey_OnKeystroke(  keyCombination, keystrokeValue )
55{
56        var oEnterKey = this._EnterKey ;
57
58        try
59        {
60                switch ( keystrokeValue )
61                {
62                        case 'Enter' :
63                                return oEnterKey.DoEnter() ;
64                                break ;
65
66                        case 'ShiftEnter' :
67                                return oEnterKey.DoShiftEnter() ;
68                                break ;
69
70                        case 'Backspace' :
71                                return oEnterKey.DoBackspace() ;
72                                break ;
73
74                        case 'Delete' :
75                                return oEnterKey.DoDelete() ;
76                }
77        }
78        catch (e)
79        {
80                // If for any reason we are not able to handle it, go
81                // ahead with the browser default behavior.
82        }
83
84        return false ;
85}
86
87/*
88 * Executes the <Enter> key behavior.
89 */
90FCKEnterKey.prototype.DoEnter = function( mode, hasShift )
91{
92        this._HasShift = ( hasShift === true ) ;
93
94        var sMode = mode || this.EnterMode ;
95
96        if ( sMode == 'br' )
97                return this._ExecuteEnterBr() ;
98        else
99                return this._ExecuteEnterBlock( sMode ) ;
100}
101
102/*
103 * Executes the <Shift>+<Enter> key behavior.
104 */
105FCKEnterKey.prototype.DoShiftEnter = function()
106{
107        return this.DoEnter( this.ShiftEnterMode, true ) ;
108}
109
110/*
111 * Executes the <Backspace> key behavior.
112 */
113FCKEnterKey.prototype.DoBackspace = function()
114{
115        var bCustom = false ;
116
117        // Get the current selection.
118        var oRange = new FCKDomRange( this.Window ) ;
119        oRange.MoveToSelection() ;
120
121        if ( !oRange.CheckIsCollapsed() )
122                return false ;
123
124        var oStartBlock = oRange.StartBlock ;
125        var oEndBlock = oRange.EndBlock ;
126
127        // The selection boundaries must be in the same "block limit" element
128        if ( oRange.StartBlockLimit == oRange.EndBlockLimit && oStartBlock && oEndBlock )
129        {
130                if ( !oRange.CheckIsCollapsed() )
131                {
132                        var bEndOfBlock = oRange.CheckEndOfBlock() ;
133
134                        oRange.DeleteContents() ;
135
136                        if ( oStartBlock != oEndBlock )
137                        {
138                                oRange.SetStart(oEndBlock,1) ;
139                                oRange.SetEnd(oEndBlock,1) ;
140
141//                              if ( bEndOfBlock )
142//                                      oEndBlock.parentNode.removeChild( oEndBlock ) ;
143                        }
144
145                        oRange.Select() ;
146
147                        bCustom = ( oStartBlock == oEndBlock ) ;
148                }
149
150                if ( oRange.CheckStartOfBlock() )
151                {
152                        var oCurrentBlock = oRange.StartBlock ;
153
154                        var ePrevious = FCKDomTools.GetPreviousSourceElement( oCurrentBlock, true, [ 'BODY', oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ;
155
156                        bCustom = this._ExecuteBackspace( oRange, ePrevious, oCurrentBlock ) ;
157                }
158                else if ( FCKBrowserInfo.IsGecko )
159                {
160                        // Firefox looses the selection when executing CheckStartOfBlock, so we must reselect.
161                        oRange.Select() ;
162                }
163        }
164
165        oRange.Release() ;
166        return bCustom ;
167}
168
169FCKEnterKey.prototype._ExecuteBackspace = function( range, previous, currentBlock )
170{
171        var bCustom = false ;
172
173        // We could be in a nested LI.
174        if ( !previous && currentBlock.nodeName.IEquals( 'LI' ) && currentBlock.parentNode.parentNode.nodeName.IEquals( 'LI' ) )
175        {
176                this._OutdentWithSelection( currentBlock, range ) ;
177                return true ;
178        }
179
180        if ( previous && previous.nodeName.IEquals( 'LI' ) )
181        {
182                var oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;
183
184                while ( oNestedList )
185                {
186                        previous = FCKDomTools.GetLastChild( oNestedList, 'LI' ) ;
187                        oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;
188                }
189        }
190
191        if ( previous && currentBlock )
192        {
193                // If we are in a LI, and the previous block is not an LI, we must outdent it.
194                if ( currentBlock.nodeName.IEquals( 'LI' ) && !previous.nodeName.IEquals( 'LI' ) )
195                {
196                        this._OutdentWithSelection( currentBlock, range ) ;
197                        return true ;
198                }
199
200                // Take a reference to the parent for post processing cleanup.
201                var oCurrentParent = currentBlock.parentNode ;
202
203                var sPreviousName = previous.nodeName.toLowerCase() ;
204                if ( FCKListsLib.EmptyElements[ sPreviousName ] != null || sPreviousName == 'table' )
205                {
206                        FCKDomTools.RemoveNode( previous ) ;
207                        bCustom = true ;
208                }
209                else
210                {
211                        // Remove the current block.
212                        FCKDomTools.RemoveNode( currentBlock ) ;
213
214                        // Remove any empty tag left by the block removal.
215                        while ( oCurrentParent.innerHTML.Trim().length == 0 )
216                        {
217                                var oParent = oCurrentParent.parentNode ;
218                                oParent.removeChild( oCurrentParent ) ;
219                                oCurrentParent = oParent ;
220                        }
221
222                        // Cleanup the previous and the current elements.
223                        FCKDomTools.TrimNode( currentBlock ) ;
224                        FCKDomTools.TrimNode( previous ) ;
225
226                        // Append a space to the previous.
227                        // Maybe it is not always desirable...
228                        // previous.appendChild( this.Window.document.createTextNode( ' ' ) ) ;
229
230                        // Set the range to the end of the previous element and bookmark it.
231                        range.SetStart( previous, 2 ) ;
232                        range.Collapse( true ) ;
233                        var oBookmark = range.CreateBookmark() ;
234
235                        // Move the contents of the block to the previous element and delete it.
236                        FCKDomTools.MoveChildren( currentBlock, previous ) ;
237
238                        // Place the selection at the bookmark.
239                        range.MoveToBookmark( oBookmark ) ;
240                        range.Select() ;
241
242                        bCustom = true ;
243                }
244        }
245
246        return bCustom ;
247}
248
249/*
250 * Executes the <Delete> key behavior.
251 */
252FCKEnterKey.prototype.DoDelete = function()
253{
254        // The <Delete> has the same effect as the <Backspace>, so we have the same
255        // results if we just move to the next block and apply the same <Backspace> logic.
256
257        var bCustom = false ;
258
259        // Get the current selection.
260        var oRange = new FCKDomRange( this.Window ) ;
261        oRange.MoveToSelection() ;
262
263        // There is just one special case for collapsed selections at the end of a block.
264        if ( oRange.CheckIsCollapsed() && oRange.CheckEndOfBlock( FCKBrowserInfo.IsGecko ) )
265        {
266                var oCurrentBlock = oRange.StartBlock ;
267
268                var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ;
269
270                bCustom = this._ExecuteBackspace( oRange, oCurrentBlock, eNext ) ;
271        }
272
273        oRange.Release() ;
274        return bCustom ;
275}
276
277FCKEnterKey.prototype._ExecuteEnterBlock = function( blockTag, range )
278{
279        // Get the current selection.
280        var oRange = range || new FCKDomRange( this.Window ) ;
281
282        // If we don't have a range, move it to the selection.
283        if ( !range )
284                oRange.MoveToSelection() ;
285
286        // The selection boundaries must be in the same "block limit" element.
287        if ( oRange.StartBlockLimit == oRange.EndBlockLimit )
288        {
289                // If the StartBlock or EndBlock are not available (for text without a
290                // block tag), we must fix them, by moving the text to a block.
291                if ( !oRange.StartBlock )
292                        this._FixBlock( oRange, true, blockTag ) ;
293
294                if ( !oRange.EndBlock )
295                        this._FixBlock( oRange, false, blockTag ) ;
296
297                // Get the current blocks.
298                var eStartBlock = oRange.StartBlock ;
299                var eEndBlock   = oRange.EndBlock ;
300
301                // Delete the current selection.
302                if ( !oRange.CheckIsEmpty() )
303                        oRange.DeleteContents() ;
304
305                // If the selection boundaries are in the same block element
306                if ( eStartBlock == eEndBlock )
307                {
308                        var eNewBlock ;
309
310                        var bIsStartOfBlock     = oRange.CheckStartOfBlock() ;
311                        var bIsEndOfBlock       = oRange.CheckEndOfBlock() ;
312
313                        if ( bIsStartOfBlock && !bIsEndOfBlock )
314                        {
315                                eNewBlock = eStartBlock.cloneNode(false) ;
316
317                                if ( FCKBrowserInfo.IsGeckoLike )
318                                        eNewBlock.innerHTML = GECKO_BOGUS ;
319
320                                // Place the new block before the current block element.
321                                eStartBlock.parentNode.insertBefore( eNewBlock, eStartBlock ) ;
322
323                                // This is tricky, but to make the new block visible correctly
324                                // we must select it.
325                                if ( FCKBrowserInfo.IsIE )
326                                {
327                                        // Move the selection to the new block.
328                                        oRange.MoveToNodeContents( eNewBlock ) ;
329
330                                        oRange.Select() ;
331                                }
332
333                                // Move the selection to the new block.
334                                oRange.MoveToElementEditStart( eStartBlock ) ;
335                        }
336                        else
337                        {
338                                // Check if the selection is at the end of the block.
339                                if ( bIsEndOfBlock )
340                                {
341                                        var sStartBlockTag = eStartBlock.tagName.toUpperCase() ;
342
343                                        // If the entire block is selected, and we are in a LI, let's decrease its indentation.
344                                        if ( bIsStartOfBlock && sStartBlockTag == 'LI' )
345                                        {
346                                                this._OutdentWithSelection( eStartBlock, oRange ) ;
347                                                oRange.Release() ;
348                                                return true ;
349                                        }
350                                        else
351                                        {
352                                                // If is a header tag, or we are in a Shift+Enter (#77),
353                                                // create a new block element.
354                                                if ( (/^H[1-6]$/).test( sStartBlockTag ) || this._HasShift )
355                                                        eNewBlock = this.Window.document.createElement( blockTag ) ;
356                                                // Otherwise, duplicate the current block.
357                                                else
358                                                {
359                                                        eNewBlock = eStartBlock.cloneNode(false) ;
360                                                        this._RecreateEndingTree( eStartBlock, eNewBlock ) ;
361                                                }
362
363                                                if ( FCKBrowserInfo.IsGeckoLike )
364                                                {
365                                                        eNewBlock.innerHTML = GECKO_BOGUS ;
366
367                                                        // If the entire block is selected, let's add a bogus in the start block.
368                                                        if ( bIsStartOfBlock )
369                                                                eStartBlock.innerHTML = GECKO_BOGUS ;
370                                                }
371                                        }
372                                }
373                                else
374                                {
375                                        // Extract the contents of the block from the selection point to the end of its contents.
376                                        oRange.SetEnd( eStartBlock, 2 ) ;
377                                        var eDocFrag = oRange.ExtractContents() ;
378
379                                        // Duplicate the block element after it.
380                                        eNewBlock = eStartBlock.cloneNode(false) ;
381
382                                        // It could be that we are in a LI with a child UL/OL. Insert a bogus to give us space to type.
383                                        FCKDomTools.TrimNode( eDocFrag.RootNode ) ;
384                                        if ( eDocFrag.RootNode.firstChild.nodeType == 1 && eDocFrag.RootNode.firstChild.tagName.toUpperCase().Equals( 'UL', 'OL' ) )
385                                                eNewBlock.innerHTML = GECKO_BOGUS ;
386
387                                        // Place the extracted contents in the duplicated block.
388                                        eDocFrag.AppendTo( eNewBlock ) ;
389
390                                        if ( FCKBrowserInfo.IsGecko )
391                                        {
392                                                // In Gecko, the last child node must be a bogus <br>.
393                                                this._AppendBogusBr( eStartBlock ) ;
394                                                this._AppendBogusBr( eNewBlock ) ;
395                                        }
396                                }
397
398                                if ( eNewBlock )
399                                {
400                                        FCKDomTools.InsertAfterNode( eStartBlock, eNewBlock ) ;
401
402                                        // Move the selection to the new block.
403                                        oRange.MoveToElementEditStart( eNewBlock ) ;
404
405                                        if ( FCKBrowserInfo.IsGecko )
406                                                eNewBlock.scrollIntoView( false ) ;
407                                }
408                        }
409                }
410                else
411                {
412                        // Move the selection to the end block.
413                        oRange.MoveToElementEditStart( eEndBlock ) ;
414                }
415
416                oRange.Select() ;
417        }
418
419        // Release the resources used by the range.
420        oRange.Release() ;
421
422        return true ;
423}
424
425FCKEnterKey.prototype._ExecuteEnterBr = function( blockTag )
426{
427        // Get the current selection.
428        var oRange = new FCKDomRange( this.Window ) ;
429        oRange.MoveToSelection() ;
430
431        // The selection boundaries must be in the same "block limit" element.
432        if ( oRange.StartBlockLimit == oRange.EndBlockLimit )
433        {
434                oRange.DeleteContents() ;
435
436                // Get the new selection (it is collapsed at this point).
437                oRange.MoveToSelection() ;
438
439                var bIsStartOfBlock     = oRange.CheckStartOfBlock() ;
440                var bIsEndOfBlock       = oRange.CheckEndOfBlock() ;
441
442                var sStartBlockTag = oRange.StartBlock ? oRange.StartBlock.tagName.toUpperCase() : '' ;
443
444                var bHasShift = this._HasShift ;
445
446                if ( !bHasShift && sStartBlockTag == 'LI' )
447                        return this._ExecuteEnterBlock( null, oRange ) ;
448
449                // If we are at the end of a header block.
450                if ( !bHasShift && bIsEndOfBlock && (/^H[1-6]$/).test( sStartBlockTag ) )
451                {
452                        FCKDebug.Output( 'BR - Header' ) ;
453
454                        // Insert a BR after the current paragraph.
455                        FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createElement( 'br' ) ) ;
456
457                        // The space is required by Gecko only to make the cursor blink.
458                        if ( FCKBrowserInfo.IsGecko )
459                                FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createTextNode( '' ) ) ;
460
461                        // IE and Gecko have different behaviors regarding the position.
462                        oRange.SetStart( oRange.StartBlock.nextSibling, FCKBrowserInfo.IsIE ? 3 : 1 ) ;
463                }
464                else
465                {
466                        FCKDebug.Output( 'BR - No Header' ) ;
467
468                        var eBr = this.Window.document.createElement( 'br' ) ;
469
470                        oRange.InsertNode( eBr ) ;
471
472                        // The space is required by Gecko only to make the cursor blink.
473                        if ( FCKBrowserInfo.IsGecko )
474                                FCKDomTools.InsertAfterNode( eBr, this.Window.document.createTextNode( '' ) ) ;
475
476                        // If we are at the end of a block, we must be sure the bogus node is available in that block.
477                        if ( bIsEndOfBlock && FCKBrowserInfo.IsGecko )
478                                this._AppendBogusBr( eBr.parentNode ) ;
479
480                        if ( FCKBrowserInfo.IsIE )
481                                oRange.SetStart( eBr, 4 ) ;
482                        else
483                                oRange.SetStart( eBr.nextSibling, 1 ) ;
484
485                }
486
487                // This collapse guarantees the cursor will be blinking.
488                oRange.Collapse( true ) ;
489
490                oRange.Select() ;
491        }
492
493        // Release the resources used by the range.
494        oRange.Release() ;
495
496        return true ;
497}
498
499// Transform a block without a block tag in a valid block (orphan text in the body or td, usually).
500FCKEnterKey.prototype._FixBlock = function( range, isStart, blockTag )
501{
502        // Bookmark the range so we can restore it later.
503        var oBookmark = range.CreateBookmark() ;
504
505        // Collapse the range to the requested ending boundary.
506        range.Collapse( isStart ) ;
507
508        // Expands it to the block contents.
509        range.Expand( 'block_contents' ) ;
510
511        // Create the fixed block.
512        var oFixedBlock = this.Window.document.createElement( blockTag ) ;
513
514        // Move the contents of the temporary range to the fixed block.
515        range.ExtractContents().AppendTo( oFixedBlock ) ;
516        FCKDomTools.TrimNode( oFixedBlock ) ;
517
518        // Insert the fixed block into the DOM.
519        range.InsertNode( oFixedBlock ) ;
520
521        // Move the range back to the bookmarked place.
522        range.MoveToBookmark( oBookmark ) ;
523}
524
525// Appends a bogus <br> at the end of the element, if not yet available.
526FCKEnterKey.prototype._AppendBogusBr = function( element )
527{
528        var eLastChild = element.getElementsByTagName('br') ;
529
530        if ( eLastChild )
531                eLastChild = eLastChild[ eLastChild.legth - 1 ] ;
532
533        if ( !eLastChild || eLastChild.getAttribute( 'type', 2 ) != '_moz' )
534                element.appendChild( FCKTools.CreateBogusBR( this.Window.document ) ) ;
535}
536
537// Recreate the elements tree at the end of the source block, at the beginning
538// of the target block. Eg.:
539//      If source = <p><u>Some</u> sample <b><i>text</i></b></p> then target = <p><b><i></i></b></p>
540//      If source = <p><u>Some</u> sample text</p> then target = <p></p>
541FCKEnterKey.prototype._RecreateEndingTree = function( source, target )
542{
543        while ( ( source = source.lastChild ) && source.nodeType == 1 && FCKListsLib.InlineChildReqElements[ source.nodeName.toLowerCase() ] != null )
544                target = target.insertBefore( source.cloneNode( false ), target.firstChild ) ;
545}
546
547// Outdents a LI, maintaining the seletion defined on a range.
548FCKEnterKey.prototype._OutdentWithSelection = function( li, range )
549{
550        var oBookmark = range.CreateBookmark() ;
551
552        FCKListHandler.OutdentListItem( li ) ;
553
554        range.MoveToBookmark( oBookmark ) ;
555        range.Select() ;
556}
Note: See TracBrowser for help on using the repository browser.