]+data-wp-view-end[^>]*><\/span><\/(?:div|span)>/g,function(a,b){var d,e=c[b];return e&&(d=wp.mce.view.get(e.options.viewType)),e&&d?d.text(e):""})},removeInternalAttrs:function(a){var b={};return _.each(a,function(a,c){-1===c.indexOf("data-mce")&&(b[c]=a)}),b},attrs:function(a){return wp.mce.view.removeInternalAttrs(wp.html.attrs(a))},instance:function(b){var d=a(b).data("wp-view");return d?c[d]:void 0},select:function(b){var c=a(b);c.hasClass("selected")||(c.addClass("selected"),a(b.firstChild).trigger("select"))},deselect:function(b){var c=a(b);c.hasClass("selected")&&(c.removeClass("selected"),a(b.firstChild).trigger("deselect"))}}}(jQuery);
\ No newline at end of file
+window.wp=window.wp||{},function(a){var b={},c={},d=wp.media,e=["encodedText"];wp.mce=wp.mce||{},wp.mce.View=function(a){a||(a={}),_.extend(this,_.pick(a,e)),this.initialize.apply(this,arguments)},_.extend(wp.mce.View.prototype,{initialize:function(){},html:function(){},render:function(){var b=this.getHtml();_.each(tinymce.editors,function(c){var d;c.plugins.wpview&&(d=c.getDoc(),a(d).find('[data-wpview-text="'+this.encodedText+'"]').html(b))},this)}}),wp.mce.View.extend=Backbone.View.extend,wp.mce.views={register:function(a,c){b[a]=c},get:function(a){return b[a]},unregister:function(a){delete b[a]},toViews:function(a){var c,d=[{content:a}];return _.each(b,function(a,b){c=d.slice(),d=[],_.each(c,function(c){var e,f=c.content;if(c.processed)return void d.push(c);for(;f&&(e=a.toView(f));)e.index&&d.push({content:f.substring(0,e.index)}),d.push({content:wp.mce.views.toView(b,e.content,e.options),processed:!0}),f=f.slice(e.index+e.content.length);f&&d.push({content:f})})}),_.pluck(d,"content").join("")},toView:function(a,b,d){var e,f,g=wp.mce.views.get(a),h=window.encodeURIComponent(b);return g?(wp.mce.views.getInstance(h)||(f=d,f.encodedText=h,e=new g.View(f),c[h]=e),wp.html.string({tag:"div",attrs:{"class":"wpview-wrap wpview-type-"+a,"data-wpview-text":h,"data-wpview-type":a,contenteditable:"false"},content:" "})):b},refreshView:function(a,b){var d,e,f,g=window.encodeURIComponent(b);f=wp.mce.views.getInstance(g),f||(e=a.toView(b),d=e.options,d.encodedText=g,f=new a.View(d),c[g]=f),wp.mce.views.render()},getInstance:function(a){return c[a]},render:function(){_.each(c,function(a){a.render()})},edit:function(b){var c=a(b).data("wpview-type"),d=wp.mce.views.get(c);d&&d.edit(b)}},wp.mce.gallery={shortcode:"gallery",toView:function(a){var b=wp.shortcode.next(this.shortcode,a);if(b)return{index:b.index,content:b.content,options:{shortcode:b.shortcode}}},View:wp.mce.View.extend({className:"editor-gallery",template:d.template("editor-gallery"),postID:a("#post_ID").val(),initialize:function(a){this.shortcode=a.shortcode,this.fetch()},fetch:function(){this.attachments=wp.media.gallery.attachments(this.shortcode,this.postID),this.attachments.more().done(_.bind(this.render,this))},getHtml:function(){var a,b=this.shortcode.attrs.named;if(this.attachments.length)return a={attachments:this.attachments.toJSON(),columns:b.columns?parseInt(b.columns,10):3},this.template(a)}}),edit:function(b){var c,d,e=wp.media.gallery,f=this;d=window.decodeURIComponent(a(b).data("wpview-text")),c=e.edit(d),c.state("gallery-edit").on("update",function(d){var g=e.shortcode(d).string();a(b).attr("data-wpview-text",window.encodeURIComponent(g)),wp.mce.views.refreshView(f,g),c.detach()})}},wp.mce.views.register("gallery",wp.mce.gallery)}(jQuery);
\ No newline at end of file
diff --git a/wp-includes/js/tinymce/plugins/wpgallery/plugin.js b/wp-includes/js/tinymce/plugins/wpgallery/plugin.js
index def97d6faf..6e645709f3 100644
--- a/wp-includes/js/tinymce/plugins/wpgallery/plugin.js
+++ b/wp-includes/js/tinymce/plugins/wpgallery/plugin.js
@@ -59,7 +59,7 @@ tinymce.PluginManager.add('wpgallery', function( editor ) {
return;
}
- // Check if the `wp.media.gallery` API exists.
+ // Check if the `wp.media` API exists.
if ( typeof wp === 'undefined' || ! wp.media ) {
return;
}
@@ -166,7 +166,11 @@ tinymce.PluginManager.add('wpgallery', function( editor ) {
});
editor.on( 'BeforeSetContent', function( event ) {
- event.content = replaceGalleryShortcodes( event.content );
+ // 'wpview' handles the gallery shortcode when present
+ if ( ! editor.plugins.wpview ) {
+ event.content = replaceGalleryShortcodes( event.content );
+ }
+
event.content = replaceAVShortcodes( event.content );
});
diff --git a/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js b/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js
index af1c41f51a..087f938ebc 100644
--- a/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js
+++ b/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js
@@ -1 +1 @@
-tinymce.PluginManager.add("wpgallery",function(a){function b(a){return a.replace(/\[gallery([^\]]*)\]/g,function(a){return c("wp-gallery",a)})}function c(a,b){return b=window.encodeURIComponent(b),'
'}function d(a,b,d){var e;return d&&d.indexOf("["+b)>-1?(e=a.length-d.length,c("wp-"+b,a.substring(0,e))+a.substring(e)):c("wp-"+b,a)}function e(a){for(var b=/\[(video-playlist|audio|video|playlist)[^\]]*\]/,c=/\[(video-playlist|audio|video|playlist)[^\]]*\]([\s\S]*?\[\/\1\])?/;b.test(a);)a=a.replace(c,d);return a}function f(a){function b(a,b){return b=new RegExp(b+'="([^"]+)"').exec(a),b?window.decodeURIComponent(b[1]):""}return a.replace(/(?:]+)?>)*(
]+>)(?:<\/p>)*/g,function(a,c){var d=b(c,"data-wp-media");return d?"
"+d+"
":a})}function g(b){var c,d,e;"IMG"===b.nodeName&&"undefined"!=typeof wp&&wp.media&&(a.dom.hasClass(b,"wp-gallery")&&wp.media.gallery?(c=wp.media.gallery,e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=c.edit(e),d.state("gallery-edit").on("update",function(e){var f=c.shortcode(e).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(f)),d.detach()})):a.dom.hasClass(b,"wp-playlist")&&wp.media.playlist?(e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=wp.media.playlist.edit(e),d.state("playlist-edit").on("update",function(c){var e=wp.media.playlist.shortcode(c).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(e)),d.detach()})):a.dom.hasClass(b,"wp-video-playlist")&&wp.media["video-playlist"]?(e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=wp.media["video-playlist"].edit(e),d.state("video-playlist-edit").on("update",function(c){var e=wp.media["video-playlist"].shortcode(c).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(e)),d.detach()})):window.console&&window.console.log("Edit AV shortcode "+window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media"))))}a.addCommand("WP_Gallery",function(){g(a.selection.getNode())}),a.on("mouseup",function(b){function c(){d.removeClass(d.select("img.wp-media-selected"),"wp-media-selected")}var d=a.dom,e=b.target;"IMG"===e.nodeName&&d.getAttrib(e,"data-wp-media")?2!==b.button&&(d.hasClass(e,"wp-media-selected")?g(e):(c(),d.addClass(e,"wp-media-selected"))):c()}),a.on("ResolveName",function(b){var c=a.dom,d=b.target;"IMG"===d.nodeName&&c.getAttrib(d,"data-wp-media")&&(c.hasClass(d,"wp-gallery")?b.name="gallery":c.hasClass(d,"wp-video")?b.name="video":c.hasClass(d,"wp-audio")?b.name="audio":c.hasClass(d,"wp-playlist")?b.name="playlist":c.hasClass(d,"wp-video-playlist")&&(b.name="video-playlist"))}),a.on("BeforeSetContent",function(a){a.content=b(a.content),a.content=e(a.content)}),a.on("PostProcess",function(a){a.get&&(a.content=f(a.content))})});
\ No newline at end of file
+tinymce.PluginManager.add("wpgallery",function(a){function b(a){return a.replace(/\[gallery([^\]]*)\]/g,function(a){return c("wp-gallery",a)})}function c(a,b){return b=window.encodeURIComponent(b),'
'}function d(a,b,d){var e;return d&&d.indexOf("["+b)>-1?(e=a.length-d.length,c("wp-"+b,a.substring(0,e))+a.substring(e)):c("wp-"+b,a)}function e(a){for(var b=/\[(video-playlist|audio|video|playlist)[^\]]*\]/,c=/\[(video-playlist|audio|video|playlist)[^\]]*\]([\s\S]*?\[\/\1\])?/;b.test(a);)a=a.replace(c,d);return a}function f(a){function b(a,b){return b=new RegExp(b+'="([^"]+)"').exec(a),b?window.decodeURIComponent(b[1]):""}return a.replace(/(?:]+)?>)*(
]+>)(?:<\/p>)*/g,function(a,c){var d=b(c,"data-wp-media");return d?"
"+d+"
":a})}function g(b){var c,d,e;"IMG"===b.nodeName&&"undefined"!=typeof wp&&wp.media&&(a.dom.hasClass(b,"wp-gallery")&&wp.media.gallery?(c=wp.media.gallery,e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=c.edit(e),d.state("gallery-edit").on("update",function(e){var f=c.shortcode(e).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(f)),d.detach()})):a.dom.hasClass(b,"wp-playlist")&&wp.media.playlist?(e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=wp.media.playlist.edit(e),d.state("playlist-edit").on("update",function(c){var e=wp.media.playlist.shortcode(c).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(e)),d.detach()})):a.dom.hasClass(b,"wp-video-playlist")&&wp.media["video-playlist"]?(e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),d=wp.media["video-playlist"].edit(e),d.state("video-playlist-edit").on("update",function(c){var e=wp.media["video-playlist"].shortcode(c).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(e)),d.detach()})):window.console&&window.console.log("Edit AV shortcode "+window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media"))))}a.addCommand("WP_Gallery",function(){g(a.selection.getNode())}),a.on("mouseup",function(b){function c(){d.removeClass(d.select("img.wp-media-selected"),"wp-media-selected")}var d=a.dom,e=b.target;"IMG"===e.nodeName&&d.getAttrib(e,"data-wp-media")?2!==b.button&&(d.hasClass(e,"wp-media-selected")?g(e):(c(),d.addClass(e,"wp-media-selected"))):c()}),a.on("ResolveName",function(b){var c=a.dom,d=b.target;"IMG"===d.nodeName&&c.getAttrib(d,"data-wp-media")&&(c.hasClass(d,"wp-gallery")?b.name="gallery":c.hasClass(d,"wp-video")?b.name="video":c.hasClass(d,"wp-audio")?b.name="audio":c.hasClass(d,"wp-playlist")?b.name="playlist":c.hasClass(d,"wp-video-playlist")&&(b.name="video-playlist"))}),a.on("BeforeSetContent",function(c){a.plugins.wpview||(c.content=b(c.content)),c.content=e(c.content)}),a.on("PostProcess",function(a){a.get&&(a.content=f(a.content))})});
\ No newline at end of file
diff --git a/wp-includes/js/tinymce/plugins/wpview/plugin.js b/wp-includes/js/tinymce/plugins/wpview/plugin.js
index 0c56ecba1f..e56e3da334 100644
--- a/wp-includes/js/tinymce/plugins/wpview/plugin.js
+++ b/wp-includes/js/tinymce/plugins/wpview/plugin.js
@@ -2,190 +2,366 @@
/**
* WordPress View plugin.
*/
-
-(function() {
- var VK = tinymce.VK,
+tinymce.PluginManager.add( 'wpview', function( editor ) {
+ var selected,
+ VK = tinymce.util.VK,
TreeWalker = tinymce.dom.TreeWalker,
- selected;
+ toRemove = false;
- tinymce.create('tinymce.plugins.wpView', {
- init : function( editor ) {
- var wpView = this;
-
- // Check if the `wp.mce` API exists.
- if ( typeof wp === 'undefined' || ! wp.mce ) {
- return;
+ function getParentView( node ) {
+ while ( node && node.nodeName !== 'BODY' ) {
+ if ( isView( node ) ) {
+ return node;
}
- editor.on( 'PreInit', function() {
- // Add elements so we can set `contenteditable` to false.
- editor.schema.addValidElements('div[*],span[*]');
- });
+ node = node.parentNode;
+ }
+ }
- // When the editor's content changes, scan the new content for
- // matching view patterns, and transform the matches into
- // view wrappers. Since the editor's DOM is outdated at this point,
- // we'll wait to render the views.
- editor.on( 'BeforeSetContent', function( e ) {
- if ( ! e.content ) {
- return;
- }
+ function isView( node ) {
+ return node && /\bwpview-wrap\b/.test( node.className );
+ }
- e.content = wp.mce.view.toViews( e.content );
- });
+ function createPadNode() {
+ return editor.dom.create( 'p', { 'data-wpview-pad': 1 },
+ ( tinymce.Env.ie && tinymce.Env.ie < 11 ) ? '' : '
' );
+ }
- // When the editor's content has been updated and the DOM has been
- // processed, render the views in the document.
- editor.on( 'SetContent', function() {
- wp.mce.view.render( editor.getDoc() );
- });
+ /**
+ * Get the text/shortcode string for a view.
+ *
+ * @param view The view wrapper's HTML id or node
+ * @returns string The text/shoercode string of the view
+ */
+ function getViewText( view ) {
+ view = getParentView( typeof view === 'string' ? editor.dom.get( view ) : view );
- editor.on( 'init', function() {
- var selection = editor.selection;
- // When a view is selected, ensure content that is being pasted
- // or inserted is added to a text node (instead of the view).
- editor.on( 'BeforeSetContent', function() {
- var walker, target,
- view = wpView.getParentView( selection.getNode() );
+ if ( view ) {
+ return window.decodeURIComponent( editor.dom.getAttrib( view, 'data-wpview-text' ) || '' );
+ }
+ return '';
+ }
- // If the selection is not within a view, bail.
- if ( ! view ) {
- return;
- }
+ /**
+ * Set the view's original text/shortcode string
+ *
+ * @param view The view wrapper's HTML id or node
+ * @param text The text string to be set
+ */
+ function setViewText( view, text ) {
+ view = getParentView( typeof view === 'string' ? editor.dom.get( view ) : view );
- // If there are no additional nodes or the next node is a
- // view, create a text node after the current view.
- if ( ! view.nextSibling || wpView.isView( view.nextSibling ) ) {
- target = editor.getDoc().createTextNode('');
- editor.dom.insertAfter( target, view );
+ if ( view ) {
+ editor.dom.setAttrib( view, 'data-wpview-text', window.encodeURIComponent( text || '' ) );
+ return true;
+ }
+ return false;
+ }
- // Otherwise, find the next text node.
- } else {
- walker = new TreeWalker( view.nextSibling, view.nextSibling );
- target = walker.next();
- }
+ function _stop( event ) {
+ event.stopPropagation();
+ }
- // Select the `target` text node.
- selection.select( target );
- selection.collapse( true );
- });
+ function select( viewNode ) {
+ var clipboard,
+ dom = editor.dom;
- // When the selection's content changes, scan any new content
- // for matching views and immediately render them.
- //
- // Runs on paste and on inserting nodes/html.
- editor.on( 'SetContent', function( e ) {
- if ( ! e.context ) {
- return;
- }
+ // Bail if node is already selected.
+ if ( viewNode === selected ) {
+ return;
+ }
- var node = selection.getNode();
+ deselect();
+ selected = viewNode;
+ dom.addClass( viewNode, 'selected' );
- if ( ! node.innerHTML ) {
- return;
- }
+ clipboard = dom.create( 'div', {
+ 'class': 'wpview-clipboard',
+ 'contenteditable': 'true'
+ }, getViewText( viewNode ) );
- node.innerHTML = wp.mce.view.toViews( node.innerHTML );
- wp.mce.view.render( node );
- });
- });
+ viewNode.appendChild( clipboard );
- // When the editor's contents are being accessed as a string,
- // transform any views back to their text representations.
- editor.on( 'PostProcess', function( e ) {
- if ( ( ! e.get && ! e.save ) || ! e.content ) {
- return;
- }
+ // Both of the following are necessary to prevent manipulating the selection/focus
+ editor.dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop );
+ editor.dom.bind( selected, 'beforedeactivate focusin focusout', _stop );
- e.content = wp.mce.view.toText( e.content );
- });
+ // select the hidden div
+ editor.selection.select( clipboard, true );
+ }
- // Triggers when the selection is changed.
- // Add the event handler to the top of the stack.
- editor.on( 'NodeChange', function( e ) {
- var view = wpView.getParentView( e.element );
+ /**
+ * Deselect a selected view and remove clipboard
+ */
+ function deselect() {
+ var clipboard,
+ dom = editor.dom;
- // Update the selected view.
- if ( view ) {
- wpView.select( view );
+ if ( selected ) {
+ clipboard = editor.dom.select( '.wpview-clipboard', selected )[0];
+ dom.unbind( clipboard );
+ dom.remove( clipboard );
- // Prevent the selection from propagating to other plugins.
- return false;
+ dom.unbind( selected, 'beforedeactivate focusin focusout click mouseup', _stop );
+ dom.removeClass( selected, 'selected' );
- // If we've clicked off of the selected view, deselect it.
- } else {
- wpView.deselect();
- }
- });
+ editor.selection.select( selected.nextSibling );
+ editor.selection.collapse();
- editor.on( 'keydown', function( event ) {
- var keyCode = event.keyCode,
- view, instance;
+ }
- // If a view isn't selected, let the event go on its merry way.
- if ( ! selected ) {
- return;
- }
+ selected = null;
+ }
- // If the caret is not within the selected view, deselect the
- // view and bail.
- view = wpView.getParentView( editor.selection.getNode() );
- if ( view !== selected ) {
- wpView.deselect();
- return;
- }
+ // Check if the `wp.mce` API exists.
+ if ( typeof wp === 'undefined' || ! wp.mce ) {
+ return;
+ }
- // If delete or backspace is pressed, delete the view.
- if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) {
- if ( (instance = wp.mce.view.instance( selected )) ) {
- instance.remove();
- wpView.deselect();
- }
- }
-
- // Let keypresses that involve the command or control keys through.
- // Also, let any of the F# keys through.
- if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) {
- return;
- }
-
- event.preventDefault();
- });
- },
-
- getParentView : function( node ) {
- while ( node ) {
- if ( this.isView( node ) ) {
- return node;
- }
-
- node = node.parentNode;
- }
- },
-
- isView : function( node ) {
- return (/(?:^|\s)wp-view-wrap(?:\s|$)/).test( node.className );
- },
-
- select : function( view ) {
- if ( view === selected ) {
- return;
- }
-
- this.deselect();
- selected = view;
- wp.mce.view.select( selected );
- },
-
- deselect : function() {
- if ( selected ) {
- wp.mce.view.deselect( selected );
- }
-
- selected = null;
+ editor.on( 'BeforeAddUndo', function( event ) {
+ if ( selected && ! toRemove ) {
+ event.preventDefault();
}
});
- // Register plugin
- tinymce.PluginManager.add( 'wpview', tinymce.plugins.wpView );
-})();
+ // When the editor's content changes, scan the new content for
+ // matching view patterns, and transform the matches into
+ // view wrappers.
+ editor.on( 'BeforeSetContent', function( e ) {
+ if ( ! e.content ) {
+ return;
+ }
+
+ e.content = wp.mce.views.toViews( e.content );
+ });
+
+ // When the editor's content has been updated and the DOM has been
+ // processed, render the views in the document.
+ editor.on( 'SetContent', function( event ) {
+ var body, padNode;
+
+ wp.mce.views.render();
+
+ // Add padding if the noneditable node is last
+ if ( event.load || ! event.set ) {
+ body = editor.getBody();
+
+ if ( isView( body.lastChild ) ) {
+ padNode = createPadNode();
+ body.appendChild( padNode );
+ editor.selection.setCursorLocation( padNode, 0 );
+ }
+ }
+
+ // refreshEmptyContentNode();
+ });
+
+ // Detect mouse down events that are adjacent to a view when a view is the first view or the last view
+ editor.on( 'click', function( event ) {
+ var body = editor.getBody(),
+ doc = editor.getDoc(),
+ scrollTop = doc.documentElement.scrollTop || body.scrollTop || 0,
+ x, y, firstNode, lastNode, padNode;
+
+ if ( event.target.nodeName === 'HTML' && ! event.metaKey && ! event.ctrlKey ) {
+ firstNode = body.firstChild;
+ lastNode = body.lastChild;
+ x = event.clientX;
+ y = event.clientY;
+
+ if ( isView( firstNode ) && ( ( x < firstNode.offsetLeft && y < ( firstNode.offsetHeight - scrollTop ) ) ||
+ y < firstNode.offsetTop ) ) {
+ // detect events above or to the left of the first view
+
+ padNode = createPadNode();
+ body.insertBefore( padNode, firstNode );
+ } else if ( isView( lastNode ) && ( x > ( lastNode.offsetLeft + lastNode.offsetWidth ) ||
+ ( ( scrollTop + y ) - ( lastNode.offsetTop + lastNode.offsetHeight ) ) > 0 ) ) {
+ // detect events to the right and below the last view
+
+ padNode = createPadNode();
+ body.appendChild( padNode );
+ }
+
+ if ( padNode ) {
+ editor.selection.setCursorLocation( padNode, 0 );
+ }
+ }
+ });
+
+ editor.on( 'init', function() {
+ var selection = editor.selection;
+ // When a view is selected, ensure content that is being pasted
+ // or inserted is added to a text node (instead of the view).
+ editor.on( 'BeforeSetContent', function() {
+ var walker, target,
+ view = getParentView( selection.getNode() );
+
+ // If the selection is not within a view, bail.
+ if ( ! view ) {
+ return;
+ }
+
+ if ( ! view.nextSibling || isView( view.nextSibling ) ) {
+ // If there are no additional nodes or the next node is a
+ // view, create a text node after the current view.
+ target = editor.getDoc().createTextNode('');
+ editor.dom.insertAfter( target, view );
+ } else {
+ // Otherwise, find the next text node.
+ walker = new TreeWalker( view.nextSibling, view.nextSibling );
+ target = walker.next();
+ }
+
+ // Select the `target` text node.
+ selection.select( target );
+ selection.collapse( true );
+ });
+
+ // When the selection's content changes, scan any new content
+ // for matching views.
+ //
+ // Runs on paste and on inserting nodes/html.
+ editor.on( 'SetContent', function( e ) {
+ if ( ! e.context ) {
+ return;
+ }
+
+ var node = selection.getNode();
+
+ if ( ! node.innerHTML ) {
+ return;
+ }
+
+ node.innerHTML = wp.mce.views.toViews( node.innerHTML );
+ });
+
+ editor.dom.bind( editor.getBody(), 'mousedown mouseup click', function( event ) {
+ var view = getParentView( event.target );
+
+ // Contain clicks inside the view wrapper
+ if ( view ) {
+ event.stopPropagation();
+
+ if ( event.type === 'click' ) {
+ if ( ! event.metaKey && ! event.ctrlKey ) {
+ if ( editor.dom.hasClass( event.target, 'edit' ) ) {
+ wp.mce.views.edit( view );
+ } else if ( editor.dom.hasClass( event.target, 'remove' ) ) {
+ editor.dom.remove( view );
+ }
+ }
+ }
+ select( view );
+ // returning false stops the ugly bars from appearing in IE11 and stops the view being selected as a range in FF
+ // unfortunately, it also inhibits the dragging fo views to a new location
+ return false;
+ } else {
+ if ( event.type === 'click' ) {
+ deselect();
+ }
+ }
+ });
+
+ });
+
+ editor.on( 'PreProcess', function( event ) {
+ var dom = editor.dom;
+
+ // Remove empty padding nodes
+ tinymce.each( dom.select( 'p[data-wpview-pad]', event.node ), function( node ) {
+ if ( dom.isEmpty( node ) ) {
+ dom.remove( node );
+ } else {
+ dom.setAttrib( node, 'data-wpview-pad', null );
+ }
+ });
+
+ // Replace the wpview node with the wpview string/shortcode?
+ tinymce.each( dom.select( 'div[data-wpview-text]', event.node ), function( node ) {
+ // Empty the wrap node
+ if ( 'textContent' in node ) {
+ node.textContent = '';
+ } else {
+ node.innerText = '';
+ }
+
+ // TODO: that makes all views into block tags (as we use
).
+ // Can use 'PostProcess' and a regex instead.
+ dom.replace( dom.create( 'p', null, window.decodeURIComponent( dom.getAttrib( node, 'data-wpview-text' ) ) ), node );
+ });
+ });
+
+ editor.on( 'keydown', function( event ) {
+ var keyCode = event.keyCode,
+ view;
+
+ // If a view isn't selected, let the event go on its merry way.
+ if ( ! selected ) {
+ return;
+ }
+
+ // Let keypresses that involve the command or control keys through.
+ // Also, let any of the F# keys through.
+ if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) ) {
+ if ( ( event.metaKey || event.ctrlKey ) && keyCode === 88 ) {
+ toRemove = selected;
+ }
+ return;
+ }
+
+ // If the caret is not within the selected view, deselect the
+ // view and bail.
+ view = getParentView( editor.selection.getNode() );
+
+ if ( view !== selected ) {
+ deselect();
+ return;
+ }
+
+ // If delete or backspace is pressed, delete the view.
+ if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) {
+ editor.dom.remove( selected );
+ }
+
+ event.preventDefault();
+ });
+
+ editor.on( 'keyup', function( event ) {
+ var padNode,
+ keyCode = event.keyCode,
+ body = editor.getBody(),
+ range;
+
+ if ( toRemove ) {
+ editor.dom.remove( toRemove );
+ toRemove = false;
+ }
+
+ if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) {
+ // Make sure there is padding if the last element is a view
+ if ( isView( body.lastChild ) ) {
+ padNode = createPadNode();
+ body.appendChild( padNode );
+
+ if ( body.childNodes.length === 2 ) {
+ editor.selection.setCursorLocation( padNode, 0 );
+ }
+ }
+
+ range = editor.selection.getRng();
+
+ // Allow an initial element in the document to be removed when it is before a view
+ if ( body.firstChild === range.startContainer && range.collapsed === true &&
+ isView( range.startContainer.nextSibling ) && range.startOffset === 0 ) {
+
+ editor.dom.remove( range.startContainer );
+ }
+ }
+ });
+
+ return {
+ getViewText: getViewText,
+ setViewText: setViewText
+ };
+});
diff --git a/wp-includes/js/tinymce/plugins/wpview/plugin.min.js b/wp-includes/js/tinymce/plugins/wpview/plugin.min.js
index 61eb4d7a65..bfe7870b46 100644
--- a/wp-includes/js/tinymce/plugins/wpview/plugin.min.js
+++ b/wp-includes/js/tinymce/plugins/wpview/plugin.min.js
@@ -1 +1 @@
-!function(){var a,b=tinymce.VK,c=tinymce.dom.TreeWalker;tinymce.create("tinymce.plugins.wpView",{init:function(d){var e=this;"undefined"!=typeof wp&&wp.mce&&(d.on("PreInit",function(){d.schema.addValidElements("div[*],span[*]")}),d.on("BeforeSetContent",function(a){a.content&&(a.content=wp.mce.view.toViews(a.content))}),d.on("SetContent",function(){wp.mce.view.render(d.getDoc())}),d.on("init",function(){var a=d.selection;d.on("BeforeSetContent",function(){var b,f,g=e.getParentView(a.getNode());g&&(!g.nextSibling||e.isView(g.nextSibling)?(f=d.getDoc().createTextNode(""),d.dom.insertAfter(f,g)):(b=new c(g.nextSibling,g.nextSibling),f=b.next()),a.select(f),a.collapse(!0))}),d.on("SetContent",function(b){if(b.context){var c=a.getNode();c.innerHTML&&(c.innerHTML=wp.mce.view.toViews(c.innerHTML),wp.mce.view.render(c))}})}),d.on("PostProcess",function(a){(a.get||a.save)&&a.content&&(a.content=wp.mce.view.toText(a.content))}),d.on("NodeChange",function(a){var b=e.getParentView(a.element);return b?(e.select(b),!1):void e.deselect()}),d.on("keydown",function(c){var f,g,h=c.keyCode;if(a){if(f=e.getParentView(d.selection.getNode()),f!==a)return void e.deselect();(h===b.DELETE||h===b.BACKSPACE)&&(g=wp.mce.view.instance(a))&&(g.remove(),e.deselect()),c.metaKey||c.ctrlKey||h>=112&&123>=h||c.preventDefault()}}))},getParentView:function(a){for(;a;){if(this.isView(a))return a;a=a.parentNode}},isView:function(a){return/(?:^|\s)wp-view-wrap(?:\s|$)/.test(a.className)},select:function(b){b!==a&&(this.deselect(),a=b,wp.mce.view.select(a))},deselect:function(){a&&wp.mce.view.deselect(a),a=null}}),tinymce.PluginManager.add("wpview",tinymce.plugins.wpView)}();
\ No newline at end of file
+tinymce.PluginManager.add("wpview",function(a){function b(a){for(;a&&"BODY"!==a.nodeName;){if(c(a))return a;a=a.parentNode}}function c(a){return a&&/\bwpview-wrap\b/.test(a.className)}function d(){return a.dom.create("p",{"data-wpview-pad":1},tinymce.Env.ie&&tinymce.Env.ie<11?"":'
')}function e(c){return c=b("string"==typeof c?a.dom.get(c):c),c?window.decodeURIComponent(a.dom.getAttrib(c,"data-wpview-text")||""):""}function f(c,d){return c=b("string"==typeof c?a.dom.get(c):c),c?(a.dom.setAttrib(c,"data-wpview-text",window.encodeURIComponent(d||"")),!0):!1}function g(a){a.stopPropagation()}function h(b){var c,d=a.dom;b!==j&&(i(),j=b,d.addClass(b,"selected"),c=d.create("div",{"class":"wpview-clipboard",contenteditable:"true"},e(b)),b.appendChild(c),a.dom.bind(c,"beforedeactivate focusin focusout",g),a.dom.bind(j,"beforedeactivate focusin focusout",g),a.selection.select(c,!0))}function i(){var b,c=a.dom;j&&(b=a.dom.select(".wpview-clipboard",j)[0],c.unbind(b),c.remove(b),c.unbind(j,"beforedeactivate focusin focusout click mouseup",g),c.removeClass(j,"selected"),a.selection.select(j.nextSibling),a.selection.collapse()),j=null}var j,k=tinymce.util.VK,l=tinymce.dom.TreeWalker,m=!1;if("undefined"!=typeof wp&&wp.mce)return a.on("BeforeAddUndo",function(a){j&&!m&&a.preventDefault()}),a.on("BeforeSetContent",function(a){a.content&&(a.content=wp.mce.views.toViews(a.content))}),a.on("SetContent",function(b){var e,f;wp.mce.views.render(),(b.load||!b.set)&&(e=a.getBody(),c(e.lastChild)&&(f=d(),e.appendChild(f),a.selection.setCursorLocation(f,0)))}),a.on("click",function(b){var e,f,g,h,i,j=a.getBody(),k=a.getDoc(),l=k.documentElement.scrollTop||j.scrollTop||0;"HTML"!==b.target.nodeName||b.metaKey||b.ctrlKey||(g=j.firstChild,h=j.lastChild,e=b.clientX,f=b.clientY,c(g)&&(eh.offsetLeft+h.offsetWidth||l+f-(h.offsetTop+h.offsetHeight)>0)&&(i=d(),j.appendChild(i)),i&&a.selection.setCursorLocation(i,0))}),a.on("init",function(){var d=a.selection;a.on("BeforeSetContent",function(){var e,f,g=b(d.getNode());g&&(!g.nextSibling||c(g.nextSibling)?(f=a.getDoc().createTextNode(""),a.dom.insertAfter(f,g)):(e=new l(g.nextSibling,g.nextSibling),f=e.next()),d.select(f),d.collapse(!0))}),a.on("SetContent",function(a){if(a.context){var b=d.getNode();b.innerHTML&&(b.innerHTML=wp.mce.views.toViews(b.innerHTML))}}),a.dom.bind(a.getBody(),"mousedown mouseup click",function(c){var d=b(c.target);return d?(c.stopPropagation(),"click"===c.type&&(c.metaKey||c.ctrlKey||(a.dom.hasClass(c.target,"edit")?wp.mce.views.edit(d):a.dom.hasClass(c.target,"remove")&&a.dom.remove(d))),h(d),!1):void("click"===c.type&&i())})}),a.on("PreProcess",function(b){var c=a.dom;tinymce.each(c.select("p[data-wpview-pad]",b.node),function(a){c.isEmpty(a)?c.remove(a):c.setAttrib(a,"data-wpview-pad",null)}),tinymce.each(c.select("div[data-wpview-text]",b.node),function(a){"textContent"in a?a.textContent="":a.innerText="",c.replace(c.create("p",null,window.decodeURIComponent(c.getAttrib(a,"data-wpview-text"))),a)})}),a.on("keydown",function(c){var d,e=c.keyCode;if(j){if(c.metaKey||c.ctrlKey||e>=112&&123>=e)return void((c.metaKey||c.ctrlKey)&&88===e&&(m=j));if(d=b(a.selection.getNode()),d!==j)return void i();(e===k.DELETE||e===k.BACKSPACE)&&a.dom.remove(j),c.preventDefault()}}),a.on("keyup",function(b){var e,f,g=b.keyCode,h=a.getBody();m&&(a.dom.remove(m),m=!1),(g===k.DELETE||g===k.BACKSPACE)&&(c(h.lastChild)&&(e=d(),h.appendChild(e),2===h.childNodes.length&&a.selection.setCursorLocation(e,0)),f=a.selection.getRng(),h.firstChild===f.startContainer&&f.collapsed===!0&&c(f.startContainer.nextSibling)&&0===f.startOffset&&a.dom.remove(f.startContainer))}),{getViewText:e,setViewText:f}});
\ No newline at end of file
diff --git a/wp-includes/js/tinymce/skins/wordpress/wp-content.css b/wp-includes/js/tinymce/skins/wordpress/wp-content.css
index 95f1f6c831..afff0c8599 100644
--- a/wp-includes/js/tinymce/skins/wordpress/wp-content.css
+++ b/wp-includes/js/tinymce/skins/wordpress/wp-content.css
@@ -198,6 +198,141 @@ img::selection {
outline: 0;
}
+
+/**
+ * WP Views
+ */
+
+/* IE hasLayout. Needed for all IE incl. 11 (ugh, not again!!) */
+.wpview-wrap {
+ width: 99.99%;
+ position: relative;
+}
+
+/* delegate the handling of the selection to the wpview tinymce plugin */
+.wpview-wrap,
+.wpview-wrap * {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/* hide the shortcode content, but allow the content to still be selected */
+.wpview-wrap .wpview-clipboard {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: -1;
+ clip: rect(1px, 1px, 1px, 1px);
+ overflow: hidden;
+ outline: 0;
+}
+
+/**
+ * Gallery preview
+ */
+.wpview-type-gallery {
+ position: relative;
+ padding: 0 0 12px;
+ margin-bottom: 16px;
+ cursor: pointer;
+}
+
+ .wpview-type-gallery:after {
+ content: '';
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+
+ .wpview-type-gallery.selected {
+ background-color: #efefef;
+}
+
+.wpview-type-gallery .toolbar {
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: #333;
+ color: white;
+ padding: 4px;
+ display: none;
+}
+
+.wpview-type-gallery.selected .toolbar {
+ display: block;
+}
+
+.wpview-type-gallery .toolbar span {
+ cursor: pointer;
+}
+
+.gallery img[data-mce-selected]:focus {
+ outline: none;
+}
+
+.gallery a {
+ cursor: default;
+}
+
+.gallery {
+ margin: auto;
+ line-height: 1;
+}
+
+.gallery .gallery-item {
+ float: left;
+ margin: 10px 0 0 0;
+ text-align: center;
+}
+
+.gallery .gallery-caption,
+.gallery .gallery-icon {
+ margin: 0;
+}
+
+.gallery-columns-1 .gallery-item {
+ width: 99%;
+}
+
+.gallery-columns-2 .gallery-item {
+ width: 49.5%;
+}
+
+.gallery-columns-3 .gallery-item {
+ width: 33%;
+}
+
+.gallery-columns-4 .gallery-item {
+ width: 24.75%;
+}
+
+.gallery-columns-5 .gallery-item {
+ width: 19.825%;
+}
+
+.gallery-columns-6 .gallery-item {
+ width: 16%;
+}
+
+.gallery-columns-7 .gallery-item {
+ width: 14%;
+}
+
+.gallery-columns-8 .gallery-item {
+ width: 12%;
+}
+
+.gallery-columns-9 .gallery-item {
+ width: 11%;
+}
+
+.gallery img {
+ border: 1px solid #cfcfcf;
+}
+
img.wp-oembed {
border: 1px dashed #888;
background: #f7f5f2 url(images/embedded.png) no-repeat scroll center center;
diff --git a/wp-includes/js/tinymce/wp-tinymce.js.gz b/wp-includes/js/tinymce/wp-tinymce.js.gz
index 8e0c8388f8..248d6e7160 100644
Binary files a/wp-includes/js/tinymce/wp-tinymce.js.gz and b/wp-includes/js/tinymce/wp-tinymce.js.gz differ
diff --git a/wp-includes/media-template.php b/wp-includes/media-template.php
index 5304899f3c..97430d0f18 100644
--- a/wp-includes/media-template.php
+++ b/wp-includes/media-template.php
@@ -648,6 +648,36 @@ function wp_print_media_templates() {
+
+
+