]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blobdiff - sources/plugins/clipboard/plugin.js
Add oembed
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / plugins / clipboard / plugin.js
diff --git a/sources/plugins/clipboard/plugin.js b/sources/plugins/clipboard/plugin.js
new file mode 100644 (file)
index 0000000..433f547
--- /dev/null
@@ -0,0 +1,2780 @@
+/**\r
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r
+ * For licensing, see LICENSE.md or http://ckeditor.com/license\r
+ */\r
+\r
+/**\r
+ * @ignore\r
+ * File overview: Clipboard support.\r
+ */\r
+\r
+//\r
+// COPY & PASTE EXECUTION FLOWS:\r
+// -- CTRL+C\r
+//             * if ( isCustomCopyCutSupported )\r
+//                     * dataTransfer.setData( 'text/html', getSelectedHtml )\r
+//             * else\r
+//                     * browser's default behavior\r
+// -- CTRL+X\r
+//             * listen onKey (onkeydown)\r
+//             * fire 'saveSnapshot' on editor\r
+//             * if ( isCustomCopyCutSupported )\r
+//                     * dataTransfer.setData( 'text/html', getSelectedHtml )\r
+//                     * extractSelectedHtml // remove selected contents\r
+//             * else\r
+//                     * browser's default behavior\r
+//             * deferred second 'saveSnapshot' event\r
+// -- CTRL+V\r
+//             * listen onKey (onkeydown)\r
+//             * simulate 'beforepaste' for non-IEs on editable\r
+//             * listen 'onpaste' on editable ('onbeforepaste' for IE)\r
+//             * fire 'beforePaste' on editor\r
+//             * if ( !canceled && ( htmlInDataTransfer || !external paste) && dataTransfer is not empty ) getClipboardDataByPastebin\r
+//             * fire 'paste' on editor\r
+//             * !canceled && fire 'afterPaste' on editor\r
+// -- Copy command\r
+//             * tryToCutCopy\r
+//                     * execCommand\r
+//             * !success && notification\r
+// -- Cut command\r
+//             * fixCut\r
+//             * tryToCutCopy\r
+//                     * execCommand\r
+//             * !success && notification\r
+// -- Paste command\r
+//             * fire 'paste' on editable ('beforepaste' for IE)\r
+//             * !canceled && execCommand 'paste'\r
+// -- Paste from native context menu & menubar\r
+//             (Fx & Webkits are handled in 'paste' default listener.\r
+//             Opera cannot be handled at all because it doesn't fire any events\r
+//             Special treatment is needed for IE, for which is this part of doc)\r
+//             * listen 'onpaste'\r
+//             * cancel native event\r
+//             * fire 'beforePaste' on editor\r
+//             * if ( !canceled && ( htmlInDataTransfer || !external paste) && dataTransfer is not empty ) getClipboardDataByPastebin\r
+//             * execIECommand( 'paste' ) -> this fires another 'paste' event, so cancel it\r
+//             * fire 'paste' on editor\r
+//             * !canceled && fire 'afterPaste' on editor\r
+//\r
+//\r
+// PASTE EVENT - PREPROCESSING:\r
+// -- Possible dataValue types: auto, text, html.\r
+// -- Possible dataValue contents:\r
+//             * text (possible \n\r)\r
+//             * htmlified text (text + br,div,p - no presentational markup & attrs - depends on browser)\r
+//             * html\r
+// -- Possible flags:\r
+//             * htmlified - if true then content is a HTML even if no markup inside. This flag is set\r
+//                     for content from editable pastebins, because they 'htmlify' pasted content.\r
+//\r
+// -- Type: auto:\r
+//             * content: htmlified text ->    filter, unify text markup (brs, ps, divs), set type: text\r
+//             * content: html ->                              filter, set type: html\r
+// -- Type: text:\r
+//             * content: htmlified text ->    filter, unify text markup\r
+//             * content: html ->                              filter, strip presentational markup, unify text markup\r
+// -- Type: html:\r
+//             * content: htmlified text ->    filter, unify text markup\r
+//             * content: html ->                              filter\r
+//\r
+// -- Phases:\r
+//             * if dataValue is empty copy data from dataTransfer to dataValue (priority 1)\r
+//             * filtering (priorities 3-5) - e.g. pastefromword filters\r
+//             * content type sniffing (priority 6)\r
+//             * markup transformations for text (priority 6)\r
+//\r
+// DRAG & DROP EXECUTION FLOWS:\r
+// -- Drag\r
+//             * save to the global object:\r
+//                     * drag timestamp (with 'cke-' prefix),\r
+//                     * selected html,\r
+//                     * drag range,\r
+//                     * editor instance.\r
+//             * put drag timestamp into event.dataTransfer.text\r
+// -- Drop\r
+//             * if events text == saved timestamp && editor == saved editor\r
+//                     internal drag & drop occurred\r
+//                     * getRangeAtDropPosition\r
+//                     * create bookmarks for drag and drop ranges starting from the end of the document\r
+//                     * dragRange.deleteContents()\r
+//                     * fire 'paste' with saved html and drop range\r
+//             * if events text == saved timestamp && editor != saved editor\r
+//                     cross editor drag & drop occurred\r
+//                     * getRangeAtDropPosition\r
+//                     * fire 'paste' with saved html\r
+//                     * dragRange.deleteContents()\r
+//                     * FF: refreshCursor on afterPaste\r
+//             * if events text != saved timestamp\r
+//                     drop form external source occurred\r
+//                     * getRangeAtDropPosition\r
+//                     * if event contains html data then fire 'paste' with html\r
+//                     * else if event contains text data then fire 'paste' with encoded text\r
+//                     * FF: refreshCursor on afterPaste\r
+\r
+'use strict';\r
+\r
+( function() {\r
+       // Register the plugin.\r
+       CKEDITOR.plugins.add( 'clipboard', {\r
+               requires: 'notification,toolbar',\r
+               // jscs:disable maximumLineLength\r
+               lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%\r
+               // jscs:enable maximumLineLength\r
+               icons: 'copy,copy-rtl,cut,cut-rtl,paste,paste-rtl', // %REMOVE_LINE_CORE%\r
+               hidpi: true, // %REMOVE_LINE_CORE%\r
+               init: function( editor ) {\r
+                       var filterType,\r
+                               filtersFactory = filtersFactoryFactory();\r
+\r
+                       if ( editor.config.forcePasteAsPlainText ) {\r
+                               filterType = 'plain-text';\r
+                       } else if ( editor.config.pasteFilter ) {\r
+                               filterType = editor.config.pasteFilter;\r
+                       }\r
+                       // On Webkit the pasteFilter defaults 'semantic-content' because pasted data is so terrible\r
+                       // that it must be always filtered.\r
+                       else if ( CKEDITOR.env.webkit && !( 'pasteFilter' in editor.config ) ) {\r
+                               filterType = 'semantic-content';\r
+                       }\r
+\r
+                       editor.pasteFilter = filtersFactory.get( filterType );\r
+\r
+                       initPasteClipboard( editor );\r
+                       initDragDrop( editor );\r
+\r
+                       // Convert image file (if present) to base64 string for Firefox. Do it as the first\r
+                       // step as the conversion is asynchronous and should hold all further paste processing.\r
+                       if ( CKEDITOR.env.gecko ) {\r
+                               var supportedImageTypes = [ 'image/png', 'image/jpeg', 'image/gif' ],\r
+                                       latestId;\r
+\r
+                               editor.on( 'paste', function( evt ) {\r
+                                       var dataObj = evt.data,\r
+                                               data = dataObj.dataValue,\r
+                                               dataTransfer = dataObj.dataTransfer;\r
+\r
+                                       // If data empty check for image content inside data transfer. http://dev.ckeditor.com/ticket/16705\r
+                                       if ( !data && dataObj.method == 'paste' && dataTransfer && dataTransfer.getFilesCount() == 1 && latestId != dataTransfer.id ) {\r
+                                               var file = dataTransfer.getFile( 0 );\r
+\r
+                                               if ( CKEDITOR.tools.indexOf( supportedImageTypes, file.type ) != -1 ) {\r
+                                                       var fileReader = new FileReader();\r
+\r
+                                                       // Convert image file to img tag with base64 image.\r
+                                                       fileReader.addEventListener( 'load', function() {\r
+                                                               evt.data.dataValue = '<img src="' + fileReader.result + '" />';\r
+                                                               editor.fire( 'paste', evt.data );\r
+                                                       }, false );\r
+\r
+                                                       // Proceed with normal flow if reading file was aborted.\r
+                                                       fileReader.addEventListener( 'abort', function() {\r
+                                                               editor.fire( 'paste', evt.data );\r
+                                                       }, false );\r
+\r
+                                                       // Proceed with normal flow if reading file failed.\r
+                                                       fileReader.addEventListener( 'error', function() {\r
+                                                               editor.fire( 'paste', evt.data );\r
+                                                       }, false );\r
+\r
+                                                       fileReader.readAsDataURL( file );\r
+\r
+                                                       latestId = dataObj.dataTransfer.id;\r
+\r
+                                                       evt.stop();\r
+                                               }\r
+                                       }\r
+                               }, null, null, 1 );\r
+                       }\r
+\r
+                       editor.on( 'paste', function( evt ) {\r
+                               // Init `dataTransfer` if `paste` event was fired without it, so it will be always available.\r
+                               if ( !evt.data.dataTransfer ) {\r
+                                       evt.data.dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer();\r
+                               }\r
+\r
+                               // If dataValue is already set (manually or by paste bin), so do not override it.\r
+                               if ( evt.data.dataValue ) {\r
+                                       return;\r
+                               }\r
+\r
+                               var dataTransfer = evt.data.dataTransfer,\r
+                                       // IE support only text data and throws exception if we try to get html data.\r
+                                       // This html data object may also be empty if we drag content of the textarea.\r
+                                       value = dataTransfer.getData( 'text/html' );\r
+\r
+                               if ( value ) {\r
+                                       evt.data.dataValue = value;\r
+                                       evt.data.type = 'html';\r
+                               } else {\r
+                                       // Try to get text data otherwise.\r
+                                       value = dataTransfer.getData( 'text/plain' );\r
+\r
+                                       if ( value ) {\r
+                                               evt.data.dataValue = editor.editable().transformPlainTextToHtml( value );\r
+                                               evt.data.type = 'text';\r
+                                       }\r
+                               }\r
+                       }, null, null, 1 );\r
+\r
+                       editor.on( 'paste', function( evt ) {\r
+                               var data = evt.data.dataValue,\r
+                                       blockElements = CKEDITOR.dtd.$block;\r
+\r
+                               // Filter webkit garbage.\r
+                               if ( data.indexOf( 'Apple-' ) > -1 ) {\r
+                                       // Replace special webkit's &nbsp; with simple space, because webkit\r
+                                       // produces them even for normal spaces.\r
+                                       data = data.replace( /<span class="Apple-converted-space">&nbsp;<\/span>/gi, ' ' );\r
+\r
+                                       // Strip <span> around white-spaces when not in forced 'html' content type.\r
+                                       // This spans are created only when pasting plain text into Webkit,\r
+                                       // but for safety reasons remove them always.\r
+                                       if ( evt.data.type != 'html' ) {\r
+                                               data = data.replace( /<span class="Apple-tab-span"[^>]*>([^<]*)<\/span>/gi, function( all, spaces ) {\r
+                                                       // Replace tabs with 4 spaces like Fx does.\r
+                                                       return spaces.replace( /\t/g, '&nbsp;&nbsp; &nbsp;' );\r
+                                               } );\r
+                                       }\r
+\r
+                                       // This br is produced only when copying & pasting HTML content.\r
+                                       if ( data.indexOf( '<br class="Apple-interchange-newline">' ) > -1 ) {\r
+                                               evt.data.startsWithEOL = 1;\r
+                                               evt.data.preSniffing = 'html'; // Mark as not text.\r
+                                               data = data.replace( /<br class="Apple-interchange-newline">/, '' );\r
+                                       }\r
+\r
+                                       // Remove all other classes.\r
+                                       data = data.replace( /(<[^>]+) class="Apple-[^"]*"/gi, '$1' );\r
+                               }\r
+\r
+                               // Strip editable that was copied from inside. (http://dev.ckeditor.com/ticket/9534)\r
+                               if ( data.match( /^<[^<]+cke_(editable|contents)/i ) ) {\r
+                                       var tmp,\r
+                                               editable_wrapper,\r
+                                               wrapper = new CKEDITOR.dom.element( 'div' );\r
+\r
+                                       wrapper.setHtml( data );\r
+                                       // Verify for sure and check for nested editor UI parts. (http://dev.ckeditor.com/ticket/9675)\r
+                                       while ( wrapper.getChildCount() == 1 &&\r
+                                                       ( tmp = wrapper.getFirst() ) &&\r
+                                                       tmp.type == CKEDITOR.NODE_ELEMENT &&    // Make sure first-child is element.\r
+                                                       ( tmp.hasClass( 'cke_editable' ) || tmp.hasClass( 'cke_contents' ) ) ) {\r
+                                               wrapper = editable_wrapper = tmp;\r
+                                       }\r
+\r
+                                       // If editable wrapper was found strip it and bogus <br> (added on FF).\r
+                                       if ( editable_wrapper )\r
+                                               data = editable_wrapper.getHtml().replace( /<br>$/i, '' );\r
+                               }\r
+\r
+                               if ( CKEDITOR.env.ie ) {\r
+                                       // &nbsp; <p> -> <p> (br.cke-pasted-remove will be removed later)\r
+                                       data = data.replace( /^&nbsp;(?: |\r\n)?<(\w+)/g, function( match, elementName ) {\r
+                                               if ( elementName.toLowerCase() in blockElements ) {\r
+                                                       evt.data.preSniffing = 'html'; // Mark as not a text.\r
+                                                       return '<' + elementName;\r
+                                               }\r
+                                               return match;\r
+                                       } );\r
+                               } else if ( CKEDITOR.env.webkit ) {\r
+                                       // </p><div><br></div> -> </p><br>\r
+                                       // We don't mark br, because this situation can happen for htmlified text too.\r
+                                       data = data.replace( /<\/(\w+)><div><br><\/div>$/, function( match, elementName ) {\r
+                                               if ( elementName in blockElements ) {\r
+                                                       evt.data.endsWithEOL = 1;\r
+                                                       return '</' + elementName + '>';\r
+                                               }\r
+                                               return match;\r
+                                       } );\r
+                               } else if ( CKEDITOR.env.gecko ) {\r
+                                       // Firefox adds bogus <br> when user pasted text followed by space(s).\r
+                                       data = data.replace( /(\s)<br>$/, '$1' );\r
+                               }\r
+\r
+                               evt.data.dataValue = data;\r
+                       }, null, null, 3 );\r
+\r
+                       editor.on( 'paste', function( evt ) {\r
+                               var dataObj = evt.data,\r
+                                       type = editor._.nextPasteType || dataObj.type,\r
+                                       data = dataObj.dataValue,\r
+                                       trueType,\r
+                                       // Default is 'html'.\r
+                                       defaultType = editor.config.clipboard_defaultContentType || 'html',\r
+                                       transferType = dataObj.dataTransfer.getTransferType( editor );\r
+\r
+                               // If forced type is 'html' we don't need to know true data type.\r
+                               if ( type == 'html' || dataObj.preSniffing == 'html' ) {\r
+                                       trueType = 'html';\r
+                               } else {\r
+                                       trueType = recogniseContentType( data );\r
+                               }\r
+\r
+                               delete editor._.nextPasteType;\r
+\r
+                               // Unify text markup.\r
+                               if ( trueType == 'htmlifiedtext' ) {\r
+                                       data = htmlifiedTextHtmlification( editor.config, data );\r
+                               }\r
+\r
+                               // Strip presentational markup & unify text markup.\r
+                               // Forced plain text.\r
+                               // Note: we do not check dontFilter option in this case, because forcePAPT was implemented\r
+                               // before pasteFilter and pasteFilter is automatically used on Webkit&Blink since 4.5, so\r
+                               // forcePAPT should have priority as it had before 4.5.\r
+                               if ( type == 'text' && trueType == 'html' ) {\r
+                                       data = filterContent( editor, data, filtersFactory.get( 'plain-text' ) );\r
+                               }\r
+                               // External paste and pasteFilter exists and filtering isn't disabled.\r
+                               else if ( transferType == CKEDITOR.DATA_TRANSFER_EXTERNAL && editor.pasteFilter && !dataObj.dontFilter ) {\r
+                                       data = filterContent( editor, data, editor.pasteFilter );\r
+                               }\r
+\r
+                               if ( dataObj.startsWithEOL ) {\r
+                                       data = '<br data-cke-eol="1">' + data;\r
+                               }\r
+                               if ( dataObj.endsWithEOL ) {\r
+                                       data += '<br data-cke-eol="1">';\r
+                               }\r
+\r
+                               if ( type == 'auto' ) {\r
+                                       type = ( trueType == 'html' || defaultType == 'html' ) ? 'html' : 'text';\r
+                               }\r
+\r
+                               dataObj.type = type;\r
+                               dataObj.dataValue = data;\r
+                               delete dataObj.preSniffing;\r
+                               delete dataObj.startsWithEOL;\r
+                               delete dataObj.endsWithEOL;\r
+                       }, null, null, 6 );\r
+\r
+                       // Inserts processed data into the editor at the end of the\r
+                       // events chain.\r
+                       editor.on( 'paste', function( evt ) {\r
+                               var data = evt.data;\r
+\r
+                               if ( data.dataValue ) {\r
+                                       editor.insertHtml( data.dataValue, data.type, data.range );\r
+\r
+                                       // Defer 'afterPaste' so all other listeners for 'paste' will be fired first.\r
+                                       // Fire afterPaste only if paste inserted some HTML.\r
+                                       setTimeout( function() {\r
+                                               editor.fire( 'afterPaste' );\r
+                                       }, 0 );\r
+                               }\r
+                       }, null, null, 1000 );\r
+               }\r
+       } );\r
+\r
+       function firePasteEvents( editor, data, withBeforePaste ) {\r
+               if ( !data.type ) {\r
+                       data.type = 'auto';\r
+               }\r
+\r
+               if ( withBeforePaste ) {\r
+                       // Fire 'beforePaste' event so clipboard flavor get customized\r
+                       // by other plugins.\r
+                       if ( editor.fire( 'beforePaste', data ) === false )\r
+                               return false; // Event canceled\r
+               }\r
+\r
+               // Do not fire paste if there is no data (dataValue and dataTranfser are empty).\r
+               // This check should be done after firing 'beforePaste' because for native paste\r
+               // 'beforePaste' is by default fired even for empty clipboard.\r
+               if ( !data.dataValue && data.dataTransfer.isEmpty() ) {\r
+                       return false;\r
+               }\r
+\r
+               if ( !data.dataValue ) {\r
+                       data.dataValue = '';\r
+               }\r
+\r
+               // Because of FF bug we need to use this hack, otherwise cursor is hidden\r
+               // or it is not possible to move it (http://dev.ckeditor.com/ticket/12420).\r
+               // Also, check that editor.toolbox exists, because the toolbar plugin might not be loaded (http://dev.ckeditor.com/ticket/13305).\r
+               if ( CKEDITOR.env.gecko && data.method == 'drop' && editor.toolbox ) {\r
+                       editor.once( 'afterPaste', function() {\r
+                               editor.toolbox.focus();\r
+                       } );\r
+               }\r
+\r
+               return editor.fire( 'paste', data );\r
+       }\r
+\r
+       function initPasteClipboard( editor ) {\r
+               var clipboard = CKEDITOR.plugins.clipboard,\r
+                       preventBeforePasteEvent = 0,\r
+                       preventPasteEvent = 0,\r
+                       inReadOnly = 0;\r
+\r
+               addListeners();\r
+               addButtonsCommands();\r
+\r
+               /**\r
+                * Gets clipboard data by directly accessing the clipboard (IE only).\r
+                *\r
+                *              editor.getClipboardData( function( data ) {\r
+                *                      if ( data )\r
+                *                              alert( data.type + ' ' + data.dataValue );\r
+                *              } );\r
+                *\r
+                * @member CKEDITOR.editor\r
+                * @param {Function/Object} callbackOrOptions For function, see the `callback` parameter documentation. The object was used before 4.7.0 with the `title` property, to set the paste dialog's title.\r
+                * @param {Function} callback A function that will be executed with the `data` property of the\r
+                * {@link CKEDITOR.editor#event-paste paste event} or `null` if none of the capturing methods succeeded.\r
+                * Since 4.7.0 the `callback` should be provided as a first argument, just like in the example above. This parameter will be removed in\r
+                * an upcoming major release.\r
+                */\r
+               editor.getClipboardData = function( callbackOrOptions, callback ) {\r
+                       // Options are optional - args shift.\r
+                       if ( !callback ) {\r
+                               callback = callbackOrOptions;\r
+                               callbackOrOptions = null;\r
+                       }\r
+\r
+                       // Listen with maximum priority to handle content before everyone else.\r
+                       // This callback will handle paste event that will be fired if direct\r
+                       // access to the clipboard succeed in IE.\r
+                       editor.on( 'paste', onPaste, null, null, 0 );\r
+\r
+                       // If command didn't succeed (only IE allows to access clipboard and only if\r
+                       // user agrees) invoke callback with null, meaning that paste is not blocked.\r
+                       if ( getClipboardDataDirectly() === false ) {\r
+                               // Direct access to the clipboard wasn't successful so remove listener.\r
+                               editor.removeListener( 'paste', onPaste );\r
+\r
+                               callback( null );\r
+                       }\r
+\r
+                       function onPaste( evt ) {\r
+                               evt.removeListener();\r
+                               evt.cancel();\r
+                               callback( evt.data );\r
+                       }\r
+               };\r
+\r
+               function addButtonsCommands() {\r
+                       addButtonCommand( 'Cut', 'cut', createCutCopyCmd( 'cut' ), 10, 1 );\r
+                       addButtonCommand( 'Copy', 'copy', createCutCopyCmd( 'copy' ), 20, 4 );\r
+                       addButtonCommand( 'Paste', 'paste', createPasteCmd(), 30, 8 );\r
+\r
+                       function addButtonCommand( buttonName, commandName, command, toolbarOrder, ctxMenuOrder ) {\r
+                               var lang = editor.lang.clipboard[ commandName ];\r
+\r
+                               editor.addCommand( commandName, command );\r
+                               editor.ui.addButton && editor.ui.addButton( buttonName, {\r
+                                       label: lang,\r
+                                       command: commandName,\r
+                                       toolbar: 'clipboard,' + toolbarOrder\r
+                               } );\r
+\r
+                               // If the "menu" plugin is loaded, register the menu item.\r
+                               if ( editor.addMenuItems ) {\r
+                                       editor.addMenuItem( commandName, {\r
+                                               label: lang,\r
+                                               command: commandName,\r
+                                               group: 'clipboard',\r
+                                               order: ctxMenuOrder\r
+                                       } );\r
+                               }\r
+                       }\r
+               }\r
+\r
+               function addListeners() {\r
+                       editor.on( 'key', onKey );\r
+                       editor.on( 'contentDom', addPasteListenersToEditable );\r
+\r
+                       // For improved performance, we're checking the readOnly state on selectionChange instead of hooking a key event for that.\r
+                       editor.on( 'selectionChange', function( evt ) {\r
+                               inReadOnly = evt.data.selection.getRanges()[ 0 ].checkReadOnly();\r
+                               setToolbarStates();\r
+                       } );\r
+\r
+                       // If the "contextmenu" plugin is loaded, register the listeners.\r
+                       if ( editor.contextMenu ) {\r
+                               editor.contextMenu.addListener( function( element, selection ) {\r
+                                       inReadOnly = selection.getRanges()[ 0 ].checkReadOnly();\r
+                                       return {\r
+                                               cut: stateFromNamedCommand( 'cut' ),\r
+                                               copy: stateFromNamedCommand( 'copy' ),\r
+                                               paste: stateFromNamedCommand( 'paste' )\r
+                                       };\r
+                               } );\r
+                       }\r
+               }\r
+\r
+               // Add events listeners to editable.\r
+               function addPasteListenersToEditable() {\r
+                       var editable = editor.editable();\r
+\r
+                       if ( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) {\r
+                               var initOnCopyCut = function( evt ) {\r
+                                       // If user tries to cut in read-only editor, we must prevent default action. (http://dev.ckeditor.com/ticket/13872)\r
+                                       if ( !editor.readOnly || evt.name != 'cut' ) {\r
+                                               clipboard.initPasteDataTransfer( evt, editor );\r
+                                       }\r
+                                       evt.data.preventDefault();\r
+                               };\r
+\r
+                               editable.on( 'copy', initOnCopyCut );\r
+                               editable.on( 'cut', initOnCopyCut );\r
+\r
+                               // Delete content with the low priority so one can overwrite cut data.\r
+                               editable.on( 'cut', function() {\r
+                                       // If user tries to cut in read-only editor, we must prevent default action. (http://dev.ckeditor.com/ticket/13872)\r
+                                       if ( !editor.readOnly ) {\r
+                                               editor.extractSelectedHtml();\r
+                                       }\r
+                               }, null, null, 999 );\r
+                       }\r
+\r
+                       // We'll be catching all pasted content in one line, regardless of whether\r
+                       // it's introduced by a document command execution (e.g. toolbar buttons) or\r
+                       // user paste behaviors (e.g. CTRL+V).\r
+                       editable.on( clipboard.mainPasteEvent, function( evt ) {\r
+                               if ( clipboard.mainPasteEvent == 'beforepaste' && preventBeforePasteEvent ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // If you've just asked yourself why preventPasteEventNow() is not here, but\r
+                               // in listener for CTRL+V and exec method of 'paste' command\r
+                               // you've asked the same question we did.\r
+                               //\r
+                               // THE ANSWER:\r
+                               //\r
+                               // First thing to notice - this answer makes sense only for IE,\r
+                               // because other browsers don't listen for 'paste' event.\r
+                               //\r
+                               // What would happen if we move preventPasteEventNow() here?\r
+                               // For:\r
+                               // * CTRL+V - IE fires 'beforepaste', so we prevent 'paste' and pasteDataFromClipboard(). OK.\r
+                               // * editor.execCommand( 'paste' ) - we fire 'beforepaste', so we prevent\r
+                               //              'paste' and pasteDataFromClipboard() and doc.execCommand( 'Paste' ). OK.\r
+                               // * native context menu - IE fires 'beforepaste', so we prevent 'paste', but unfortunately\r
+                               //              on IE we fail with pasteDataFromClipboard() here, because of... we don't know why, but\r
+                               //              we just fail, so... we paste nothing. FAIL.\r
+                               // * native menu bar - the same as for native context menu.\r
+                               //\r
+                               // But don't you know any way to distinguish first two cases from last two?\r
+                               // Only one - special flag set in CTRL+V handler and exec method of 'paste'\r
+                               // command. And that's what we did using preventPasteEventNow().\r
+\r
+                               pasteDataFromClipboard( evt );\r
+                       } );\r
+\r
+                       // It's not possible to clearly handle all four paste methods (ctrl+v, native menu bar\r
+                       // native context menu, editor's command) in one 'paste/beforepaste' event in IE.\r
+                       //\r
+                       // For ctrl+v & editor's command it's easy to handle pasting in 'beforepaste' listener,\r
+                       // so we do this. For another two methods it's better to use 'paste' event.\r
+                       //\r
+                       // 'paste' is always being fired after 'beforepaste' (except of weird one on opening native\r
+                       // context menu), so for two methods handled in 'beforepaste' we're canceling 'paste'\r
+                       // using preventPasteEvent state.\r
+                       //\r
+                       // 'paste' event in IE is being fired before getClipboardDataByPastebin executes its callback.\r
+                       //\r
+                       // QUESTION: Why didn't you handle all 4 paste methods in handler for 'paste'?\r
+                       //              Wouldn't this just be simpler?\r
+                       // ANSWER: Then we would have to evt.data.preventDefault() only for native\r
+                       //              context menu and menu bar pastes. The same with execIECommand().\r
+                       //              That would force us to mark CTRL+V and editor's paste command with\r
+                       //              special flag, other than preventPasteEvent. But we still would have to\r
+                       //              have preventPasteEvent for the second event fired by execIECommand.\r
+                       //              Code would be longer and not cleaner.\r
+                       if ( clipboard.mainPasteEvent == 'beforepaste' ) {\r
+                               editable.on( 'paste', function( evt ) {\r
+                                       if ( preventPasteEvent ) {\r
+                                               return;\r
+                                       }\r
+\r
+                                       // Cancel next 'paste' event fired by execIECommand( 'paste' )\r
+                                       // at the end of this callback.\r
+                                       preventPasteEventNow();\r
+\r
+                                       // Prevent native paste.\r
+                                       evt.data.preventDefault();\r
+\r
+                                       pasteDataFromClipboard( evt );\r
+\r
+                                       // Force IE to paste content into pastebin so pasteDataFromClipboard will work.\r
+                                       execIECommand( 'paste' );\r
+                               } );\r
+\r
+                               // If mainPasteEvent is 'beforePaste' (IE before Edge),\r
+                               // dismiss the (wrong) 'beforepaste' event fired on context/toolbar menu open. (http://dev.ckeditor.com/ticket/7953)\r
+                               editable.on( 'contextmenu', preventBeforePasteEventNow, null, null, 0 );\r
+\r
+                               editable.on( 'beforepaste', function( evt ) {\r
+                                       // Do not prevent event on CTRL+V and SHIFT+INS because it blocks paste (http://dev.ckeditor.com/ticket/11970).\r
+                                       if ( evt.data && !evt.data.$.ctrlKey && !evt.data.$.shiftKey )\r
+                                               preventBeforePasteEventNow();\r
+                               }, null, null, 0 );\r
+                       }\r
+\r
+                       editable.on( 'beforecut', function() {\r
+                               !preventBeforePasteEvent && fixCut( editor );\r
+                       } );\r
+\r
+                       var mouseupTimeout;\r
+\r
+                       // Use editor.document instead of editable in non-IEs for observing mouseup\r
+                       // since editable won't fire the event if selection process started within\r
+                       // iframe and ended out of the editor (http://dev.ckeditor.com/ticket/9851).\r
+                       editable.attachListener( CKEDITOR.env.ie ? editable : editor.document.getDocumentElement(), 'mouseup', function() {\r
+                               mouseupTimeout = setTimeout( function() {\r
+                                       setToolbarStates();\r
+                               }, 0 );\r
+                       } );\r
+\r
+                       // Make sure that deferred mouseup callback isn't executed after editor instance\r
+                       // had been destroyed. This may happen when editor.destroy() is called in parallel\r
+                       // with mouseup event (i.e. a button with onclick callback) (http://dev.ckeditor.com/ticket/10219).\r
+                       editor.on( 'destroy', function() {\r
+                               clearTimeout( mouseupTimeout );\r
+                       } );\r
+\r
+                       editable.on( 'keyup', setToolbarStates );\r
+               }\r
+\r
+               // Create object representing Cut or Copy commands.\r
+               function createCutCopyCmd( type ) {\r
+                       return {\r
+                               type: type,\r
+                               canUndo: type == 'cut', // We can't undo copy to clipboard.\r
+                               startDisabled: true,\r
+                               fakeKeystroke: type == 'cut' ? CKEDITOR.CTRL + 88 /*X*/ :  CKEDITOR.CTRL + 67 /*C*/,\r
+                               exec: function() {\r
+                                       // Attempts to execute the Cut and Copy operations.\r
+                                       function tryToCutCopy( type ) {\r
+                                               if ( CKEDITOR.env.ie )\r
+                                                       return execIECommand( type );\r
+\r
+                                               // non-IEs part\r
+                                               try {\r
+                                                       // Other browsers throw an error if the command is disabled.\r
+                                                       return editor.document.$.execCommand( type, false, null );\r
+                                               } catch ( e ) {\r
+                                                       return false;\r
+                                               }\r
+                                       }\r
+\r
+                                       this.type == 'cut' && fixCut();\r
+\r
+                                       var success = tryToCutCopy( this.type );\r
+\r
+                                       if ( !success ) {\r
+                                               // Show cutError or copyError.\r
+                                               editor.showNotification( editor.lang.clipboard[ this.type + 'Error' ] ); // jshint ignore:line\r
+                                       }\r
+\r
+                                       return success;\r
+                               }\r
+                       };\r
+               }\r
+\r
+               function createPasteCmd() {\r
+                       return {\r
+                               // Snapshots are done manually by editable.insertXXX methods.\r
+                               canUndo: false,\r
+                               async: true,\r
+                               fakeKeystroke: CKEDITOR.CTRL + 86 /*V*/,\r
+\r
+                               /**\r
+                                * The default implementation of the paste command.\r
+                                *\r
+                                * @private\r
+                                * @param {CKEDITOR.editor} editor An instance of the editor where the command is being executed.\r
+                                * @param {Object/String} data If `data` is a string, then it is considered content that is being pasted.\r
+                                * Otherwise it is treated as an object with options.\r
+                                * @param {Boolean/String} [data.notification=true] Content for a notification shown after an unsuccessful\r
+                                * paste attempt. If `false`, the notification will not be displayed. This parameter was added in 4.7.0.\r
+                                * @param {String} [data.type='html'] The type of pasted content. There are two allowed values:\r
+                                * * 'html'\r
+                                * * 'text'\r
+                                * @param {String/Object} data.dataValue Content being pasted. If this parameter is an object, it\r
+                                * is supposed to be a `data` property of the {@link CKEDITOR.editor#paste} event.\r
+                                * @param {CKEDITOR.plugins.clipboard.dataTransfer} data.dataTransfer Data transfer instance connected\r
+                                * with the current paste action.\r
+                                * @member CKEDITOR.editor.commands.paste\r
+                                */\r
+                               exec: function( editor, data ) {\r
+                                       data = typeof data !== 'undefined' && data !== null ? data : {};\r
+\r
+                                       var cmd = this,\r
+                                               notification = typeof data.notification !== 'undefined' ? data.notification : true,\r
+                                               forcedType = data.type,\r
+                                               keystroke = CKEDITOR.tools.keystrokeToString( editor.lang.common.keyboard,\r
+                                                       editor.getCommandKeystroke( this ) ),\r
+                                               msg = typeof notification === 'string' ? notification : editor.lang.clipboard.pasteNotification\r
+                                                       .replace( /%1/, '<kbd aria-label="' + keystroke.aria + '">' + keystroke.display + '</kbd>' ),\r
+                                               pastedContent = typeof data === 'string' ? data : data.dataValue;\r
+\r
+                                       function callback( data, withBeforePaste ) {\r
+                                               withBeforePaste = typeof withBeforePaste !== 'undefined' ? withBeforePaste : true;\r
+\r
+                                               if ( data ) {\r
+                                                       data.method = 'paste';\r
+\r
+                                                       if ( !data.dataTransfer ) {\r
+                                                               data.dataTransfer = clipboard.initPasteDataTransfer();\r
+                                                       }\r
+\r
+                                                       firePasteEvents( editor, data, withBeforePaste );\r
+                                               } else if ( notification ) {\r
+                                                       editor.showNotification( msg, 'info', editor.config.clipboard_notificationDuration );\r
+                                               }\r
+\r
+                                               editor.fire( 'afterCommandExec', {\r
+                                                       name: 'paste',\r
+                                                       command: cmd,\r
+                                                       returnValue: !!data\r
+                                               } );\r
+                                       }\r
+\r
+                                       // Force type for the next paste.\r
+                                       if ( forcedType ) {\r
+                                               editor._.nextPasteType = forcedType;\r
+                                       } else {\r
+                                               delete editor._.nextPasteType;\r
+                                       }\r
+\r
+                                       if ( typeof pastedContent === 'string' ) {\r
+                                               callback( {\r
+                                                       dataValue: pastedContent\r
+                                               } );\r
+                                       } else {\r
+                                               editor.getClipboardData( callback );\r
+                                       }\r
+                               }\r
+                       };\r
+               }\r
+\r
+               function preventPasteEventNow() {\r
+                       preventPasteEvent = 1;\r
+                       // For safety reason we should wait longer than 0/1ms.\r
+                       // We don't know how long execution of quite complex getClipboardData will take\r
+                       // and in for example 'paste' listener execCommand() (which fires 'paste') is called\r
+                       // after getClipboardData finishes.\r
+                       // Luckily, it's impossible to immediately fire another 'paste' event we want to handle,\r
+                       // because we only handle there native context menu and menu bar.\r
+                       setTimeout( function() {\r
+                               preventPasteEvent = 0;\r
+                       }, 100 );\r
+               }\r
+\r
+               function preventBeforePasteEventNow() {\r
+                       preventBeforePasteEvent = 1;\r
+                       setTimeout( function() {\r
+                               preventBeforePasteEvent = 0;\r
+                       }, 10 );\r
+               }\r
+\r
+               // Tries to execute any of the paste, cut or copy commands in IE. Returns a\r
+               // boolean indicating that the operation succeeded.\r
+               // @param {String} command *LOWER CASED* name of command ('paste', 'cut', 'copy').\r
+               function execIECommand( command ) {\r
+                       var doc = editor.document,\r
+                               body = doc.getBody(),\r
+                               enabled = false,\r
+                               onExec = function() {\r
+                                       enabled = true;\r
+                               };\r
+\r
+                       // The following seems to be the only reliable way to detect that\r
+                       // clipboard commands are enabled in IE. It will fire the\r
+                       // onpaste/oncut/oncopy events only if the security settings allowed\r
+                       // the command to execute.\r
+                       body.on( command, onExec );\r
+\r
+                       // IE7: document.execCommand has problem to paste into positioned element.\r
+                       if ( CKEDITOR.env.version > 7 ) {\r
+                               doc.$.execCommand( command );\r
+                       } else {\r
+                               doc.$.selection.createRange().execCommand( command );\r
+                       }\r
+\r
+                       body.removeListener( command, onExec );\r
+\r
+                       return enabled;\r
+               }\r
+\r
+               // Cutting off control type element in IE standards breaks the selection entirely. (http://dev.ckeditor.com/ticket/4881)\r
+               function fixCut() {\r
+                       if ( !CKEDITOR.env.ie || CKEDITOR.env.quirks )\r
+                               return;\r
+\r
+                       var sel = editor.getSelection(),\r
+                               control, range, dummy;\r
+\r
+                       if ( ( sel.getType() == CKEDITOR.SELECTION_ELEMENT ) && ( control = sel.getSelectedElement() ) ) {\r
+                               range = sel.getRanges()[ 0 ];\r
+                               dummy = editor.document.createText( '' );\r
+                               dummy.insertBefore( control );\r
+                               range.setStartBefore( dummy );\r
+                               range.setEndAfter( control );\r
+                               sel.selectRanges( [ range ] );\r
+\r
+                               // Clear up the fix if the paste wasn't succeeded.\r
+                               setTimeout( function() {\r
+                                       // Element still online?\r
+                                       if ( control.getParent() ) {\r
+                                               dummy.remove();\r
+                                               sel.selectElement( control );\r
+                                       }\r
+                               }, 0 );\r
+                       }\r
+               }\r
+\r
+               // Allow to peek clipboard content by redirecting the\r
+               // pasting content into a temporary bin and grab the content of it.\r
+               function getClipboardDataByPastebin( evt, callback ) {\r
+                       var doc = editor.document,\r
+                               editable = editor.editable(),\r
+                               cancel = function( evt ) {\r
+                                       evt.cancel();\r
+                               },\r
+                               blurListener;\r
+\r
+                       // Avoid recursions on 'paste' event or consequent paste too fast. (http://dev.ckeditor.com/ticket/5730)\r
+                       if ( doc.getById( 'cke_pastebin' ) )\r
+                               return;\r
+\r
+                       var sel = editor.getSelection();\r
+                       var bms = sel.createBookmarks();\r
+\r
+                       // http://dev.ckeditor.com/ticket/11384. On IE9+ we use native selectionchange (i.e. editor#selectionCheck) to cache the most\r
+                       // recent selection which we then lock on editable blur. See selection.js for more info.\r
+                       // selectionchange fired before getClipboardDataByPastebin() cached selection\r
+                       // before creating bookmark (cached selection will be invalid, because bookmarks modified the DOM),\r
+                       // so we need to fire selectionchange one more time, to store current seleciton.\r
+                       // Selection will be locked when we focus pastebin.\r
+                       if ( CKEDITOR.env.ie )\r
+                               sel.root.fire( 'selectionchange' );\r
+\r
+                       // Create container to paste into.\r
+                       // For rich content we prefer to use "body" since it holds\r
+                       // the least possibility to be splitted by pasted content, while this may\r
+                       // breaks the text selection on a frame-less editable, "div" would be\r
+                       // the best one in that case.\r
+                       // In another case on old IEs moving the selection into a "body" paste bin causes error panic.\r
+                       // Body can't be also used for Opera which fills it with <br>\r
+                       // what is indistinguishable from pasted <br> (copying <br> in Opera isn't possible,\r
+                       // but it can be copied from other browser).\r
+                       var pastebin = new CKEDITOR.dom.element(\r
+                               ( CKEDITOR.env.webkit || editable.is( 'body' ) ) && !CKEDITOR.env.ie ? 'body' : 'div', doc );\r
+\r
+                       pastebin.setAttributes( {\r
+                               id: 'cke_pastebin',\r
+                               'data-cke-temp': '1'\r
+                       } );\r
+\r
+                       var containerOffset = 0,\r
+                               offsetParent,\r
+                               win = doc.getWindow();\r
+\r
+                       if ( CKEDITOR.env.webkit ) {\r
+                               // It's better to paste close to the real paste destination, so inherited styles\r
+                               // (which Webkits will try to compensate by styling span) differs less from the destination's one.\r
+                               editable.append( pastebin );\r
+                               // Style pastebin like .cke_editable, to minimize differences between origin and destination. (http://dev.ckeditor.com/ticket/9754)\r
+                               pastebin.addClass( 'cke_editable' );\r
+\r
+                               // Compensate position of offsetParent.\r
+                               if ( !editable.is( 'body' ) ) {\r
+                                       // We're not able to get offsetParent from pastebin (body element), so check whether\r
+                                       // its parent (editable) is positioned.\r
+                                       if ( editable.getComputedStyle( 'position' ) != 'static' )\r
+                                               offsetParent = editable;\r
+                                       // And if not - safely get offsetParent from editable.\r
+                                       else\r
+                                               offsetParent = CKEDITOR.dom.element.get( editable.$.offsetParent );\r
+\r
+                                       containerOffset = offsetParent.getDocumentPosition().y;\r
+                               }\r
+                       } else {\r
+                               // Opera and IE doesn't allow to append to html element.\r
+                               editable.getAscendant( CKEDITOR.env.ie ? 'body' : 'html', 1 ).append( pastebin );\r
+                       }\r
+\r
+                       pastebin.setStyles( {\r
+                               position: 'absolute',\r
+                               // Position the bin at the top (+10 for safety) of viewport to avoid any subsequent document scroll.\r
+                               top: ( win.getScrollPosition().y - containerOffset + 10 ) + 'px',\r
+                               width: '1px',\r
+                               // Caret has to fit in that height, otherwise browsers like Chrome & Opera will scroll window to show it.\r
+                               // Set height equal to viewport's height - 20px (safety gaps), minimum 1px.\r
+                               height: Math.max( 1, win.getViewPaneSize().height - 20 ) + 'px',\r
+                               overflow: 'hidden',\r
+                               // Reset styles that can mess up pastebin position.\r
+                               margin: 0,\r
+                               padding: 0\r
+                       } );\r
+\r
+                       // Paste fails in Safari when the body tag has 'user-select: none'. (http://dev.ckeditor.com/ticket/12506)\r
+                       if ( CKEDITOR.env.safari )\r
+                               pastebin.setStyles( CKEDITOR.tools.cssVendorPrefix( 'user-select', 'text' ) );\r
+\r
+                       // Check if the paste bin now establishes new editing host.\r
+                       var isEditingHost = pastebin.getParent().isReadOnly();\r
+\r
+                       if ( isEditingHost ) {\r
+                               // Hide the paste bin.\r
+                               pastebin.setOpacity( 0 );\r
+                               // And make it editable.\r
+                               pastebin.setAttribute( 'contenteditable', true );\r
+                       }\r
+                       // Transparency is not enough since positioned non-editing host always shows\r
+                       // resize handler, pull it off the screen instead.\r
+                       else {\r
+                               pastebin.setStyle( editor.config.contentsLangDirection == 'ltr' ? 'left' : 'right', '-10000px' );\r
+                       }\r
+\r
+                       editor.on( 'selectionChange', cancel, null, null, 0 );\r
+\r
+                       // Webkit fill fire blur on editable when moving selection to\r
+                       // pastebin (if body is used). Cancel it because it causes incorrect\r
+                       // selection lock in case of inline editor (http://dev.ckeditor.com/ticket/10644).\r
+                       // The same seems to apply to Firefox (http://dev.ckeditor.com/ticket/10787).\r
+                       if ( CKEDITOR.env.webkit || CKEDITOR.env.gecko )\r
+                               blurListener = editable.once( 'blur', cancel, null, null, -100 );\r
+\r
+                       // Temporarily move selection to the pastebin.\r
+                       isEditingHost && pastebin.focus();\r
+                       var range = new CKEDITOR.dom.range( pastebin );\r
+                       range.selectNodeContents( pastebin );\r
+                       var selPastebin = range.select();\r
+\r
+                       // If non-native paste is executed, IE will open security alert and blur editable.\r
+                       // Editable will then lock selection inside itself and after accepting security alert\r
+                       // this selection will be restored. We overwrite stored selection, so it's restored\r
+                       // in pastebin. (http://dev.ckeditor.com/ticket/9552)\r
+                       if ( CKEDITOR.env.ie ) {\r
+                               blurListener = editable.once( 'blur', function() {\r
+                                       editor.lockSelection( selPastebin );\r
+                               } );\r
+                       }\r
+\r
+                       var scrollTop = CKEDITOR.document.getWindow().getScrollPosition().y;\r
+\r
+                       // Wait a while and grab the pasted contents.\r
+                       setTimeout( function() {\r
+                               // Restore main window's scroll position which could have been changed\r
+                               // by browser in cases described in http://dev.ckeditor.com/ticket/9771.\r
+                               if ( CKEDITOR.env.webkit )\r
+                                       CKEDITOR.document.getBody().$.scrollTop = scrollTop;\r
+\r
+                               // Blur will be fired only on non-native paste. In other case manually remove listener.\r
+                               blurListener && blurListener.removeListener();\r
+\r
+                               // Restore properly the document focus. (http://dev.ckeditor.com/ticket/8849)\r
+                               if ( CKEDITOR.env.ie )\r
+                                       editable.focus();\r
+\r
+                               // IE7: selection must go before removing pastebin. (http://dev.ckeditor.com/ticket/8691)\r
+                               sel.selectBookmarks( bms );\r
+                               pastebin.remove();\r
+\r
+                               // Grab the HTML contents.\r
+                               // We need to look for a apple style wrapper on webkit it also adds\r
+                               // a div wrapper if you copy/paste the body of the editor.\r
+                               // Remove hidden div and restore selection.\r
+                               var bogusSpan;\r
+                               if ( CKEDITOR.env.webkit && ( bogusSpan = pastebin.getFirst() ) && ( bogusSpan.is && bogusSpan.hasClass( 'Apple-style-span' ) ) )\r
+                                       pastebin = bogusSpan;\r
+\r
+                               editor.removeListener( 'selectionChange', cancel );\r
+                               callback( pastebin.getHtml() );\r
+                       }, 0 );\r
+               }\r
+\r
+               // Try to get content directly on IE from clipboard, without native event\r
+               // being fired before. In other words - synthetically get clipboard data, if it's possible.\r
+               // mainPasteEvent will be fired, so if forced native paste:\r
+               // * worked, getClipboardDataByPastebin will grab it,\r
+               // * didn't work, dataValue and dataTransfer will be empty and editor#paste won't be fired.\r
+               // Clipboard data can be accessed directly only on IEs older than Edge.\r
+               // On other browsers we should fire beforePaste event and return false.\r
+               function getClipboardDataDirectly() {\r
+                       if ( clipboard.mainPasteEvent == 'paste' ) {\r
+                               editor.fire( 'beforePaste', { type: 'auto', method: 'paste' } );\r
+                               return false;\r
+                       }\r
+\r
+                       // Prevent IE from pasting at the begining of the document.\r
+                       editor.focus();\r
+\r
+                       // Command will be handled by 'beforepaste', but as\r
+                       // execIECommand( 'paste' ) will fire also 'paste' event\r
+                       // we're canceling it.\r
+                       preventPasteEventNow();\r
+\r
+                       // http://dev.ckeditor.com/ticket/9247: Lock focus to prevent IE from hiding toolbar for inline editor.\r
+                       var focusManager = editor.focusManager;\r
+                       focusManager.lock();\r
+\r
+                       if ( editor.editable().fire( clipboard.mainPasteEvent ) && !execIECommand( 'paste' ) ) {\r
+                               focusManager.unlock();\r
+                               return false;\r
+                       }\r
+                       focusManager.unlock();\r
+\r
+                       return true;\r
+               }\r
+\r
+               // Listens for some clipboard related keystrokes, so they get customized.\r
+               // Needs to be bind to keydown event.\r
+               function onKey( event ) {\r
+                       if ( editor.mode != 'wysiwyg' )\r
+                               return;\r
+\r
+                       switch ( event.data.keyCode ) {\r
+                               // Paste\r
+                               case CKEDITOR.CTRL + 86: // CTRL+V\r
+                               case CKEDITOR.SHIFT + 45: // SHIFT+INS\r
+                                       var editable = editor.editable();\r
+\r
+                                       // Cancel 'paste' event because ctrl+v is for IE handled\r
+                                       // by 'beforepaste'.\r
+                                       preventPasteEventNow();\r
+\r
+                                       // Simulate 'beforepaste' event for all browsers using 'paste' as main event.\r
+                                       if ( clipboard.mainPasteEvent == 'paste' ) {\r
+                                               editable.fire( 'beforepaste' );\r
+                                       }\r
+\r
+                                       return;\r
+\r
+                                       // Cut\r
+                               case CKEDITOR.CTRL + 88: // CTRL+X\r
+                               case CKEDITOR.SHIFT + 46: // SHIFT+DEL\r
+                                       // Save Undo snapshot.\r
+                                       editor.fire( 'saveSnapshot' ); // Save before cut\r
+                                       setTimeout( function() {\r
+                                               editor.fire( 'saveSnapshot' ); // Save after cut\r
+                                       }, 50 ); // OSX is slow (http://dev.ckeditor.com/ticket/11416).\r
+                       }\r
+               }\r
+\r
+               function pasteDataFromClipboard( evt ) {\r
+                       // Default type is 'auto', but can be changed by beforePaste listeners.\r
+                       var eventData = {\r
+                                       type: 'auto',\r
+                                       method: 'paste',\r
+                                       dataTransfer: clipboard.initPasteDataTransfer( evt )\r
+                               };\r
+\r
+                       eventData.dataTransfer.cacheData();\r
+\r
+                       // Fire 'beforePaste' event so clipboard flavor get customized by other plugins.\r
+                       // If 'beforePaste' is canceled continue executing getClipboardDataByPastebin and then do nothing\r
+                       // (do not fire 'paste', 'afterPaste' events). This way we can grab all - synthetically\r
+                       // and natively pasted content and prevent its insertion into editor\r
+                       // after canceling 'beforePaste' event.\r
+                       var beforePasteNotCanceled = editor.fire( 'beforePaste', eventData ) !== false;\r
+\r
+                       // Do not use paste bin if the browser let us get HTML or files from dataTranfer.\r
+                       if ( beforePasteNotCanceled && clipboard.canClipboardApiBeTrusted( eventData.dataTransfer, editor ) ) {\r
+                               evt.data.preventDefault();\r
+                               setTimeout( function() {\r
+                                       firePasteEvents( editor, eventData );\r
+                               }, 0 );\r
+                       } else {\r
+                               getClipboardDataByPastebin( evt, function( data ) {\r
+                                       // Clean up.\r
+                                       eventData.dataValue = data.replace( /<span[^>]+data-cke-bookmark[^<]*?<\/span>/ig, '' );\r
+\r
+                                       // Fire remaining events (without beforePaste)\r
+                                       beforePasteNotCanceled && firePasteEvents( editor, eventData );\r
+                               } );\r
+                       }\r
+               }\r
+\r
+               function setToolbarStates() {\r
+                       if ( editor.mode != 'wysiwyg' )\r
+                               return;\r
+\r
+                       var pasteState = stateFromNamedCommand( 'paste' );\r
+\r
+                       editor.getCommand( 'cut' ).setState( stateFromNamedCommand( 'cut' ) );\r
+                       editor.getCommand( 'copy' ).setState( stateFromNamedCommand( 'copy' ) );\r
+                       editor.getCommand( 'paste' ).setState( pasteState );\r
+                       editor.fire( 'pasteState', pasteState );\r
+               }\r
+\r
+               function stateFromNamedCommand( command ) {\r
+                       if ( inReadOnly && command in { paste: 1, cut: 1 } )\r
+                               return CKEDITOR.TRISTATE_DISABLED;\r
+\r
+                       if ( command == 'paste' )\r
+                               return CKEDITOR.TRISTATE_OFF;\r
+\r
+                       // Cut, copy - check if the selection is not empty.\r
+                       var sel = editor.getSelection(),\r
+                               ranges = sel.getRanges(),\r
+                               selectionIsEmpty = sel.getType() == CKEDITOR.SELECTION_NONE || ( ranges.length == 1 && ranges[ 0 ].collapsed );\r
+\r
+                       return selectionIsEmpty ? CKEDITOR.TRISTATE_DISABLED : CKEDITOR.TRISTATE_OFF;\r
+               }\r
+       }\r
+\r
+       // Returns:\r
+       // * 'htmlifiedtext' if content looks like transformed by browser from plain text.\r
+       //              See clipboard/paste.html TCs for more info.\r
+       // * 'html' if it is not 'htmlifiedtext'.\r
+       function recogniseContentType( data ) {\r
+               if ( CKEDITOR.env.webkit ) {\r
+                       // Plain text or ( <div><br></div> and text inside <div> ).\r
+                       if ( !data.match( /^[^<]*$/g ) && !data.match( /^(<div><br( ?\/)?><\/div>|<div>[^<]*<\/div>)*$/gi ) )\r
+                               return 'html';\r
+               } else if ( CKEDITOR.env.ie ) {\r
+                       // Text and <br> or ( text and <br> in <p> - paragraphs can be separated by new \r\n ).\r
+                       if ( !data.match( /^([^<]|<br( ?\/)?>)*$/gi ) && !data.match( /^(<p>([^<]|<br( ?\/)?>)*<\/p>|(\r\n))*$/gi ) )\r
+                               return 'html';\r
+               } else if ( CKEDITOR.env.gecko ) {\r
+                       // Text or <br>.\r
+                       if ( !data.match( /^([^<]|<br( ?\/)?>)*$/gi ) )\r
+                               return 'html';\r
+               } else {\r
+                       return 'html';\r
+               }\r
+\r
+               return 'htmlifiedtext';\r
+       }\r
+\r
+       // This function transforms what browsers produce when\r
+       // pasting plain text into editable element (see clipboard/paste.html TCs\r
+       // for more info) into correct HTML (similar to that produced by text2Html).\r
+       function htmlifiedTextHtmlification( config, data ) {\r
+               function repeatParagraphs( repeats ) {\r
+                       // Repeat blocks floor((n+1)/2) times.\r
+                       // Even number of repeats - add <br> at the beginning of last <p>.\r
+                       return CKEDITOR.tools.repeat( '</p><p>', ~~( repeats / 2 ) ) + ( repeats % 2 == 1 ? '<br>' : '' );\r
+               }\r
+\r
+                       // Replace adjacent white-spaces (EOLs too - Fx sometimes keeps them) with one space.\r
+               data = data.replace( /\s+/g, ' ' )\r
+                       // Remove spaces from between tags.\r
+                       .replace( /> +</g, '><' )\r
+                       // Normalize XHTML syntax and upper cased <br> tags.\r
+                       .replace( /<br ?\/>/gi, '<br>' );\r
+\r
+               // IE - lower cased tags.\r
+               data = data.replace( /<\/?[A-Z]+>/g, function( match ) {\r
+                       return match.toLowerCase();\r
+               } );\r
+\r
+               // Don't touch single lines (no <br|p|div>) - nothing to do here.\r
+               if ( data.match( /^[^<]$/ ) )\r
+                       return data;\r
+\r
+               // Webkit.\r
+               if ( CKEDITOR.env.webkit && data.indexOf( '<div>' ) > -1 ) {\r
+                               // One line break at the beginning - insert <br>\r
+                       data = data.replace( /^(<div>(<br>|)<\/div>)(?!$|(<div>(<br>|)<\/div>))/g, '<br>' )\r
+                               // Two or more - reduce number of new lines by one.\r
+                               .replace( /^(<div>(<br>|)<\/div>){2}(?!$)/g, '<div></div>' );\r
+\r
+                       // Two line breaks create one paragraph in Webkit.\r
+                       if ( data.match( /<div>(<br>|)<\/div>/ ) ) {\r
+                               data = '<p>' + data.replace( /(<div>(<br>|)<\/div>)+/g, function( match ) {\r
+                                       return repeatParagraphs( match.split( '</div><div>' ).length + 1 );\r
+                               } ) + '</p>';\r
+                       }\r
+\r
+                       // One line break create br.\r
+                       data = data.replace( /<\/div><div>/g, '<br>' );\r
+\r
+                       // Remove remaining divs.\r
+                       data = data.replace( /<\/?div>/g, '' );\r
+               }\r
+\r
+               // Opera and Firefox and enterMode != BR.\r
+               if ( CKEDITOR.env.gecko && config.enterMode != CKEDITOR.ENTER_BR ) {\r
+                       // Remove bogus <br> - Fx generates two <brs> for one line break.\r
+                       // For two line breaks it still produces two <brs>, but it's better to ignore this case than the first one.\r
+                       if ( CKEDITOR.env.gecko )\r
+                               data = data.replace( /^<br><br>$/, '<br>' );\r
+\r
+                       // This line satisfy edge case when for Opera we have two line breaks\r
+                       //data = data.replace( /)\r
+\r
+                       if ( data.indexOf( '<br><br>' ) > -1 ) {\r
+                               // Two line breaks create one paragraph, three - 2, four - 3, etc.\r
+                               data = '<p>' + data.replace( /(<br>){2,}/g, function( match ) {\r
+                                       return repeatParagraphs( match.length / 4 );\r
+                               } ) + '</p>';\r
+                       }\r
+               }\r
+\r
+               return switchEnterMode( config, data );\r
+       }\r
+\r
+       function filtersFactoryFactory() {\r
+               var filters = {};\r
+\r
+               function setUpTags() {\r
+                       var tags = {};\r
+\r
+                       for ( var tag in CKEDITOR.dtd ) {\r
+                               if ( tag.charAt( 0 ) != '$' && tag != 'div' && tag != 'span' ) {\r
+                                       tags[ tag ] = 1;\r
+                               }\r
+                       }\r
+\r
+                       return tags;\r
+               }\r
+\r
+               function createSemanticContentFilter() {\r
+                       var filter = new CKEDITOR.filter();\r
+\r
+                       filter.allow( {\r
+                               $1: {\r
+                                       elements: setUpTags(),\r
+                                       attributes: true,\r
+                                       styles: false,\r
+                                       classes: false\r
+                               }\r
+                       } );\r
+\r
+                       return filter;\r
+               }\r
+\r
+               return {\r
+                       get: function( type ) {\r
+                               if ( type == 'plain-text' ) {\r
+                                       // Does this look confusing to you? Did we forget about enter mode?\r
+                                       // It is a trick that let's us creating one filter for edidtor, regardless of its\r
+                                       // activeEnterMode (which as the name indicates can change during runtime).\r
+                                       //\r
+                                       // How does it work?\r
+                                       // The active enter mode is passed to the filter.applyTo method.\r
+                                       // The filter first marks all elements except <br> as disallowed and then tries to remove\r
+                                       // them. However, it cannot remove e.g. a <p> element completely, because it's a basic structural element,\r
+                                       // so it tries to replace it with an element created based on the active enter mode, eventually doing nothing.\r
+                                       //\r
+                                       // Now you can sleep well.\r
+                                       return filters.plainText || ( filters.plainText = new CKEDITOR.filter( 'br' ) );\r
+                               } else if ( type == 'semantic-content' ) {\r
+                                       return filters.semanticContent || ( filters.semanticContent = createSemanticContentFilter() );\r
+                               } else if ( type ) {\r
+                                       // Create filter based on rules (string or object).\r
+                                       return new CKEDITOR.filter( type );\r
+                               }\r
+\r
+                               return null;\r
+                       }\r
+               };\r
+       }\r
+\r
+       function filterContent( editor, data, filter ) {\r
+               var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data ),\r
+                       writer = new CKEDITOR.htmlParser.basicWriter();\r
+\r
+               filter.applyTo( fragment, true, false, editor.activeEnterMode );\r
+               fragment.writeHtml( writer );\r
+\r
+               return writer.getHtml();\r
+       }\r
+\r
+       function switchEnterMode( config, data ) {\r
+               if ( config.enterMode == CKEDITOR.ENTER_BR ) {\r
+                       data = data.replace( /(<\/p><p>)+/g, function( match ) {\r
+                               return CKEDITOR.tools.repeat( '<br>', match.length / 7 * 2 );\r
+                       } ).replace( /<\/?p>/g, '' );\r
+               } else if ( config.enterMode == CKEDITOR.ENTER_DIV ) {\r
+                       data = data.replace( /<(\/)?p>/g, '<$1div>' );\r
+               }\r
+\r
+               return data;\r
+       }\r
+\r
+       function preventDefaultSetDropEffectToNone( evt ) {\r
+               evt.data.preventDefault();\r
+               evt.data.$.dataTransfer.dropEffect = 'none';\r
+       }\r
+\r
+       function initDragDrop( editor ) {\r
+               var clipboard = CKEDITOR.plugins.clipboard;\r
+\r
+               editor.on( 'contentDom', function() {\r
+                       var editable = editor.editable(),\r
+                               dropTarget = CKEDITOR.plugins.clipboard.getDropTarget( editor ),\r
+                               top = editor.ui.space( 'top' ),\r
+                               bottom = editor.ui.space( 'bottom' );\r
+\r
+                       // -------------- DRAGOVER TOP & BOTTOM --------------\r
+\r
+                       // Not allowing dragging on toolbar and bottom (http://dev.ckeditor.com/ticket/12613).\r
+                       clipboard.preventDefaultDropOnElement( top );\r
+                       clipboard.preventDefaultDropOnElement( bottom );\r
+\r
+                       // -------------- DRAGSTART --------------\r
+                       // Listed on dragstart to mark internal and cross-editor drag & drop\r
+                       // and save range and selected HTML.\r
+\r
+                       editable.attachListener( dropTarget, 'dragstart', fireDragEvent );\r
+\r
+                       // Make sure to reset data transfer (in case dragend was not called or was canceled).\r
+                       editable.attachListener( editor, 'dragstart', clipboard.resetDragDataTransfer, clipboard, null, 1 );\r
+\r
+                       // Create a dataTransfer object and save it globally.\r
+                       editable.attachListener( editor, 'dragstart', function( evt ) {\r
+                               clipboard.initDragDataTransfer( evt, editor );\r
+                       }, null, null, 2 );\r
+\r
+                       editable.attachListener( editor, 'dragstart', function() {\r
+                               // Save drag range globally for cross editor D&D.\r
+                               var dragRange = clipboard.dragRange = editor.getSelection().getRanges()[ 0 ];\r
+\r
+                               // Store number of children, so we can later tell if any text node was split on drop. (http://dev.ckeditor.com/ticket/13011, http://dev.ckeditor.com/ticket/13447)\r
+                               if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {\r
+                                       clipboard.dragStartContainerChildCount = dragRange ? getContainerChildCount( dragRange.startContainer ) : null;\r
+                                       clipboard.dragEndContainerChildCount = dragRange ? getContainerChildCount( dragRange.endContainer ) : null;\r
+                               }\r
+                       }, null, null, 100 );\r
+\r
+                       // -------------- DRAGEND --------------\r
+                       // Clean up on dragend.\r
+\r
+                       editable.attachListener( dropTarget, 'dragend', fireDragEvent );\r
+\r
+                       // Init data transfer if someone wants to use it in dragend.\r
+                       editable.attachListener( editor, 'dragend', clipboard.initDragDataTransfer, clipboard, null, 1 );\r
+\r
+                       // When drag & drop is done we need to reset dataTransfer so the future\r
+                       // external drop will be not recognize as internal.\r
+                       editable.attachListener( editor, 'dragend', clipboard.resetDragDataTransfer, clipboard, null, 100 );\r
+\r
+                       // -------------- DRAGOVER --------------\r
+                       // We need to call preventDefault on dragover because otherwise if\r
+                       // we drop image it will overwrite document.\r
+\r
+                       editable.attachListener( dropTarget, 'dragover', function( evt ) {\r
+                               // Edge requires this handler to have `preventDefault()` regardless of the situation.\r
+                               if ( CKEDITOR.env.edge ) {\r
+                                       evt.data.preventDefault();\r
+                                       return;\r
+                               }\r
+\r
+                               var target = evt.data.getTarget();\r
+\r
+                               // Prevent reloading page when dragging image on empty document (http://dev.ckeditor.com/ticket/12619).\r
+                               if ( target && target.is && target.is( 'html' ) ) {\r
+                                       evt.data.preventDefault();\r
+                                       return;\r
+                               }\r
+\r
+                               // If we do not prevent default dragover on IE the file path\r
+                               // will be loaded and we will lose content. On the other hand\r
+                               // if we prevent it the cursor will not we shown, so we prevent\r
+                               // dragover only on IE, on versions which support file API and only\r
+                               // if the event contains files.\r
+                               if ( CKEDITOR.env.ie &&\r
+                                       CKEDITOR.plugins.clipboard.isFileApiSupported &&\r
+                                       evt.data.$.dataTransfer.types.contains( 'Files' ) ) {\r
+                                       evt.data.preventDefault();\r
+                               }\r
+                       } );\r
+\r
+                       // -------------- DROP --------------\r
+\r
+                       editable.attachListener( dropTarget, 'drop', function( evt ) {\r
+                               // Do nothing if event was already prevented. (http://dev.ckeditor.com/ticket/13879)\r
+                               if ( evt.data.$.defaultPrevented ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // Cancel native drop.\r
+                               evt.data.preventDefault();\r
+\r
+                               var target = evt.data.getTarget(),\r
+                                       readOnly = target.isReadOnly();\r
+\r
+                               // Do nothing if drop on non editable element (http://dev.ckeditor.com/ticket/13015).\r
+                               // The <html> tag isn't editable (body is), but we want to allow drop on it\r
+                               // (so it is possible to drop below editor contents).\r
+                               if ( readOnly && !( target.type == CKEDITOR.NODE_ELEMENT && target.is( 'html' ) ) ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // Getting drop position is one of the most complex parts.\r
+                               var dropRange = clipboard.getRangeAtDropPosition( evt, editor ),\r
+                                       dragRange = clipboard.dragRange;\r
+\r
+                               // Do nothing if it was not possible to get drop range.\r
+                               if ( !dropRange ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // Fire drop.\r
+                               fireDragEvent( evt, dragRange, dropRange  );\r
+                       }, null, null, 9999 );\r
+\r
+                       // Create dataTransfer or get it, if it was created before.\r
+                       editable.attachListener( editor, 'drop', clipboard.initDragDataTransfer, clipboard, null, 1 );\r
+\r
+                       // Execute drop action, fire paste.\r
+                       editable.attachListener( editor, 'drop', function( evt ) {\r
+                               var data = evt.data;\r
+\r
+                               if ( !data ) {\r
+                                       return;\r
+                               }\r
+\r
+                               // Let user modify drag and drop range.\r
+                               var dropRange = data.dropRange,\r
+                                       dragRange = data.dragRange,\r
+                                       dataTransfer = data.dataTransfer;\r
+\r
+                               if ( dataTransfer.getTransferType( editor ) == CKEDITOR.DATA_TRANSFER_INTERNAL ) {\r
+                                       // Execute drop with a timeout because otherwise selection, after drop,\r
+                                       // on IE is in the drag position, instead of drop position.\r
+                                       setTimeout( function() {\r
+                                               clipboard.internalDrop( dragRange, dropRange, dataTransfer, editor );\r
+                                       }, 0 );\r
+                               } else if ( dataTransfer.getTransferType( editor ) == CKEDITOR.DATA_TRANSFER_CROSS_EDITORS ) {\r
+                                       crossEditorDrop( dragRange, dropRange, dataTransfer );\r
+                               } else {\r
+                                       externalDrop( dropRange, dataTransfer );\r
+                               }\r
+                       }, null, null, 9999 );\r
+\r
+                       // Cross editor drag and drop (drag in one Editor and drop in the other).\r
+                       function crossEditorDrop( dragRange, dropRange, dataTransfer ) {\r
+                               // Paste event should be fired before delete contents because otherwise\r
+                               // Chrome have a problem with drop range (Chrome split the drop\r
+                               // range container so the offset is bigger then container length).\r
+                               dropRange.select();\r
+                               firePasteEvents( editor, { dataTransfer: dataTransfer, method: 'drop' }, 1 );\r
+\r
+                               // Remove dragged content and make a snapshot.\r
+                               dataTransfer.sourceEditor.fire( 'saveSnapshot' );\r
+\r
+                               dataTransfer.sourceEditor.editable().extractHtmlFromRange( dragRange );\r
+\r
+                               // Make some selection before saving snapshot, otherwise error will be thrown, because\r
+                               // there will be no valid selection after content is removed.\r
+                               dataTransfer.sourceEditor.getSelection().selectRanges( [ dragRange ] );\r
+                               dataTransfer.sourceEditor.fire( 'saveSnapshot' );\r
+                       }\r
+\r
+                       // Drop from external source.\r
+                       function externalDrop( dropRange, dataTransfer ) {\r
+                               // Paste content into the drop position.\r
+                               dropRange.select();\r
+\r
+                               firePasteEvents( editor, { dataTransfer: dataTransfer, method: 'drop' }, 1 );\r
+\r
+                               // Usually we reset DataTranfer on dragend,\r
+                               // but dragend is called on the same element as dragstart\r
+                               // so it will not be called on on external drop.\r
+                               clipboard.resetDragDataTransfer();\r
+                       }\r
+\r
+                       // Fire drag/drop events (dragstart, dragend, drop).\r
+                       function fireDragEvent( evt, dragRange, dropRange ) {\r
+                               var eventData = {\r
+                                               $: evt.data.$,\r
+                                               target: evt.data.getTarget()\r
+                                       };\r
+\r
+                               if ( dragRange ) {\r
+                                       eventData.dragRange = dragRange;\r
+                               }\r
+                               if ( dropRange ) {\r
+                                       eventData.dropRange = dropRange;\r
+                               }\r
+\r
+                               if ( editor.fire( evt.name, eventData ) === false ) {\r
+                                       evt.data.preventDefault();\r
+                               }\r
+                       }\r
+\r
+                       function getContainerChildCount( container ) {\r
+                               if ( container.type != CKEDITOR.NODE_ELEMENT ) {\r
+                                       container = container.getParent();\r
+                               }\r
+\r
+                               return container.getChildCount();\r
+                       }\r
+               } );\r
+       }\r
+\r
+       /**\r
+        * @singleton\r
+        * @class CKEDITOR.plugins.clipboard\r
+        */\r
+       CKEDITOR.plugins.clipboard = {\r
+               /**\r
+                * True if the environment allows to set data on copy or cut manually. This value is false in IE, because this browser\r
+                * shows the security dialog window when the script tries to set clipboard data and on iOS, because custom data is\r
+                * not saved to clipboard there.\r
+                *\r
+                * @since 4.5\r
+                * @readonly\r
+                * @property {Boolean}\r
+                */\r
+               isCustomCopyCutSupported: !CKEDITOR.env.ie && !CKEDITOR.env.iOS,\r
+\r
+               /**\r
+                * True if the environment supports MIME types and custom data types in dataTransfer/cliboardData getData/setData methods.\r
+                *\r
+                * @since 4.5\r
+                * @readonly\r
+                * @property {Boolean}\r
+                */\r
+               isCustomDataTypesSupported: !CKEDITOR.env.ie,\r
+\r
+               /**\r
+                * True if the environment supports File API.\r
+                *\r
+                * @since 4.5\r
+                * @readonly\r
+                * @property {Boolean}\r
+                */\r
+               isFileApiSupported: !CKEDITOR.env.ie || CKEDITOR.env.version > 9,\r
+\r
+               /**\r
+                * Main native paste event editable should listen to.\r
+                *\r
+                * **Note:** Safari does not like the {@link CKEDITOR.editor#beforePaste} event &mdash; it sometimes does not\r
+                * handle <kbd>Ctrl+C</kbd> properly. This is probably caused by some race condition between events.\r
+                * Chrome, Firefox and Edge work well with both events, so it is better to use {@link CKEDITOR.editor#paste}\r
+                * which will handle pasting from e.g. browsers' menu bars.\r
+                * IE7/8 does not like the {@link CKEDITOR.editor#paste} event for which it is throwing random errors.\r
+                *\r
+                * @since 4.5\r
+                * @readonly\r
+                * @property {String}\r
+                */\r
+               mainPasteEvent: ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) ? 'beforepaste' : 'paste',\r
+\r
+               /**\r
+                * Returns `true` if it is expected that a browser provides HTML data through the Clipboard API.\r
+                * If not, this method returns `false` and as a result CKEditor will use the paste bin. Read more in\r
+                * the [Clipboard Integration](http://docs.ckeditor.com/#!/guide/dev_clipboard-section-clipboard-api) guide.\r
+                *\r
+                * @since 4.5.2\r
+                * @returns {Boolean}\r
+                */\r
+               canClipboardApiBeTrusted: function( dataTransfer, editor ) {\r
+                       // If it's an internal or cross-editor data transfer, then it means that custom cut/copy/paste support works\r
+                       // and that the data were put manually on the data transfer so we can be sure that it's available.\r
+                       if ( dataTransfer.getTransferType( editor ) != CKEDITOR.DATA_TRANSFER_EXTERNAL ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // In Chrome we can trust Clipboard API, with the exception of Chrome on Android (in both - mobile and desktop modes), where\r
+                       // clipboard API is not available so we need to check it (http://dev.ckeditor.com/ticket/13187).\r
+                       if ( CKEDITOR.env.chrome && !dataTransfer.isEmpty() ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // Because of a Firefox bug HTML data are not available in some cases (e.g. paste from Word), in such cases we\r
+                       // need to use the pastebin (http://dev.ckeditor.com/ticket/13528, https://bugzilla.mozilla.org/show_bug.cgi?id=1183686).\r
+                       if ( CKEDITOR.env.gecko && ( dataTransfer.getData( 'text/html' ) || dataTransfer.getFilesCount() ) ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // Safari fixed clipboard in 10.1 (https://bugs.webkit.org/show_bug.cgi?id=19893) (http://dev.ckeditor.com/ticket/16982).\r
+                       // However iOS version still doesn't work well enough (https://bugs.webkit.org/show_bug.cgi?id=19893#c34).\r
+                       if ( CKEDITOR.env.safari && CKEDITOR.env.version >= 603 && !CKEDITOR.env.iOS ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // In older Safari and IE HTML data is not available though the Clipboard API.\r
+                       // In Edge things are a bit messy at the moment -\r
+                       // https://connect.microsoft.com/IE/feedback/details/1572456/edge-clipboard-api-text-html-content-messed-up-in-event-clipboarddata\r
+                       // It is safer to use the paste bin in unknown cases.\r
+                       return false;\r
+               },\r
+\r
+               /**\r
+                * Returns the element that should be used as the target for the drop event.\r
+                *\r
+                * @since 4.5\r
+                * @param {CKEDITOR.editor} editor The editor instance.\r
+                * @returns {CKEDITOR.dom.domObject} the element that should be used as the target for the drop event.\r
+                */\r
+               getDropTarget: function( editor ) {\r
+                       var editable = editor.editable();\r
+\r
+                       // http://dev.ckeditor.com/ticket/11123 Firefox needs to listen on document, because otherwise event won't be fired.\r
+                       // http://dev.ckeditor.com/ticket/11086 IE8 cannot listen on document.\r
+                       if ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) || editable.isInline() ) {\r
+                               return editable;\r
+                       } else {\r
+                               return editor.document;\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * IE 8 & 9 split text node on drop so the first node contains the\r
+                * text before the drop position and the second contains the rest. If you\r
+                * drag the content from the same node you will be not be able to get\r
+                * it (the range becomes invalid), so you need to join them back.\r
+                *\r
+                * Note that the first node in IE 8 & 9 is the original node object\r
+                * but with shortened content.\r
+                *\r
+                *              Before:\r
+                *                --- Text Node A ----------------------------------\r
+                *                                                           /\\r
+                *                                                      Drag position\r
+                *\r
+                *              After (IE 8 & 9):\r
+                *                --- Text Node A -----  --- Text Node B -----------\r
+                *                                     /\                    /\\r
+                *                                Drop position        Drag position\r
+                *                                                       (invalid)\r
+                *\r
+                *              After (other browsers):\r
+                *                --- Text Node A ----------------------------------\r
+                *                                     /\                    /\\r
+                *                                Drop position        Drag position\r
+                *\r
+                * **Note:** This function is in the public scope for tests usage only.\r
+                *\r
+                * @since 4.5\r
+                * @private\r
+                * @param {CKEDITOR.dom.range} dragRange The drag range.\r
+                * @param {CKEDITOR.dom.range} dropRange The drop range.\r
+                * @param {Number} preDragStartContainerChildCount The number of children of the drag range start container before the drop.\r
+                * @param {Number} preDragEndContainerChildCount The number of children of the drag range end container before the drop.\r
+                */\r
+               fixSplitNodesAfterDrop: function( dragRange, dropRange, preDragStartContainerChildCount, preDragEndContainerChildCount ) {\r
+                       var dropContainer = dropRange.startContainer;\r
+\r
+                       if (\r
+                               typeof preDragEndContainerChildCount != 'number' ||\r
+                               typeof preDragStartContainerChildCount != 'number'\r
+                       ) {\r
+                               return;\r
+                       }\r
+\r
+                       // We are only concerned about ranges anchored in elements.\r
+                       if ( dropContainer.type != CKEDITOR.NODE_ELEMENT ) {\r
+                               return;\r
+                       }\r
+\r
+                       if ( handleContainer( dragRange.startContainer, dropContainer, preDragStartContainerChildCount ) ) {\r
+                               return;\r
+                       }\r
+\r
+                       if ( handleContainer( dragRange.endContainer, dropContainer, preDragEndContainerChildCount ) ) {\r
+                               return;\r
+                       }\r
+\r
+                       function handleContainer( dragContainer, dropContainer, preChildCount ) {\r
+                               var dragElement = dragContainer;\r
+                               if ( dragElement.type == CKEDITOR.NODE_TEXT ) {\r
+                                       dragElement = dragContainer.getParent();\r
+                               }\r
+\r
+                               if ( dragElement.equals( dropContainer ) && preChildCount != dropContainer.getChildCount() ) {\r
+                                       applyFix( dropRange );\r
+                                       return true;\r
+                               }\r
+                       }\r
+\r
+                       function applyFix( dropRange ) {\r
+                               var nodeBefore = dropRange.startContainer.getChild( dropRange.startOffset - 1 ),\r
+                                       nodeAfter = dropRange.startContainer.getChild( dropRange.startOffset );\r
+\r
+                               if (\r
+                                       nodeBefore && nodeBefore.type == CKEDITOR.NODE_TEXT &&\r
+                                       nodeAfter && nodeAfter.type == CKEDITOR.NODE_TEXT\r
+                               ) {\r
+                                       var offset = nodeBefore.getLength();\r
+\r
+                                       nodeBefore.setText( nodeBefore.getText() + nodeAfter.getText() );\r
+                                       nodeAfter.remove();\r
+\r
+                                       dropRange.setStart( nodeBefore, offset );\r
+                                       dropRange.collapse( true );\r
+                               }\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Checks whether turning the drag range into bookmarks will invalidate the drop range.\r
+                * This usually happens when the drop range shares the container with the drag range and is\r
+                * located after the drag range, but there are countless edge cases.\r
+                *\r
+                * This function is stricly related to {@link #internalDrop} which toggles\r
+                * order in which it creates bookmarks for both ranges based on a value returned\r
+                * by this method. In some cases this method returns a value which is not necessarily\r
+                * true in terms of what it was meant to check, but it is convenient, because\r
+                * we know how it is interpreted in {@link #internalDrop}, so the correct\r
+                * behavior of the entire algorithm is assured.\r
+                *\r
+                * **Note:** This function is in the public scope for tests usage only.\r
+                *\r
+                * @since 4.5\r
+                * @private\r
+                * @param {CKEDITOR.dom.range} dragRange The first range to compare.\r
+                * @param {CKEDITOR.dom.range} dropRange The second range to compare.\r
+                * @returns {Boolean} `true` if the first range is before the second range.\r
+                */\r
+               isDropRangeAffectedByDragRange: function( dragRange, dropRange ) {\r
+                       var dropContainer = dropRange.startContainer,\r
+                               dropOffset = dropRange.endOffset;\r
+\r
+                       // Both containers are the same and drop offset is at the same position or later.\r
+                       // " A L] A " " M A "\r
+                       //       ^ ^\r
+                       if ( dragRange.endContainer.equals( dropContainer ) && dragRange.endOffset <= dropOffset ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // Bookmark for drag start container will mess up with offsets.\r
+                       // " O [L A " " M A "\r
+                       //           ^       ^\r
+                       if (\r
+                               dragRange.startContainer.getParent().equals( dropContainer ) &&\r
+                               dragRange.startContainer.getIndex() < dropOffset\r
+                       ) {\r
+                               return true;\r
+                       }\r
+\r
+                       // Bookmark for drag end container will mess up with offsets.\r
+                       // " O] L A " " M A "\r
+                       //           ^       ^\r
+                       if (\r
+                               dragRange.endContainer.getParent().equals( dropContainer ) &&\r
+                               dragRange.endContainer.getIndex() < dropOffset\r
+                       ) {\r
+                               return true;\r
+                       }\r
+\r
+                       return false;\r
+               },\r
+\r
+               /**\r
+                * Internal drag and drop (drag and drop in the same editor instance).\r
+                *\r
+                * **Note:** This function is in the public scope for tests usage only.\r
+                *\r
+                * @since 4.5\r
+                * @private\r
+                * @param {CKEDITOR.dom.range} dragRange The first range to compare.\r
+                * @param {CKEDITOR.dom.range} dropRange The second range to compare.\r
+                * @param {CKEDITOR.plugins.clipboard.dataTransfer} dataTransfer\r
+                * @param {CKEDITOR.editor} editor\r
+                */\r
+               internalDrop: function( dragRange, dropRange, dataTransfer, editor ) {\r
+                       var clipboard = CKEDITOR.plugins.clipboard,\r
+                               editable = editor.editable(),\r
+                               dragBookmark, dropBookmark, isDropRangeAffected;\r
+\r
+                       // Save and lock snapshot so there will be only\r
+                       // one snapshot for both remove and insert content.\r
+                       editor.fire( 'saveSnapshot' );\r
+                       editor.fire( 'lockSnapshot', { dontUpdate: 1 } );\r
+\r
+                       if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {\r
+                               this.fixSplitNodesAfterDrop(\r
+                                       dragRange,\r
+                                       dropRange,\r
+                                       clipboard.dragStartContainerChildCount,\r
+                                       clipboard.dragEndContainerChildCount\r
+                               );\r
+                       }\r
+\r
+                       // Because we manipulate multiple ranges we need to do it carefully,\r
+                       // changing one range (event creating a bookmark) may make other invalid.\r
+                       // We need to change ranges into bookmarks so we can manipulate them easily in the future.\r
+                       // We can change the range which is later in the text before we change the preceding range.\r
+                       // We call isDropRangeAffectedByDragRange to test the order of ranges.\r
+                       isDropRangeAffected = this.isDropRangeAffectedByDragRange( dragRange, dropRange );\r
+                       if ( !isDropRangeAffected ) {\r
+                               dragBookmark = dragRange.createBookmark( false );\r
+                       }\r
+                       dropBookmark = dropRange.clone().createBookmark( false );\r
+                       if ( isDropRangeAffected ) {\r
+                               dragBookmark = dragRange.createBookmark( false );\r
+                       }\r
+\r
+                       // Check if drop range is inside range.\r
+                       // This is an edge case when we drop something on editable's margin/padding.\r
+                       // That space is not treated as a part of the range we drag, so it is possible to drop there.\r
+                       // When we drop, browser tries to find closest drop position and it finds it inside drag range. (http://dev.ckeditor.com/ticket/13453)\r
+                       var startNode = dragBookmark.startNode,\r
+                               endNode = dragBookmark.endNode,\r
+                               dropNode = dropBookmark.startNode,\r
+                               dropInsideDragRange =\r
+                                       // Must check endNode because dragRange could be collapsed in some edge cases (simulated DnD).\r
+                                       endNode &&\r
+                                       ( startNode.getPosition( dropNode ) & CKEDITOR.POSITION_PRECEDING ) &&\r
+                                       ( endNode.getPosition( dropNode ) & CKEDITOR.POSITION_FOLLOWING );\r
+\r
+                       // If the drop range happens to be inside drag range change it's position to the beginning of the drag range.\r
+                       if ( dropInsideDragRange ) {\r
+                               // We only change position of bookmark span that is connected with dropBookmark.\r
+                               // dropRange will be overwritten and set to the dropBookmark later.\r
+                               dropNode.insertBefore( startNode );\r
+                       }\r
+\r
+                       // No we can safely delete content for the drag range...\r
+                       dragRange = editor.createRange();\r
+                       dragRange.moveToBookmark( dragBookmark );\r
+                       editable.extractHtmlFromRange( dragRange, 1 );\r
+\r
+                       // ...and paste content into the drop position.\r
+                       dropRange = editor.createRange();\r
+                       dropRange.moveToBookmark( dropBookmark );\r
+\r
+                       // We do not select drop range, because of may be in the place we can not set the selection\r
+                       // (e.g. between blocks, in case of block widget D&D). We put range to the paste event instead.\r
+                       firePasteEvents( editor, { dataTransfer: dataTransfer, method: 'drop', range: dropRange }, 1 );\r
+\r
+                       editor.fire( 'unlockSnapshot' );\r
+               },\r
+\r
+               /**\r
+                * Gets the range from the `drop` event.\r
+                *\r
+                * @since 4.5\r
+                * @param {Object} domEvent A native DOM drop event object.\r
+                * @param {CKEDITOR.editor} editor The source editor instance.\r
+                * @returns {CKEDITOR.dom.range} range at drop position.\r
+                */\r
+               getRangeAtDropPosition: function( dropEvt, editor ) {\r
+                       var $evt = dropEvt.data.$,\r
+                               x = $evt.clientX,\r
+                               y = $evt.clientY,\r
+                               $range,\r
+                               defaultRange = editor.getSelection( true ).getRanges()[ 0 ],\r
+                               range = editor.createRange();\r
+\r
+                       // Make testing possible.\r
+                       if ( dropEvt.data.testRange )\r
+                               return dropEvt.data.testRange;\r
+\r
+                       // Webkits.\r
+                       if ( document.caretRangeFromPoint && editor.document.$.caretRangeFromPoint( x, y ) ) {\r
+                               $range = editor.document.$.caretRangeFromPoint( x, y );\r
+                               range.setStart( CKEDITOR.dom.node( $range.startContainer ), $range.startOffset );\r
+                               range.collapse( true );\r
+                       }\r
+                       // FF.\r
+                       else if ( $evt.rangeParent ) {\r
+                               range.setStart( CKEDITOR.dom.node( $evt.rangeParent ), $evt.rangeOffset );\r
+                               range.collapse( true );\r
+                       }\r
+                       // IEs 9+.\r
+                       // We check if editable is focused to make sure that it's an internal DnD. External DnD must use the second\r
+                       // mechanism because of http://dev.ckeditor.com/ticket/13472#comment:6.\r
+                       else if ( CKEDITOR.env.ie && CKEDITOR.env.version > 8 && defaultRange && editor.editable().hasFocus ) {\r
+                               // On IE 9+ range by default is where we expected it.\r
+                               // defaultRange may be undefined if dragover was canceled (file drop).\r
+                               return defaultRange;\r
+                       }\r
+                       // IE 8 and all IEs if !defaultRange or external DnD.\r
+                       else if ( document.body.createTextRange ) {\r
+                               // To use this method we need a focus (which may be somewhere else in case of external drop).\r
+                               editor.focus();\r
+\r
+                               $range = editor.document.getBody().$.createTextRange();\r
+                               try {\r
+                                       var sucess = false;\r
+\r
+                                       // If user drop between text line IEs moveToPoint throws exception:\r
+                                       //\r
+                                       //              Lorem ipsum pulvinar purus et euismod\r
+                                       //\r
+                                       //              dolor sit amet,| consectetur adipiscing\r
+                                       //                             *\r
+                                       //              vestibulum tincidunt augue eget tempus.\r
+                                       //\r
+                                       // * - drop position\r
+                                       // | - expected cursor position\r
+                                       //\r
+                                       // So we try to call moveToPoint with +-1px up to +-20px above or\r
+                                       // below original drop position to find nearest good drop position.\r
+                                       for ( var i = 0; i < 20 && !sucess; i++ ) {\r
+                                               if ( !sucess ) {\r
+                                                       try {\r
+                                                               $range.moveToPoint( x, y - i );\r
+                                                               sucess = true;\r
+                                                       } catch ( err ) {\r
+                                                       }\r
+                                               }\r
+                                               if ( !sucess ) {\r
+                                                       try {\r
+                                                               $range.moveToPoint( x, y + i );\r
+                                                               sucess = true;\r
+                                                       } catch ( err ) {\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if ( sucess ) {\r
+                                               var id = 'cke-temp-' + ( new Date() ).getTime();\r
+                                               $range.pasteHTML( '<span id="' + id + '">\u200b</span>' );\r
+\r
+                                               var span = editor.document.getById( id );\r
+                                               range.moveToPosition( span, CKEDITOR.POSITION_BEFORE_START );\r
+                                               span.remove();\r
+                                       } else {\r
+                                               // If the fist method does not succeed we might be next to\r
+                                               // the short element (like header):\r
+                                               //\r
+                                               //              Lorem ipsum pulvinar purus et euismod.\r
+                                               //\r
+                                               //\r
+                                               //              SOME HEADER|        *\r
+                                               //\r
+                                               //\r
+                                               //              vestibulum tincidunt augue eget tempus.\r
+                                               //\r
+                                               // * - drop position\r
+                                               // | - expected cursor position\r
+                                               //\r
+                                               // In such situation elementFromPoint returns proper element. Using getClientRect\r
+                                               // it is possible to check if the cursor should be at the beginning or at the end\r
+                                               // of paragraph.\r
+                                               var $element = editor.document.$.elementFromPoint( x, y ),\r
+                                                       element = new CKEDITOR.dom.element( $element ),\r
+                                                       rect;\r
+\r
+                                               if ( !element.equals( editor.editable() ) && element.getName() != 'html' ) {\r
+                                                       rect = element.getClientRect();\r
+\r
+                                                       if ( x < rect.left ) {\r
+                                                               range.setStartAt( element, CKEDITOR.POSITION_AFTER_START );\r
+                                                               range.collapse( true );\r
+                                                       } else {\r
+                                                               range.setStartAt( element, CKEDITOR.POSITION_BEFORE_END );\r
+                                                               range.collapse( true );\r
+                                                       }\r
+                                               }\r
+                                               // If drop happens on no element elementFromPoint returns html or body.\r
+                                               //\r
+                                               //              *      |Lorem ipsum pulvinar purus et euismod.\r
+                                               //\r
+                                               //                     vestibulum tincidunt augue eget tempus.\r
+                                               //\r
+                                               // * - drop position\r
+                                               // | - expected cursor position\r
+                                               //\r
+                                               // In such case we can try to use default selection. If startContainer is not\r
+                                               // 'editable' element it is probably proper selection.\r
+                                               else if ( defaultRange && defaultRange.startContainer &&\r
+                                                       !defaultRange.startContainer.equals( editor.editable() ) ) {\r
+                                                       return defaultRange;\r
+\r
+                                               // Otherwise we can not find any drop position and we have to return null\r
+                                               // and cancel drop event.\r
+                                               } else {\r
+                                                       return null;\r
+                                               }\r
+\r
+                                       }\r
+                               } catch ( err ) {\r
+                                       return null;\r
+                               }\r
+                       } else {\r
+                               return null;\r
+                       }\r
+\r
+                       return range;\r
+               },\r
+\r
+               /**\r
+                * This function tries to link the `evt.data.dataTransfer` property of the {@link CKEDITOR.editor#dragstart},\r
+                * {@link CKEDITOR.editor#dragend} and {@link CKEDITOR.editor#drop} events to a single\r
+                * {@link CKEDITOR.plugins.clipboard.dataTransfer} object.\r
+                *\r
+                * This method is automatically used by the core of the drag and drop functionality and\r
+                * usually does not have to be called manually when using the drag and drop events.\r
+                *\r
+                * This method behaves differently depending on whether the drag and drop events were fired\r
+                * artificially (to represent a non-native drag and drop) or whether they were caused by the native drag and drop.\r
+                *\r
+                * If the native event is not available, then it will create a new {@link CKEDITOR.plugins.clipboard.dataTransfer}\r
+                * instance (if it does not exist already) and will link it to this and all following event objects until\r
+                * the {@link #resetDragDataTransfer} method is called. It means that all three drag and drop events must be fired\r
+                * in order to ensure that the data transfer is bound correctly.\r
+                *\r
+                * If the native event is available, then the {@link CKEDITOR.plugins.clipboard.dataTransfer} is identified\r
+                * by its ID and a new instance is assigned to the `evt.data.dataTransfer` only if the ID changed or\r
+                * the {@link #resetDragDataTransfer} method was called.\r
+                *\r
+                * @since 4.5\r
+                * @param {CKEDITOR.dom.event} [evt] A drop event object.\r
+                * @param {CKEDITOR.editor} [sourceEditor] The source editor instance.\r
+                */\r
+               initDragDataTransfer: function( evt, sourceEditor ) {\r
+                       // Create a new dataTransfer object based on the drop event.\r
+                       // If this event was used on dragstart to create dataTransfer\r
+                       // both dataTransfer objects will have the same id.\r
+                       var nativeDataTransfer = evt.data.$ ? evt.data.$.dataTransfer : null,\r
+                               dataTransfer = new this.dataTransfer( nativeDataTransfer, sourceEditor );\r
+\r
+                       if ( !nativeDataTransfer ) {\r
+                               // No native event.\r
+                               if ( this.dragData ) {\r
+                                       dataTransfer = this.dragData;\r
+                               } else {\r
+                                       this.dragData = dataTransfer;\r
+                               }\r
+                       } else {\r
+                               // Native event. If there is the same id we will replace dataTransfer with the one\r
+                               // created on drag, because it contains drag editor, drag content and so on.\r
+                               // Otherwise (in case of drag from external source) we save new object to\r
+                               // the global clipboard.dragData.\r
+                               if ( this.dragData && dataTransfer.id == this.dragData.id ) {\r
+                                       dataTransfer = this.dragData;\r
+                               } else {\r
+                                       this.dragData = dataTransfer;\r
+                               }\r
+                       }\r
+\r
+                       evt.data.dataTransfer = dataTransfer;\r
+               },\r
+\r
+               /**\r
+                * Removes the global {@link #dragData} so the next call to {@link #initDragDataTransfer}\r
+                * always creates a new instance of {@link CKEDITOR.plugins.clipboard.dataTransfer}.\r
+                *\r
+                * @since 4.5\r
+                */\r
+               resetDragDataTransfer: function() {\r
+                       this.dragData = null;\r
+               },\r
+\r
+               /**\r
+                * Global object storing the data transfer of the current drag and drop operation.\r
+                * Do not use it directly, use {@link #initDragDataTransfer} and {@link #resetDragDataTransfer}.\r
+                *\r
+                * Note: This object is global (meaning that it is not related to a single editor instance)\r
+                * in order to handle drag and drop from one editor into another.\r
+                *\r
+                * @since 4.5\r
+                * @private\r
+                * @property {CKEDITOR.plugins.clipboard.dataTransfer} dragData\r
+                */\r
+\r
+               /**\r
+                * Range object to save the drag range and remove its content after the drop.\r
+                *\r
+                * @since 4.5\r
+                * @private\r
+                * @property {CKEDITOR.dom.range} dragRange\r
+                */\r
+\r
+               /**\r
+                * Initializes and links data transfer objects based on the paste event. If the data\r
+                * transfer object was already initialized on this event, the function will\r
+                * return that object. In IE it is not possible to link copy/cut and paste events\r
+                * so the method always returns a new object. The same happens if there is no paste event\r
+                * passed to the method.\r
+                *\r
+                * @since 4.5\r
+                * @param {CKEDITOR.dom.event} [evt] A paste event object.\r
+                * @param {CKEDITOR.editor} [sourceEditor] The source editor instance.\r
+                * @returns {CKEDITOR.plugins.clipboard.dataTransfer} The data transfer object.\r
+                */\r
+               initPasteDataTransfer: function( evt, sourceEditor ) {\r
+                       if ( !this.isCustomCopyCutSupported ) {\r
+                               // Edge does not support custom copy/cut, but it have some useful data in the clipboardData (http://dev.ckeditor.com/ticket/13755).\r
+                               return new this.dataTransfer( ( CKEDITOR.env.edge && evt && evt.data.$ && evt.data.$.clipboardData ) || null, sourceEditor );\r
+                       } else if ( evt && evt.data && evt.data.$ ) {\r
+                               var dataTransfer = new this.dataTransfer( evt.data.$.clipboardData, sourceEditor );\r
+\r
+                               if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) {\r
+                                       dataTransfer = this.copyCutData;\r
+                                       dataTransfer.$ = evt.data.$.clipboardData;\r
+                               } else {\r
+                                       this.copyCutData = dataTransfer;\r
+                               }\r
+\r
+                               return dataTransfer;\r
+                       } else {\r
+                               return new this.dataTransfer( null, sourceEditor );\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Prevents dropping on the specified element.\r
+                *\r
+                * @since 4.5\r
+                * @param {CKEDITOR.dom.element} element The element on which dropping should be disabled.\r
+                */\r
+               preventDefaultDropOnElement: function( element ) {\r
+                       element && element.on( 'dragover', preventDefaultSetDropEffectToNone );\r
+               }\r
+       };\r
+\r
+       // Data type used to link drag and drop events.\r
+       //\r
+       // In IE URL data type is buggie and there is no way to mark drag & drop  without\r
+       // modifying text data (which would be displayed if user drop content to the textarea)\r
+       // so we just read dragged text.\r
+       //\r
+       // In Chrome and Firefox we can use custom data types.\r
+       var clipboardIdDataType = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ? 'cke/id' : 'Text';\r
+       /**\r
+        * Facade for the native `dataTransfer`/`clipboadData` object to hide all differences\r
+        * between browsers.\r
+        *\r
+        * @since 4.5\r
+        * @class CKEDITOR.plugins.clipboard.dataTransfer\r
+        * @constructor Creates a class instance.\r
+        * @param {Object} [nativeDataTransfer] A native data transfer object.\r
+        * @param {CKEDITOR.editor} [editor] The source editor instance. If the editor is defined, dataValue will\r
+        * be created based on the editor content and the type will be 'html'.\r
+        */\r
+       CKEDITOR.plugins.clipboard.dataTransfer = function( nativeDataTransfer, editor ) {\r
+               if ( nativeDataTransfer ) {\r
+                       this.$ = nativeDataTransfer;\r
+               }\r
+\r
+               this._ = {\r
+                       metaRegExp: /^<meta.*?>/i,\r
+                       bodyRegExp: /<body(?:[\s\S]*?)>([\s\S]*)<\/body>/i,\r
+                       fragmentRegExp: /<!--(?:Start|End)Fragment-->/g,\r
+\r
+                       data: {},\r
+                       files: [],\r
+\r
+                       normalizeType: function( type ) {\r
+                               type = type.toLowerCase();\r
+\r
+                               if ( type == 'text' || type == 'text/plain' ) {\r
+                                       return 'Text'; // IE support only Text and URL;\r
+                               } else if ( type == 'url' ) {\r
+                                       return 'URL'; // IE support only Text and URL;\r
+                               } else {\r
+                                       return type;\r
+                               }\r
+                       }\r
+               };\r
+\r
+               // Check if ID is already created.\r
+               this.id = this.getData( clipboardIdDataType );\r
+\r
+               // If there is no ID we need to create it. Different browsers needs different ID.\r
+               if ( !this.id ) {\r
+                       if ( clipboardIdDataType == 'Text' ) {\r
+                               // For IE10+ only Text data type is supported and we have to compare dragged\r
+                               // and dropped text. If the ID is not set it means that empty string was dragged\r
+                               // (ex. image with no alt). We change null to empty string.\r
+                               this.id = '';\r
+                       } else {\r
+                               // String for custom data type.\r
+                               this.id = 'cke-' + CKEDITOR.tools.getUniqueId();\r
+                       }\r
+               }\r
+\r
+               // In IE10+ we can not use any data type besides text, so we do not call setData.\r
+               if ( clipboardIdDataType != 'Text' ) {\r
+                       // Try to set ID so it will be passed from the drag to the drop event.\r
+                       // On some browsers with some event it is not possible to setData so we\r
+                       // need to catch exceptions.\r
+                       try {\r
+                               this.$.setData( clipboardIdDataType, this.id );\r
+                       } catch ( err ) {}\r
+               }\r
+\r
+               if ( editor ) {\r
+                       this.sourceEditor = editor;\r
+\r
+                       this.setData( 'text/html', editor.getSelectedHtml( 1 ) );\r
+\r
+                       // Without setData( 'text', ... ) on dragstart there is no drop event in Safari.\r
+                       // Also 'text' data is empty as drop to the textarea does not work if we do not put there text.\r
+                       if ( clipboardIdDataType != 'Text' && !this.getData( 'text/plain' ) ) {\r
+                               this.setData( 'text/plain', editor.getSelection().getSelectedText() );\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Data transfer ID used to bind all dataTransfer\r
+                * objects based on the same event (e.g. in drag and drop events).\r
+                *\r
+                * @readonly\r
+                * @property {String} id\r
+                */\r
+\r
+               /**\r
+                * A native DOM event object.\r
+                *\r
+                * @readonly\r
+                * @property {Object} $\r
+                */\r
+\r
+               /**\r
+                * Source editor &mdash; the editor where the drag starts.\r
+                * Might be undefined if the drag starts outside the editor (e.g. when dropping files to the editor).\r
+                *\r
+                * @readonly\r
+                * @property {CKEDITOR.editor} sourceEditor\r
+                */\r
+\r
+               /**\r
+                * Private properties and methods.\r
+                *\r
+                * @private\r
+                * @property {Object} _\r
+                */\r
+       };\r
+\r
+       /**\r
+        * Data transfer operation (drag and drop or copy and paste) started and ended in the same\r
+        * editor instance.\r
+        *\r
+        * @since 4.5\r
+        * @readonly\r
+        * @property {Number} [=1]\r
+        * @member CKEDITOR\r
+        */\r
+       CKEDITOR.DATA_TRANSFER_INTERNAL = 1;\r
+\r
+       /**\r
+        * Data transfer operation (drag and drop or copy and paste) started in one editor\r
+        * instance and ended in another.\r
+        *\r
+        * @since 4.5\r
+        * @readonly\r
+        * @property {Number} [=2]\r
+        * @member CKEDITOR\r
+        */\r
+       CKEDITOR.DATA_TRANSFER_CROSS_EDITORS = 2;\r
+\r
+       /**\r
+        * Data transfer operation (drag and drop or copy and paste) started outside of the editor.\r
+        * The source of the data may be a textarea, HTML, another application, etc.\r
+        *\r
+        * @since 4.5\r
+        * @readonly\r
+        * @property {Number} [=3]\r
+        * @member CKEDITOR\r
+        */\r
+       CKEDITOR.DATA_TRANSFER_EXTERNAL = 3;\r
+\r
+       CKEDITOR.plugins.clipboard.dataTransfer.prototype = {\r
+               /**\r
+                * Facade for the native `getData` method.\r
+                *\r
+                * @param {String} type The type of data to retrieve.\r
+                * @param {Boolean} [getNative=false] Indicates if the whole, original content of the dataTransfer should be returned.\r
+                * Introduced in CKEditor 4.7.0.\r
+                * @returns {String} type Stored data for the given type or an empty string if the data for that type does not exist.\r
+                */\r
+               getData: function( type, getNative ) {\r
+                       function isEmpty( data ) {\r
+                               return data === undefined || data === null || data === '';\r
+                       }\r
+\r
+                       function filterUnwantedCharacters( data ) {\r
+                               if ( typeof data !== 'string' ) {\r
+                                       return data;\r
+                               }\r
+\r
+                               var htmlEnd = data.indexOf( '</html>' );\r
+\r
+                               if ( htmlEnd !== -1 ) {\r
+                                       // Just cut everything after `</html>`, so everything after htmlEnd index + length of `</html>`.\r
+                                       // Required to workaround bug: https://bugs.chromium.org/p/chromium/issues/detail?id=696978\r
+                                       return data.substring( 0, htmlEnd + 7 );\r
+                               }\r
+\r
+                               return data;\r
+                       }\r
+\r
+                       type = this._.normalizeType( type );\r
+\r
+                       var data = this._.data[ type ],\r
+                               result;\r
+\r
+                       if ( isEmpty( data ) ) {\r
+                               try {\r
+                                       data = this.$.getData( type );\r
+                               } catch ( e ) {}\r
+                       }\r
+\r
+                       if ( isEmpty( data ) ) {\r
+                               data = '';\r
+                       }\r
+\r
+                       // Some browsers add <meta http-equiv="content-type" content="text/html; charset=utf-8"> at the begging of the HTML data\r
+                       // or surround it with <html><head>...</head><body>(some content)<!--StartFragment--> and <!--EndFragment-->(some content)</body></html>\r
+                       // This code removes meta tags and returns only the contents of the <body> element if found. Note that\r
+                       // some significant content may be placed outside Start/EndFragment comments so it's kept.\r
+                       //\r
+                       // See http://dev.ckeditor.com/ticket/13583 for more details.\r
+                       // Additionally http://dev.ckeditor.com/ticket/16847 adds a flag allowing to get the whole, original content.\r
+                       if ( type == 'text/html' && !getNative ) {\r
+                               data = data.replace( this._.metaRegExp, '' );\r
+\r
+                               // Keep only contents of the <body> element\r
+                               result = this._.bodyRegExp.exec( data );\r
+                               if ( result && result.length ) {\r
+                                       data = result[ 1 ];\r
+\r
+                                       // Remove also comments.\r
+                                       data = data.replace( this._.fragmentRegExp, '' );\r
+                               }\r
+                       }\r
+                       // Firefox on Linux put files paths as a text/plain data if there are files\r
+                       // in the dataTransfer object. We need to hide it, because files should be\r
+                       // handled on paste only if dataValue is empty.\r
+                       else if ( type == 'Text' && CKEDITOR.env.gecko && this.getFilesCount() &&\r
+                               data.substring( 0, 7 ) == 'file://' ) {\r
+                               data = '';\r
+                       }\r
+\r
+                       return filterUnwantedCharacters( data );\r
+               },\r
+\r
+               /**\r
+                * Facade for the native `setData` method.\r
+                *\r
+                * @param {String} type The type of data to retrieve.\r
+                * @param {String} value The data to add.\r
+                */\r
+               setData: function( type, value ) {\r
+                       type = this._.normalizeType( type );\r
+\r
+                       this._.data[ type ] = value;\r
+\r
+                       // There is "Unexpected call to method or property access." error if you try\r
+                       // to set data of unsupported type on IE.\r
+                       if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && type != 'URL' && type != 'Text' ) {\r
+                               return;\r
+                       }\r
+\r
+                       // If we use the text type to bind the ID, then if someone tries to set the text, we must also\r
+                       // update ID accordingly. http://dev.ckeditor.com/ticket/13468.\r
+                       if ( clipboardIdDataType == 'Text' && type == 'Text' ) {\r
+                               this.id = value;\r
+                       }\r
+\r
+                       try {\r
+                               this.$.setData( type, value );\r
+                       } catch ( e ) {}\r
+               },\r
+\r
+               /**\r
+                * Gets the data transfer type.\r
+                *\r
+                * @param {CKEDITOR.editor} targetEditor The drop/paste target editor instance.\r
+                * @returns {Number} Possible values: {@link CKEDITOR#DATA_TRANSFER_INTERNAL},\r
+                * {@link CKEDITOR#DATA_TRANSFER_CROSS_EDITORS}, {@link CKEDITOR#DATA_TRANSFER_EXTERNAL}.\r
+                */\r
+               getTransferType: function( targetEditor ) {\r
+                       if ( !this.sourceEditor ) {\r
+                               return CKEDITOR.DATA_TRANSFER_EXTERNAL;\r
+                       } else if ( this.sourceEditor == targetEditor ) {\r
+                               return CKEDITOR.DATA_TRANSFER_INTERNAL;\r
+                       } else {\r
+                               return CKEDITOR.DATA_TRANSFER_CROSS_EDITORS;\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Copies the data from the native data transfer to a private cache.\r
+                * This function is needed because the data from the native data transfer\r
+                * is available only synchronously to the event listener. It is not possible\r
+                * to get the data asynchronously, after a timeout, and the {@link CKEDITOR.editor#paste}\r
+                * event is fired asynchronously &mdash; hence the need for caching the data.\r
+                */\r
+               cacheData: function() {\r
+                       if ( !this.$ ) {\r
+                               return;\r
+                       }\r
+\r
+                       var that = this,\r
+                               i, file;\r
+\r
+                       function getAndSetData( type ) {\r
+                               type = that._.normalizeType( type );\r
+\r
+                               var data = that.getData( type, true );\r
+                               if ( data ) {\r
+                                       that._.data[ type ] = data;\r
+                               }\r
+                       }\r
+\r
+                       // Copy data.\r
+                       if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {\r
+                               if ( this.$.types ) {\r
+                                       for ( i = 0; i < this.$.types.length; i++ ) {\r
+                                               getAndSetData( this.$.types[ i ] );\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               getAndSetData( 'Text' );\r
+                               getAndSetData( 'URL' );\r
+                       }\r
+\r
+                       // Copy files references.\r
+                       file = this._getImageFromClipboard();\r
+                       if ( ( this.$ && this.$.files ) || file ) {\r
+                               this._.files = [];\r
+\r
+                               // Edge have empty files property with no length (http://dev.ckeditor.com/ticket/13755).\r
+                               if ( this.$.files && this.$.files.length ) {\r
+                                       for ( i = 0; i < this.$.files.length; i++ ) {\r
+                                               this._.files.push( this.$.files[ i ] );\r
+                                       }\r
+                               }\r
+\r
+                               // Don't include $.items if both $.files and $.items contains files, because,\r
+                               // according to spec and browsers behavior, they contain the same files.\r
+                               if ( this._.files.length === 0 && file ) {\r
+                                       this._.files.push( file );\r
+                               }\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Gets the number of files in the dataTransfer object.\r
+                *\r
+                * @returns {Number} The number of files.\r
+                */\r
+               getFilesCount: function() {\r
+                       if ( this._.files.length ) {\r
+                               return this._.files.length;\r
+                       }\r
+\r
+                       if ( this.$ && this.$.files && this.$.files.length ) {\r
+                               return this.$.files.length;\r
+                       }\r
+\r
+                       return this._getImageFromClipboard() ? 1 : 0;\r
+               },\r
+\r
+               /**\r
+                * Gets the file at the index given.\r
+                *\r
+                * @param {Number} i Index.\r
+                * @returns {File} File instance.\r
+                */\r
+               getFile: function( i ) {\r
+                       if ( this._.files.length ) {\r
+                               return this._.files[ i ];\r
+                       }\r
+\r
+                       if ( this.$ && this.$.files && this.$.files.length ) {\r
+                               return this.$.files[ i ];\r
+                       }\r
+\r
+                       // File or null if the file was not found.\r
+                       return i === 0 ? this._getImageFromClipboard() : undefined;\r
+               },\r
+\r
+               /**\r
+                * Checks if the data transfer contains any data.\r
+                *\r
+                * @returns {Boolean} `true` if the object contains no data.\r
+                */\r
+               isEmpty: function() {\r
+                       var typesToCheck = {},\r
+                               type;\r
+\r
+                       // If dataTransfer contains files it is not empty.\r
+                       if ( this.getFilesCount() ) {\r
+                               return false;\r
+                       }\r
+\r
+                       // Add custom types.\r
+                       for ( type in this._.data ) {\r
+                               typesToCheck[ type ] = 1;\r
+                       }\r
+\r
+                       // Add native types.\r
+                       if ( this.$ ) {\r
+                               if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {\r
+                                       if ( this.$.types ) {\r
+                                               for ( var i = 0; i < this.$.types.length; i++ ) {\r
+                                                       typesToCheck[ this.$.types[ i ] ] = 1;\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       typesToCheck.Text = 1;\r
+                                       typesToCheck.URL = 1;\r
+                               }\r
+                       }\r
+\r
+                       // Remove ID.\r
+                       if ( clipboardIdDataType != 'Text' ) {\r
+                               typesToCheck[ clipboardIdDataType ] = 0;\r
+                       }\r
+\r
+                       for ( type in typesToCheck ) {\r
+                               if ( typesToCheck[ type ] && this.getData( type ) !== '' ) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+\r
+                       return true;\r
+               },\r
+\r
+               /**\r
+                * When the content of the clipboard is pasted in Chrome, the clipboard data object has an empty `files` property,\r
+                * but it is possible to get the file as `items[0].getAsFile();` (http://dev.ckeditor.com/ticket/12961).\r
+                *\r
+                * @private\r
+                * @returns {File} File instance or `null` if not found.\r
+                */\r
+               _getImageFromClipboard: function() {\r
+                       var file;\r
+\r
+                       if ( this.$ && this.$.items && this.$.items[ 0 ] ) {\r
+                               try {\r
+                                       file = this.$.items[ 0 ].getAsFile();\r
+                                       // Duck typing\r
+                                       if ( file && file.type ) {\r
+                                               return file;\r
+                                       }\r
+                               } catch ( err ) {\r
+                                       // noop\r
+                               }\r
+                       }\r
+\r
+                       return undefined;\r
+               }\r
+       };\r
+} )();\r
+\r
+/**\r
+ * The default content type that is used when pasted data cannot be clearly recognized as HTML or text.\r
+ *\r
+ * For example: `'foo'` may come from a plain text editor or a website. It is not possible to recognize the content\r
+ * type in this case, so the default type will be used. At the same time it is clear that `'<b>example</b> text'` is\r
+ * HTML and its origin is a web page, email or another rich text editor.\r
+ *\r
+ * **Note:** If content type is text, then styles of the paste context are preserved.\r
+ *\r
+ *             CKEDITOR.config.clipboard_defaultContentType = 'text';\r
+ *\r
+ * See also the {@link CKEDITOR.editor#paste} event and read more about the integration with clipboard\r
+ * in the [Clipboard Deep Dive guide](#!/guide/dev_clipboard).\r
+ *\r
+ * @since 4.0\r
+ * @cfg {'html'/'text'} [clipboard_defaultContentType='html']\r
+ * @member CKEDITOR.config\r
+ */\r
+\r
+/**\r
+ * Fired after the user initiated a paste action, but before the data is inserted into the editor.\r
+ * The listeners to this event are able to process the content before its insertion into the document.\r
+ *\r
+ * Read more about the integration with clipboard in the [Clipboard Deep Dive guide](#!/guide/dev_clipboard).\r
+ *\r
+ * See also:\r
+ *\r
+ * * the {@link CKEDITOR.config#pasteFilter} option,\r
+ * * the {@link CKEDITOR.editor#drop} event,\r
+ * * the {@link CKEDITOR.plugins.clipboard.dataTransfer} class.\r
+ *\r
+ * @since 3.1\r
+ * @event paste\r
+ * @member CKEDITOR.editor\r
+ * @param {CKEDITOR.editor} editor This editor instance.\r
+ * @param data\r
+ * @param {String} data.type The type of data in `data.dataValue`. Usually `'html'` or `'text'`, but for listeners\r
+ * with a priority smaller than `6` it may also be `'auto'` which means that the content type has not been recognised yet\r
+ * (this will be done by the content type sniffer that listens with priority `6`).\r
+ * @param {String} data.dataValue HTML to be pasted.\r
+ * @param {String} data.method Indicates the data transfer method. It could be drag and drop or copy and paste.\r
+ * Possible values: `'drop'`, `'paste'`. Introduced in CKEditor 4.5.\r
+ * @param {CKEDITOR.plugins.clipboard.dataTransfer} data.dataTransfer Facade for the native dataTransfer object\r
+ * which provides access to various data types and files, and passes some data between linked events\r
+ * (like drag and drop). Introduced in CKEditor 4.5.\r
+ * @param {Boolean} [data.dontFilter=false] Whether the {@link CKEDITOR.editor#pasteFilter paste filter} should not\r
+ * be applied to data. This option has no effect when `data.type` equals `'text'` which means that for instance\r
+ * {@link CKEDITOR.config#forcePasteAsPlainText} has a higher priority. Introduced in CKEditor 4.5.\r
+ */\r
+\r
+/**\r
+ * Fired before the {@link #paste} event. Allows to preset data type.\r
+ *\r
+ * **Note:** This event is deprecated. Add a `0` priority listener for the\r
+ * {@link #paste} event instead.\r
+ *\r
+ * @deprecated\r
+ * @event beforePaste\r
+ * @member CKEDITOR.editor\r
+ */\r
+\r
+/**\r
+ * Fired after the {@link #paste} event if content was modified. Note that if the paste\r
+ * event does not insert any data, the `afterPaste` event will not be fired.\r
+ *\r
+ * @event afterPaste\r
+ * @member CKEDITOR.editor\r
+ */\r
+\r
+/**\r
+ * Facade for the native `drop` event. Fired when the native `drop` event occurs.\r
+ *\r
+ * **Note:** To manipulate dropped data, use the {@link CKEDITOR.editor#paste} event.\r
+ * Use the `drop` event only to control drag and drop operations (e.g. to prevent the ability to drop some content).\r
+ *\r
+ * Read more about integration with drag and drop in the [Clipboard Deep Dive guide](#!/guide/dev_clipboard).\r
+ *\r
+ * See also:\r
+ *\r
+ * * The {@link CKEDITOR.editor#paste} event,\r
+ * * The {@link CKEDITOR.editor#dragstart} and {@link CKEDITOR.editor#dragend} events,\r
+ * * The {@link CKEDITOR.plugins.clipboard.dataTransfer} class.\r
+ *\r
+ * @since 4.5\r
+ * @event drop\r
+ * @member CKEDITOR.editor\r
+ * @param {CKEDITOR.editor} editor This editor instance.\r
+ * @param data\r
+ * @param {Object} data.$ Native drop event.\r
+ * @param {CKEDITOR.dom.node} data.target Drop target.\r
+ * @param {CKEDITOR.plugins.clipboard.dataTransfer} data.dataTransfer DataTransfer facade.\r
+ * @param {CKEDITOR.dom.range} data.dragRange Drag range, lets you manipulate the drag range.\r
+ * Note that dragged HTML is saved as `text/html` data on `dragstart` so if you change the drag range\r
+ * on drop, dropped HTML will not change. You need to change it manually using\r
+ * {@link CKEDITOR.plugins.clipboard.dataTransfer#setData dataTransfer.setData}.\r
+ * @param {CKEDITOR.dom.range} data.dropRange Drop range, lets you manipulate the drop range.\r
+ */\r
+\r
+/**\r
+ * Facade for the native `dragstart` event. Fired when the native `dragstart` event occurs.\r
+ *\r
+ * This event can be canceled in order to block the drag start operation. It can also be fired to mimic the start of the drag and drop\r
+ * operation. For instance, the `widget` plugin uses this option to integrate its custom block widget drag and drop with\r
+ * the entire system.\r
+ *\r
+ * Read more about integration with drag and drop in the [Clipboard Deep Dive guide](#!/guide/dev_clipboard).\r
+ *\r
+ * See also:\r
+ *\r
+ * * The {@link CKEDITOR.editor#paste} event,\r
+ * * The {@link CKEDITOR.editor#drop} and {@link CKEDITOR.editor#dragend} events,\r
+ * * The {@link CKEDITOR.plugins.clipboard.dataTransfer} class.\r
+ *\r
+ * @since 4.5\r
+ * @event dragstart\r
+ * @member CKEDITOR.editor\r
+ * @param {CKEDITOR.editor} editor This editor instance.\r
+ * @param data\r
+ * @param {Object} data.$ Native dragstart event.\r
+ * @param {CKEDITOR.dom.node} data.target Drag target.\r
+ * @param {CKEDITOR.plugins.clipboard.dataTransfer} data.dataTransfer DataTransfer facade.\r
+ */\r
+\r
+/**\r
+ * Facade for the native `dragend` event. Fired when the native `dragend` event occurs.\r
+ *\r
+ * Read more about integration with drag and drop in the [Clipboard Deep Dive guide](#!/guide/dev_clipboard).\r
+ *\r
+ * See also:\r
+ *\r
+ * * The {@link CKEDITOR.editor#paste} event,\r
+ * * The {@link CKEDITOR.editor#drop} and {@link CKEDITOR.editor#dragend} events,\r
+ * * The {@link CKEDITOR.plugins.clipboard.dataTransfer} class.\r
+ *\r
+ * @since 4.5\r
+ * @event dragend\r
+ * @member CKEDITOR.editor\r
+ * @param {CKEDITOR.editor} editor This editor instance.\r
+ * @param data\r
+ * @param {Object} data.$ Native dragend event.\r
+ * @param {CKEDITOR.dom.node} data.target Drag target.\r
+ * @param {CKEDITOR.plugins.clipboard.dataTransfer} data.dataTransfer DataTransfer facade.\r
+ */\r
+\r
+/**\r
+ * Defines a filter which is applied to external data pasted or dropped into the editor. Possible values are:\r
+ *\r
+ * * `'plain-text'` &ndash; Content will be pasted as a plain text.\r
+ * * `'semantic-content'` &ndash; Known tags (except `div`, `span`) with all attributes (except\r
+ * `style` and `class`) will be kept.\r
+ * * `'h1 h2 p div'` &ndash; Custom rules compatible with {@link CKEDITOR.filter}.\r
+ * * `null` &ndash; Content will not be filtered by the paste filter (but it still may be filtered\r
+ * by [Advanced Content Filter](#!/guide/dev_advanced_content_filter)). This value can be used to\r
+ * disable the paste filter in Chrome and Safari, where this option defaults to `'semantic-content'`.\r
+ *\r
+ * Example:\r
+ *\r
+ *             config.pasteFilter = 'plain-text';\r
+ *\r
+ * Custom setting:\r
+ *\r
+ *             config.pasteFilter = 'h1 h2 p ul ol li; img[!src, alt]; a[!href]';\r
+ *\r
+ * Based on this configuration option, a proper {@link CKEDITOR.filter} instance will be defined and assigned to the editor\r
+ * as a {@link CKEDITOR.editor#pasteFilter}. You can tweak the paste filter settings on the fly on this object\r
+ * as well as delete or replace it.\r
+ *\r
+ *             var editor = CKEDITOR.replace( 'editor', {\r
+ *                     pasteFilter: 'semantic-content'\r
+ *             } );\r
+ *\r
+ *             editor.on( 'instanceReady', function() {\r
+ *                     // The result of this will be that all semantic content will be preserved\r
+ *                     // except tables.\r
+ *                     editor.pasteFilter.disallow( 'table' );\r
+ *             } );\r
+ *\r
+ * Note that the paste filter is applied only to **external** data. There are three data sources:\r
+ *\r
+ * * copied and pasted in the same editor (internal),\r
+ * * copied from one editor and pasted into another (cross-editor),\r
+ * * coming from all other sources like websites, MS Word, etc. (external).\r
+ *\r
+ * If {@link CKEDITOR.config#allowedContent Advanced Content Filter} is not disabled, then\r
+ * it will also be applied to pasted and dropped data. The paste filter job is to "normalize"\r
+ * external data which often needs to be handled differently than content produced by the editor.\r
+ *\r
+ * This setting defaults to `'semantic-content'` in Chrome, Opera and Safari (all Blink and Webkit based browsers)\r
+ * due to messy HTML which these browsers keep in the clipboard. In other browsers it defaults to `null`.\r
+ *\r
+ * @since 4.5\r
+ * @cfg {String} [pasteFilter='semantic-content' in Chrome and Safari and `null` in other browsers]\r
+ * @member CKEDITOR.config\r
+ */\r
+\r
+/**\r
+ * {@link CKEDITOR.filter Content filter} which is used when external data is pasted or dropped into the editor\r
+ * or a forced paste as plain text occurs.\r
+ *\r
+ * This object might be used on the fly to define rules for pasted external content.\r
+ * This object is available and used if the {@link CKEDITOR.plugins.clipboard clipboard} plugin is enabled and\r
+ * {@link CKEDITOR.config#pasteFilter} or {@link CKEDITOR.config#forcePasteAsPlainText} was defined.\r
+ *\r
+ * To enable the filter:\r
+ *\r
+ *             var editor = CKEDITOR.replace( 'editor', {\r
+ *                     pasteFilter: 'plain-text'\r
+ *             } );\r
+ *\r
+ * You can also modify the filter on the fly later on:\r
+ *\r
+ *             editor.pasteFilter = new CKEDITOR.filter( 'p h1 h2; a[!href]' );\r
+ *\r
+ * Note that the paste filter is only applied to **external** data. There are three data sources:\r
+ *\r
+ * * copied and pasted in the same editor (internal),\r
+ * * copied from one editor and pasted into another (cross-editor),\r
+ * * coming from all other sources like websites, MS Word, etc. (external).\r
+ *\r
+ * If {@link CKEDITOR.config#allowedContent Advanced Content Filter} is not disabled, then\r
+ * it will also be applied to pasted and dropped data. The paste filter job is to "normalize"\r
+ * external data which often needs to be handled differently than content produced by the editor.\r
+ *\r
+ * @since 4.5\r
+ * @readonly\r
+ * @property {CKEDITOR.filter} [pasteFilter]\r
+ * @member CKEDITOR.editor\r
+ */\r
+\r
+/**\r
+ * Duration of the notification displayed after pasting was blocked by the browser.\r
+ *\r
+ * @since 4.7.0\r
+ * @cfg {Number} [clipboard_notificationDuration=10000]\r
+ * @member CKEDITOR.config\r
+ */\r
+CKEDITOR.config.clipboard_notificationDuration = 10000;\r