Index: src/wp-includes/js/mce-view.js
===================================================================
--- src/wp-includes/js/mce-view.js	(revision 31556)
+++ src/wp-includes/js/mce-view.js	(working copy)
@@ -64,6 +64,8 @@
 		 * Returns the settings of a view type.
 		 *
 		 * @param {String} type The view type.
+		 *
+		 * @return {Function} The view constructor.
 		 */
 		get: function( type ) {
 			return views[ type ];
@@ -142,6 +144,8 @@
 		 * @param {String} type    The view type.
 		 * @param {String} text    The textual representation of the view.
 		 * @param {Object} options Options.
+		 *
+		 * @return {wp.mce.View} The view instance.
 		 */
 		createInstance: function( type, text, options ) {
 			var View = this.get( type ),
@@ -163,10 +167,27 @@
 		/**
 		 * Get a view instance.
 		 *
-		 * @param {String} text The textual representation of the view.
+		 * @param {(String|HTMLElement)} object The textual representation of the view or the view node.
+		 *
+		 * @return {wp.mce.View} The view instance.
 		 */
-		getInstance: function( text ) {
-			return instances[ encodeURIComponent( text ) ];
+		getInstance: function( object ) {
+			if ( typeof object === 'string' ) {
+				return instances[ encodeURIComponent( object ) ];
+			}
+
+			return instances[ $( object ).data( 'wpview-text' ) ];
+		},
+
+		/**
+		 * Given a view node, get the view's text.
+		 *
+		 * @param {HTMLElement} node The view node.
+		 *
+		 * @return {String} The textual representation of the view.
+		 */
+		getText: function( node ) {
+			return decodeURIComponent( $( node ).data( 'wpview-text' ) || '' );
 		},
 
 		/**
@@ -188,8 +209,7 @@
 		 * @param {HTMLElement}    node   The view node to update.
 		 */
 		update: function( text, editor, node ) {
-			var oldText = decodeURIComponent( $( node ).data( 'wpview-text' ) ),
-				instance = this.getInstance( oldText );
+			var instance = this.getInstance( node );
 
 			if ( instance ) {
 				instance.update( text, editor, node );
@@ -203,14 +223,27 @@
 		 * @param {HTMLElement}    node   The view node to edit.
 		 */
 		edit: function( editor, node ) {
-			var text = decodeURIComponent( $( node ).data( 'wpview-text' ) ),
-				instance = this.getInstance( text );
+			var instance = this.getInstance( node );
 
 			if ( instance && instance.edit ) {
-				instance.edit( text, function( text ) {
+				instance.edit( instance.text, function( text ) {
 					instance.update( text, editor, node );
 				} );
 			}
+		},
+
+		/**
+		 * Remove a given view node from the DOM.
+		 *
+		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
+		 * @param {HTMLElement}    node   The view node to remove.
+		 */
+		remove: function( editor, node ) {
+			var instance = this.getInstance( node );
+
+			if ( instance ) {
+				instance.remove( editor, node );
+			}
 		}
 	};
 
@@ -218,7 +251,7 @@
 	 * A Backbone-like View constructor intended for use when rendering a TinyMCE View.
 	 * The main difference is that the TinyMCE View is not tied to a particular DOM node.
 	 *
-	 * @param {Object} Options.
+	 * @param {Object} options Options.
 	 */
 	wp.mce.View = function( options ) {
 		_.extend( this, options );
@@ -227,7 +260,7 @@
 
 	wp.mce.View.extend = Backbone.View.extend;
 
-	_.extend( wp.mce.View.prototype, {
+	_.extend( wp.mce.View.prototype, Backbone.Events, {
 
 		/**
 		 * The content.
@@ -275,9 +308,9 @@
 			this.replaceMarkers();
 
 			if ( this.getContent() ) {
-				this.setContent( this.getContent(), function( editor, node ) {
+				this.setContent( this.getContent(), function( editor, node, contentNode ) {
 					$( node ).data( 'rendered', true );
-					this.bindNodes.apply( this, arguments );
+					this.trigger( 'bind', editor, node, contentNode );
 				}, force ? null : false );
 			} else {
 				this.setLoader();
@@ -285,36 +318,16 @@
 		},
 
 		/**
-		 * Binds a given rendered view node.
-		 * Runs after a view node's content is added to the DOM.
-		 *
-		 * @param {tinymce.Editor} editor      The TinyMCE editor instance the view node is in.
-		 * @param {HTMLElement}    node        The view node.
-		 * @param {HTMLElement}    contentNode The view's content node.
-		 */
-		bindNodes: function( /* editor, node, contentNode */ ) {},
-
-		/**
 		 * Unbinds all view nodes tied to this view instance.
 		 * Runs before their content is removed from the DOM.
 		 */
 		unbind: function() {
-			this.getNodes( function() {
-				this.unbindNodes.apply( this, arguments );
+			this.getNodes( function( editor, node, contentNode ) {
+				this.trigger( 'unbind', editor, node, contentNode );
 			}, true );
 		},
 
 		/**
-		 * Unbinds a given view node.
-		 * Runs before the view node's content is removed from the DOM.
-		 *
-		 * @param {tinymce.Editor} editor      The TinyMCE editor instance the view node is in.
-		 * @param {HTMLElement}    node        The view node.
-		 * @param {HTMLElement}    contentNode The view's content node.
-		 */
-		unbindNodes: function( /* editor, node, contentNode */ ) {},
-
-		/**
 		 * Gets all the TinyMCE editor instances that support views.
 		 *
 		 * @param {Function} callback A callback.
@@ -631,6 +644,17 @@
 			$( node ).data( 'rendered', false );
 			editor.dom.setAttrib( node, 'data-wpview-text', encodeURIComponent( text ) );
 			wp.mce.views.createInstance( this.type, text, this.match( text ).options ).render();
+		},
+
+		/**
+		 * Remove a given view node from the DOM.
+		 *
+		 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
+		 * @param {HTMLElement}    node   The view node to remove.
+		 */
+		remove: function( editor, node ) {
+			this.trigger( 'unbind', editor, node, $( node ).find( '.wpview-content' ).get( 0 ) );
+			editor.dom.remove( node );
 		}
 	} );
 } )( window, window.wp, window.jQuery );
Index: src/wp-includes/js/tinymce/plugins/wpview/plugin.js
===================================================================
--- src/wp-includes/js/tinymce/plugins/wpview/plugin.js	(revision 31556)
+++ src/wp-includes/js/tinymce/plugins/wpview/plugin.js	(working copy)
@@ -72,11 +72,9 @@
 	}
 
 	function removeView( view ) {
-		// TODO: trigger an event to run a clean up function.
-		// Maybe `jQuery( view ).trigger( 'remove' );`?
 		editor.undoManager.transact( function() {
 			handleEnter( view );
-			editor.dom.remove( view );
+			wp.mce.views.remove( editor, view );
 		});
 	}
 
@@ -107,7 +105,7 @@
 		clipboard = dom.create( 'div', {
 			'class': 'wpview-clipboard',
 			'contenteditable': 'true'
-		}, decodeURIComponent( editor.dom.getAttrib( viewNode, 'data-wpview-text' ) ) );
+		}, wp.mce.views.getText( viewNode ) );
 
 		editor.dom.select( '.wpview-body', viewNode )[0].appendChild( clipboard );
 
