<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[28993] trunk/src: Media grid, round 2.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/28993">28993</a></dd>
<dt>Author</dt> <dd>helen</dd>
<dt>Date</dt> <dd>2014-07-04 03:38:33 +0000 (Fri, 04 Jul 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Media grid, round 2. Expect much more to come.

* Instead of a sidebar for details, utilize a modal. The modal experience allows for a larger preview, editing images, audio/video previews, and previous/next navigation, like the theme browser. Think of it as an attachment browser.
* Show some details in the grid view to more easily distinguish items.

props ericlewis, wonderboymusic, JerrySarcastic. see <a href="http://core.trac.wordpress.org/ticket/24716">#24716</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminuploadphp">trunk/src/wp-admin/upload.php</a></li>
<li><a href="#trunksrcwpincludescssmediaviewscss">trunk/src/wp-includes/css/media-views.css</a></li>
<li><a href="#trunksrcwpincludesjsmediaaudiovideojs">trunk/src/wp-includes/js/media-audiovideo.js</a></li>
<li><a href="#trunksrcwpincludesjsmediagridjs">trunk/src/wp-includes/js/media-grid.js</a></li>
<li><a href="#trunksrcwpincludesjsmediaviewsjs">trunk/src/wp-includes/js/media-views.js</a></li>
<li><a href="#trunksrcwpincludesmediatemplatephp">trunk/src/wp-includes/media-template.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminuploadphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/upload.php (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/upload.php    2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-admin/upload.php       2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -24,16 +24,8 @@
</span><span class="cx">  wp_enqueue_media();
</span><span class="cx">  wp_enqueue_script( 'media-grid' );
</span><span class="cx">  wp_enqueue_script( 'media' );
</span><del>-       
</del><ins>+
</ins><span class="cx">   require_once( ABSPATH . 'wp-admin/admin-header.php' );
</span><del>-       ?><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
</del><span class="cx">   include( ABSPATH . 'wp-admin/admin-footer.php' );
</span><span class="cx">  exit;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunksrcwpincludescssmediaviewscss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/css/media-views.css (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/css/media-views.css        2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-includes/css/media-views.css   2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -750,6 +750,30 @@
</span><span class="cx">  max-height: 100%;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.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;
+}
+
</ins><span class="cx"> .attachment-preview .thumbnail:after {
</span><span class="cx">  content: '';
</span><span class="cx">  display: block;
</span><span class="lines">@@ -909,6 +933,22 @@
</span><span class="cx">  border-radius: 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.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%;
+}
+
</ins><span class="cx"> /**
</span><span class="cx">  * Attachments Browser
</span><span class="cx">  */
</span><span class="lines">@@ -924,6 +964,10 @@
</span><span class="cx">  height: 50px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.attachments-browser.hide-sidebar .media-toolbar {
+       right: 0;
+}
+
</ins><span class="cx"> .attachments-browser .media-toolbar-primary > .media-button,
</span><span class="cx"> .attachments-browser .media-toolbar-primary > .media-button-group,
</span><span class="cx"> .attachments-browser .media-toolbar-secondary > .media-button,
</span><span class="lines">@@ -942,6 +986,92 @@
</span><span class="cx">  outline: none;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.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;
+}
+
</ins><span class="cx"> .attachments-browser .instructions {
</span><span class="cx">  display: inline-block;
</span><span class="cx">  margin-top: 16px;
</span><span class="lines">@@ -2388,11 +2518,11 @@
</span><span class="cx">  line-height: 29px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.media-grid-view-switch {
-       position: fixed;
-       right: 10px;
-       top: 44px;
-       z-index: 300;
</del><ins>+.media-grid-view .view-switch {
+       display: inline-block;
+       float: none;
+       margin-top: 13px;
+       vertical-align: middle;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><span class="lines">@@ -2427,7 +2557,221 @@
</span><span class="cx">  display: none;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/**
+ * 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 .wp-media-wrapper {
+       margin-top: 20px;
+}
+
+.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;
+}
+
</ins><span class="cx"> .media-grid-view .media-frame-content {
</span><ins>+       background-color: transparent;
</ins><span class="cx">   bottom: 40px;
</span><span class="cx"> }
</span><span class="cx"> @media screen and (max-width: 782px) {
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediaaudiovideojs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-audiovideo.js (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-audiovideo.js     2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-includes/js/media-audiovideo.js        2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -26,6 +26,17 @@
</span><span class="cx">                  }
</span><span class="cx">          },
</span><span class="cx"> 
</span><ins>+               removeAllPlayers: function() {
+                       var p;
+
+                       if ( window.mejs && window.mejs.players ) {
+                               for ( p in window.mejs.players ) {
+                                       window.mejs.players[p].pause();
+                                       this.removePlayer( window.mejs.players[p] );
+                               }
+                       }
+               },
+
</ins><span class="cx">           /**
</span><span class="cx">           * Pauses the current object's instances of MediaElementPlayer
</span><span class="cx">           */
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediagridjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-grid.js (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-grid.js   2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-includes/js/media-grid.js      2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-(function( $, _, Backbone, wp ) {
</del><ins>+(function($, _, Backbone, wp) {
</ins><span class="cx">   var media = wp.media, l10n;
</span><span class="cx"> 
</span><span class="cx">  // Link any localized strings.
</span><span class="lines">@@ -10,6 +10,96 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * 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 );
+                       }
+               }
+       });
+
+       /**
</ins><span class="cx">    * wp.media.view.MediaFrame.Manage
</span><span class="cx">   *
</span><span class="cx">   * A generic management frame workflow.
</span><span class="lines">@@ -36,7 +126,8 @@
</span><span class="cx">                          library:   {},
</span><span class="cx">                          multiple:  false,
</span><span class="cx">                          state:     'library',
</span><del>-                               uploader:  true
</del><ins>+                                uploader:  true,
+                               mode:      [ 'grid', 'edit' ]
</ins><span class="cx">                   });
</span><span class="cx"> 
</span><span class="cx">                  // Ensure core and media grid view UI is enabled.
</span><span class="lines">@@ -111,18 +202,59 @@
</span><span class="cx">                                  router:     false,
</span><span class="cx">                                  content:    'browse',
</span><span class="cx">                                  filterable: 'mime-types'
</span><del>-                               }),
-
-                               new media.controller.EditImage( { model: options.editImage } )
</del><ins>+                                })
</ins><span class="cx">                   ]);
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          bindHandlers: function() {
</span><span class="cx">                  this.on( 'content:create:browse', this.browseContent, this );
</span><span class="cx">                  this.on( 'content:render:edit-image', this.editImageContent, this );
</span><ins>+
+                       // 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 );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><ins>+               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 ) );
+               },
+
</ins><span class="cx">           /**
</span><ins>+                * 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
+                       });
+               },
+
+               /**
</ins><span class="cx">            * Content
</span><span class="cx">           *
</span><span class="cx">           * @param {Object} content
</span><span class="lines">@@ -143,6 +275,7 @@
</span><span class="cx">                          display:    state.get('displaySettings'),
</span><span class="cx">                          dragInfo:   state.get('dragInfo'),
</span><span class="cx">                          bulkEdit:   true,
</span><ins>+                               sidebar:    false,
</ins><span class="cx"> 
</span><span class="cx">                          suggestedWidth:  state.get('suggestedWidth'),
</span><span class="cx">                          suggestedHeight: state.get('suggestedHeight'),
</span><span class="lines">@@ -162,5 +295,194 @@
</span><span class="cx"> 
</span><span class="cx">          }
</span><span class="cx">  });
</span><del>-       
-}( jQuery, _, Backbone, wp ));
</del><span class="cx">\ No newline at end of file
</span><ins>+
+       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 );
+               },
+
+               render: function() {
+                       media.view.Attachment.Details.prototype.render.apply( this, arguments );
+
+                       media.mixin.removeAllPlayers();
+                       $( 'audio, video', this.$el ).each( function (i, elem) {
+                               var el = media.view.MediaDetails.prepareSrc( elem );
+                               new MediaElementPlayer( el, media.mixin.mejsSettings );
+                       } );
+               }
+       });
+
+       /**
+        * 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));
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediaviewsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-views.js (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-views.js  2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-includes/js/media-views.js     2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -337,16 +337,11 @@
</span><span class="cx">  });
</span><span class="cx"> 
</span><span class="cx">  /**
</span><del>-        * wp.media.controller.State
-        *
-        * A state is a step in a workflow that when set will trigger the controllers
-        * for the regions to be updated as specified in the frame. This is the base
-        * class that the various states used in wp.media extend.
-        *
-        * @constructor
-        * @augments Backbone.Model
</del><ins>+         * 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.
</ins><span class="cx">    */
</span><del>-       media.controller.State = Backbone.Model.extend({
</del><ins>+        media.controller._State = Backbone.Model.extend({
</ins><span class="cx">           constructor: function() {
</span><span class="cx">                  this.on( 'activate', this._preActivate, this );
</span><span class="cx">                  this.on( 'activate', this.activate, this );
</span><span class="lines">@@ -354,13 +349,11 @@
</span><span class="cx">                  this.on( 'deactivate', this._deactivate, this );
</span><span class="cx">                  this.on( 'deactivate', this.deactivate, this );
</span><span class="cx">                  this.on( 'reset', this.reset, this );
</span><del>-                       this.on( 'ready', this._ready, this );
</del><span class="cx">                   this.on( 'ready', this.ready, this );
</span><span class="cx">                  /**
</span><span class="cx">                   * Call parent constructor with passed arguments
</span><span class="cx">                   */
</span><span class="cx">                  Backbone.Model.apply( this, arguments );
</span><del>-                       this.on( 'change:menu', this._updateMenu, this );
</del><span class="cx">           },
</span><span class="cx"> 
</span><span class="cx">          /**
</span><span class="lines">@@ -382,18 +375,58 @@
</span><span class="cx">          /**
</span><span class="cx">           * @access private
</span><span class="cx">           */
</span><del>-               _ready: function() {
-                       this._updateMenu();
</del><ins>+                _preActivate: function() {
+                       this.active = true;
</ins><span class="cx">           },
</span><span class="cx">          /**
</span><span class="cx">           * @access private
</span><span class="cx">           */
</span><del>-               _preActivate: function() {
-                       this.active = true;
</del><ins>+                _postActivate: function() {},
+               /**
+                * @access private
+                */
+               _deactivate: function() {
+                       this.active = false;
+               }
+       });
+
+       /**
+        * wp.media.controller.State
+        *
+        * A state is a step in a workflow that when set will trigger the controllers
+        * for the regions to be updated as specified in the frame. This is the base
+        * class that the various states used in wp.media extend.
+        *
+        * @constructor
+        * @augments Backbone.Model
+        */
+       media.controller.State = media.controller._State.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 );
+                       this.on( 'ready', this.ready, this );
+                       /**
+                        * Call parent constructor with passed arguments
+                        */
+                       Backbone.Model.apply( this, arguments );
+                       this.on( 'change:menu', this._updateMenu, this );
</ins><span class="cx">           },
</span><ins>+
</ins><span class="cx">           /**
</span><span class="cx">           * @access private
</span><span class="cx">           */
</span><ins>+               _ready: function() {
+                       this._updateMenu();
+               },
+
+               /**
+                * @access private
+                */
</ins><span class="cx">           _postActivate: function() {
</span><span class="cx">                  this.on( 'change:menu', this._menu, this );
</span><span class="cx">                  this.on( 'change:titleMode', this._title, this );
</span><span class="lines">@@ -1758,7 +1791,8 @@
</span><span class="cx">                  _.defaults( this.options, {
</span><span class="cx">                          title:    '',
</span><span class="cx">                          modal:    true,
</span><del>-                               uploader: true
</del><ins>+                                uploader: true,
+                               mode:     ['select']
</ins><span class="cx">                   });
</span><span class="cx"> 
</span><span class="cx">                  // Ensure core UI is enabled.
</span><span class="lines">@@ -4530,7 +4564,7 @@
</span><span class="cx">                  var selection = this.options.selection;
</span><span class="cx"> 
</span><span class="cx">                  this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
</span><del>-                       this.model.on( 'change:sizes change:uploading', this.render, this );
</del><ins>+                        this.model.on( 'change', this.render, this );
</ins><span class="cx">                   this.model.on( 'change:title', this._syncTitle, this );
</span><span class="cx">                  this.model.on( 'change:caption', this._syncCaption, this );
</span><span class="cx">                  this.model.on( 'change:percent', this.progress, this );
</span><span class="lines">@@ -4583,7 +4617,7 @@
</span><span class="cx">                                  compat:        false,
</span><span class="cx">                                  alt:           '',
</span><span class="cx">                                  description:   ''
</span><del>-                               });
</del><ins>+                                }, this.options );
</ins><span class="cx"> 
</span><span class="cx">                  options.buttons  = this.buttons;
</span><span class="cx">                  options.describe = this.controller.state().get('describe');
</span><span class="lines">@@ -4633,11 +4667,17 @@
</span><span class="cx">           */
</span><span class="cx">          toggleSelectionHandler: function( event ) {
</span><span class="cx">                  var method;
</span><del>-
</del><span class="cx">                   // Catch enter and space events
</span><span class="cx">                  if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
</span><span class="cx">                          return;
</span><span class="cx">                  }
</span><ins>+
+                       // 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;
+                       }
+
</ins><span class="cx">                   if ( event.shiftKey ) {
</span><span class="cx">                          method = 'between';
</span><span class="cx">                  } else if ( event.ctrlKey || event.metaKey ) {
</span><span class="lines">@@ -5168,10 +5208,11 @@
</span><span class="cx">           */
</span><span class="cx">          createAttachmentView: function( attachment ) {
</span><span class="cx">                  var view = new this.options.AttachmentView({
</span><del>-                               controller: this.controller,
-                               model:      attachment,
-                               collection: this.collection,
-                               selection:  this.options.selection
</del><ins>+                                controller:           this.controller,
+                               model:                attachment,
+                               collection:           this.collection,
+                               selection:            this.options.selection,
+                               showAttachmentFields: this.options.showAttachmentFields
</ins><span class="cx">                   });
</span><span class="cx"> 
</span><span class="cx">                  return this._viewsByCid[ attachment.cid ] = view;
</span><span class="lines">@@ -5468,7 +5509,6 @@
</span><span class="cx">          }
</span><span class="cx">  });
</span><span class="cx"> 
</span><del>-
</del><span class="cx">   /**
</span><span class="cx">   * wp.media.view.AttachmentsBrowser
</span><span class="cx">   *
</span><span class="lines">@@ -5486,13 +5526,18 @@
</span><span class="cx">                          filters: false,
</span><span class="cx">                          search:  true,
</span><span class="cx">                          display: false,
</span><del>-
</del><ins>+                                sidebar: true,
+                               showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ),
</ins><span class="cx">                           AttachmentView: media.view.Attachment.Library
</span><span class="cx">                  });
</span><span class="cx"> 
</span><span class="cx">                  this.createToolbar();
</span><span class="cx">                  this.updateContent();
</span><del>-                       this.createSidebar();
</del><ins>+                        if ( this.options.sidebar ) {
+                               this.createSidebar();
+                       } else {
+                               this.$el.addClass( 'hide-sidebar' );
+                       }
</ins><span class="cx"> 
</span><span class="cx">                  this.collection.on( 'add remove reset', this.updateContent, this );
</span><span class="cx">          },
</span><span class="lines">@@ -5517,6 +5562,20 @@
</span><span class="cx"> 
</span><span class="cx">                  this.views.add( this.toolbar );
</span><span class="cx"> 
</span><ins>+                       // 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() );
+                       }
+
</ins><span class="cx">                   filters = this.options.filters;
</span><span class="cx">                  if ( 'uploaded' === filters ) {
</span><span class="cx">                          FiltersConstructor = media.view.AttachmentFilters.Uploaded;
</span><span class="lines">@@ -5611,11 +5670,12 @@
</span><span class="cx">                  this.removeContent();
</span><span class="cx"> 
</span><span class="cx">                  this.attachments = new media.view.Attachments({
</span><del>-                               controller: this.controller,
-                               collection: this.collection,
-                               selection:  this.options.selection,
-                               model:      this.model,
-                               sortable:   this.options.sortable,
</del><ins>+                                controller:           this.controller,
+                               collection:           this.collection,
+                               selection:            this.options.selection,
+                               model:                this.model,
+                               sortable:             this.options.sortable,
+                               showAttachmentFields: this.options.showAttachmentFields,
</ins><span class="cx"> 
</span><span class="cx">                          // The single `Attachment` view to be used in the `Attachments` view.
</span><span class="cx">                          AttachmentView: this.options.AttachmentView
</span></span></pre></div>
<a id="trunksrcwpincludesmediatemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/media-template.php (28992 => 28993)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/media-template.php 2014-07-04 02:17:22 UTC (rev 28992)
+++ trunk/src/wp-includes/media-template.php    2014-07-04 03:38:33 UTC (rev 28993)
</span><span class="lines">@@ -220,6 +220,15 @@
</span><span class="cx">          </div>
</span><span class="cx">  </script>
</span><span class="cx"> 
</span><ins>+       <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>
+
</ins><span class="cx">   <script type="text/html" id="tmpl-uploader-status">
</span><span class="cx">          <h3><?php _e( 'Uploading' ); ?></h3>
</span><span class="cx">          <a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a>
</span><span class="lines">@@ -241,7 +250,128 @@
</span><span class="cx">          <span class="upload-error-message">{{ data.message }}</span>
</span><span class="cx">  </script>
</span><span class="cx"> 
</span><ins>+       <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>
+                       <# if ( 'audio' === data.type ) { #>
+                       <div class="wp-media-wrapper">
+                               <audio style="visibility: hidden" controls class="wp-audio-shortcode" width="100%" preload="none">
+                                       <source type="{{ data.mime }}" src="{{ data.url }}"/>
+                               </audio>
+                       </div>
+                       <# } else if ( 'video' === data.type ) { #>
+                       <div style="max-width: 100%; width: {{ data.width }}px" class="wp-media-wrapper">
+                               <video controls class="wp-video-shortcode" preload="metadata"
+                                       width="{{ data.width }}" height="{{ data.height }}"
+                                       <# if ( data.image && data.image.src !== data.icon ) { #>poster="{{ data.image.src }}"<# } #>>
+                                       <source type="{{ data.mime }}" src="{{ data.url }}"/>
+                               </video>
+                       </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>
+
</ins><span class="cx">   <script type="text/html" id="tmpl-attachment">
</span><ins>+               <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
+               <div class="inline-toolbar">
+                       <div class="dashicons dashicons-edit edit edit-media"></div>
+               </div>
+               <# } #>
</ins><span class="cx">           <div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}">
</span><span class="cx">                  <# if ( data.uploading ) { #>
</span><span class="cx">                          <div class="media-progress-bar"><div></div></div>
</span><span class="lines">@@ -251,13 +381,15 @@
</span><span class="cx">                                          <img src="{{ data.size.url }}" draggable="false" alt="" />
</span><span class="cx">                                  </div>
</span><span class="cx">                          </div>
</span><del>-                       <# } else { #>
</del><ins>+                        <# } else {
+                               if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) {
+                               #><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><#
+                               } #>
</ins><span class="cx">                           <img src="{{ data.icon }}" class="icon" draggable="false" />
</span><span class="cx">                          <div class="filename">
</span><span class="cx">                                  <div>{{ data.filename }}</div>
</span><span class="cx">                          </div>
</span><span class="cx">                  <# } #>
</span><del>-
</del><span class="cx">                   <# if ( data.buttons.close ) { #>
</span><span class="cx">                          <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
</span><span class="cx">                  <# } #>
</span><span class="lines">@@ -268,8 +400,8 @@
</span><span class="cx">          </div>
</span><span class="cx">          <#
</span><span class="cx">          var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly';
</span><del>-               if ( data.describe ) { #>
-                       <# if ( 'image' === data.type ) { #>
</del><ins>+                if ( data.describe ) {
+                       if ( 'image' === data.type ) { #>
</ins><span class="cx">                           <input type="text" value="{{ data.caption }}" class="describe" data-setting="caption"
</span><span class="cx">                                  placeholder="<?php esc_attr_e('Caption this image&hellip;'); ?>" {{ maybeReadOnly }} />
</span><span class="cx">                  <# } else { #>
</span><span class="lines">@@ -281,8 +413,31 @@
</span><span class="cx">                                  <# } else { #>
</span><span class="cx">                                          placeholder="<?php esc_attr_e('Describe this media file&hellip;'); ?>"
</span><span class="cx">                                  <# } #> {{ maybeReadOnly }} />
</span><del>-                       <# } #>
</del><ins>+                        <# }
+               }
+
+               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>
</ins><span class="cx">           <# } #>
</span><ins>+
</ins><span class="cx">   </script>
</span><span class="cx"> 
</span><span class="cx">  <script type="text/html" id="tmpl-attachment-details">
</span></span></pre>
</div>
</div>

</body>
</html>