]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/tinymce/plugins/wpeditimage/plugin.js
WordPress 4.7
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / wpeditimage / plugin.js
index acda79975061fe1b37e486d674bb3a47ab5c8365..25d3356265b53bf322a97f157f7544061da1ca1b 100644 (file)
@@ -1,13 +1,9 @@
 /* global tinymce */
 tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
-       var floatingToolbar, serializer,
-               DOM = tinymce.DOM,
-               settings = editor.settings,
-               Factory = tinymce.ui.Factory,
+       var toolbar, serializer, touchOnImage, pasteInCaption,
                each = tinymce.each,
-               iOS = tinymce.Env.iOS,
-               toolbarIsHidden = true,
-               editorWrapParent = tinymce.$( '#postdivrich' );
+               trim = tinymce.trim,
+               iOS = tinymce.Env.iOS;
 
        function isPlaceholder( node ) {
                return !! ( editor.dom.getAttrib( node, 'data-mce-placeholder' ) || editor.dom.getAttrib( node, 'data-mce-object' ) );
@@ -41,7 +37,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                        tooltip: tooltip,
                        icon: 'dashicon dashicons-align-' + direction,
                        cmd: 'alignnone' === name ? 'wpAlignNone' : 'Justify' + direction.slice( 0, 1 ).toUpperCase() + direction.slice( 1 ),
-                       onPostRender: function() {
+                       onPostRender: function() {
                                var self = this;
 
                                editor.on( 'NodeChange', function( event ) {
@@ -64,298 +60,64 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                } );
        } );
 
-       function toolbarConfig() {
-               var toolbarItems = [],
-                       buttonGroup;
-
-               each( [ 'wp_img_alignleft', 'wp_img_aligncenter', 'wp_img_alignright', 'wp_img_alignnone', 'wp_img_edit', 'wp_img_remove' ], function( item ) {
-                       var itemName;
-
-                       function bindSelectorChanged() {
-                               var selection = editor.selection;
-
-                               if ( item.settings.stateSelector ) {
-                                       selection.selectorChanged( item.settings.stateSelector, function( state ) {
-                                               item.active( state );
-                                       }, true );
-                               }
-
-                               if ( item.settings.disabledStateSelector ) {
-                                       selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
-                                               item.disabled( state );
-                                       } );
-                               }
-                       }
-
-                       if ( item === '|' ) {
-                               buttonGroup = null;
-                       } else {
-                               if ( Factory.has( item ) ) {
-                                       item = {
-                                               type: item
-                                       };
-
-                                       if ( settings.toolbar_items_size ) {
-                                               item.size = settings.toolbar_items_size;
-                                       }
-
-                                       toolbarItems.push( item );
-
-                                       buttonGroup = null;
-                               } else {
-                                       if ( ! buttonGroup ) {
-                                               buttonGroup = {
-                                                       type: 'buttongroup',
-                                                       items: []
-                                               };
-
-                                               toolbarItems.push( buttonGroup );
-                                       }
-
-                                       if ( editor.buttons[ item ] ) {
-                                               itemName = item;
-                                               item = editor.buttons[ itemName ];
-
-                                               if ( typeof item === 'function' ) {
-                                                       item = item();
-                                               }
-
-                                               item.type = item.type || 'button';
-
-                                               if ( settings.toolbar_items_size ) {
-                                                       item.size = settings.toolbar_items_size;
-                                               }
-
-                                               item = Factory.create( item );
-                                               buttonGroup.items.push( item );
-
-                                               if ( editor.initialized ) {
-                                                       bindSelectorChanged();
-                                               } else {
-                                                       editor.on( 'init', bindSelectorChanged );
-                                               }
-                                       }
-                               }
-                       }
-               } );
-
-               return {
-                       type: 'panel',
-                       layout: 'stack',
-                       classes: 'toolbar-grp inline-toolbar-grp wp-image-toolbar',
-                       ariaRoot: true,
-                       ariaRemember: true,
-                       items: [
-                               {
-                                       type: 'toolbar',
-                                       layout: 'flow',
-                                       items: toolbarItems
-                               }
-                       ]
-               };
-       }
-
-       floatingToolbar = Factory.create( toolbarConfig() ).renderTo( document.body ).hide();
-
-       floatingToolbar.reposition = function() {
-               var top, left, minTop, className,
-                       windowPos, adminbar, mceToolbar, boundary,
-                       boundaryMiddle, boundaryVerticalMiddle, spaceTop,
-                       spaceBottom, windowWidth, toolbarWidth, toolbarHalf,
-                       iframe, iframePos, iframeWidth, iframeHeigth,
-                       toolbarNodeHeight, verticalSpaceNeeded,
-                       toolbarNode = this.getEl(),
-                       buffer = 5,
-                       margin = 8,
-                       adminbarHeight = 0,
-                       imageNode = editor.selection.getNode();
-
-               if ( ! imageNode || imageNode.nodeName !== 'IMG' ) {
-                       return this;
-               }
-
-               windowPos = window.pageYOffset || document.documentElement.scrollTop;
-               adminbar = tinymce.$( '#wpadminbar' )[0];
-               mceToolbar = tinymce.$( '.mce-tinymce .mce-toolbar-grp' )[0];
-               boundary = imageNode.getBoundingClientRect();
-               boundaryMiddle = ( boundary.left + boundary.right ) / 2;
-               boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2;
-               spaceTop = boundary.top;
-               spaceBottom = iframeHeigth - boundary.bottom;
-               windowWidth = window.innerWidth;
-               toolbarWidth = toolbarNode.offsetWidth;
-               toolbarHalf = toolbarWidth / 2;
-               iframe = document.getElementById( editor.id + '_ifr' );
-               iframePos = DOM.getPos( iframe );
-               iframeWidth = iframe.offsetWidth;
-               iframeHeigth = iframe.offsetHeight;
-               toolbarNodeHeight = toolbarNode.offsetHeight;
-               verticalSpaceNeeded = toolbarNodeHeight + margin + buffer;
-
-               if ( iOS ) {
-                       top = boundary.top + iframePos.y + margin;
-               } else {
-                       if ( spaceTop >= verticalSpaceNeeded ) {
-                               className = ' mce-arrow-down';
-                               top = boundary.top + iframePos.y - toolbarNodeHeight - margin;
-                       } else if ( spaceBottom >= verticalSpaceNeeded ) {
-                               className = ' mce-arrow-up';
-                               top = boundary.bottom + iframePos.y;
-                       } else {
-                               top = buffer;
-
-                               if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) {
-                                       className = ' mce-arrow-down';
-                               } else {
-                                       className = ' mce-arrow-up';
-                               }
-                       }
-               }
-
-               // Make sure the image toolbar is below the main toolbar.
-               if ( mceToolbar ) {
-                       minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight;
-               } else {
-                       minTop = iframePos.y;
-               }
-
-               // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window.
-               if ( windowPos ) {
-                       if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) {
-                               adminbarHeight = adminbar.clientHeight;
-                       }
-
-                       if ( windowPos + adminbarHeight > minTop ) {
-                               minTop = windowPos + adminbarHeight;
-                       }
-               }
-
-               if ( top && minTop && ( minTop + buffer > top ) ) {
-                       top = minTop + buffer;
-                       className = '';
-               }
-
-               left = boundaryMiddle - toolbarHalf;
-               left += iframePos.x;
-
-               if ( boundary.left < 0 || boundary.right > iframeWidth ) {
-                       left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2;
-               } else if ( toolbarWidth >= windowWidth ) {
-                       className += ' mce-arrow-full';
-                       left = 0;
-               } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) ||
-                       ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) {
-
-                       left = ( windowWidth - toolbarWidth ) / 2;
-               } else if ( left < iframePos.x ) {
-                       className += ' mce-arrow-left';
-                       left = boundary.left + iframePos.x;
-               } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) {
-                       className += ' mce-arrow-right';
-                       left = boundary.right - toolbarWidth + iframePos.x;
+       editor.once( 'preinit', function() {
+               if ( editor.wp && editor.wp._createToolbar ) {
+                       toolbar = editor.wp._createToolbar( [
+                               'wp_img_alignleft',
+                               'wp_img_aligncenter',
+                               'wp_img_alignright',
+                               'wp_img_alignnone',
+                               'wp_img_edit',
+                               'wp_img_remove'
+                       ] );
                }
+       } );
 
-               if ( ! iOS ) {
-                       toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
-                       toolbarNode.className += className;
+       editor.on( 'wptoolbar', function( event ) {
+               if ( event.element.nodeName === 'IMG' && ! isPlaceholder( event.element ) ) {
+                       event.toolbar = toolbar;
                }
+       } );
 
-               DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
-
-               return this;
-       };
+       function isNonEditable( node ) {
+               var parent = editor.$( node ).parents( '[contenteditable]' );
+               return parent && parent.attr( 'contenteditable' ) === 'false';
+       }
 
+       // Safari on iOS fails to select images in contentEditoble mode on touch.
+       // Select them again.
        if ( iOS ) {
-               // Safari on iOS fails to select image nodes in contentEditoble mode on touch/click.
-               // Select them again.
-               editor.on( 'click', function( event ) {
-                       if ( event.target.nodeName === 'IMG' ) {
-                               var node = event.target;
-
-                               window.setTimeout( function() {
-                                       editor.selection.select( node );
-                               }, 200 );
-                       } else {
-                               floatingToolbar.hide();
-                       }
-               });
-       }
+               editor.on( 'init', function() {
+                       editor.on( 'touchstart', function( event ) {
+                               if ( event.target.nodeName === 'IMG' && ! isNonEditable( event.target ) ) {
+                                       touchOnImage = true;
+                               }
+                       });
 
-       editor.on( 'nodechange', function( event ) {
-               var delay = iOS ? 350 : 100;
+                       editor.dom.bind( editor.getDoc(), 'touchmove', function() {
+                               touchOnImage = false;
+                       });
 
-               if ( event.element.nodeName !== 'IMG' || isPlaceholder( event.element ) ) {
-                       floatingToolbar.hide();
-                       return;
-               }
+                       editor.on( 'touchend', function( event ) {
+                               if ( touchOnImage && event.target.nodeName === 'IMG' && ! isNonEditable( event.target ) ) {
+                                       var node = event.target;
 
-               setTimeout( function() {
-                       var element = editor.selection.getNode();
+                                       touchOnImage = false;
 
-                       if ( element.nodeName === 'IMG' && ! isPlaceholder( element ) ) {
-                               if ( floatingToolbar._visible ) {
-                                       floatingToolbar.reposition();
-                               } else {
-                                       floatingToolbar.show();
+                                       window.setTimeout( function() {
+                                               editor.selection.select( node );
+                                               editor.nodeChanged();
+                                       }, 100 );
+                               } else if ( toolbar ) {
+                                       toolbar.hide();
                                }
-                       } else {
-                               floatingToolbar.hide();
-                       }
-               }, delay );
-       } );
-
-       function hide() {
-               if ( ! toolbarIsHidden ) {
-                       floatingToolbar.hide();
-               }
+                       });
+               });
        }
 
-       floatingToolbar.on( 'show', function() {
-               toolbarIsHidden = false;
-
-               if ( this._visible ) {
-                       this.reposition();
-                       DOM.addClass( this.getEl(), 'mce-inline-toolbar-grp-active' );
-               }
-       } );
-
-       floatingToolbar.on( 'hide', function() {
-               toolbarIsHidden = true;
-               DOM.removeClass( this.getEl(), 'mce-inline-toolbar-grp-active' );
-       } );
-
-       floatingToolbar.on( 'keydown', function( event ) {
-               if ( event.keyCode === 27 ) {
-                       hide();
-                       editor.focus();
-               }
-       } );
-
-       DOM.bind( window, 'resize scroll', function() {
-               if ( ! toolbarIsHidden && editorWrapParent.hasClass( 'wp-editor-expand' ) ) {
-                       hide();
-               }
-       });
-
-       editor.on( 'init', function() {
-               editor.dom.bind( editor.getWin(), 'scroll', hide );
-       });
-
-       editor.on( 'blur hide', hide );
-
-       // 119 = F8
-       editor.shortcuts.add( 'Alt+119', '', function() {
-               var node = floatingToolbar.find( 'toolbar' )[0];
-
-               if ( node ) {
-                       node.focus( true );
-               }
-       });
-
        function parseShortcode( content ) {
                return content.replace( /(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) {
-                       var id, align, classes, caption, img, width,
-                               trim = tinymce.trim;
+                       var id, align, classes, caption, img, width;
 
                        id = b.match( /id=['"]([^'"]*)['"] ?/ );
                        if ( id ) {
@@ -416,38 +178,35 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
        }
 
        function getShortcode( content ) {
-               return content.replace( /<div (?:id="attachment_|class="mceTemp)[^>]*>([\s\S]+?)<\/div>/g, function( a, b ) {
+               return content.replace( /(?:<div [^>]+mceTemp[^>]+>)?\s*(<dl [^>]+wp-caption[^>]+>[\s\S]+?<\/dl>)\s*(?:<\/div>)?/g, function( all, dl ) {
                        var out = '';
 
-                       if ( b.indexOf('<img ') === -1 ) {
-                               // Broken caption. The user managed to drag the image out?
-                               // Try to return the caption text as a paragraph.
-                               out = b.match( /<dd [^>]+>([\s\S]+?)<\/dd>/i );
-
-                               if ( out && out[1] ) {
-                                       return '<p>' + out[1] + '</p>';
-                               }
-
-                               return '';
+                       if ( dl.indexOf('<img ') === -1 || dl.indexOf('</p>') !== -1 ) {
+                               // Broken caption. The user managed to drag the image out or type in the wrapper div?
+                               // Remove the <dl>, <dd> and <dt> and return the remaining text.
+                               return dl.replace( /<d[ldt]( [^>]+)?>/g, '' ).replace( /<\/d[ldt]>/g, '' );
                        }
 
-                       out = b.replace( /\s*<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) {
+                       out = dl.replace( /\s*<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) {
                                var id, classes, align, width;
 
                                width = c.match( /width="([0-9]*)"/ );
                                width = ( width && width[1] ) ? width[1] : '';
 
+                               classes = b.match( /class="([^"]*)"/ );
+                               classes = ( classes && classes[1] ) ? classes[1] : '';
+                               align = classes.match( /align[a-z]+/i ) || 'alignnone';
+
                                if ( ! width || ! caption ) {
+                                       if ( 'alignnone' !== align[0] ) {
+                                               c = c.replace( /><img/, ' class="' + align[0] + '"><img' );
+                                       }
                                        return c;
                                }
 
                                id = b.match( /id="([^"]*)"/ );
                                id = ( id && id[1] ) ? id[1] : '';
 
-                               classes = b.match( /class="([^"]*)"/ );
-                               classes = ( classes && classes[1] ) ? classes[1] : '';
-
-                               align = classes.match( /align[a-z]+/i ) || 'alignnone';
                                classes = classes.replace( /wp-caption ?|align[a-z]+ ?/gi, '' );
 
                                if ( classes ) {
@@ -468,7 +227,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                        if ( out.indexOf('[caption') === -1 ) {
                                // the caption html seems broken, try to find the image that may be wrapped in a link
                                // and may be followed by <p> with the caption text.
-                               out = b.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
+                               out = dl.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
                        }
 
                        return out;
@@ -591,6 +350,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
        function updateImage( imageNode, imageData ) {
                var classes, className, node, html, parent, wrap, linkNode,
                        captionNode, dd, dl, id, attrs, linkAttrs, width, height, align,
+                       $imageNode, srcset, src,
                        dom = editor.dom;
 
                classes = tinymce.explode( imageData.extraClasses, ' ' );
@@ -708,15 +468,15 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
 
                                if ( parent = dom.getParent( node, 'p' ) ) {
                                        parent.parentNode.insertBefore( wrap, parent );
-
-                                       if ( dom.isEmpty( parent ) ) {
-                                               dom.remove( parent );
-                                       }
                                } else {
                                        node.parentNode.insertBefore( wrap, node );
                                }
 
                                editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node );
+
+                               if ( parent && dom.isEmpty( parent ) ) {
+                                       dom.remove( parent );
+                               }
                        }
                } else if ( captionNode ) {
                        // Remove the caption wrapper and place the image in new paragraph
@@ -726,6 +486,19 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                        dom.remove( captionNode );
                }
 
+               $imageNode = editor.$( imageNode );
+               srcset = $imageNode.attr( 'srcset' );
+               src = $imageNode.attr( 'src' );
+
+               // Remove srcset and sizes if the image file was edited or the image was replaced.
+               if ( srcset && src ) {
+                       src = src.replace( /[?#].*/, '' );
+
+                       if ( srcset.indexOf( src ) === -1 ) {
+                               $imageNode.attr( 'srcset', null ).attr( 'sizes', null );
+                       }
+               }
+
                if ( wp.media.events ) {
                        wp.media.events.trigger( 'editor:image-update', {
                                editor: editor,
@@ -781,12 +554,10 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
        }
 
        function removeImage( node ) {
-               var wrap;
+               var wrap = editor.dom.getParent( node, 'div.mceTemp' );
 
-               if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) {
-                       wrap = node;
-               } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
-                       wrap = editor.dom.getParent( node, 'div.mceTemp' );
+               if ( ! wrap && node.nodeName === 'IMG' ) {
+                       wrap = editor.dom.getParent( node, 'a' );
                }
 
                if ( wrap ) {
@@ -823,7 +594,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                        var captionField = {
                                type: 'textbox',
                                flex: 1,
-                               name: 'caption',
+                               name: 'wpcaption',
                                minHeight: 60,
                                multiline: true,
                                scroll: true,
@@ -849,11 +620,12 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                editor.on( 'wpImageFormSubmit', function( event ) {
                        var data = event.imgData.data,
                                imgNode = event.imgData.node,
-                               caption = event.imgData.caption,
+                               caption = event.imgData.wpcaption,
                                captionId = '',
                                captionAlign = '',
                                captionWidth = '',
-                               wrap, parent, node, html, imgId;
+                               imgId = null,
+                               wrap, parent, node, html;
 
                        // Temp image id so we can find the node later
                        data.id = '__wp-temp-img-id';
@@ -990,15 +762,15 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
 
                                                if ( parent = dom.getParent( node, 'p' ) ) {
                                                        parent.parentNode.insertBefore( wrap, parent );
-
-                                                       if ( dom.isEmpty( parent ) ) {
-                                                               dom.remove( parent );
-                                                       }
                                                } else {
                                                        node.parentNode.insertBefore( wrap, node );
                                                }
 
                                                editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node );
+
+                                               if ( parent && dom.isEmpty( parent ) ) {
+                                                       dom.remove( parent );
+                                               }
                                        }
                                } else {
                                        if ( wrap ) {
@@ -1019,7 +791,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                        }
 
                        imgNode = dom.get('__wp-temp-img-id');
-                       dom.setAttrib( imgNode, 'id', imgId );
+                       dom.setAttrib( imgNode, 'id', imgId || null );
                        event.imgData.node = imgNode;
                });
 
@@ -1032,21 +804,12 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                                parent = dom.select( 'dd.wp-caption-dd', parent )[0];
 
                                if ( parent ) {
-                                       data.caption = editor.serializer.serialize( parent )
+                                       data.wpcaption = editor.serializer.serialize( parent )
                                                .replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' );
                                }
                        }
                });
 
-               dom.bind( editor.getDoc(), 'dragstart', function( event ) {
-                       var node = editor.selection.getNode();
-
-                       // Prevent dragging images out of the caption elements
-                       if ( node.nodeName === 'IMG' && dom.getParent( node, '.wp-caption' ) ) {
-                               event.preventDefault();
-                       }
-               });
-
                // Prevent IE11 from making dl.wp-caption resizable
                if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) {
                        // The 'mscontrolselect' event is supported only in IE11+
@@ -1088,21 +851,73 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                                }
                        });
                }
-    });
+       });
+
+       editor.on( 'pastePostProcess', function( event ) {
+               // Pasting in a caption node.
+               if ( editor.dom.getParent( editor.selection.getNode(), 'dd.wp-caption-dd' ) ) {
+                       // Remove "non-block" elements that should not be in captions.
+                       editor.$( 'img, audio, video, object, embed, iframe, script, style', event.node ).remove();
+
+                       editor.$( '*', event.node ).each( function( i, node ) {
+                               if ( editor.dom.isBlock( node ) ) {
+                                       // Insert <br> where the blocks used to be. Makes it look better after pasting in the caption.
+                                       if ( tinymce.trim( node.textContent || node.innerText ) ) {
+                                               editor.dom.insertAfter( editor.dom.create( 'br' ), node );
+                                               editor.dom.remove( node, true );
+                                       } else {
+                                               editor.dom.remove( node );
+                                       }
+                               }
+                       });
+
+                       // Trim <br> tags.
+                       editor.$( 'br',  event.node ).each( function( i, node ) {
+                               if ( ! node.nextSibling || node.nextSibling.nodeName === 'BR' ||
+                                       ! node.previousSibling || node.previousSibling.nodeName === 'BR' ) {
+
+                                       editor.dom.remove( node );
+                               }
+                       } );
+
+                       // Pasted HTML is cleaned up for inserting in the caption.
+                       pasteInCaption = true;
+               }
+       });
 
        editor.on( 'BeforeExecCommand', function( event ) {
-               var node, p, DL, align, replacement,
+               var node, p, DL, align, replacement, captionParent,
                        cmd = event.command,
                        dom = editor.dom;
 
-               if ( cmd === 'mceInsertContent' ) {
-                       // When inserting content, if the caret is inside a caption create new paragraph under
-                       // and move the caret there
-                       if ( node = dom.getParent( editor.selection.getNode(), 'div.mceTemp' ) ) {
-                               p = dom.create( 'p' );
-                               dom.insertAfter( p, node );
-                               editor.selection.setCursorLocation( p, 0 );
-                               editor.nodeChanged();
+               if ( cmd === 'mceInsertContent' || cmd === 'Indent' || cmd === 'Outdent' ) {
+                       node = editor.selection.getNode();
+                       captionParent = dom.getParent( node, 'div.mceTemp' );
+
+                       if ( captionParent ) {
+                               if ( cmd === 'mceInsertContent' ) {
+                                       if ( pasteInCaption ) {
+                                               pasteInCaption = false;
+                                               // We are in the caption element, and in 'paste' context,
+                                               // and the pasted HTML was cleaned up on 'pastePostProcess' above.
+                                               // Let it be pasted in the caption.
+                                               return;
+                                       }
+
+                                       // The paste is somewhere else in the caption DL element.
+                                       // Prevent pasting in there as it will break the caption.
+                                       // Make new paragraph under the caption DL and move the caret there.
+                                       p = dom.create( 'p' );
+                                       dom.insertAfter( p, captionParent );
+                                       editor.selection.setCursorLocation( p, 0 );
+                                       editor.nodeChanged();
+                               } else {
+                                       // Clicking Indent or Outdent while an image with a caption is selected breaks the caption.
+                                       // See #38313.
+                                       event.preventDefault();
+                                       event.stopImmediatePropagation();
+                                       return false;
+                               }
                        }
                } else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' || cmd === 'wpAlignNone' ) {
                        node = editor.selection.getNode();
@@ -1121,13 +936,13 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                                replacement = ' ' + align;
                        }
 
-                       node.className = node.className.replace( / ?align(left|center|right|none)/g, '' ) + replacement;
+                       node.className = trim( node.className.replace( / ?align(left|center|right|none)/g, '' ) + replacement );
 
                        editor.nodeChanged();
                        event.preventDefault();
 
-                       if ( floatingToolbar ) {
-                               floatingToolbar.reposition();
+                       if ( toolbar ) {
+                               toolbar.reposition();
                        }
 
                        editor.fire( 'ExecCommand', {
@@ -1208,6 +1023,12 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                return getShortcode( content );
        };
 
+       editor.on( 'beforeGetContent', function( event ) {
+               if ( event.format !== 'raw' ) {
+                       editor.$( 'img[id="__wp-temp-img-id"]' ).attr( 'id', null );
+               }       
+       });
+
        editor.on( 'BeforeSetContent', function( event ) {
                if ( event.format !== 'raw' ) {
                        event.content = editor.wpSetImgCaption( event.content );
@@ -1220,6 +1041,50 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
                }
        });
 
+       ( function() {
+               var wrap;
+
+               editor.on( 'dragstart', function() {
+                       var node = editor.selection.getNode();
+
+                       if ( node.nodeName === 'IMG' ) {
+                               wrap = editor.dom.getParent( node, '.mceTemp' );
+
+                               if ( ! wrap && node.parentNode.nodeName === 'A' && ! hasTextContent( node.parentNode ) ) {
+                                       wrap = node.parentNode;
+                               }
+                       }
+               } );
+
+               editor.on( 'drop', function( event ) {
+                       var dom = editor.dom,
+                               rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint( event.clientX, event.clientY, editor.getDoc() );
+
+                       // Don't allow anything to be dropped in a captioned image.
+                       if ( rng && dom.getParent( rng.startContainer, '.mceTemp' ) ) {
+                               event.preventDefault();
+                       } else if ( wrap ) {
+                               event.preventDefault();
+
+                               editor.undoManager.transact( function() {
+                                       if ( rng ) {
+                                               editor.selection.setRng( rng );
+                                       }
+
+                                       editor.selection.setNode( wrap );
+                                       dom.remove( wrap );
+                               } );
+                       }
+
+                       wrap = null;
+               } );
+       } )();
+
+       // Add to editor.wp
+       editor.wp = editor.wp || {};
+       editor.wp.isPlaceholder = isPlaceholder;
+
+       // Back-compat.
        return {
                _do_shcode: parseShortcode,
                _get_shcode: getShortcode