Index: src/wp-admin/upload.php
===================================================================
--- src/wp-admin/upload.php	(revision 28992)
+++ src/wp-admin/upload.php	(working copy)
@@ -24,16 +24,8 @@
 	wp_enqueue_media();
 	wp_enqueue_script( 'media-grid' );
 	wp_enqueue_script( 'media' );
-	
+
 	require_once( ABSPATH . 'wp-admin/admin-header.php' );
-	?><div class="view-switch media-grid-view-switch">
-		<a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">
-			<img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>
-		</a>
-		<a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">
-			<img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>
-		</a>
-	</div><?php
 	include( ABSPATH . 'wp-admin/admin-footer.php' );
 	exit;
 }
Index: src/wp-includes/css/media-views.css
===================================================================
--- src/wp-includes/css/media-views.css	(revision 28991)
+++ src/wp-includes/css/media-views.css	(working copy)
@@ -750,6 +750,30 @@
 	max-height: 100%;
 }
 
+.attachment-preview.type-audio .thumbnail,
+.attachment-preview.type-video .thumbnail {
+	z-index: 1;
+	margin: 5%;
+	max-width: 90%;
+	max-height: 90%;
+}
+
+.media-frame-content .attachment-preview.type-audio .icon,
+.media-frame-content .attachment-preview.type-video .icon {
+	z-index: 2;
+	background: #f1f1f1;
+	position: relative;
+	padding: 0;
+	top: 15%;
+	left: auto;
+	right: auto;
+}
+
+.attachment-preview.type-audio .filename,
+.attachment-preview.type-video .filename {
+	z-index: 3;
+}
+
 .attachment-preview .thumbnail:after {
 	content: '';
 	display: block;
@@ -909,6 +933,22 @@
 	border-radius: 0;
 }
 
+.attachment .data-fields {
+	margin: 5px 0 0;
+}
+
+.attachment .data-field {
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	display: block;
+	line-height: 19px;
+	height: 19px;
+	text-align: left;
+	width: 90%;
+	margin: 0 5%;
+}
+
 /**
  * Attachments Browser
  */
@@ -924,6 +964,10 @@
 	height: 50px;
 }
 
+.attachments-browser.hide-sidebar .media-toolbar {
+	right: 0;
+}
+
 .attachments-browser .media-toolbar-primary > .media-button,
 .attachments-browser .media-toolbar-primary > .media-button-group,
 .attachments-browser .media-toolbar-secondary > .media-button,
@@ -942,6 +986,92 @@
 	outline: none;
 }
 
+.inline-toolbar {
+	position: absolute;
+	top: 0;
+	left: 0;
+	display: none;
+	z-index: 100;
+}
+
+.inline-toolbar .remove {
+	display: none;
+}
+
+.active-video .inline-toolbar .remove {
+	display: inline-block;
+}
+
+.attachment:hover .inline-toolbar {
+	display: block;
+}
+
+.inline-toolbar div,
+.inline-toolbar .inline-media-control {
+	display: inline-block;
+	margin-top: 4px;
+	margin-left: 4px;
+	padding: 2px;
+	width: 20px;
+	height: 20px;
+	box-shadow: 0 1px 3px rgba(0,0,0,0.5);
+	background-color: #000;
+	background-color: rgba(0,0,0,0.9);
+	cursor: pointer;
+	color: white;
+	font-size: 20px;
+}
+
+.ie8 .inline-toolbar div,
+.ie7 .inline-toolbar div {
+	display: inline;
+	padding: 0;
+}
+
+.inline-media-control span {
+	display: block;
+	width: 16px;
+	height: 16px;
+	margin: 2px;
+	background: url(/wp-includes/js/mediaelement/controls.png) 0 0 no-repeat;
+}
+
+.inline-media-control.active span {
+	margin: 2px;
+	background-position: 0 -16px;
+}
+
+.inline-media-control.paused span {
+	margin: 2px;
+	background-position: 0 0;
+}
+
+audio#inline-media-node {
+	display: none;
+}
+
+video#inline-media-node {
+	position: relative;
+	z-index: 5;
+	top: 0;
+	left: 0;
+}
+
+.inline-video-wrap {
+	width: 100%;
+	height: auto;
+	position: absolute;
+	z-index: 5;
+	background: #000;
+	padding: 10px 0 5px;
+	top: 0;
+	left: 0;
+}
+
+.attachments-browser.hide-sidebar .attachments {
+	right: 0;
+}
+
 .attachments-browser .instructions {
 	display: inline-block;
 	margin-top: 16px;
@@ -2388,11 +2518,11 @@
 	line-height: 29px;
 }
 
-.media-grid-view-switch {
-	position: fixed;
-	right: 10px;
-	top: 44px;
-	z-index: 300;
+.media-grid-view .view-switch {
+	display: inline-block;
+	float: none;
+	margin-top: 13px;
+	vertical-align: middle;
 }
 
 /**
@@ -2427,7 +2557,217 @@
 	display: none;
 }
 
+/**
+ * Copied styles from the Add theme toolbar.
+ *
+ * This should be OOCSS'd so both use a shared selector.
+ */
+.media-grid-view .media-toolbar {
+	background: #fff;
+	-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
+	box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	color: #555;
+	display: inline-block;
+	font-size: 13px;
+	padding: 0 20px;
+	position: relative;
+	width: 100%;
+}
+
+/**
+ * The left and right buttons are copied from the expanded theme details modal.
+ *
+ * This should be OOCSS'd so both use a shared selector.
+ */
+.edit-attachment-frame .edit-media-header .left,
+.edit-attachment-frame .edit-media-header .right {
+	cursor: pointer;
+	color: #777;
+	background-color: transparent;
+	height: 48px;
+	width: 54px;
+	float: left;
+	text-align: center;
+	border: 0;
+	border-right: 1px solid #ddd;
+}
+
+.edit-attachment-frame .edit-media-header .right:before,
+.edit-attachment-frame .edit-media-header .left:before {
+	font: normal 20px/50px 'dashicons' !important;
+	display: inline;
+	font-weight: 300;
+}
+
+
+.edit-attachment-frame .edit-media-header .left:before {
+	content: '\f340';
+}
+
+.edit-attachment-frame .edit-media-header .right:before {
+	content: '\f344';
+}
+
+.edit-attachment-frame .edit-media-header .left.disabled,
+.edit-attachment-frame .edit-media-header .right.disabled,
+.edit-attachment-frame .edit-media-header .left.disabled:hover,
+.edit-attachment-frame .edit-media-header .right.disabled:hover {
+	color: #ccc;
+	background: inherit;
+	cursor: inherit;
+}
+
+.edit-attachment-frame .edit-media-header .close:hover,
+.edit-attachment-frame .edit-media-header .right:hover,
+.edit-attachment-frame .edit-media-header .left:hover,
+.edit-attachment-frame .edit-media-header .close:focus,
+.edit-attachment-frame .edit-media-header .right:focus,
+.edit-attachment-frame .edit-media-header .left:focus {
+	background: #0074a2;
+	color: #fff;
+}
+
+.edit-attachment-frame .media-frame-content,
+.edit-attachment-frame .media-frame-router {
+	left: 0;
+}
+
+/* Hiding this for the moment instead of removing it from the template. */
+.edit-attachment-frame h3 {
+	display: none;
+}
+
+.edit-attachment-frame .attachment-details {
+	position: absolute;
+	overflow: auto;
+	top: 0;
+	bottom: 0;
+	right: 0;
+	left: 0;
+}
+
+.edit-attachment-frame .attachment-info {
+	border-bottom: 0;
+	border-right: 1px solid #ddd;
+	bottom: 0;
+	position: absolute;
+	top: 0;
+	left: 0;
+	margin-bottom: 0;
+	padding: 2% 4%;
+	right: 50%;
+}
+
+.edit-attachment-frame .attachment-info .thumbnail {
+	max-width: none;
+	max-height: none;
+}
+
+.edit-attachment-frame .attachment-info .thumbnail-image img {
+	margin: 0;
+}
+
+.edit-attachment-frame .attachment-info .thumbnail-image:after {
+	-webkit-box-shadow: none;
+	        box-shadow: none;
+}
+
+.edit-attachment-frame .attachment-info .thumbnail img {
+	max-width: none;
+	max-height: 50%;
+}
+
+.edit-attachment-frame .attachment-info .details {
+	float: none;
+}
+
+.edit-attachment-frame .attachment-fields {
+	bottom: 0;
+	padding: 2% 4%;
+	position: absolute;
+	top: 0;
+	left: 50%;
+	right: 0;
+}
+
+.edit-attachment-frame .attachment-fields .setting {
+	display: block;
+	float: left;
+	width: 100%;
+	margin: 1px 0;
+}
+
+.edit-attachment-frame .attachment-fields .setting label {
+	display: block;
+}
+
+.edit-attachment-frame .attachment-fields .setting .link-to-custom {
+	margin: 3px 0;
+}
+
+.edit-attachment-frame .attachment-fields .setting .name {
+	min-width: 30%;
+	margin-right: 4%;
+	font-size: 12px;
+	text-align: right;
+}
+
+.edit-attachment-frame .attachment-fields .setting select {
+	max-width: 65%;
+}
+
+.edit-attachment-frame .attachment-fields .setting input[type="checkbox"],
+.edit-attachment-frame .attachment-fields .field input[type="checkbox"] {
+	width: 16px;
+	float: none;
+	margin: 8px 3px 0;
+	padding: 0;
+}
+
+.edit-attachment-frame .attachment-fields .setting span {
+	float: left;
+	min-height: 22px;
+	padding-top: 8px;
+	line-height: 16px;
+	font-weight: normal;
+	color: #666;
+}
+
+.edit-attachment-frame .attachment-fields .setting input[type="text"],
+.edit-attachment-frame .attachment-fields .setting input[type="password"],
+.edit-attachment-frame .attachment-fields .setting input[type="number"],
+.edit-attachment-frame .attachment-fields .setting input[type="search"],
+.edit-attachment-frame .attachment-fields .setting input[type="email"],
+.edit-attachment-frame .attachment-fields .setting input[type="url"],
+.edit-attachment-frame .attachment-fields .setting textarea,
+.edit-attachment-frame .attachment-fields .setting .value {
+	margin: 1px;
+	width: 65%;
+	float: right;
+	padding: 6px 8px;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+}
+
+.edit-attachment-frame .attachment-fields .setting textarea {
+	height: 62px;
+	resize: vertical;
+}
+
+.edit-attachment-frame .attachment-fields select {
+	margin-top: 3px;
+}
+
+.media-grid-view.hide-router .media-frame-title {
+	box-shadow: none;
+}
+
 .media-grid-view .media-frame-content {
+	background-color: transparent;
 	bottom: 40px;
 }
 @media screen and (max-width: 782px) {
Index: src/wp-includes/js/media-audiovideo.js
===================================================================
--- src/wp-includes/js/media-audiovideo.js	(revision 28991)
+++ src/wp-includes/js/media-audiovideo.js	(working copy)
@@ -915,15 +915,138 @@
 		}
 	});
 
+	media.InlineMedia = Backbone.View.extend( _.extend( {}, media.mixin, {
+		initialize: function() {
+			_.bindAll( this, 'setMedia', 'unsetMedia', 'pauseMedia', 'mediaSuccess' );
+
+			this.settings = baseSettings;
+			this.settings.success = this.mediaSuccess;
+			this.isPaused = false;
+		},
+
+		players : [],
+
+		mediaSuccess: function( mediaElement ) {
+			var $el = this.currentEl;
+
+			this.mejs = mediaElement;
+			this.mejs.load();
+			this.mejs.play();
+			this.mejs.addEventListener( 'ended', function() {
+				$el.removeClass( 'active paused' );
+			} );
+		},
+
+		appendNode: function( $el ) {
+			var url = $el.attr( 'href' ),
+				type = $el.data( 'type' ),
+				attrs,
+				wrapClass,
+				$node;
+
+			wrapClass = 'inline-media-wrap inline-' + type + '-wrap';
+			$node = $( '<div class="' + wrapClass + '"></div>' );
+
+			attrs = {
+				id: 'inline-media-node',
+				preload: 'none',
+				src: url,
+				height: 'auto',
+				width: '100%'
+			};
+
+			$node.append( $( '<' + type + ' />' ).attr( attrs ) );
+
+			if ( 'audio' === type ) {
+				$( 'body' ).append( $node.hide() );
+			} else {
+				$el.parents( '.attachment' ).prepend( $node ).addClass( 'active-video' );
+			}
+		},
+
+		playMedia: function( $el ) {
+			var self = this;
+			this.currentEl = $el;
+			this.appendNode( $el );
+			this.isPaused = false;
+
+			_.delay( function () {
+				var player = new MediaElement( $( '#inline-media-node' ).get(0), self.settings );
+				self.players.push( player );
+			}, 50 );
+		},
+
+		pauseMedia: function(docEvent, e) {
+			if ( this.isPaused ) {
+				this.mejs.play();
+				this.isPaused = false;
+			} else {
+				this.mejs.pause();
+				this.isPaused = true;
+			}
+
+			$( e.currentTarget )[ ( this.isPaused ? 'add' : 'remove' ) + 'Class' ]( 'paused' );
+		},
+
+		setMedia: function(docEvent, e) {
+			var $el = $( e.currentTarget );
+			this.unsetMedia();
+
+			$el.addClass( 'active' );
+
+			this.playMedia( $el );
+		},
+
+		unsetMedia: function() {
+			var wraps = $( '.inline-media-wrap' );
+
+			$( '.inline-media-control' ).removeClass( 'active paused' );
+			this.isPaused = false;
+			this.unsetPlayers();
+
+			wraps.parents( '.attachment' ).removeClass( 'active-video' );
+			wraps.remove();
+		}
+	} ) );
+
 	/**
 	 * Event binding
 	 */
 	function init() {
+		var inlineMedia = new media.InlineMedia(), currentInline;
+
 		$(document.body)
 			.on( 'click', '.add-media-source', function( e ) {
 				media.frame.lastMime = $( e.currentTarget ).data( 'mime' );
 				media.frame.setState( 'add-' + media.frame.defaults.id + '-source' );
+			} )
+			.on( 'click', '.inline-media-control', function( e ) {
+				var $el = $( e.currentTarget );
+				e.preventDefault();
+				e.stopPropagation();
+
+				$el.parents( '.attachment' ).blur();
+
+				if ( $el.hasClass( 'active' ) ) {
+					if ( currentInline && currentInline !== e.currentTarget ) {
+						$( document ).trigger( 'inline:media:off', [ e ] );
+					} else {
+						$( document ).trigger( 'inline:media:pause', [ e ] );
+					}
+				} else {
+					$( document ).trigger( 'inline:media:on', [ e ] );
+				}
+				currentInline = e.currentTarget;
+			} )
+			.on( 'click', '.active-video .remove', function(e) {
+				$( e.currentTarget ).parents( '.attachment' ).blur();
+				inlineMedia.unsetMedia();
 			} );
+
+		$( document )
+			.on( 'inline:media:on', inlineMedia.setMedia )
+			.on( 'inline:media:pause', inlineMedia.pauseMedia )
+			.on( 'inline:media:off', inlineMedia.unsetMedia );
 	}
 
 	$( init );
Index: src/wp-includes/js/media-grid.js
===================================================================
--- src/wp-includes/js/media-grid.js	(revision 28992)
+++ src/wp-includes/js/media-grid.js	(working copy)
@@ -1,4 +1,4 @@
-(function( $, _, Backbone, wp ) {
+(function($, _, Backbone, wp) {
 	var media = wp.media, l10n;
 
 	// Link any localized strings.
@@ -10,6 +10,150 @@
 	}
 
 	/**
+	 * A more abstracted state, because media.controller.State expects
+	 * specific regions (menu, title, etc.) to exist on the frame, which do not
+	 * exist in media.view.Frame.EditAttachment.
+	 */
+	media.controller._State = Backbone.Model.extend({
+		constructor: function() {
+			this.on( 'activate', this._preActivate, this );
+			this.on( 'activate', this.activate, this );
+			this.on( 'activate', this._postActivate, this );
+			this.on( 'deactivate', this._deactivate, this );
+			this.on( 'deactivate', this.deactivate, this );
+			this.on( 'reset', this.reset, this );
+			this.on( 'ready', this.ready, this );
+			/**
+			 * Call parent constructor with passed arguments
+			 */
+			Backbone.Model.apply( this, arguments );
+		},
+
+		/**
+		 * @abstract
+		 */
+		ready: function() {},
+		/**
+		 * @abstract
+		 */
+		activate: function() {},
+		/**
+		 * @abstract
+		 */
+		deactivate: function() {},
+		/**
+		 * @abstract
+		 */
+		reset: function() {},
+		/**
+		 * @access private
+		 */
+		_preActivate: function() {
+			this.active = true;
+		},
+		/**
+		 * @access private
+		 */
+		_postActivate: function() {},
+		/**
+		 * @access private
+		 */
+		_deactivate: function() {
+			this.active = false;
+		}
+	});
+
+	/**
+	 * A state for editing (cropping, etc.) an image.
+	 *
+	 * @constructor
+	 * @augments wp.media.controller.State
+	 * @augments Backbone.Model
+	 */
+	media.controller.EditImageNoFrame = media.controller._State.extend({
+		defaults: {
+			id:      'edit-attachment',
+			title:   l10n.editImage,
+			// Region mode defaults.
+			menu:    false,
+			router:  'edit-metadata',
+			content: 'edit-metadata',
+			toolbar: 'toolbar',
+
+			url:     ''
+		},
+
+		initialize: function() {
+			media.controller._State.prototype.initialize.apply( this, arguments );
+		},
+
+		activate: function() {
+			this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
+		},
+
+		_postActivate: function() {
+			this._content();
+			this._router();
+		},
+
+		deactivate: function() {
+			this.stopListening( this.frame );
+		},
+
+		toolbar: function() {
+			var frame = this.frame,
+				lastState = frame.lastState(),
+				previous = lastState && lastState.id;
+
+			frame.toolbar.set( new media.view.Toolbar({
+				controller: frame,
+				items: {
+					back: {
+						style: 'primary',
+						text:     l10n.back,
+						priority: 20,
+						click:    function() {
+							if ( previous ) {
+								frame.setState( previous );
+							} else {
+								frame.close();
+							}
+						}
+					}
+				}
+			}) );
+		},
+
+		/**
+		 * @access private
+		 */
+		_router: function() {
+			var router = this.frame.router,
+				mode = this.get('router'),
+				view;
+
+			this.frame.$el.toggleClass( 'hide-router', ! mode );
+			if ( ! mode ) {
+				return;
+			}
+
+			this.frame.router.render( mode );
+
+			view = router.get();
+			if ( view && view.select ) {
+				view.select( this.frame.content.mode() );
+			}
+		},
+
+		_content: function() {
+			var mode = this.get( 'content' );
+			if ( mode ) {
+				this.frame[ 'content' ].render( mode );
+			}
+		}
+	});
+
+	/**
 	 * wp.media.view.MediaFrame.Manage
 	 *
 	 * A generic management frame workflow.
@@ -36,7 +180,8 @@
 				library:   {},
 				multiple:  false,
 				state:     'library',
-				uploader:  true
+				uploader:  true,
+				mode:      [ 'grid', 'edit' ]
 			});
 
 			// Ensure core and media grid view UI is enabled.
@@ -111,9 +256,7 @@
 					router:     false,
 					content:    'browse',
 					filterable: 'mime-types'
-				}),
-
-				new media.controller.EditImage( { model: options.editImage } )
+				})
 			]);
 		},
 
@@ -120,9 +263,52 @@
 		bindHandlers: function() {
 			this.on( 'content:create:browse', this.browseContent, this );
 			this.on( 'content:render:edit-image', this.editImageContent, this );
+
+			// Handle a frame-level event for editing an attachment.
+			this.on( 'edit:attachment', this.editAttachment, this );
+			this.on( 'edit:attachment:next', this.editNextAttachment, this );
+			this.on( 'edit:attachment:previous', this.editPreviousAttachment, this );
 		},
 
+		editPreviousAttachment: function( currentModel ) {
+			var library = this.state().get('library'),
+			    currentModelIndex = library.indexOf( currentModel );
+			this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) );
+		},
+
+		editNextAttachment: function( currentModel ) {
+			var library = this.state().get('library'),
+			    currentModelIndex = library.indexOf( currentModel );
+			this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) );
+		},
+
 		/**
+		 * Open the Edit Attachment modal.
+		 */
+		editAttachment: function( model ) {
+			var library = this.state().get('library'), hasPrevious, hasNext;
+			if ( library.indexOf( model ) > 0 ) {
+				hasPrevious = true;
+			}
+			else {
+				hasPrevious = false;
+			}
+			if ( library.indexOf( model ) < library.length - 1 ) {
+				hasNext = true;
+			}
+			else {
+				hasNext = false;
+			}
+
+			new media.view.Frame.EditAttachment({
+				hasPrevious:    hasPrevious,
+				hasNext:        hasNext,
+				model:          model,
+				gridController: this
+			});
+		},
+
+		/**
 		 * Content
 		 *
 		 * @param {Object} content
@@ -143,6 +329,7 @@
 				display:    state.get('displaySettings'),
 				dragInfo:   state.get('dragInfo'),
 				bulkEdit:   true,
+				sidebar:    false,
 
 				suggestedWidth:  state.get('suggestedWidth'),
 				suggestedHeight: state.get('suggestedHeight'),
@@ -162,5 +349,184 @@
 
 		}
 	});
-	
-}( jQuery, _, Backbone, wp ));
\ No newline at end of file
+
+	media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({
+		template: wp.template( 'attachment-details-two-column' ),
+
+		initialize: function() {
+			this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
+			this.model.on( 'change:sizes change:uploading', this.render, this );
+			this.model.on( 'change:title', this._syncTitle, this );
+			this.model.on( 'change:caption', this._syncCaption, this );
+			this.model.on( 'change:percent', this.progress, this );
+
+			// Update the selection.
+			this.model.on( 'add', this.select, this );
+			this.model.on( 'remove', this.deselect, this );
+		}
+	});
+
+	/**
+	 * A frame for editing the details of a specific media item.
+	 *
+	 * Opens in a modal by default.
+	 *
+	 * Requires an attachment model to be passed in the options hash under `model`.
+	 */
+	media.view.Frame.EditAttachment = media.view.Frame.extend({
+
+		className: 'edit-attachment-frame',
+		template: media.template( 'edit-attachment-frame' ),
+		regions:   [ 'router', 'content' ],
+
+		events: {
+			'click':                    'collapse',
+			'click .delete-media-item': 'deleteMediaItem',
+			'click .left':              'previousMediaItem',
+			'click .right':             'nextMediaItem'
+		},
+
+		initialize: function( options ) {
+			var self = this;
+			media.view.Frame.prototype.initialize.apply( this, arguments );
+
+			_.defaults( this.options, {
+				modal: true,
+				state: 'edit-attachment'
+			});
+
+			this.createStates();
+
+			this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
+			this.on( 'content:render:edit-image', this.editImageContentUgh, this );
+
+			// Only need a tab to Edit Image for images.
+			if ( this.model.get( 'type' ) === 'image' ) {
+				this.on( 'router:create', this.createRouter, this );
+				this.on( 'router:render', this.browseRouter, this );
+			}
+
+			// Initialize modal container view.
+			if ( this.options.modal ) {
+				this.modal = new media.view.Modal({
+					controller: this,
+					title:      this.options.title
+				});
+
+				// Completely destroy the modal DOM element when closing it.
+				this.modal.close = function() {
+					self.modal.remove();
+				};
+
+				this.modal.content( this );
+				this.modal.open();
+			}
+		},
+
+		/**
+		 * Add the default states to the frame.
+		 */
+		createStates: function() {
+			this.states.add([
+				new media.controller.EditImageNoFrame( { model: this.model } )
+			]);
+		},
+
+		/**
+		 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
+		 */
+		render: function() {
+			// Activate the default state if no active state exists.
+			if ( ! this.state() && this.options.state ) {
+				this.setState( this.options.state );
+			}
+			/**
+			 * call 'render' directly on the parent class
+			 */
+			return media.view.Frame.prototype.render.apply( this, arguments );
+		},
+
+		/**
+		 * Content region rendering callback for the `edit-metadata` mode.
+		 */
+		editMetadataContent: function() {
+			var view = new media.view.Attachment.Details.TwoColumn({
+				controller: this,
+				model:      this.model
+			});
+			this.content.set( view );
+		},
+
+		/**
+		 * For some reason the view doesn't exist in the DOM yet, don't have the
+		 * patience to track this down right now.
+		 */
+		editImageContentUgh: function() {
+			_.defer( _.bind( this.editImageContent, this ) );
+		},
+
+		/**
+		 * Render the EditImage view into the frame's content region.
+		 */
+		editImageContent: function() {
+			var view = new media.view.EditImage( { model: this.model, controller: this } ).render();
+
+			this.content.set( view );
+
+			// after creating the wrapper view, load the actual editor via an ajax call
+			view.loadEditor();
+		},
+
+		/**
+		 * Create the router view.
+		 *
+		 * @param {Object} router
+		 * @this wp.media.controller.Region
+		 */
+		createRouter: function( router ) {
+			router.view = new media.view.Router({
+				controller: this
+			});
+		},
+
+		/**
+		 * Router rendering callback.
+		 *
+		 * @param  media.view.Router view Instantiated in this.createRouter()
+		 */
+		browseRouter: function( view ) {
+			view.set({
+				'edit-metadata': {
+					text:     'Edit Metadata',
+					priority: 20
+				},
+				'edit-image': {
+					text:     'Edit Image',
+					priority: 40
+				}
+			});
+		},
+
+		/**
+		 * Click handler to switch to the previous media item.
+		 */
+		previousMediaItem: function() {
+			if ( ! this.options.hasPrevious )
+				return;
+			this.modal.close();
+			this.options.gridController.trigger( 'edit:attachment:previous', this.model );
+		},
+
+		/**
+		 * Click handler to switch to the next media item.
+		 */
+		nextMediaItem: function() {
+			if ( ! this.options.hasNext )
+				return;
+			this.modal.close();
+			this.options.gridController.trigger( 'edit:attachment:next', this.model );
+		}
+
+	});
+
+}(jQuery, _, Backbone, wp));
\ No newline at end of file
Index: src/wp-includes/js/media-views.js
===================================================================
--- src/wp-includes/js/media-views.js	(revision 28992)
+++ src/wp-includes/js/media-views.js	(working copy)
@@ -1758,7 +1758,8 @@
 			_.defaults( this.options, {
 				title:    '',
 				modal:    true,
-				uploader: true
+				uploader: true,
+				mode:     ['select']
 			});
 
 			// Ensure core UI is enabled.
@@ -4530,7 +4531,7 @@
 			var selection = this.options.selection;
 
 			this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
-			this.model.on( 'change:sizes change:uploading', this.render, this );
+			this.model.on( 'change', this.render, this );
 			this.model.on( 'change:title', this._syncTitle, this );
 			this.model.on( 'change:caption', this._syncCaption, this );
 			this.model.on( 'change:percent', this.progress, this );
@@ -4583,7 +4584,7 @@
 					compat:        false,
 					alt:           '',
 					description:   ''
-				});
+				}, this.options );
 
 			options.buttons  = this.buttons;
 			options.describe = this.controller.state().get('describe');
@@ -4633,11 +4634,17 @@
 		 */
 		toggleSelectionHandler: function( event ) {
 			var method;
-
 			// Catch enter and space events
 			if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
 				return;
 			}
+
+			// In the grid view, bubble up an edit:attachment event to the controller.
+			if ( _.contains( this.controller.options.mode, 'grid' ) ) {
+				this.controller.trigger( 'edit:attachment', this.model );
+				return;
+			}
+
 			if ( event.shiftKey ) {
 				method = 'between';
 			} else if ( event.ctrlKey || event.metaKey ) {
@@ -5168,10 +5175,11 @@
 		 */
 		createAttachmentView: function( attachment ) {
 			var view = new this.options.AttachmentView({
-				controller: this.controller,
-				model:      attachment,
-				collection: this.collection,
-				selection:  this.options.selection
+				controller:           this.controller,
+				model:                attachment,
+				collection:           this.collection,
+				selection:            this.options.selection,
+				showAttachmentFields: this.options.showAttachmentFields
 			});
 
 			return this._viewsByCid[ attachment.cid ] = view;
@@ -5468,7 +5476,6 @@
 		}
 	});
 
-
 	/**
 	 * wp.media.view.AttachmentsBrowser
 	 *
@@ -5486,13 +5493,18 @@
 				filters: false,
 				search:  true,
 				display: false,
-
+				sidebar: true,
+				showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ),
 				AttachmentView: media.view.Attachment.Library
 			});
 
 			this.createToolbar();
 			this.updateContent();
-			this.createSidebar();
+			if ( this.options.sidebar ) {
+				this.createSidebar();
+			} else {
+				this.$el.addClass( 'hide-sidebar' );
+			}
 
 			this.collection.on( 'add remove reset', this.updateContent, this );
 		},
@@ -5517,6 +5529,20 @@
 
 			this.views.add( this.toolbar );
 
+			// Feels odd to bring the global media library switcher into the Attachment
+			// browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
+			// which the controller can tap into and add this view?
+			if ( _.contains( this.controller.options.mode, 'grid' ) ) {
+				var libraryViewSwitcherConstructor = media.View.extend({
+					className: 'view-switch media-grid-view-switch',
+					template: media.template( 'media-library-view-switcher')
+				});
+				this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({
+					controller: this.controller,
+					priority: -90
+				}).render() );
+			}
+
 			filters = this.options.filters;
 			if ( 'uploaded' === filters ) {
 				FiltersConstructor = media.view.AttachmentFilters.Uploaded;
@@ -5611,11 +5637,12 @@
 			this.removeContent();
 
 			this.attachments = new media.view.Attachments({
-				controller: this.controller,
-				collection: this.collection,
-				selection:  this.options.selection,
-				model:      this.model,
-				sortable:   this.options.sortable,
+				controller:           this.controller,
+				collection:           this.collection,
+				selection:            this.options.selection,
+				model:                this.model,
+				sortable:             this.options.sortable,
+				showAttachmentFields: this.options.showAttachmentFields,
 
 				// The single `Attachment` view to be used in the `Attachments` view.
 				AttachmentView: this.options.AttachmentView
Index: src/wp-includes/media-template.php
===================================================================
--- src/wp-includes/media-template.php	(revision 28991)
+++ src/wp-includes/media-template.php	(working copy)
@@ -220,6 +220,15 @@
 		</div>
 	</script>
 
+	<script type="text/html" id="tmpl-media-library-view-switcher">
+		<a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">
+			<img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>
+		</a>
+		<a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">
+			<img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>
+		</a>
+	</script>
+
 	<script type="text/html" id="tmpl-uploader-status">
 		<h3><?php _e( 'Uploading' ); ?></h3>
 		<a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a>
@@ -241,7 +250,117 @@
 		<span class="upload-error-message">{{ data.message }}</span>
 	</script>
 
+	<script type="text/html" id="tmpl-edit-attachment-frame">
+		<div class="edit-media-header">
+			<button class="left dashicons dashicons-no<# if ( ! data.hasPrevious ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit previous media item' ); ?></span></button>
+			<button class="right dashicons dashicons-no<# if ( ! data.hasNext ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit next media item' ); ?></span></button>
+		</div>
+		<div class="media-frame-router"></div>
+		<div class="media-frame-content"></div>
+		<div class="media-frame-toolbar"></div>
+	</script>
+
+	<script type="text/html" id="tmpl-attachment-details-two-column">
+		<h3>
+			<?php _e('Attachment Details'); ?>
+
+			<span class="settings-save-status">
+				<span class="spinner"></span>
+				<span class="saved"><?php esc_html_e('Saved.'); ?></span>
+			</span>
+		</h3>
+		<div class="attachment-info">
+			<div class="thumbnail thumbnail-{{ data.type }}">
+				<# if ( data.uploading ) { #>
+					<div class="media-progress-bar"><div></div></div>
+				<# } else if ( 'image' === data.type ) { #>
+					<img src="{{ data.sizes.full.url }}" draggable="false" />
+				<# } else { #>
+					<img src="{{ data.icon }}" class="icon" draggable="false" />
+				<# } #>
+			</div>
+			<div class="details">
+				<div class="filename">{{ data.filename }}</div>
+				<div class="uploaded">{{ data.dateFormatted }}</div>
+
+				<div class="file-size">{{ data.filesizeHumanReadable }}</div>
+				<# if ( 'image' === data.type && ! data.uploading ) { #>
+					<# if ( data.width && data.height ) { #>
+						<div class="dimensions">{{ data.width }} &times; {{ data.height }}</div>
+					<# } #>
+
+					<# if ( data.can.save ) { #>
+						<a class="edit-attachment" href="{{ data.editLink }}&amp;image-editor" target="_blank"><?php _e( 'Edit Image' ); ?></a>
+						<a class="refresh-attachment" href="#"><?php _e( 'Refresh' ); ?></a>
+					<# } #>
+				<# } #>
+
+				<# if ( data.fileLength ) { #>
+					<div class="file-length"><?php _e( 'Length:' ); ?> {{ data.fileLength }}</div>
+				<# } #>
+
+				<# if ( ! data.uploading && data.can.remove ) { #>
+					<?php if ( MEDIA_TRASH ): ?>
+						<a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a>
+					<?php else: ?>
+						<a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a>
+					<?php endif; ?>
+				<# } #>
+
+				<div class="compat-meta">
+					<# if ( data.compat && data.compat.meta ) { #>
+						{{{ data.compat.meta }}}
+					<# } #>
+				</div>
+			</div>
+		</div>
+		<div class="attachment-fields">
+			<label class="setting" data-setting="url">
+				<span class="name"><?php _e('URL'); ?></span>
+				<input type="text" value="{{ data.url }}" readonly />
+			</label>
+			<# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #>
+			<label class="setting" data-setting="title">
+				<span class="name"><?php _e('Title'); ?></span>
+				<input type="text" value="{{ data.title }}" {{ maybeReadOnly }} />
+			</label>
+			<label class="setting" data-setting="caption">
+				<span class="name"><?php _e('Caption'); ?></span>
+				<textarea {{ maybeReadOnly }}>{{ data.caption }}</textarea>
+			</label>
+			<# if ( 'image' === data.type ) { #>
+				<label class="setting" data-setting="alt">
+					<span class="name"><?php _e('Alt Text'); ?></span>
+					<input type="text" value="{{ data.alt }}" {{ maybeReadOnly }} />
+				</label>
+			<# } #>
+			<label class="setting" data-setting="description">
+				<span class="name"><?php _e('Description'); ?></span>
+				<textarea {{ maybeReadOnly }}>{{ data.description }}</textarea>
+			</label>
+			<label class="setting">
+					<span class="name"><?php _e( 'Uploaded By' ); ?></span>
+					<span class="value">{{ data.authorName }}</span>
+				</label>
+			<# if ( data.uploadedTo ) { #>
+				<label class="setting">
+					<span class="name"><?php _e('Uploaded To'); ?></span>
+					<span class="value"><a href="{{ data.uploadedToLink }}">{{ data.uploadedToTitle }}</a></span>
+				</label>
+			<# } #>
+		</div>
+	</script>
+
 	<script type="text/html" id="tmpl-attachment">
+		<# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
+		<div class="inline-toolbar">
+			<div class="dashicons dashicons-edit edit edit-media"></div>
+			<div class="dashicons dashicons-no-alt remove"></div>
+			<# if ( 'audio' === data.type || 'video' === data.type ) { #>
+			<a class="inline-media-control" href="{{ data.url }}" data-type="{{ data.type }}"><span></span></a>
+			<# } #>
+		</div>
+		<# } #>
 		<div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}">
 			<# if ( data.uploading ) { #>
 				<div class="media-progress-bar"><div></div></div>
@@ -251,13 +370,15 @@
 						<img src="{{ data.size.url }}" draggable="false" alt="" />
 					</div>
 				</div>
-			<# } else { #>
+			<# } else {
+				if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) {
+				#><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><#
+				} #>
 				<img src="{{ data.icon }}" class="icon" draggable="false" />
 				<div class="filename">
 					<div>{{ data.filename }}</div>
 				</div>
 			<# } #>
-
 			<# if ( data.buttons.close ) { #>
 				<a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
 			<# } #>
@@ -268,8 +389,8 @@
 		</div>
 		<#
 		var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly';
-		if ( data.describe ) { #>
-			<# if ( 'image' === data.type ) { #>
+		if ( data.describe ) {
+			if ( 'image' === data.type ) { #>
 				<input type="text" value="{{ data.caption }}" class="describe" data-setting="caption"
 					placeholder="<?php esc_attr_e('Caption this image&hellip;'); ?>" {{ maybeReadOnly }} />
 			<# } else { #>
@@ -281,8 +402,31 @@
 					<# } else { #>
 						placeholder="<?php esc_attr_e('Describe this media file&hellip;'); ?>"
 					<# } #> {{ maybeReadOnly }} />
-			<# } #>
+			<# }
+		}
+
+		if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
+		<div class="data-fields">
+		<# _.each( data.showAttachmentFields, function( field ) { #>
+			<div class="data-field data-{{ field }}"><#
+				if ( 'uploadedTo' === field ) {
+					if ( data[field] ) {
+					#><?php _e( 'Uploaded To:' ) ?><#
+					} else {
+					#><?php _e( 'Unattached' ) ?><#
+					}
+				} else if ( 'title' === field && ! data[ field ] ) {
+				#><?php _e( '(No title)' ) ?><#
+				}
+
+				if ( data[ field ] ) {
+					#>{{ data[ field ] }}<#
+				}
+			#></div>
+		<# }); #>
+		</div>
 		<# } #>
+
 	</script>
 
 	<script type="text/html" id="tmpl-attachment-details">
