Nation Notes module contributed by Z&H Healthcare.
[openemr.git] / library / custom_template / ckeditor / _source / plugins / enterkey / plugin.js
blobe2e831fb93937d04b422613a3c3a644be0b38965
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
6 (function()
8         CKEDITOR.plugins.add( 'enterkey',
9         {
10                 requires : [ 'keystrokes', 'indent' ],
12                 init : function( editor )
13                 {
14                         var specialKeys = editor.specialKeys;
15                         specialKeys[ 13 ] = enter;
16                         specialKeys[ CKEDITOR.SHIFT + 13 ] = shiftEnter;
17                 }
18         });
20         CKEDITOR.plugins.enterkey =
21         {
22                 enterBlock : function( editor, mode, range, forceMode )
23                 {
24                         // Get the range for the current selection.
25                         range = range || getRange( editor );
27                         // We may not have valid ranges to work on, like when inside a
28                         // contenteditable=false element.
29                         if ( !range )
30                                 return;
32                         var doc = range.document;
34                         // Exit the list when we're inside an empty list item block. (#5376)
35                         if ( range.checkStartOfBlock() && range.checkEndOfBlock() )
36                         {
37                                 var path = new CKEDITOR.dom.elementPath( range.startContainer ),
38                                                 block = path.block;
40                                 if ( block && ( block.is( 'li' ) || block.getParent().is( 'li' ) ) )
41                                 {
42                                         editor.execCommand( 'outdent' );
43                                         return;
44                                 }
45                         }
47                         // Determine the block element to be used.
48                         var blockTag = ( mode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
50                         // Split the range.
51                         var splitInfo = range.splitBlock( blockTag );
53                         if ( !splitInfo )
54                                 return;
56                         // Get the current blocks.
57                         var previousBlock       = splitInfo.previousBlock,
58                                 nextBlock               = splitInfo.nextBlock;
60                         var isStartOfBlock      = splitInfo.wasStartOfBlock,
61                                 isEndOfBlock    = splitInfo.wasEndOfBlock;
63                         var node;
65                         // If this is a block under a list item, split it as well. (#1647)
66                         if ( nextBlock )
67                         {
68                                 node = nextBlock.getParent();
69                                 if ( node.is( 'li' ) )
70                                 {
71                                         nextBlock.breakParent( node );
72                                         nextBlock.move( nextBlock.getNext(), 1 );
73                                 }
74                         }
75                         else if ( previousBlock && ( node = previousBlock.getParent() ) && node.is( 'li' ) )
76                         {
77                                 previousBlock.breakParent( node );
78                                 range.moveToElementEditStart( previousBlock.getNext() );
79                                 previousBlock.move( previousBlock.getPrevious() );
80                         }
82                         // If we have both the previous and next blocks, it means that the
83                         // boundaries were on separated blocks, or none of them where on the
84                         // block limits (start/end).
85                         if ( !isStartOfBlock && !isEndOfBlock )
86                         {
87                                 // If the next block is an <li> with another list tree as the first
88                                 // child, we'll need to append a filler (<br>/NBSP) or the list item
89                                 // wouldn't be editable. (#1420)
90                                 if ( nextBlock.is( 'li' )
91                                          && ( node = nextBlock.getFirst( CKEDITOR.dom.walker.invisible( true ) ) )
92                                          && node.is && node.is( 'ul', 'ol' ) )
93                                         ( CKEDITOR.env.ie ? doc.createText( '\xa0' ) : doc.createElement( 'br' ) ).insertBefore( node );
95                                 // Move the selection to the end block.
96                                 if ( nextBlock )
97                                         range.moveToElementEditStart( nextBlock );
98                         }
99                         else
100                         {
101                                 var newBlock,
102                                         newBlockDir;
104                                 if ( previousBlock )
105                                 {
106                                         // Do not enter this block if it's a header tag, or we are in
107                                         // a Shift+Enter (#77). Create a new block element instead
108                                         // (later in the code).
109                                         if ( previousBlock.is( 'li' ) || !headerTagRegex.test( previousBlock.getName() ) )
110                                         {
111                                                 // Otherwise, duplicate the previous block.
112                                                 newBlock = previousBlock.clone();
113                                         }
114                                 }
115                                 else if ( nextBlock )
116                                         newBlock = nextBlock.clone();
118                                 if ( !newBlock )
119                                 {
120                                         newBlock = doc.createElement( blockTag );
121                                         if ( previousBlock && ( newBlockDir = previousBlock.getDirection() ) )
122                                                 newBlock.setAttribute( 'dir', newBlockDir );
123                                 }
124                                 // Force the enter block unless we're talking of a list item.
125                                 else if ( forceMode && !newBlock.is( 'li' ) )
126                                         newBlock.renameNode( blockTag );
128                                 // Recreate the inline elements tree, which was available
129                                 // before hitting enter, so the same styles will be available in
130                                 // the new block.
131                                 var elementPath = splitInfo.elementPath;
132                                 if ( elementPath )
133                                 {
134                                         for ( var i = 0, len = elementPath.elements.length ; i < len ; i++ )
135                                         {
136                                                 var element = elementPath.elements[ i ];
138                                                 if ( element.equals( elementPath.block ) || element.equals( elementPath.blockLimit ) )
139                                                         break;
141                                                 if ( CKEDITOR.dtd.$removeEmpty[ element.getName() ] )
142                                                 {
143                                                         element = element.clone();
144                                                         newBlock.moveChildren( element );
145                                                         newBlock.append( element );
146                                                 }
147                                         }
148                                 }
150                                 if ( !CKEDITOR.env.ie )
151                                         newBlock.appendBogus();
153                                 range.insertNode( newBlock );
155                                 // This is tricky, but to make the new block visible correctly
156                                 // we must select it.
157                                 // The previousBlock check has been included because it may be
158                                 // empty if we have fixed a block-less space (like ENTER into an
159                                 // empty table cell).
160                                 if ( CKEDITOR.env.ie && isStartOfBlock && ( !isEndOfBlock || !previousBlock.getChildCount() ) )
161                                 {
162                                         // Move the selection to the new block.
163                                         range.moveToElementEditStart( isEndOfBlock ? previousBlock : newBlock );
164                                         range.select();
165                                 }
167                                 // Move the selection to the new block.
168                                 range.moveToElementEditStart( isStartOfBlock && !isEndOfBlock ? nextBlock : newBlock );
169                 }
171                         if ( !CKEDITOR.env.ie )
172                         {
173                                 if ( nextBlock )
174                                 {
175                                         // If we have split the block, adds a temporary span at the
176                                         // range position and scroll relatively to it.
177                                         var tmpNode = doc.createElement( 'span' );
179                                         // We need some content for Safari.
180                                         tmpNode.setHtml( '&nbsp;' );
182                                         range.insertNode( tmpNode );
183                                         tmpNode.scrollIntoView();
184                                         range.deleteContents();
185                                 }
186                                 else
187                                 {
188                                         // We may use the above scroll logic for the new block case
189                                         // too, but it gives some weird result with Opera.
190                                         newBlock.scrollIntoView();
191                                 }
192                         }
194                         range.select();
195                 },
197                 enterBr : function( editor, mode, range, forceMode )
198                 {
199                         // Get the range for the current selection.
200                         range = range || getRange( editor );
202                         // We may not have valid ranges to work on, like when inside a
203                         // contenteditable=false element.
204                         if ( !range )
205                                 return;
207                         var doc = range.document;
209                         // Determine the block element to be used.
210                         var blockTag = ( mode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
212                         var isEndOfBlock = range.checkEndOfBlock();
214                         var elementPath = new CKEDITOR.dom.elementPath( editor.getSelection().getStartElement() );
216                         var startBlock = elementPath.block,
217                                 startBlockTag = startBlock && elementPath.block.getName();
219                         var isPre = false;
221                         if ( !forceMode && startBlockTag == 'li' )
222                         {
223                                 enterBlock( editor, mode, range, forceMode );
224                                 return;
225                         }
227                         // If we are at the end of a header block.
228                         if ( !forceMode && isEndOfBlock && headerTagRegex.test( startBlockTag ) )
229                         {
230                                 var newBlock,
231                                         newBlockDir;
233                                 if ( ( newBlockDir = startBlock.getDirection() ) )
234                                 {
235                                         newBlock = doc.createElement( 'div' );
236                                         newBlock.setAttribute( 'dir', newBlockDir );
237                                         newBlock.insertAfter( startBlock );
238                                         range.setStart( newBlock, 0 );
239                                 }
240                                 else
241                                 {
242                                         // Insert a <br> after the current paragraph.
243                                         doc.createElement( 'br' ).insertAfter( startBlock );
245                                         // A text node is required by Gecko only to make the cursor blink.
246                                         if ( CKEDITOR.env.gecko )
247                                                 doc.createText( '' ).insertAfter( startBlock );
249                                         // IE has different behaviors regarding position.
250                                         range.setStartAt( startBlock.getNext(), CKEDITOR.env.ie ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_START );
251                                 }
252                         }
253                         else
254                         {
255                                 var lineBreak;
257                                 isPre = ( startBlockTag == 'pre' );
259                                 // Gecko prefers <br> as line-break inside <pre> (#4711).
260                                 if ( isPre && !CKEDITOR.env.gecko )
261                                         lineBreak = doc.createText( CKEDITOR.env.ie ? '\r' : '\n' );
262                                 else
263                                         lineBreak = doc.createElement( 'br' );
265                                 range.deleteContents();
266                                 range.insertNode( lineBreak );
268                                 // A text node is required by Gecko only to make the cursor blink.
269                                 // We need some text inside of it, so the bogus <br> is properly
270                                 // created.
271                                 if ( !CKEDITOR.env.ie )
272                                         doc.createText( '\ufeff' ).insertAfter( lineBreak );
274                                 // If we are at the end of a block, we must be sure the bogus node is available in that block.
275                                 if ( isEndOfBlock && !CKEDITOR.env.ie )
276                                         lineBreak.getParent().appendBogus();
278                                 // Now we can remove the text node contents, so the caret doesn't
279                                 // stop on it.
280                                 if ( !CKEDITOR.env.ie )
281                                         lineBreak.getNext().$.nodeValue = '';
282                                 // IE has different behavior regarding position.
283                                 if ( CKEDITOR.env.ie )
284                                         range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END );
285                                 else
286                                         range.setStartAt( lineBreak.getNext(), CKEDITOR.POSITION_AFTER_START );
288                                 // Scroll into view, for non IE.
289                                 if ( !CKEDITOR.env.ie )
290                                 {
291                                         var dummy = null;
293                                         // BR is not positioned in Opera and Webkit.
294                                         if ( !CKEDITOR.env.gecko )
295                                         {
296                                                 dummy = doc.createElement( 'span' );
297                                                 // We need have some contents for Webkit to position it
298                                                 // under parent node. ( #3681)
299                                                 dummy.setHtml('&nbsp;');
300                                         }
301                                         else
302                                                 dummy = doc.createElement( 'br' );
304                                         dummy.insertBefore( lineBreak.getNext() );
305                                         dummy.scrollIntoView();
306                                         dummy.remove();
307                                 }
308                         }
310                         // This collapse guarantees the cursor will be blinking.
311                         range.collapse( true );
313                         range.select( isPre );
314                 }
315         };
317         var plugin = CKEDITOR.plugins.enterkey,
318                 enterBr = plugin.enterBr,
319                 enterBlock = plugin.enterBlock,
320                 headerTagRegex = /^h[1-6]$/;
322         function shiftEnter( editor )
323         {
324                 // Only effective within document.
325                 if ( editor.mode != 'wysiwyg' )
326                         return false;
328                 // On SHIFT+ENTER:
329                 // 1. We want to enforce the mode to be respected, instead
330                 // of cloning the current block. (#77)
331                 // 2. Always perform a block break when inside <pre> (#5402).
332                 if ( editor.getSelection().getStartElement().hasAscendant( 'pre', true ) )
333                 {
334                         setTimeout( function() { enterBlock( editor, editor.config.enterMode, null, true ); }, 0 );
335                         return true;
336                 }
337                 else
338                         return enter( editor, editor.config.shiftEnterMode, 1 );
339         }
341         function enter( editor, mode, forceMode )
342         {
343                 forceMode = editor.config.forceEnterMode || forceMode;
345                 // Only effective within document.
346                 if ( editor.mode != 'wysiwyg' )
347                         return false;
349                 if ( !mode )
350                         mode = editor.config.enterMode;
352                 // Use setTimout so the keys get cancelled immediatelly.
353                 setTimeout( function()
354                         {
355                                 editor.fire( 'saveSnapshot' );  // Save undo step.
356                                 if ( mode == CKEDITOR.ENTER_BR || editor.getSelection().getStartElement().hasAscendant( 'pre', 1 ) )
357                                         enterBr( editor, mode, null, forceMode );
358                                 else
359                                         enterBlock( editor, mode, null, forceMode );
361                         }, 0 );
363                 return true;
364         }
367         function getRange( editor )
368         {
369                 // Get the selection ranges.
370                 var ranges = editor.getSelection().getRanges( true );
372                 // Delete the contents of all ranges except the first one.
373                 for ( var i = ranges.length - 1 ; i > 0 ; i-- )
374                 {
375                         ranges[ i ].deleteContents();
376                 }
378                 // Return the first range.
379                 return ranges[ 0 ];
380         }
381 })();