diff --git a/wp-includes/js/mce-view.js b/wp-includes/js/mce-view.js index ba89e976a9..8242336eaf 100644 --- a/wp-includes/js/mce-view.js +++ b/wp-includes/js/mce-view.js @@ -311,8 +311,7 @@ window.wp = window.wp || {}; render: function( scope ) { $( '.wp-view-wrap', scope ).each( function() { var wrapper = $(this), - id = wrapper.data('wp-view'), - view = instances[ id ]; + view = wp.mce.view.instance( this ); if ( ! view ) return; @@ -359,6 +358,17 @@ window.wp = window.wp || {}; return wp.mce.view.removeInternalAttrs( wp.html.attrs( content ) ); }, + // ### instance( scope ) + // + // Accepts a MCE view wrapper `node` (i.e. a node with the + // `wp-view-wrap` class). + instance: function( node ) { + var id = $( node ).data('wp-view'); + + if ( id ) + return instances[ id ]; + }, + // ### Select a view. // // Accepts a MCE view wrapper `node` (i.e. a node with the diff --git a/wp-includes/js/tinymce/plugins/wpview/editor_plugin_src.js b/wp-includes/js/tinymce/plugins/wpview/editor_plugin_src.js index ed12deb21a..f7044c461a 100644 --- a/wp-includes/js/tinymce/plugins/wpview/editor_plugin_src.js +++ b/wp-includes/js/tinymce/plugins/wpview/editor_plugin_src.js @@ -3,12 +3,13 @@ */ (function() { - + var VK = tinymce.VK, + TreeWalker = tinymce.dom.TreeWalker, + selected; tinymce.create('tinymce.plugins.wpView', { init : function( editor, url ) { - var wpView = this, - selected; + var wpView = this; // Check if the `wp.mce` API exists. if ( typeof wp === 'undefined' || ! wp.mce ) @@ -38,6 +39,33 @@ editor.onInit.add( function( editor ) { + // When a view is selected, ensure content that is being pasted + // or inserted is added to a text node (instead of the view). + editor.selection.onBeforeSetContent.add( function( selection, o ) { + var view = wpView.getParentView( selection.getNode() ), + walker, target; + + // If the selection is not within a view, bail. + if ( ! view ) + return; + + // 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 ); + + // Otherwise, find the next text node. + } else { + 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 and immediately render them. // @@ -66,35 +94,84 @@ }); // Triggers when the selection is changed. - editor.onNodeChange.add( function( editor, controlManager, node, collapsed, o ) { + // Add the event handler to the top of the stack. + editor.onNodeChange.addToTop( function( editor, controlManager, node, collapsed, o ) { var view = wpView.getParentView( node ); - // If we've clicked off of the selected view, deselect it. - if ( selected && selected !== view ) - wp.mce.view.deselect( selected ); + // Update the selected view. + if ( view ) { + wpView.select( view ); - // Bail if we're not selecting another view. - if ( ! view ) + // Prevent the selection from propagating to other plugins. + return false; + + // If we've clicked off of the selected view, deselect it. + } else { + wpView.deselect(); + } + }); + + editor.onKeyDown.addToTop( function( editor, event ) { + var keyCode = event.keyCode, + view, instance; + + // If a view isn't selected, let the event go on its merry way. + if ( ! selected ) return; - // Update the selected view. - selected = view; - wp.mce.view.select( selected ); + // 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; + } - // Prevent the selection from propagating to other plugins. - return false; + // 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 ( /(?:^|\s)wp-view-wrap(?:\s|$)/.test( node.className ) ) + 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; + }, + getInfo : function() { return { longname : 'WordPress Views',