Nation Notes module contributed by Z&H Healthcare.
[openemr.git] / library / custom_template / ckeditor / _source / plugins / wysiwygarea / plugin.js
blobdc1b56b3a6844c0de85d9c3decf08b0dc4a092ba
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 /**
7  * @fileOverview The "wysiwygarea" plugin. It registers the "wysiwyg" editing
8  *              mode, which handles the main editing area space.
9  */
11 (function()
13         // List of elements in which has no way to move editing focus outside.
14         var nonExitableElementNames = { table:1,pre:1 };
16         // Matching an empty paragraph at the end of document.
17         var emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>|&nbsp;|\u00A0|&#160;)?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi;
19         var notWhitespaceEval = CKEDITOR.dom.walker.whitespaces( true );
21         // Elements that could have empty new line around, including table, pre-formatted block, hr, page-break. (#6554)
22         function nonExitable( element )
23         {
24                 return ( element.getName() in nonExitableElementNames )
25                                 || element.isBlockBoundary() && CKEDITOR.dtd.$empty[ element.getName() ];
26         }
29         function onInsert( insertFunc )
30         {
31                 return function( evt )
32                 {
33                         if ( this.mode == 'wysiwyg' )
34                         {
35                                 this.focus();
37                                 this.fire( 'saveSnapshot' );
39                                 insertFunc.call( this, evt.data );
41                                 // Save snaps after the whole execution completed.
42                                 // This's a workaround for make DOM modification's happened after
43                                 // 'insertElement' to be included either, e.g. Form-based dialogs' 'commitContents'
44                                 // call.
45                                 CKEDITOR.tools.setTimeout( function()
46                                    {
47                                            this.fire( 'saveSnapshot' );
48                                    }, 0, this );
49                         }
50                 };
51         }
53         function doInsertHtml( data )
54         {
55                 if ( this.dataProcessor )
56                         data = this.dataProcessor.toHtml( data );
58                 // HTML insertion only considers the first range.
59                 var selection = this.getSelection(),
60                         range = selection.getRanges()[ 0 ];
62                 if ( range.checkReadOnly() )
63                         return;
65                 if ( CKEDITOR.env.ie )
66                 {
67                         var selIsLocked = selection.isLocked;
69                         if ( selIsLocked )
70                                 selection.unlock();
72                         var $sel = selection.getNative();
74                         // Delete control selections to avoid IE bugs on pasteHTML.
75                         if ( $sel.type == 'Control' )
76                                 $sel.clear();
77                         else if ( selection.getType() == CKEDITOR.SELECTION_TEXT )
78                         {
79                                 // Due to IE bugs on handling contenteditable=false blocks
80                                 // (#6005), we need to make some checks and eventually
81                                 // delete the selection first.
83                                 range = selection.getRanges()[ 0 ];
84                                 var endContainer = range && range.endContainer;
86                                 if ( endContainer &&
87                                                 endContainer.type == CKEDITOR.NODE_ELEMENT &&
88                                                 endContainer.getAttribute( 'contenteditable' ) == 'false' &&
89                                                 range.checkBoundaryOfElement( endContainer, CKEDITOR.END ) )
90                                 {
91                                         range.setEndAfter( range.endContainer );
92                                         range.deleteContents();
93                                 }
94                         }
96                         try
97                         {
98                                 $sel.createRange().pasteHTML( data );
99                         }
100                         catch (e) {}
102                         if ( selIsLocked )
103                                 this.getSelection().lock();
104                 }
105                 else
106                         this.document.$.execCommand( 'inserthtml', false, data );
108                 // Webkit does not scroll to the cursor position after pasting (#5558)
109                 if ( CKEDITOR.env.webkit )
110                 {
111                         selection = this.getSelection();
112                         selection.scrollIntoView();
113                 }
114         }
116         function doInsertText( text )
117         {
118                 var selection = this.getSelection(),
119                         mode = selection.getStartElement().hasAscendant( 'pre', true ) ?
120                                    CKEDITOR.ENTER_BR : this.config.enterMode,
121                         isEnterBrMode = mode == CKEDITOR.ENTER_BR;
123                 var html = CKEDITOR.tools.htmlEncode( text.replace( /\r\n|\r/g, '\n' ) );
125                 // Convert leading and trailing whitespaces into &nbsp;
126                 html = html.replace( /^[ \t]+|[ \t]+$/g, function( match, offset, s )
127                         {
128                                 if ( match.length == 1 )        // one space, preserve it
129                                         return '&nbsp;';
130                                 else if ( !offset )             // beginning of block
131                                         return CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 ) + ' ';
132                                 else                            // end of block
133                                         return ' ' + CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 );
134                         } );
136                 // Convert subsequent whitespaces into &nbsp;
137                 html = html.replace( /[ \t]{2,}/g, function ( match )
138                    {
139                            return CKEDITOR.tools.repeat( '&nbsp;', match.length - 1 ) + ' ';
140                    } );
142                 var paragraphTag = mode == CKEDITOR.ENTER_P ? 'p' : 'div';
144                 // Two line-breaks create one paragraph.
145                 if ( !isEnterBrMode )
146                 {
147                         html = html.replace( /(\n{2})([\s\S]*?)(?:$|\1)/g,
148                                 function( match, group1, text )
149                                 {
150                                         return '<'+paragraphTag + '>' + text + '</' + paragraphTag + '>';
151                                 });
152                 }
154                 // One <br> per line-break.
155                 html = html.replace( /\n/g, '<br>' );
157                 // Compensate padding <br> for non-IE.
158                 if ( !( isEnterBrMode || CKEDITOR.env.ie ) )
159                 {
160                         html = html.replace( new RegExp( '<br>(?=</' + paragraphTag + '>)' ), function( match )
161                         {
162                                 return CKEDITOR.tools.repeat( match, 2 );
163                         } );
164                 }
166                 // Inline styles have to be inherited in Firefox.
167                 if ( CKEDITOR.env.gecko || CKEDITOR.env.webkit )
168                 {
169                         var path = new CKEDITOR.dom.elementPath( selection.getStartElement() ),
170                                 context = [];
172                         for ( var i = 0; i < path.elements.length; i++ )
173                         {
174                                 var tag = path.elements[ i ].getName();
175                                 if ( tag in CKEDITOR.dtd.$inline )
176                                         context.unshift( path.elements[ i ].getOuterHtml().match( /^<.*?>/) );
177                                 else if ( tag in CKEDITOR.dtd.$block )
178                                         break;
179                         }
181                         // Reproduce the context  by preceding the pasted HTML with opening inline tags.
182                         html = context.join( '' ) + html;
183                 }
185                 doInsertHtml.call( this, html );
186         }
188         function doInsertElement( element )
189         {
190                 var selection = this.getSelection(),
191                                 ranges = selection.getRanges(),
192                                 elementName = element.getName(),
193                                 isBlock = CKEDITOR.dtd.$block[ elementName ];
195                 var selIsLocked = selection.isLocked;
197                 if ( selIsLocked )
198                         selection.unlock();
200                 var range, clone, lastElement, bookmark;
202                 for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
203                 {
204                         range = ranges[ i ];
206                                 if ( !range.checkReadOnly() )
207                                 {
208                                         // Remove the original contents, merge splitted nodes.
209                                         range.deleteContents( 1 );
211                                         clone = !i && element || element.clone( 1 );
213                                         // If we're inserting a block at dtd-violated position, split
214                                         // the parent blocks until we reach blockLimit.
215                                         var current, dtd;
216                                         if ( isBlock )
217                                         {
218                                                 while ( ( current = range.getCommonAncestor( 0, 1 ) )
219                                                                 && ( dtd = CKEDITOR.dtd[ current.getName() ] )
220                                                                 && !( dtd && dtd [ elementName ] ) )
221                                                 {
222                                                         // Split up inline elements.
223                                                         if ( current.getName() in CKEDITOR.dtd.span )
224                                                                 range.splitElement( current );
225                                                         // If we're in an empty block which indicate a new paragraph,
226                                                         // simply replace it with the inserting block.(#3664)
227                                                         else if ( range.checkStartOfBlock()
228                                                                         && range.checkEndOfBlock() )
229                                                         {
230                                                                 range.setStartBefore( current );
231                                                                 range.collapse( true );
232                                                                 current.remove();
233                                                         }
234                                                         else
235                                                                 range.splitBlock();
236                                                 }
237                                         }
239                                         // Insert the new node.
240                                         range.insertNode( clone );
242                                         // Save the last element reference so we can make the
243                                         // selection later.
244                                         if ( !lastElement )
245                                                 lastElement = clone;
246                                 }
247                         }
249                         if ( lastElement )
250                         {
251                                 range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END );
253                                 // If we're inserting a block element immediatelly followed by
254                                 // another block element, the selection must move there. (#3100,#5436)
255                                 if ( isBlock )
256                                 {
257                                         var next = lastElement.getNext( notWhitespaceEval ),
258                                                 nextName = next && next.type == CKEDITOR.NODE_ELEMENT && next.getName();
260                                         // Check if it's a block element that accepts text.
261                                         if ( nextName && CKEDITOR.dtd.$block[ nextName ] && CKEDITOR.dtd[ nextName ]['#'] )
262                                                 range.moveToElementEditStart( next );
263                                 }
264                         }
266                         selection.selectRanges( [ range ] );
268                 if ( selIsLocked )
269                         this.getSelection().lock();
270         }
272         // DOM modification here should not bother dirty flag.(#4385)
273         function restoreDirty( editor )
274         {
275                 if ( !editor.checkDirty() )
276                         setTimeout( function(){ editor.resetDirty(); }, 0 );
277         }
279         var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ),
280                 isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );
282         function isNotEmpty( node )
283         {
284                 return isNotWhitespace( node ) && isNotBookmark( node );
285         }
287         function isNbsp( node )
288         {
289                 return node.type == CKEDITOR.NODE_TEXT
290                            && CKEDITOR.tools.trim( node.getText() ).match( /^(?:&nbsp;|\xa0)$/ );
291         }
293         function restoreSelection( selection )
294         {
295                 if ( selection.isLocked )
296                 {
297                         selection.unlock();
298                         setTimeout( function() { selection.lock(); }, 0 );
299                 }
300         }
302         function isBlankParagraph( block )
303         {
304                 return block.getOuterHtml().match( emptyParagraphRegexp );
305         }
307         isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true );
309         // Gecko need a key event to 'wake up' the editing
310         // ability when document is empty.(#3864, #5781)
311         function activateEditing( editor )
312         {
313                 var win = editor.window,
314                         doc = editor.document,
315                         body = editor.document.getBody(),
316                         bodyChildsNum = body.getChildren().count();
318                 if ( !bodyChildsNum || ( bodyChildsNum == 1&& body.getFirst().hasAttribute( '_moz_editor_bogus_node' ) ) )
319                 {
320                         restoreDirty( editor );
322                         // Memorize scroll position to restore it later (#4472).
323                         var hostDocument = editor.element.getDocument();
324                         var hostDocumentElement = hostDocument.getDocumentElement();
325                         var scrollTop = hostDocumentElement.$.scrollTop;
326                         var scrollLeft = hostDocumentElement.$.scrollLeft;
328                         // Simulating keyboard character input by dispatching a keydown of white-space text.
329                         var keyEventSimulate = doc.$.createEvent( "KeyEvents" );
330                         keyEventSimulate.initKeyEvent( 'keypress', true, true, win.$, false,
331                                 false, false, false, 0, 32 );
332                         doc.$.dispatchEvent( keyEventSimulate );
334                         if ( scrollTop != hostDocumentElement.$.scrollTop || scrollLeft != hostDocumentElement.$.scrollLeft )
335                                 hostDocument.getWindow().$.scrollTo( scrollLeft, scrollTop );
337                         // Restore the original document status by placing the cursor before a bogus br created (#5021).
338                         bodyChildsNum && body.getFirst().remove();
339                         doc.getBody().appendBogus();
340                         var nativeRange = new CKEDITOR.dom.range( doc );
341                         nativeRange.setStartAt( body , CKEDITOR.POSITION_AFTER_START );
342                         nativeRange.select();
343                 }
344         }
346         /**
347          *  Auto-fixing block-less content by wrapping paragraph (#3190), prevent
348          *  non-exitable-block by padding extra br.(#3189)
349          */
350         function onSelectionChangeFixBody( evt )
351         {
352                 var editor = evt.editor,
353                         path = evt.data.path,
354                         blockLimit = path.blockLimit,
355                         selection = evt.data.selection,
356                         range = selection.getRanges()[0],
357                         body = editor.document.getBody(),
358                         enterMode = editor.config.enterMode;
360                 if ( CKEDITOR.env.gecko )
361                 {
362                         activateEditing( editor );
364                         // Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
365                         var pathBlock = path.block || path.blockLimit;
366                         if ( pathBlock && !pathBlock.getBogus() )
367                         {
368                                 editor.fire( 'updateSnapshot' );
369                                 restoreDirty( editor );
370                                 pathBlock.appendBogus();
371                         }
372                 }
374                 // When enterMode set to block, we'll establing new paragraph only if we're
375                 // selecting inline contents right under body. (#3657)
376                 if ( enterMode != CKEDITOR.ENTER_BR
377                      && range.collapsed
378                          && blockLimit.getName() == 'body'
379                          && !path.block )
380                 {
381                         editor.fire( 'updateSnapshot' );
382                         restoreDirty( editor );
383                         CKEDITOR.env.ie && restoreSelection( selection );
385                         var fixedBlock = range.fixBlock( true,
386                                         editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p'  );
388                         // For IE, we should remove any filler node which was introduced before.
389                         if ( CKEDITOR.env.ie )
390                         {
391                                 var first = fixedBlock.getFirst( isNotEmpty );
392                                 first && isNbsp( first ) && first.remove();
393                         }
395                         // If the fixed block is actually blank and is already followed by an exitable blank
396                         // block, we should revert the fix and move into the existed one. (#3684)
397                         if ( isBlankParagraph( fixedBlock ) )
398                         {
399                                 var element = fixedBlock.getNext( isNotWhitespace );
400                                 if ( element &&
401                                          element.type == CKEDITOR.NODE_ELEMENT &&
402                                          !nonExitable( element ) )
403                                 {
404                                         range.moveToElementEditStart( element );
405                                         fixedBlock.remove();
406                                 }
407                                 else
408                                 {
409                                         element = fixedBlock.getPrevious( isNotWhitespace );
410                                         if ( element &&
411                                                  element.type == CKEDITOR.NODE_ELEMENT &&
412                                                  !nonExitable( element ) )
413                                         {
414                                                 range.moveToElementEditEnd( element );
415                                                 fixedBlock.remove();
416                                         }
417                                 }
418                         }
420                         range.select();
421                         // Notify non-IE that selection has changed.
422                         if ( !CKEDITOR.env.ie )
423                         {
424                                 // Make sure next selection change is correct.  (#6811)
425                                 editor.forceNextSelectionCheck();
426                                 editor.selectionChange();
427                         }
428                 }
430                 // All browsers are incapable to moving cursor out of certain non-exitable
431                 // blocks (e.g. table, list, pre) at the end of document, make this happen by
432                 // place a bogus node there, which would be later removed by dataprocessor.
433                 var walkerRange = new CKEDITOR.dom.range( editor.document ),
434                         walker = new CKEDITOR.dom.walker( walkerRange );
435                 walkerRange.selectNodeContents( body );
436                 walker.evaluator = function( node )
437                 {
438                         return node.type == CKEDITOR.NODE_ELEMENT && ( node.getName() in nonExitableElementNames );
439                 };
440                 walker.guard = function( node, isMoveout )
441                 {
442                         return !( ( node.type == CKEDITOR.NODE_TEXT && isNotWhitespace( node ) ) || isMoveout );
443                 };
445                 if ( walker.previous() )
446                 {
447                         editor.fire( 'updateSnapshot' );
448                         restoreDirty( editor );
449                         CKEDITOR.env.ie && restoreSelection( selection );
451                         var paddingBlock;
452                         if ( enterMode != CKEDITOR.ENTER_BR )
453                                 paddingBlock = body.append( new CKEDITOR.dom.element( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) );
454                         else
455                                 paddingBlock = body;
457                         if ( !CKEDITOR.env.ie )
458                                 paddingBlock.appendBogus();
459                 }
460         }
462         CKEDITOR.plugins.add( 'wysiwygarea',
463         {
464                 requires : [ 'editingblock' ],
466                 init : function( editor )
467                 {
468                         var fixForBody = ( editor.config.enterMode != CKEDITOR.ENTER_BR )
469                                 ? editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false;
471                         var frameLabel = editor.lang.editorTitle.replace( '%1', editor.name );
473                         var contentDomReadyHandler;
474                         editor.on( 'editingBlockReady', function()
475                                 {
476                                         var mainElement,
477                                                 iframe,
478                                                 isLoadingData,
479                                                 isPendingFocus,
480                                                 frameLoaded,
481                                                 fireMode;
484                                         // Support for custom document.domain in IE.
485                                         var isCustomDomain = CKEDITOR.env.isCustomDomain();
487                                         // Creates the iframe that holds the editable document.
488                                         var createIFrame = function( data )
489                                         {
490                                                 if ( iframe )
491                                                         iframe.remove();
493                                                 var src =
494                                                         'document.open();' +
496                                                         // The document domain must be set any time we
497                                                         // call document.open().
498                                                         ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) +
500                                                         'document.close();';
502                                                 // With IE, the custom domain has to be taken care at first,
503                                                 // for other browers, the 'src' attribute should be left empty to
504                                                 // trigger iframe's 'load' event.
505                                                 src =
506                                                         CKEDITOR.env.air ?
507                                                                 'javascript:void(0)' :
508                                                         CKEDITOR.env.ie ?
509                                                                 'javascript:void(function(){' + encodeURIComponent( src ) + '}())'
510                                                         :
511                                                                 '';
513                                                 iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' +
514                                                         ' style="width:100%;height:100%"' +
515                                                         ' frameBorder="0"' +
516                                                         ' title="' + frameLabel + '"' +
517                                                         ' src="' + src + '"' +
518                                                         ' tabIndex="' + ( CKEDITOR.env.webkit? -1 : editor.tabIndex ) + '"' +
519                                                         ' allowTransparency="true"' +
520                                                         '></iframe>' );
522                                                 // Running inside of Firefox chrome the load event doesn't bubble like in a normal page (#5689)
523                                                 if ( document.location.protocol == 'chrome:' )
524                                                         CKEDITOR.event.useCapture = true;
526                                                 // With FF, it's better to load the data on iframe.load. (#3894,#4058)
527                                                 iframe.on( 'load', function( ev )
528                                                         {
529                                                                 frameLoaded = 1;
530                                                                 ev.removeListener();
532                                                                 var doc = iframe.getFrameDocument();
533                                                                 doc.write( data );
535                                                                 CKEDITOR.env.air && contentDomReady( doc.getWindow().$ );
536                                                         });
538                                                 // Reset adjustment back to default (#5689)
539                                                 if ( document.location.protocol == 'chrome:' )
540                                                         CKEDITOR.event.useCapture = false;
542                                                 // The container must be visible when creating the iframe in FF (#5956)
543                                                 var element = editor.element,
544                                                         isHidden = CKEDITOR.env.gecko && !element.isVisible(),
545                                                         previousStyles = {};
546                                                 if ( isHidden )
547                                                 {
548                                                         element.show();
549                                                         previousStyles = {
550                                                                 position : element.getStyle( 'position' ),
551                                                                 top : element.getStyle( 'top' )
552                                                         };
553                                                         element.setStyles( { position : 'absolute', top : '-3000px' } );
554                                                 }
556                                                 mainElement.append( iframe );
558                                                 if ( isHidden )
559                                                 {
560                                                         setTimeout( function()
561                                                         {
562                                                                 element.hide();
563                                                                 element.setStyles( previousStyles );
564                                                         }, 1000 );
565                                                 }
566                                         };
568                                         // The script that launches the bootstrap logic on 'domReady', so the document
569                                         // is fully editable even before the editing iframe is fully loaded (#4455).
570                                         contentDomReadyHandler = CKEDITOR.tools.addFunction( contentDomReady );
571                                         var activationScript =
572                                                 '<script id="cke_actscrpt" type="text/javascript" data-cke-temp="1">' +
573                                                         ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) +
574                                                         'window.parent.CKEDITOR.tools.callFunction( ' + contentDomReadyHandler + ', window );' +
575                                                 '</script>';
577                                         // Editing area bootstrap code.
578                                         function contentDomReady( domWindow )
579                                         {
580                                                 if ( !frameLoaded )
581                                                         return;
582                                                 frameLoaded = 0;
584                                                 editor.fire( 'ariaWidget', iframe );
586                                                 var domDocument = domWindow.document,
587                                                         body = domDocument.body;
589                                                 // Remove this script from the DOM.
590                                                 var script = domDocument.getElementById( "cke_actscrpt" );
591                                                 script && script.parentNode.removeChild( script );
593                                                 body.spellcheck = !editor.config.disableNativeSpellChecker;
595                                                 if ( CKEDITOR.env.ie )
596                                                 {
597                                                         // Don't display the focus border.
598                                                         body.hideFocus = true;
600                                                         // Disable and re-enable the body to avoid IE from
601                                                         // taking the editing focus at startup. (#141 / #523)
602                                                         body.disabled = true;
603                                                         body.contentEditable = true;
604                                                         body.removeAttribute( 'disabled' );
605                                                 }
606                                                 else
607                                                 {
608                                                         // Avoid opening design mode in a frame window thread,
609                                                         // which will cause host page scrolling.(#4397)
610                                                         setTimeout( function()
611                                                         {
612                                                                 // Prefer 'contentEditable' instead of 'designMode'. (#3593)
613                                                                 if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900
614                                                                                 || CKEDITOR.env.opera )
615                                                                         domDocument.$.body.contentEditable = true;
616                                                                 else if ( CKEDITOR.env.webkit )
617                                                                         domDocument.$.body.parentNode.contentEditable = true;
618                                                                 else
619                                                                         domDocument.$.designMode = 'on';
620                                                         }, 0 );
621                                                 }
623                                                 CKEDITOR.env.gecko && CKEDITOR.tools.setTimeout( activateEditing, 0, null, editor );
625                                                 domWindow       = editor.window = new CKEDITOR.dom.window( domWindow );
626                                                 domDocument     = editor.document       = new CKEDITOR.dom.document( domDocument );
628                                                 domDocument.on( 'dblclick', function( evt )
629                                                 {
630                                                         var element = evt.data.getTarget(),
631                                                                 data = { element : element, dialog : '' };
632                                                         editor.fire( 'doubleclick', data );
633                                                         data.dialog && editor.openDialog( data.dialog );
634                                                 });
636                                                 // Prevent automatic submission in IE #6336
637                                                 CKEDITOR.env.ie && domDocument.on( 'click', function( evt )
638                                                 {
639                                                         var element = evt.data.getTarget();
640                                                         if ( element.is( 'input' ) )
641                                                         {
642                                                                 var type = element.getAttribute( 'type' );
643                                                                 if ( type == 'submit' || type == 'reset' )
644                                                                         evt.data.preventDefault();
645                                                         }
646                                                 });
648                                                 // Gecko/Webkit need some help when selecting control type elements. (#3448)
649                                                 if ( !( CKEDITOR.env.ie || CKEDITOR.env.opera ) )
650                                                 {
651                                                         domDocument.on( 'mousedown', function( ev )
652                                                         {
653                                                                 var control = ev.data.getTarget();
654                                                                 if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) )
655                                                                         editor.getSelection().selectElement( control );
656                                                         } );
657                                                 }
659                                                 if ( CKEDITOR.env.gecko )
660                                                 {
661                                                         domDocument.on( 'mouseup', function( ev )
662                                                         {
663                                                                 if ( ev.data.$.button == 2 )
664                                                                 {
665                                                                         var target = ev.data.getTarget();
667                                                                         // Prevent right click from selecting an empty block even
668                                                                         // when selection is anchored inside it. (#5845)
669                                                                         if ( !target.getOuterHtml().replace( emptyParagraphRegexp, '' ) )
670                                                                         {
671                                                                                 var range = new CKEDITOR.dom.range( domDocument );
672                                                                                 range.moveToElementEditStart( target );
673                                                                                 range.select( true );
674                                                                         }
675                                                                 }
676                                                         } );
677                                                 }
679                                                 // Prevent the browser opening links in read-only blocks. (#6032)
680                                                 domDocument.on( 'click', function( ev )
681                                                         {
682                                                                 ev = ev.data;
683                                                                 if ( ev.getTarget().is( 'a' ) && ev.$.button != 2 )
684                                                                         ev.preventDefault();
685                                                         });
687                                                 // Webkit: avoid from editing form control elements content.
688                                                 if ( CKEDITOR.env.webkit )
689                                                 {
690                                                         // Prevent from tick checkbox/radiobox/select
691                                                         domDocument.on( 'click', function( ev )
692                                                         {
693                                                                 if ( ev.data.getTarget().is( 'input', 'select' ) )
694                                                                         ev.data.preventDefault();
695                                                         } );
697                                                         // Prevent from editig textfield/textarea value.
698                                                         domDocument.on( 'mouseup', function( ev )
699                                                         {
700                                                                 if ( ev.data.getTarget().is( 'input', 'textarea' ) )
701                                                                         ev.data.preventDefault();
702                                                         } );
703                                                 }
705                                                 // IE standard compliant in editing frame doesn't focus the editor when
706                                                 // clicking outside actual content, manually apply the focus. (#1659)
707                                                 if ( CKEDITOR.env.ie
708                                                         && domDocument.$.compatMode == 'CSS1Compat'
709                                                                 || CKEDITOR.env.gecko
710                                                                 || CKEDITOR.env.opera )
711                                                 {
712                                                         var htmlElement = domDocument.getDocumentElement();
713                                                         htmlElement.on( 'mousedown', function( evt )
714                                                         {
715                                                                 // Setting focus directly on editor doesn't work, we
716                                                                 // have to use here a temporary element to 'redirect'
717                                                                 // the focus.
718                                                                 if ( evt.data.getTarget().equals( htmlElement ) )
719                                                                 {
720                                                                         if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 )
721                                                                                 blinkCursor();
722                                                                         focusGrabber.focus();
723                                                                 }
724                                                         } );
725                                                 }
727                                                 var focusTarget = CKEDITOR.env.ie ? iframe : domWindow;
728                                                 focusTarget.on( 'blur', function()
729                                                         {
730                                                                 editor.focusManager.blur();
731                                                         });
733                                                 var wasFocused;
735                                                 focusTarget.on( 'focus', function()
736                                                         {
737                                                                 var doc = editor.document;
739                                                                 if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 )
740                                                                         blinkCursor();
741                                                                 else if ( CKEDITOR.env.opera )
742                                                                         doc.getBody().focus();
743                                                                 // Webkit needs focus for the first time on the HTML element. (#6153)
744                                                                 else if ( CKEDITOR.env.webkit )
745                                                                 {
746                                                                         if ( !wasFocused )
747                                                                         {
748                                                                                 editor.document.getDocumentElement().focus();
749                                                                                 wasFocused = 1;
750                                                                         }
751                                                                 }
753                                                                 editor.focusManager.focus();
754                                                         });
756                                                 var keystrokeHandler = editor.keystrokeHandler;
757                                                 if ( keystrokeHandler )
758                                                         keystrokeHandler.attach( domDocument );
760                                                 if ( CKEDITOR.env.ie )
761                                                 {
762                                                         domDocument.getDocumentElement().addClass( domDocument.$.compatMode );
763                                                         // Override keystrokes which should have deletion behavior
764                                                         //  on control types in IE . (#4047)
765                                                         domDocument.on( 'keydown', function( evt )
766                                                         {
767                                                                 var keyCode = evt.data.getKeystroke();
769                                                                 // Backspace OR Delete.
770                                                                 if ( keyCode in { 8 : 1, 46 : 1 } )
771                                                                 {
772                                                                         var sel = editor.getSelection(),
773                                                                                 control = sel.getSelectedElement();
775                                                                         if ( control )
776                                                                         {
777                                                                                 // Make undo snapshot.
778                                                                                 editor.fire( 'saveSnapshot' );
780                                                                                 // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will
781                                                                                 // break up the selection, safely manage it here. (#4795)
782                                                                                 var bookmark = sel.getRanges()[ 0 ].createBookmark();
783                                                                                 // Remove the control manually.
784                                                                                 control.remove();
785                                                                                 sel.selectBookmarks( [ bookmark ] );
787                                                                                 editor.fire( 'saveSnapshot' );
789                                                                                 evt.data.preventDefault();
790                                                                         }
791                                                                 }
792                                                         } );
794                                                         // PageUp/PageDown scrolling is broken in document
795                                                         // with standard doctype, manually fix it. (#4736)
796                                                         if ( domDocument.$.compatMode == 'CSS1Compat' )
797                                                         {
798                                                                 var pageUpDownKeys = { 33 : 1, 34 : 1 };
799                                                                 domDocument.on( 'keydown', function( evt )
800                                                                 {
801                                                                         if ( evt.data.getKeystroke() in pageUpDownKeys )
802                                                                         {
803                                                                                 setTimeout( function ()
804                                                                                 {
805                                                                                         editor.getSelection().scrollIntoView();
806                                                                                 }, 0 );
807                                                                         }
808                                                                 } );
809                                                         }
810                                                 }
812                                                 // Adds the document body as a context menu target.
813                                                 if ( editor.contextMenu )
814                                                         editor.contextMenu.addTarget( domDocument, editor.config.browserContextMenuOnCtrl !== false );
816                                                 setTimeout( function()
817                                                         {
818                                                                 editor.fire( 'contentDom' );
820                                                                 if ( fireMode )
821                                                                 {
822                                                                         editor.mode = 'wysiwyg';
823                                                                         editor.fire( 'mode' );
824                                                                         fireMode = false;
825                                                                 }
827                                                                 isLoadingData = false;
829                                                                 if ( isPendingFocus )
830                                                                 {
831                                                                         editor.focus();
832                                                                         isPendingFocus = false;
833                                                                 }
834                                                                 setTimeout( function()
835                                                                 {
836                                                                         editor.fire( 'dataReady' );
837                                                                 }, 0 );
839                                                                 // IE, Opera and Safari may not support it and throw errors.
840                                                                 try { editor.document.$.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ); } catch(e) {}
841                                                                 if ( editor.config.disableObjectResizing )
842                                                                 {
843                                                                         try
844                                                                         {
845                                                                                 editor.document.$.execCommand( 'enableObjectResizing', false, false );
846                                                                         }
847                                                                         catch(e)
848                                                                         {
849                                                                                 // For browsers in which the above method failed, we can cancel the resizing on the fly (#4208)
850                                                                                 editor.document.getBody().on( CKEDITOR.env.ie ? 'resizestart' : 'resize', function( evt )
851                                                                                 {
852                                                                                         evt.data.preventDefault();
853                                                                                 });
854                                                                         }
855                                                                 }
857                                                                 /*
858                                                                  * IE BUG: IE might have rendered the iframe with invisible contents.
859                                                                  * (#3623). Push some inconsequential CSS style changes to force IE to
860                                                                  * refresh it.
861                                                                  *
862                                                                  * Also, for some unknown reasons, short timeouts (e.g. 100ms) do not
863                                                                  * fix the problem. :(
864                                                                  */
865                                                                 if ( CKEDITOR.env.ie )
866                                                                 {
867                                                                         setTimeout( function()
868                                                                                 {
869                                                                                         if ( editor.document )
870                                                                                         {
871                                                                                                 var $body = editor.document.$.body;
872                                                                                                 $body.runtimeStyle.marginBottom = '0px';
873                                                                                                 $body.runtimeStyle.marginBottom = '';
874                                                                                         }
875                                                                                 }, 1000 );
876                                                                 }
877                                                         },
878                                                         0 );
879                                         }
881                                         editor.addMode( 'wysiwyg',
882                                                 {
883                                                         load : function( holderElement, data, isSnapshot )
884                                                         {
885                                                                 mainElement = holderElement;
887                                                                 if ( CKEDITOR.env.ie && CKEDITOR.env.quirks )
888                                                                         holderElement.setStyle( 'position', 'relative' );
890                                                                 // The editor data "may be dirty" after this
891                                                                 // point.
892                                                                 editor.mayBeDirty = true;
894                                                                 fireMode = true;
896                                                                 if ( isSnapshot )
897                                                                         this.loadSnapshotData( data );
898                                                                 else
899                                                                         this.loadData( data );
900                                                         },
902                                                         loadData : function( data )
903                                                         {
904                                                                 isLoadingData = true;
906                                                                 var config = editor.config,
907                                                                         fullPage = config.fullPage,
908                                                                         docType = config.docType;
910                                                                 // Build the additional stuff to be included into <head>.
911                                                                 var headExtra =
912                                                                         '<style type="text/css" data-cke-temp="1">' +
913                                                                                 editor._.styles.join( '\n' ) +
914                                                                         '</style>';
916                                                                 !fullPage && ( headExtra =
917                                                                         CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ) +
918                                                                         headExtra );
920                                                                 var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" data-cke-temp="1" />' : '';
922                                                                 if ( fullPage )
923                                                                 {
924                                                                         // Search and sweep out the doctype declaration.
925                                                                         data = data.replace( /<!DOCTYPE[^>]*>/i, function( match )
926                                                                                 {
927                                                                                         editor.docType = docType = match;
928                                                                                         return '';
929                                                                                 });
930                                                                 }
932                                                                 // Get the HTML version of the data.
933                                                                 if ( editor.dataProcessor )
934                                                                         data = editor.dataProcessor.toHtml( data, fixForBody );
936                                                                 if ( fullPage )
937                                                                 {
938                                                                         // Check if the <body> tag is available.
939                                                                         if ( !(/<body[\s|>]/).test( data ) )
940                                                                                 data = '<body>' + data;
942                                                                         // Check if the <html> tag is available.
943                                                                         if ( !(/<html[\s|>]/).test( data ) )
944                                                                                 data = '<html>' + data + '</html>';
946                                                                         // Check if the <head> tag is available.
947                                                                         if ( !(/<head[\s|>]/).test( data ) )
948                                                                                 data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' ) ;
949                                                                         else if ( !(/<title[\s|>]/).test( data ) )
950                                                                                 data = data.replace( /<head[^>]*>/, '$&<title></title>' ) ;
952                                                                         // The base must be the first tag in the HEAD, e.g. to get relative
953                                                                         // links on styles.
954                                                                         baseTag && ( data = data.replace( /<head>/, '$&' + baseTag ) );
956                                                                         // Inject the extra stuff into <head>.
957                                                                         // Attention: do not change it before testing it well. (V2)
958                                                                         // This is tricky... if the head ends with <meta ... content type>,
959                                                                         // Firefox will break. But, it works if we place our extra stuff as
960                                                                         // the last elements in the HEAD.
961                                                                         data = data.replace( /<\/head\s*>/, headExtra + '$&' );
963                                                                         // Add the DOCTYPE back to it.
964                                                                         data = docType + data;
965                                                                 }
966                                                                 else
967                                                                 {
968                                                                         data =
969                                                                                 config.docType +
970                                                                                 '<html dir="' + config.contentsLangDirection + '"' +
971                                                                                         ' lang="' + ( config.contentsLanguage || editor.langCode ) + '">' +
972                                                                                 '<head>' +
973                                                                                         '<title>' + frameLabel + '</title>' +
974                                                                                         baseTag +
975                                                                                         headExtra +
976                                                                                 '</head>' +
977                                                                                 '<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) +
978                                                                                                   ( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) +
979                                                                                                   '>' +
980                                                                                         data +
981                                                                                 '</html>';
982                                                                 }
984                                                                 data += activationScript;
987                                                                 // The iframe is recreated on each call of setData, so we need to clear DOM objects
988                                                                 this.onDispose();
989                                                                 createIFrame( data );
990                                                         },
992                                                         getData : function()
993                                                         {
994                                                                 var config = editor.config,
995                                                                         fullPage = config.fullPage,
996                                                                         docType = fullPage && editor.docType,
997                                                                         doc = iframe.getFrameDocument();
999                                                                 var data = fullPage
1000                                                                         ? doc.getDocumentElement().getOuterHtml()
1001                                                                         : doc.getBody().getHtml();
1003                                                                 if ( editor.dataProcessor )
1004                                                                         data = editor.dataProcessor.toDataFormat( data, fixForBody );
1006                                                                 // Reset empty if the document contains only one empty paragraph.
1007                                                                 if ( config.ignoreEmptyParagraph )
1008                                                                         data = data.replace( emptyParagraphRegexp, function( match, lookback ) { return lookback; } );
1010                                                                 if ( docType )
1011                                                                         data = docType + '\n' + data;
1013                                                                 return data;
1014                                                         },
1016                                                         getSnapshotData : function()
1017                                                         {
1018                                                                 return iframe.getFrameDocument().getBody().getHtml();
1019                                                         },
1021                                                         loadSnapshotData : function( data )
1022                                                         {
1023                                                                 iframe.getFrameDocument().getBody().setHtml( data );
1024                                                         },
1026                                                         onDispose : function()
1027                                                         {
1028                                                                 if ( !editor.document )
1029                                                                         return;
1031                                                                 editor.document.getDocumentElement().clearCustomData();
1032                                                                 editor.document.getBody().clearCustomData();
1034                                                                 editor.window.clearCustomData();
1035                                                                 editor.document.clearCustomData();
1037                                                                 iframe.clearCustomData();
1039                                                                 /*
1040                                                                 * IE BUG: When destroying editor DOM with the selection remains inside
1041                                                                 * editing area would break IE7/8's selection system, we have to put the editing
1042                                                                 * iframe offline first. (#3812 and #5441)
1043                                                                 */
1044                                                                 iframe.remove();
1045                                                         },
1047                                                         unload : function( holderElement )
1048                                                         {
1049                                                                 this.onDispose();
1051                                                                 editor.window = editor.document = iframe = mainElement = isPendingFocus = null;
1053                                                                 editor.fire( 'contentDomUnload' );
1054                                                         },
1056                                                         focus : function()
1057                                                         {
1058                                                                 var win = editor.window;
1060                                                                 if ( isLoadingData )
1061                                                                         isPendingFocus = true;
1062                                                                 // Temporary solution caused by #6025, supposed be unified by #6154.
1063                                                                 else if ( CKEDITOR.env.opera && editor.document )
1064                                                                 {
1065                                                                         // Required for Opera when switching focus
1066                                                                         // from another iframe, e.g. panels. (#6444)
1067                                                                         var iframe = editor.window.$.frameElement;
1068                                                                         iframe.blur(), iframe.focus();
1069                                                                         editor.document.getBody().focus();
1071                                                                         editor.selectionChange();
1072                                                                 }
1073                                                                 else if ( !CKEDITOR.env.opera && win )
1074                                                                 {
1075                                                                         // AIR needs a while to focus when moving from a link.
1076                                                                         CKEDITOR.env.air ? setTimeout( function () { win.focus(); }, 0 ) : win.focus();
1077                                                                         editor.selectionChange();
1078                                                                 }
1079                                                         }
1080                                                 });
1082                                         editor.on( 'insertHtml', onInsert( doInsertHtml ) , null, null, 20 );
1083                                         editor.on( 'insertElement', onInsert( doInsertElement ), null, null, 20 );
1084                                         editor.on( 'insertText', onInsert( doInsertText ), null, null, 20 );
1085                                         // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189)
1086                                         editor.on( 'selectionChange', onSelectionChangeFixBody, null, null, 1 );
1087                                 });
1089                         var titleBackup;
1090                         // Setting voice label as window title, backup the original one
1091                         // and restore it before running into use.
1092                         editor.on( 'contentDom', function()
1093                                 {
1094                                         var title = editor.document.getElementsByTag( 'title' ).getItem( 0 );
1095                                         title.data( 'cke-title', editor.document.$.title );
1096                                         editor.document.$.title = frameLabel;
1097                                 });
1099                         // IE>=8 stricts mode doesn't have 'contentEditable' in effect
1100                         // on element unless it has layout. (#5562)
1101                         if ( CKEDITOR.document.$.documentMode >= 8 )
1102                         {
1103                                 editor.addCss( 'html.CSS1Compat [contenteditable=false]{ min-height:0 !important;}' );
1105                                 var selectors = [];
1106                                 for ( var tag in CKEDITOR.dtd.$removeEmpty )
1107                                         selectors.push( 'html.CSS1Compat ' + tag + '[contenteditable=false]' );
1108                                 editor.addCss( selectors.join( ',' ) + '{ display:inline-block;}' );
1109                         }
1110                         // Set the HTML style to 100% to have the text cursor in affect (#6341)
1111                         else if ( CKEDITOR.env.gecko )
1112                                 editor.addCss( 'html { height: 100% !important; }' );
1114                         // Switch on design mode for a short while and close it after then.
1115                         function blinkCursor( retry )
1116                         {
1117                                 CKEDITOR.tools.tryThese(
1118                                         function()
1119                                         {
1120                                                 editor.document.$.designMode = 'on';
1121                                                 setTimeout( function()
1122                                                 {
1123                                                         editor.document.$.designMode = 'off';
1124                                                         if ( CKEDITOR.currentInstance == editor )
1125                                                                 editor.document.getBody().focus();
1126                                                 }, 50 );
1127                                         },
1128                                         function()
1129                                         {
1130                                                 // The above call is known to fail when parent DOM
1131                                                 // tree layout changes may break design mode. (#5782)
1132                                                 // Refresh the 'contentEditable' is a cue to this.
1133                                                 editor.document.$.designMode = 'off';
1134                                                 var body = editor.document.getBody();
1135                                                 body.setAttribute( 'contentEditable', false );
1136                                                 body.setAttribute( 'contentEditable', true );
1137                                                 // Try it again once..
1138                                                 !retry && blinkCursor( 1 );
1139                                         });
1140                         }
1142                         // Create an invisible element to grab focus.
1143                         if ( CKEDITOR.env.gecko || CKEDITOR.env.ie || CKEDITOR.env.opera )
1144                         {
1145                                 var focusGrabber;
1146                                 editor.on( 'uiReady', function()
1147                                 {
1148                                         focusGrabber = editor.container.append( CKEDITOR.dom.element.createFromHtml(
1149                                                 // Use 'span' instead of anything else to fly under the screen-reader radar. (#5049)
1150                                                 '<span tabindex="-1" style="position:absolute;" role="presentation"></span>' ) );
1152                                         focusGrabber.on( 'focus', function()
1153                                                 {
1154                                                         editor.focus();
1155                                                 } );
1157                                         editor.focusGrabber = focusGrabber;
1158                                 } );
1159                                 editor.on( 'destroy', function()
1160                                 {
1161                                         CKEDITOR.tools.removeFunction( contentDomReadyHandler );
1162                                         focusGrabber.clearCustomData();
1163                                         delete editor.focusGrabber;
1164                                 } );
1165                         }
1167                         // Disable form elements editing mode provided by some browers. (#5746)
1168                         editor.on( 'insertElement', function ( evt )
1169                         {
1170                                 var element = evt.data;
1171                                 if ( element.type == CKEDITOR.NODE_ELEMENT
1172                                                 && ( element.is( 'input' ) || element.is( 'textarea' ) ) )
1173                                 {
1174                                         // We should flag that the element was locked by our code so
1175                                         // it'll be editable by the editor functions (#6046).
1176                                         if ( !element.isReadOnly() )
1177                                                 element.data( 'cke-editable', element.hasAttribute( 'contenteditable' ) ? 'true' : '1' );
1178                                         element.setAttribute( 'contentEditable', false );
1179                                 }
1180                         });
1182                 }
1183         });
1185         // Fixing Firefox 'Back-Forward Cache' break design mode. (#4514)
1186         if ( CKEDITOR.env.gecko )
1187         {
1188                 (function()
1189                 {
1190                         var body = document.body;
1192                         if ( !body )
1193                                 window.addEventListener( 'load', arguments.callee, false );
1194                         else
1195                         {
1196                                 var currentHandler = body.getAttribute( 'onpageshow' );
1197                                 body.setAttribute( 'onpageshow', ( currentHandler ? currentHandler + ';' : '') +
1198                                                         'event.persisted && (function(){' +
1199                                                                 'var allInstances = CKEDITOR.instances, editor, doc;' +
1200                                                                 'for ( var i in allInstances )' +
1201                                                                 '{' +
1202                                                                 '       editor = allInstances[ i ];' +
1203                                                                 '       doc = editor.document;' +
1204                                                                 '       if ( doc )' +
1205                                                                 '       {' +
1206                                                                 '               doc.$.designMode = "off";' +
1207                                                                 '               doc.$.designMode = "on";' +
1208                                                                 '       }' +
1209                                                                 '}' +
1210                                                 '})();' );
1211                         }
1212                 } )();
1214         }
1215 })();
1218  * Disables the ability of resize objects (image and tables) in the editing
1219  * area.
1220  * @type Boolean
1221  * @default false
1222  * @example
1223  * config.disableObjectResizing = true;
1224  */
1225 CKEDITOR.config.disableObjectResizing = false;
1228  * Disables the "table tools" offered natively by the browser (currently
1229  * Firefox only) to make quick table editing operations, like adding or
1230  * deleting rows and columns.
1231  * @type Boolean
1232  * @default true
1233  * @example
1234  * config.disableNativeTableHandles = false;
1235  */
1236 CKEDITOR.config.disableNativeTableHandles = true;
1239  * Disables the built-in spell checker while typing natively available in the
1240  * browser (currently Firefox and Safari only).<br /><br />
1242  * Even if word suggestions will not appear in the CKEditor context menu, this
1243  * feature is useful to help quickly identifying misspelled words.<br /><br />
1245  * This setting is currently compatible with Firefox only due to limitations in
1246  * other browsers.
1247  * @type Boolean
1248  * @default true
1249  * @example
1250  * config.disableNativeSpellChecker = false;
1251  */
1252 CKEDITOR.config.disableNativeSpellChecker = true;
1255  * Whether the editor must output an empty value ("") if it's contents is made
1256  * by an empty paragraph only.
1257  * @type Boolean
1258  * @default true
1259  * @example
1260  * config.ignoreEmptyParagraph = false;
1261  */
1262 CKEDITOR.config.ignoreEmptyParagraph = true;
1265  * Fired when data is loaded and ready for retrieval in an editor instance.
1266  * @name CKEDITOR.editor#dataReady
1267  * @event
1268  */
1271  * Fired when some elements are added to the document
1272  * @name CKEDITOR.editor#ariaWidget
1273  * @event
1274  * @param {Object} element The element being added
1275  */