]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blobdiff - sources/plugins/clipboard/plugin.js
Update to 4.7.3
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / clipboard / plugin.js
index 5c387b3a74944db5e1383fef9f2480637b6f7d35..433f547366860502fcc91e013e45148b955a4c5c 100644 (file)
@@ -44,7 +44,6 @@
 // -- Paste command\r
 //             * fire 'paste' on editable ('beforepaste' for IE)\r
 //             * !canceled && execCommand 'paste'\r
-//             * !success && fire 'pasteDialog' on editor\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
 ( function() {\r
        // Register the plugin.\r
        CKEDITOR.plugins.add( 'clipboard', {\r
-               requires: 'dialog',\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,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
+               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
                        initPasteClipboard( editor );\r
                        initDragDrop( editor );\r
 \r
-                       CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) );\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
                                                data = dataObj.dataValue,\r
                                                dataTransfer = dataObj.dataTransfer;\r
 \r
-                                       // If data empty check for image content inside data transfer. #16705\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
                                        data = data.replace( /(<[^>]+) class="Apple-[^"]*"/gi, '$1' );\r
                                }\r
 \r
-                               // Strip editable that was copied from inside. (#9534)\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. (#9675)\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
 \r
                        editor.on( 'paste', function( evt ) {\r
                                var dataObj = evt.data,\r
-                                       type = dataObj.type,\r
+                                       type = editor._.nextPasteType || dataObj.type,\r
                                        data = dataObj.dataValue,\r
                                        trueType,\r
                                        // Default is 'html'.\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 (dialog or forcePAPT).\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
                                        }, 0 );\r
                                }\r
                        }, null, null, 1000 );\r
-\r
-                       editor.on( 'pasteDialog', function( evt ) {\r
-                               // TODO it's possible that this setTimeout is not needed any more,\r
-                               // because of changes introduced in the same commit as this comment.\r
-                               // Editor.getClipboardData adds listener to the dialog's events which are\r
-                               // fired after a while (not like 'showDialog').\r
-                               setTimeout( function() {\r
-                                       // Open default paste dialog.\r
-                                       editor.openDialog( 'paste', evt.data );\r
-                               }, 0 );\r
-                       } );\r
                }\r
        } );\r
 \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 (#12420).\r
-               // Also, check that editor.toolbox exists, because the toolbar plugin might not be loaded (#13305).\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
                addButtonsCommands();\r
 \r
                /**\r
-                * Gets clipboard data by directly accessing the clipboard (IE only) or opening the paste dialog window.\r
+                * Gets clipboard data by directly accessing the clipboard (IE only).\r
                 *\r
-                *              editor.getClipboardData( { title: 'Get my data' }, function( data ) {\r
+                *              editor.getClipboardData( function( data ) {\r
                 *                      if ( data )\r
                 *                              alert( data.type + ' ' + data.dataValue );\r
                 *              } );\r
                 *\r
                 * @member CKEDITOR.editor\r
-                * @param {Object} options\r
-                * @param {String} [options.title] The title of the paste dialog window.\r
-                * @param {Function} callback A function that will be executed with `data.type` and `data.dataValue`\r
-                * or `null` if none of the capturing methods succeeded.\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( options, callback ) {\r
-                       var beforePasteNotCanceled = false,\r
-                               dataType = 'auto',\r
-                               dialogCommited = false;\r
-\r
+               editor.getClipboardData = function( callbackOrOptions, callback ) {\r
                        // Options are optional - args shift.\r
                        if ( !callback ) {\r
-                               callback = options;\r
-                               options = null;\r
+                               callback = callbackOrOptions;\r
+                               callbackOrOptions = null;\r
                        }\r
 \r
                        // Listen with maximum priority to handle content before everyone else.\r
                        // access to the clipboard succeed in IE.\r
                        editor.on( 'paste', onPaste, null, null, 0 );\r
 \r
-                       // Listen at the end of listeners chain to see if event wasn't canceled\r
-                       // and to retrieve modified data.type.\r
-                       editor.on( 'beforePaste', onBeforePaste, null, null, 1000 );\r
-\r
-                       // getClipboardDataDirectly() will fire 'beforePaste' synchronously, so we can\r
-                       // check if it was canceled and if any listener modified data.type.\r
-\r
                        // If command didn't succeed (only IE allows to access clipboard and only if\r
-                       // user agrees) open and handle paste dialog.\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
-                               // If beforePaste was canceled do not open dialog.\r
-                               // Add listeners only if dialog really opened. 'pasteDialog' can be canceled.\r
-                               if ( beforePasteNotCanceled && editor.fire( 'pasteDialog', onDialogOpen ) ) {\r
-                                       editor.on( 'pasteDialogCommit', onDialogCommit );\r
-\r
-                                       // 'dialogHide' will be fired after 'pasteDialogCommit'.\r
-                                       editor.on( 'dialogHide', function( evt ) {\r
-                                               evt.removeListener();\r
-                                               evt.data.removeListener( 'pasteDialogCommit', onDialogCommit );\r
-\r
-                                               // Because Opera has to wait a while in pasteDialog we have to wait here.\r
-                                               setTimeout( function() {\r
-                                                       // Notify even if user canceled dialog (clicked 'cancel', ESC, etc).\r
-                                                       if ( !dialogCommited )\r
-                                                               callback( null );\r
-                                               }, 10 );\r
-                                       } );\r
-                               } else {\r
-                                       callback( null );\r
-                               }\r
+                               callback( null );\r
                        }\r
 \r
                        function onPaste( evt ) {\r
                                evt.cancel();\r
                                callback( evt.data );\r
                        }\r
-\r
-                       function onBeforePaste( evt ) {\r
-                               evt.removeListener();\r
-                               beforePasteNotCanceled = true;\r
-                               dataType = evt.data.type;\r
-                       }\r
-\r
-                       function onDialogCommit( evt ) {\r
-                               evt.removeListener();\r
-                               // Cancel pasteDialogCommit so paste dialog won't automatically fire\r
-                               // 'paste' evt by itself.\r
-                               evt.cancel();\r
-                               dialogCommited = true;\r
-                               callback( {\r
-                                       type: dataType,\r
-                                       dataValue: evt.data.dataValue,\r
-                                       dataTransfer: evt.data.dataTransfer,\r
-                                       method: 'paste'\r
-                               } );\r
-                       }\r
-\r
-                       function onDialogOpen() {\r
-                               this.customTitle = ( options && options.title );\r
-                       }\r
                };\r
 \r
                function addButtonsCommands() {\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. (#13872)\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
 \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. (#13872)\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
                                        pasteDataFromClipboard( evt );\r
 \r
                                        // Force IE to paste content into pastebin so pasteDataFromClipboard will work.\r
-                                       if ( !execIECommand( 'paste' ) ) {\r
-                                               editor.openDialog( 'paste' );\r
-                                       }\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. (#7953)\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 (#11970).\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
                        // 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 (#9851).\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
 \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) (#10219).\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
                                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
-                                               fire = function( data, withBeforePaste ) {\r
-                                                       data && firePasteEvents( editor, data, !!withBeforePaste );\r
-\r
-                                                       editor.fire( 'afterCommandExec', {\r
-                                                               name: 'paste',\r
-                                                               command: cmd,\r
-                                                               returnValue: !!data\r
-                                                       } );\r
-                                               };\r
-\r
-                                       // Check data precisely - don't open dialog on empty string.\r
-                                       if ( typeof data == 'string' )\r
-                                               fire( {\r
-                                                               dataValue: data,\r
-                                                               method: 'paste',\r
-                                                               dataTransfer: clipboard.initPasteDataTransfer()\r
-                                                       }, 1 );\r
-                                       else\r
-                                               editor.getClipboardData( fire );\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
                        return enabled;\r
                }\r
 \r
-               // Cutting off control type element in IE standards breaks the selection entirely. (#4881)\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
                                blurListener;\r
 \r
-                       // Avoid recursions on 'paste' event or consequent paste too fast. (#5730)\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
-                       // #11384. On IE9+ we use native selectionchange (i.e. editor#selectionCheck) to cache the most\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
                                // 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. (#9754)\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
                                padding: 0\r
                        } );\r
 \r
-                       // Paste fails in Safari when the body tag has 'user-select: none'. (#12506)\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
 \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 (#10644).\r
-                       // The same seems to apply to Firefox (#10787).\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
                        // 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. (#9552)\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
                        // 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 #9771.\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. (#8849)\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. (#8691)\r
+                               // IE7: selection must go before removing pastebin. (http://dev.ckeditor.com/ticket/8691)\r
                                sel.selectBookmarks( bms );\r
                                pastebin.remove();\r
 \r
                // On other browsers we should fire beforePaste event and return false.\r
                function getClipboardDataDirectly() {\r
                        if ( clipboard.mainPasteEvent == 'paste' ) {\r
-                               // beforePaste should be fired when dialog open so it can be canceled.\r
                                editor.fire( 'beforePaste', { type: 'auto', method: 'paste' } );\r
                                return false;\r
                        }\r
                        // we're canceling it.\r
                        preventPasteEventNow();\r
 \r
-                       // #9247: Lock focus to prevent IE from hiding toolbar for inline editor.\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
                                        editor.fire( 'saveSnapshot' ); // Save before cut\r
                                        setTimeout( function() {\r
                                                editor.fire( 'saveSnapshot' ); // Save after cut\r
-                                       }, 50 ); // OSX is slow (#11416).\r
+                                       }, 50 ); // OSX is slow (http://dev.ckeditor.com/ticket/11416).\r
                        }\r
                }\r
 \r
 \r
                        // -------------- DRAGOVER TOP & BOTTOM --------------\r
 \r
-                       // Not allowing dragging on toolbar and bottom (#12613).\r
+                       // Not allowing dragging on toolbar and bottom (http://dev.ckeditor.com/ticket/12613).\r
                        clipboard.preventDefaultDropOnElement( top );\r
                        clipboard.preventDefaultDropOnElement( bottom );\r
 \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. (#13011, #13447)\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
                        // 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 (#12619).\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
                        // -------------- DROP --------------\r
 \r
                        editable.attachListener( dropTarget, 'drop', function( evt ) {\r
-                               // Do nothing if event was already prevented. (#13879)\r
+                               // Do nothing if event was already prevented. (http://dev.ckeditor.com/ticket/13879)\r
                                if ( evt.data.$.defaultPrevented ) {\r
                                        return;\r
                                }\r
                                var target = evt.data.getTarget(),\r
                                        readOnly = target.isReadOnly();\r
 \r
-                               // Do nothing if drop on non editable element (#13015).\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
                        }\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 (#13187).\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 (#13528, https://bugzilla.mozilla.org/show_bug.cgi?id=1183686).\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
-                       // In Safari and IE HTML data is not available though the Clipboard API.\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
                getDropTarget: function( editor ) {\r
                        var editable = editor.editable();\r
 \r
-                       // #11123 Firefox needs to listen on document, because otherwise event won't be fired.\r
-                       // #11086 IE8 cannot listen on document.\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
                        // 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. (#13453)\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
                                return dropEvt.data.testRange;\r
 \r
                        // Webkits.\r
-                       if ( document.caretRangeFromPoint ) {\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
                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 (#13755).\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
                 * 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 ) {\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
                        // 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 #13583 for more details.\r
-                       if ( type == 'text/html' ) {\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
                                data = '';\r
                        }\r
 \r
-                       return data;\r
+                       return filterUnwantedCharacters( data );\r
                },\r
 \r
                /**\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. #13468.\r
+                       // update ID accordingly. http://dev.ckeditor.com/ticket/13468.\r
                        if ( clipboardIdDataType == 'Text' && type == 'Text' ) {\r
                                this.id = value;\r
                        }\r
                        function getAndSetData( type ) {\r
                                type = that._.normalizeType( type );\r
 \r
-                               var data = that.getData( type );\r
+                               var data = that.getData( type, true );\r
                                if ( data ) {\r
                                        that._.data[ type ] = data;\r
                                }\r
                        if ( ( this.$ && this.$.files ) || file ) {\r
                                this._.files = [];\r
 \r
-                               // Edge have empty files property with no length (#13755).\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
                 * 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();` (#12961).\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
  * @member CKEDITOR.editor\r
  */\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
  * @member CKEDITOR.editor\r
  */\r
 \r
-/**\r
- * Internal event to open the Paste dialog window.\r
- *\r
- * @private\r
- * @event pasteDialog\r
- * @member CKEDITOR.editor\r
- * @param {CKEDITOR.editor} editor This editor instance.\r
- * @param {Function} [data] Callback that will be passed to {@link CKEDITOR.editor#openDialog}.\r
- */\r
-\r
 /**\r
  * Facade for the native `drop` event. Fired when the native `drop` event occurs.\r
  *\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