2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
8 CKEDITOR
.plugins
.add( 'enterkey',
10 requires
: [ 'keystrokes', 'indent' ],
12 init : function( editor
)
14 var specialKeys
= editor
.specialKeys
;
15 specialKeys
[ 13 ] = enter
;
16 specialKeys
[ CKEDITOR
.SHIFT
+ 13 ] = shiftEnter
;
20 CKEDITOR
.plugins
.enterkey
=
22 enterBlock : function( editor
, mode
, range
, forceMode
)
24 // Get the range for the current selection.
25 range
= range
|| getRange( editor
);
27 var doc
= range
.document
;
29 // Exit the list when we're inside an empty list item block. (#5376)
30 if ( range
.checkStartOfBlock() && range
.checkEndOfBlock() )
32 var path
= new CKEDITOR
.dom
.elementPath( range
.startContainer
),
35 if ( block
.is( 'li' ) || block
.getParent().is( 'li' ) )
37 editor
.execCommand( 'outdent' );
42 // Determine the block element to be used.
43 var blockTag
= ( mode
== CKEDITOR
.ENTER_DIV
? 'div' : 'p' );
46 var splitInfo
= range
.splitBlock( blockTag
);
51 // Get the current blocks.
52 var previousBlock
= splitInfo
.previousBlock
,
53 nextBlock
= splitInfo
.nextBlock
;
55 var isStartOfBlock
= splitInfo
.wasStartOfBlock
,
56 isEndOfBlock
= splitInfo
.wasEndOfBlock
;
60 // If this is a block under a list item, split it as well. (#1647)
63 node
= nextBlock
.getParent();
64 if ( node
.is( 'li' ) )
66 nextBlock
.breakParent( node
);
67 nextBlock
.move( nextBlock
.getNext(), true );
70 else if ( previousBlock
&& ( node
= previousBlock
.getParent() ) && node
.is( 'li' ) )
72 previousBlock
.breakParent( node
);
73 range
.moveToElementEditStart( previousBlock
.getNext() );
74 previousBlock
.move( previousBlock
.getPrevious() );
77 // If we have both the previous and next blocks, it means that the
78 // boundaries were on separated blocks, or none of them where on the
79 // block limits (start/end).
80 if ( !isStartOfBlock
&& !isEndOfBlock
)
82 // If the next block is an <li> with another list tree as the first
83 // child, we'll need to append a filler (<br>/NBSP) or the list item
84 // wouldn't be editable. (#1420)
85 if ( nextBlock
.is( 'li' )
86 && ( node
= nextBlock
.getFirst( CKEDITOR
.dom
.walker
.invisible( true ) ) )
87 && node
.is
&& node
.is( 'ul', 'ol' ) )
88 ( CKEDITOR
.env
.ie
? doc
.createText( '\xa0' ) : doc
.createElement( 'br' ) ).insertBefore( node
);
90 // Move the selection to the end block.
92 range
.moveToElementEditStart( nextBlock
);
100 // Do not enter this block if it's a header tag, or we are in
101 // a Shift+Enter (#77). Create a new block element instead
102 // (later in the code).
103 if ( previousBlock
.is( 'li' ) ||
104 !( forceMode
|| headerTagRegex
.test( previousBlock
.getName() ) ) )
106 // Otherwise, duplicate the previous block.
107 newBlock
= previousBlock
.clone();
110 else if ( nextBlock
)
111 newBlock
= nextBlock
.clone();
114 newBlock
= doc
.createElement( blockTag
);
116 // Recreate the inline elements tree, which was available
117 // before hitting enter, so the same styles will be available in
119 var elementPath
= splitInfo
.elementPath
;
122 for ( var i
= 0, len
= elementPath
.elements
.length
; i
< len
; i
++ )
124 var element
= elementPath
.elements
[ i
];
126 if ( element
.equals( elementPath
.block
) || element
.equals( elementPath
.blockLimit
) )
129 if ( CKEDITOR
.dtd
.$removeEmpty
[ element
.getName() ] )
131 element
= element
.clone();
132 newBlock
.moveChildren( element
);
133 newBlock
.append( element
);
138 if ( !CKEDITOR
.env
.ie
)
139 newBlock
.appendBogus();
141 range
.insertNode( newBlock
);
143 // This is tricky, but to make the new block visible correctly
144 // we must select it.
145 // The previousBlock check has been included because it may be
146 // empty if we have fixed a block-less space (like ENTER into an
147 // empty table cell).
148 if ( CKEDITOR
.env
.ie
&& isStartOfBlock
&& ( !isEndOfBlock
|| !previousBlock
.getChildCount() ) )
150 // Move the selection to the new block.
151 range
.moveToElementEditStart( isEndOfBlock
? previousBlock
: newBlock
);
155 // Move the selection to the new block.
156 range
.moveToElementEditStart( isStartOfBlock
&& !isEndOfBlock
? nextBlock
: newBlock
);
159 if ( !CKEDITOR
.env
.ie
)
163 // If we have split the block, adds a temporary span at the
164 // range position and scroll relatively to it.
165 var tmpNode
= doc
.createElement( 'span' );
167 // We need some content for Safari.
168 tmpNode
.setHtml( ' ' );
170 range
.insertNode( tmpNode
);
171 tmpNode
.scrollIntoView();
172 range
.deleteContents();
176 // We may use the above scroll logic for the new block case
177 // too, but it gives some weird result with Opera.
178 newBlock
.scrollIntoView();
185 enterBr : function( editor
, mode
, range
, forceMode
)
187 // Get the range for the current selection.
188 range
= range
|| getRange( editor
);
190 var doc
= range
.document
;
192 // Determine the block element to be used.
193 var blockTag
= ( mode
== CKEDITOR
.ENTER_DIV
? 'div' : 'p' );
195 var isEndOfBlock
= range
.checkEndOfBlock();
197 var elementPath
= new CKEDITOR
.dom
.elementPath( editor
.getSelection().getStartElement() );
199 var startBlock
= elementPath
.block
,
200 startBlockTag
= startBlock
&& elementPath
.block
.getName();
204 if ( !forceMode
&& startBlockTag
== 'li' )
206 enterBlock( editor
, mode
, range
, forceMode
);
210 // If we are at the end of a header block.
211 if ( !forceMode
&& isEndOfBlock
&& headerTagRegex
.test( startBlockTag
) )
213 // Insert a <br> after the current paragraph.
214 doc
.createElement( 'br' ).insertAfter( startBlock
);
216 // A text node is required by Gecko only to make the cursor blink.
217 if ( CKEDITOR
.env
.gecko
)
218 doc
.createText( '' ).insertAfter( startBlock
);
220 // IE has different behaviors regarding position.
221 range
.setStartAt( startBlock
.getNext(), CKEDITOR
.env
.ie
? CKEDITOR
.POSITION_BEFORE_START
: CKEDITOR
.POSITION_AFTER_START
);
227 isPre
= ( startBlockTag
== 'pre' );
229 // Gecko prefers <br> as line-break inside <pre> (#4711).
230 if ( isPre
&& !CKEDITOR
.env
.gecko
)
231 lineBreak
= doc
.createText( CKEDITOR
.env
.ie
? '\r' : '\n' );
233 lineBreak
= doc
.createElement( 'br' );
235 range
.deleteContents();
236 range
.insertNode( lineBreak
);
238 // A text node is required by Gecko only to make the cursor blink.
239 // We need some text inside of it, so the bogus <br> is properly
241 if ( !CKEDITOR
.env
.ie
)
242 doc
.createText( '\ufeff' ).insertAfter( lineBreak
);
244 // If we are at the end of a block, we must be sure the bogus node is available in that block.
245 if ( isEndOfBlock
&& !CKEDITOR
.env
.ie
)
246 lineBreak
.getParent().appendBogus();
248 // Now we can remove the text node contents, so the caret doesn't
250 if ( !CKEDITOR
.env
.ie
)
251 lineBreak
.getNext().$.nodeValue
= '';
252 // IE has different behavior regarding position.
253 if ( CKEDITOR
.env
.ie
)
254 range
.setStartAt( lineBreak
, CKEDITOR
.POSITION_AFTER_END
);
256 range
.setStartAt( lineBreak
.getNext(), CKEDITOR
.POSITION_AFTER_START
);
258 // Scroll into view, for non IE.
259 if ( !CKEDITOR
.env
.ie
)
263 // BR is not positioned in Opera and Webkit.
264 if ( !CKEDITOR
.env
.gecko
)
266 dummy
= doc
.createElement( 'span' );
267 // We need have some contents for Webkit to position it
268 // under parent node. ( #3681)
269 dummy
.setHtml(' ');
272 dummy
= doc
.createElement( 'br' );
274 dummy
.insertBefore( lineBreak
.getNext() );
275 dummy
.scrollIntoView();
280 // This collapse guarantees the cursor will be blinking.
281 range
.collapse( true );
283 range
.select( isPre
);
287 var plugin
= CKEDITOR
.plugins
.enterkey
,
288 enterBr
= plugin
.enterBr
,
289 enterBlock
= plugin
.enterBlock
,
290 headerTagRegex
= /^h[1-6]$/;
292 function shiftEnter( editor
)
294 // On SHIFT+ENTER we want to enforce the mode to be respected, instead
295 // of cloning the current block. (#77)
296 return enter( editor
, editor
.config
.shiftEnterMode
, true );
299 function enter( editor
, mode
, forceMode
)
301 forceMode
= editor
.config
.forceEnterMode
|| forceMode
;
303 // Only effective within document.
304 if ( editor
.mode
!= 'wysiwyg' )
308 mode
= editor
.config
.enterMode
;
310 // Use setTimout so the keys get cancelled immediatelly.
311 setTimeout( function()
313 editor
.fire( 'saveSnapshot' ); // Save undo step.
314 if ( mode
== CKEDITOR
.ENTER_BR
|| editor
.getSelection().getStartElement().hasAscendant( 'pre', true ) )
315 enterBr( editor
, mode
, null, forceMode
);
317 enterBlock( editor
, mode
, null, forceMode
);
325 function getRange( editor
)
327 // Get the selection ranges.
328 var ranges
= editor
.getSelection().getRanges();
330 // Delete the contents of all ranges except the first one.
331 for ( var i
= ranges
.length
- 1 ; i
> 0 ; i
-- )
333 ranges
[ i
].deleteContents();
336 // Return the first range.