<!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>[27445] trunk/src: At long last, a first pass at bringing the image editor into the media modal.</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/27445">27445</a></dd>
<dt>Author</dt> <dd>helen</dd>
<dt>Date</dt> <dd>2014-03-06 22:54:32 +0000 (Thu, 06 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>At long last, a first pass at bringing the image editor into the media modal. props gcorne, DH-Shredder, tomauger. see <a href="http://core.trac.wordpress.org/ticket/21811">#21811</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminjsimageeditjs">trunk/src/wp-admin/js/image-edit.js</a></li>
<li><a href="#trunksrcwpincludescssmediaviewscss">trunk/src/wp-includes/css/media-views.css</a></li>
<li><a href="#trunksrcwpincludesjsmediaeditorjs">trunk/src/wp-includes/js/media-editor.js</a></li>
<li><a href="#trunksrcwpincludesjsmediamodelsjs">trunk/src/wp-includes/js/media-models.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>
<li><a href="#trunksrcwpincludesmediaphp">trunk/src/wp-includes/media.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunksrcwpincludesversionphp">trunk/src/wp-includes/version.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminjsimageeditjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/js/image-edit.js (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/js/image-edit.js      2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-admin/js/image-edit.js 2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -5,6 +5,7 @@
</span><span class="cx">  iasapi : {},
</span><span class="cx">  hold : {},
</span><span class="cx">  postid : '',
</span><ins>+       _view : false,
</ins><span class="cx"> 
</span><span class="cx">  intval : function(f) {
</span><span class="cx">          return f | 0;
</span><span class="lines">@@ -241,11 +242,18 @@
</span><span class="cx">          $.post(ajaxurl, data, function(r) {
</span><span class="cx">                  $('#image-editor-' + postid).empty().append(r);
</span><span class="cx">                  t.toggleEditor(postid, 0);
</span><ins>+                       // refresh the attachment model so that changes propagate
+                       if ( this._view ) {
+                               this._view.refresh();
+                       }
</ins><span class="cx">           });
</span><span class="cx">  },
</span><span class="cx"> 
</span><span class="cx">  save : function(postid, nonce) {
</span><del>-               var data, target = this.getTarget(postid), history = this.filterHistory(postid, 0);
</del><ins>+                var data,
+                       target = this.getTarget(postid),
+                       history = this.filterHistory(postid, 0),
+                       self = this;
</ins><span class="cx"> 
</span><span class="cx">          if ( '' === history ) {
</span><span class="cx">                  return false;
</span><span class="lines">@@ -283,11 +291,17 @@
</span><span class="cx">                          $('#imgedit-response-' + postid).html('<div class="updated"><p>' + ret.msg + '</p></div>');
</span><span class="cx">                  }
</span><span class="cx"> 
</span><del>-                       imageEdit.close(postid);
</del><ins>+                        if ( self._view ) {
+                               self._view.save();
+                       } else {
+                               imageEdit.close(postid);
+                       }
</ins><span class="cx">           });
</span><span class="cx">  },
</span><span class="cx"> 
</span><del>-       open : function(postid, nonce) {
</del><ins>+        open : function( postid, nonce, view ) {
+               this._view = view;
+
</ins><span class="cx">           var data, elem = $('#image-editor-' + postid), head = $('#media-head-' + postid),
</span><span class="cx">                  btn = $('#imgedit-open-btn-' + postid), spin = btn.siblings('.spinner');
</span><span class="cx"> 
</span><span class="lines">@@ -319,8 +333,10 @@
</span><span class="cx">  },
</span><span class="cx"> 
</span><span class="cx">  initCrop : function(postid, image, parent) {
</span><del>-               var t = this, selW = $('#imgedit-sel-width-' + postid),
-                       selH = $('#imgedit-sel-height-' + postid);
</del><ins>+                var t = this,
+                       selW = $('#imgedit-sel-width-' + postid),
+                       selH = $('#imgedit-sel-height-' + postid),
+                       $img;
</ins><span class="cx"> 
</span><span class="cx">          t.iasapi = $(image).imgAreaSelect({
</span><span class="cx">                  parent: parent,
</span><span class="lines">@@ -330,7 +346,13 @@
</span><span class="cx">                  minWidth: 3,
</span><span class="cx">                  minHeight: 3,
</span><span class="cx"> 
</span><del>-                       onInit: function() {
</del><ins>+                        onInit: function( img ) {
+                               // Ensure that the imgareaselect wrapper elements are position:absolute
+                               // (even if we're in a position:fixed modal)
+                               $img = $( img );
+                               $img.next().css( 'position', 'absolute' )
+                                       .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' );
+
</ins><span class="cx">                           parent.children().mousedown(function(e){
</span><span class="cx">                                  var ratio = false, sel, defRatio;
</span><span class="cx"> 
</span><span class="lines">@@ -397,10 +419,22 @@
</span><span class="cx"> 
</span><span class="cx">          this.iasapi = {};
</span><span class="cx">          this.hold = {};
</span><del>-               $('#image-editor-' + postid).fadeOut('fast', function() {
-                       $('#media-head-' + postid).fadeIn('fast');
-                       $(this).empty();
-               });
</del><ins>+
+               // If we've loaded the editor in the context of a Media Modal, then switch to the previous view,
+               // whatever that might have been.
+               if ( this._view ){
+                       this._view.back();
+               }
+
+               // In case we are not accessing the image editor in the context of a View, close the editor the old-skool way
+               else {
+                       $('#image-editor-' + postid).fadeOut('fast', function() {
+                               $('#media-head-' + postid).fadeIn('fast');
+                               $(this).empty();
+                       });
+               }
+
+
</ins><span class="cx">   },
</span><span class="cx"> 
</span><span class="cx">  notsaved : function(postid) {
</span></span></pre></div>
<a id="trunksrcwpincludescssmediaviewscss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/css/media-views.css (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/css/media-views.css        2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/css/media-views.css   2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -1432,6 +1432,22 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Image Editor
+ */
+
+.media-frame .image-editor {
+       padding: 16px;
+}
+
+.media-frame .imgedit-wrap table td {
+       vertical-align: top;
+       padding-top: 0;
+}
+
+.media-frame .imgedit-wrap table td.imgedit-settings {
+       width: 250px;
+}
+/**
</ins><span class="cx">  * Embed from URL and Image Details
</span><span class="cx">  */
</span><span class="cx"> .embed-url {
</span><span class="lines">@@ -1490,6 +1506,10 @@
</span><span class="cx">  display: block;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.media-embed .edit-attachment {
+       margin-left: 10px;
+}
+
</ins><span class="cx"> .media-embed .thumbnail:after {
</span><span class="cx">  content: '';
</span><span class="cx">  display: block;
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediaeditorjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-editor.js (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-editor.js 2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/js/media-editor.js    2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -698,7 +698,7 @@
</span><span class="cx"> 
</span><span class="cx">                  this._frame = wp.media({
</span><span class="cx">                          state: 'featured-image',
</span><del>-                               states: [ new wp.media.controller.FeaturedImage() ]
</del><ins>+                                states: [ new wp.media.controller.FeaturedImage() , new wp.media.controller.EditImage() ]
</ins><span class="cx">                   });
</span><span class="cx"> 
</span><span class="cx">                  this._frame.on( 'toolbar:create:featured-image', function( toolbar ) {
</span><span class="lines">@@ -710,6 +710,17 @@
</span><span class="cx">                          });
</span><span class="cx">                  }, this._frame );
</span><span class="cx"> 
</span><ins>+                       this._frame.on( 'content:render:edit-image', function() {
+                               var selection = this.state('featured-image').get('selection'),
+                                       view = new wp.media.view.EditImage( { model: selection.single(), controller: this } ).render();
+
+                               this.content.set( view );
+
+                               // after bringing in the frame, load the actual editor via an ajax call
+                               view.loadEditor();
+
+                       }, this._frame );
+
</ins><span class="cx">                   this._frame.state('featured-image').on( 'select', this.select );
</span><span class="cx">                  return this._frame;
</span><span class="cx">          },
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediamodelsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-models.js (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-models.js 2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/js/media-models.js    2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -371,6 +371,7 @@
</span><span class="cx"> 
</span><span class="cx">          bindAttachmentListeners: function() {
</span><span class="cx">                  this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
</span><ins>+                       this.listenTo( this.attachment, 'change', this.updateSize );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><span class="cx">          changeAttachment: function( attachment, props ) {
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediaviewsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-views.js (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-views.js  2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/js/media-views.js     2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -483,6 +483,54 @@
</span><span class="cx">          };
</span><span class="cx">  });
</span><span class="cx"> 
</span><ins>+       media.selectionSync = {
+               syncSelection: function() {
+                       var selection = this.get('selection'),
+                               manager = this.frame._selection;
+
+                       if ( ! this.get('syncSelection') || ! manager || ! selection ) {
+                               return;
+                       }
+
+                       // If the selection supports multiple items, validate the stored
+                       // attachments based on the new selection's conditions. Record
+                       // the attachments that are not included; we'll maintain a
+                       // reference to those. Other attachments are considered in flux.
+                       if ( selection.multiple ) {
+                               selection.reset( [], { silent: true });
+                               selection.validateAll( manager.attachments );
+                               manager.difference = _.difference( manager.attachments.models, selection.models );
+                       }
+
+                       // Sync the selection's single item with the master.
+                       selection.single( manager.single );
+               },
+
+               /**
+                * Record the currently active attachments, which is a combination
+                * of the selection's attachments and the set of selected
+                * attachments that this specific selection considered invalid.
+                * Reset the difference and record the single attachment.
+                */
+               recordSelection: function() {
+                       var selection = this.get('selection'),
+                               manager = this.frame._selection;
+
+                       if ( ! this.get('syncSelection') || ! manager || ! selection ) {
+                               return;
+                       }
+
+                       if ( selection.multiple ) {
+                               manager.attachments.reset( selection.toArray().concat( manager.difference ) );
+                               manager.difference = [];
+                       } else {
+                               manager.attachments.add( selection.toArray() );
+                       }
+
+                       manager.single = selection._single;
+               }
+       };
+
</ins><span class="cx">   /**
</span><span class="cx">   * wp.media.controller.Library
</span><span class="cx">   *
</span><span class="lines">@@ -635,53 +683,8 @@
</span><span class="cx">                  return _.contains( media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
</span><span class="cx">          },
</span><span class="cx"> 
</span><del>-               syncSelection: function() {
-                       var selection = this.get('selection'),
-                               manager = this.frame._selection;
</del><span class="cx"> 
</span><del>-                       if ( ! this.get('syncSelection') || ! manager || ! selection ) {
-                               return;
-                       }
-
-                       // If the selection supports multiple items, validate the stored
-                       // attachments based on the new selection's conditions. Record
-                       // the attachments that are not included; we'll maintain a
-                       // reference to those. Other attachments are considered in flux.
-                       if ( selection.multiple ) {
-                               selection.reset( [], { silent: true });
-                               selection.validateAll( manager.attachments );
-                               manager.difference = _.difference( manager.attachments.models, selection.models );
-                       }
-
-                       // Sync the selection's single item with the master.
-                       selection.single( manager.single );
-               },
-
</del><span class="cx">           /**
</span><del>-                * Record the currently active attachments, which is a combination
-                * of the selection's attachments and the set of selected
-                * attachments that this specific selection considered invalid.
-                * Reset the difference and record the single attachment.
-                */
-               recordSelection: function() {
-                       var selection = this.get('selection'),
-                               manager = this.frame._selection;
-
-                       if ( ! this.get('syncSelection') || ! manager || ! selection ) {
-                               return;
-                       }
-
-                       if ( selection.multiple ) {
-                               manager.attachments.reset( selection.toArray().concat( manager.difference ) );
-                               manager.difference = [];
-                       } else {
-                               manager.attachments.add( selection.toArray() );
-                       }
-
-                       manager.single = selection._single;
-               },
-
-               /**
</del><span class="cx">            * If the state is active, no items are selected, and the current
</span><span class="cx">           * content mode is not an option in the state's router (provided
</span><span class="cx">           * the state has a router), reset the content mode to the default.
</span><span class="lines">@@ -734,6 +737,8 @@
</span><span class="cx">          }
</span><span class="cx">  });
</span><span class="cx"> 
</span><ins>+       _.extend( media.controller.Library.prototype, media.selectionSync );
+
</ins><span class="cx">   /**
</span><span class="cx">   * wp.media.controller.ImageDetails
</span><span class="cx">   *
</span><span class="lines">@@ -989,7 +994,7 @@
</span><span class="cx">                  toolbar:    'featured-image',
</span><span class="cx">                  title:      l10n.setFeaturedImageTitle,
</span><span class="cx">                  priority:   60,
</span><del>-                       syncSelection: false
</del><ins>+                        syncSelection: true
</ins><span class="cx">           }, media.controller.Library.prototype.defaults ),
</span><span class="cx"> 
</span><span class="cx">          initialize: function() {
</span><span class="lines">@@ -1070,7 +1075,7 @@
</span><span class="cx">                  toolbar:    'replace',
</span><span class="cx">                  title:      l10n.replaceImageTitle,
</span><span class="cx">                  priority:   60,
</span><del>-                       syncSelection: false
</del><ins>+                        syncSelection: true
</ins><span class="cx">           }, media.controller.Library.prototype.defaults ),
</span><span class="cx"> 
</span><span class="cx">          initialize: function( options ) {
</span><span class="lines">@@ -1121,6 +1126,63 @@
</span><span class="cx">  });
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * wp.media.controller.EditImage
+        *
+        * @constructor
+        * @augments wp.media.controller.State
+        * @augments Backbone.Model
+        */
+       media.controller.EditImage = media.controller.State.extend({
+               defaults: {
+                       id: 'edit-image',
+                       url: '',
+                       menu: false,
+                       toolbar: 'edit-image',
+                       title: l10n.editImage,
+                       content: 'edit-image',
+                       syncSelection: true
+               },
+
+               activate: function() {
+                       if ( ! this.get('selection') ) {
+                               this.set( 'selection', new media.model.Selection() );
+                       }
+                       this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
+                       this.syncSelection();
+               },
+
+               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();
+                                                       }
+                                               }
+                                       }
+                               }
+                       }) );
+               }
+       });
+
+       _.extend( media.controller.EditImage.prototype, media.selectionSync );
+
+       /**
</ins><span class="cx">    * wp.media.controller.ReplaceVideo
</span><span class="cx">   *
</span><span class="cx">   * Replace a selected single video
</span><span class="lines">@@ -1928,6 +1990,8 @@
</span><span class="cx">                          // Embed states.
</span><span class="cx">                          new media.controller.Embed(),
</span><span class="cx"> 
</span><ins>+                               new media.controller.EditImage( { selection: options.selection } ),
+
</ins><span class="cx">                           // Gallery states.
</span><span class="cx">                          new media.controller.CollectionEdit({
</span><span class="cx">                                  type:           'image',
</span><span class="lines">@@ -2043,6 +2107,7 @@
</span><span class="cx"> 
</span><span class="cx">                          content: {
</span><span class="cx">                                  'embed':          'embedContent',
</span><ins>+                                       'edit-image':     'editImageContent',
</ins><span class="cx">                                   'edit-selection': 'editSelectionContent'
</span><span class="cx">                          },
</span><span class="cx"> 
</span><span class="lines">@@ -2195,6 +2260,17 @@
</span><span class="cx">                  this.content.set( view );
</span><span class="cx">          },
</span><span class="cx"> 
</span><ins>+               editImageContent: function() {
+                       var selection = this.state().get('selection'),
+                               view = new media.view.EditImage( { model: selection.single(), controller: this } ).render();
+
+                       this.content.set( view );
+
+                       // after creating the wrapper view, load the actual editor via an ajax call
+                       view.loadEditor();
+
+               },
+
</ins><span class="cx">           // Toolbars
</span><span class="cx"> 
</span><span class="cx">          /**
</span><span class="lines">@@ -2537,6 +2613,7 @@
</span><span class="cx">                  media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
</span><span class="cx">                  this.on( 'menu:create:image-details', this.createMenu, this );
</span><span class="cx">                  this.on( 'content:render:image-details', this.renderImageDetailsContent, this );
</span><ins>+                       this.on( 'content:render:edit-image', this.editImageContent, this );
</ins><span class="cx">                   this.on( 'menu:render:image-details', this.renderMenu, this );
</span><span class="cx">                  this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
</span><span class="cx">                  // override the select toolbar
</span><span class="lines">@@ -2560,7 +2637,11 @@
</span><span class="cx">                                  toolbar: 'replace',
</span><span class="cx">                                  priority:  80,
</span><span class="cx">                                  displaySettings: true
</span><del>-                               })
</del><ins>+                                }),
+                               new media.controller.EditImage( {
+                                       image: this.image,
+                                       selection: this.options.selection
+                               } )
</ins><span class="cx">                   ]);
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -2575,6 +2656,32 @@
</span><span class="cx"> 
</span><span class="cx">          },
</span><span class="cx"> 
</span><ins>+               editImageContent: function() {
+                       var state = this.state(),
+                               attachment = state.get('image').attachment,
+                               model,
+                               view;
+
+                       if ( ! attachment ) {
+                               return;
+                       }
+
+                       model = state.get('selection').single();
+
+                       if ( ! model ) {
+                               model = attachment;
+                       }
+
+                       view = new media.view.EditImage( { model: model, controller: this } ).render();
+
+                       this.content.set( view );
+
+                       // after bringing in the frame, load the actual editor via an ajax call
+                       view.loadEditor();
+
+               },
+
+
</ins><span class="cx">           renderMenu: function( view ) {
</span><span class="cx">                  var lastState = this.lastState(),
</span><span class="cx">                          previous = lastState && lastState.id,
</span><span class="lines">@@ -5864,8 +5971,9 @@
</span><span class="cx">                  }
</span><span class="cx">          },
</span><span class="cx"> 
</span><del>-               editAttachment: function() {
-                       this.$el.addClass('needs-refresh');
</del><ins>+                editAttachment: function( event ) {
+                       event.preventDefault();
+                       this.controller.setState( 'edit-image' );
</ins><span class="cx">           },
</span><span class="cx">          /**
</span><span class="cx">           * @param {Object} event
</span><span class="lines">@@ -5875,6 +5983,7 @@
</span><span class="cx">                  event.preventDefault();
</span><span class="cx">                  this.model.fetch();
</span><span class="cx">          }
</span><ins>+
</ins><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="lines">@@ -6158,10 +6267,15 @@
</span><span class="cx">  media.view.ImageDetails = media.view.Settings.AttachmentDisplay.extend({
</span><span class="cx">          className: 'image-details',
</span><span class="cx">          template:  media.template('image-details'),
</span><del>-
</del><ins>+                events: _.defaults( media.view.Settings.AttachmentDisplay.prototype.events, {
+                       'click .edit-attachment': 'editAttachment'
+               } ),
</ins><span class="cx">           initialize: function() {
</span><span class="cx">                  // used in AttachmentDisplay.prototype.updateLinkTo
</span><span class="cx">                  this.options.attachment = this.model.attachment;
</span><ins>+                       if ( this.model.attachment ) {
+                               this.listenTo( this.model.attachment, 'change:url', this.updateUrl );
+                       }
</ins><span class="cx">                   media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -6177,7 +6291,6 @@
</span><span class="cx">                  }, this.options );
</span><span class="cx">          },
</span><span class="cx"> 
</span><del>-
</del><span class="cx">           render: function() {
</span><span class="cx">                  var self = this,
</span><span class="cx">                          args = arguments;
</span><span class="lines">@@ -6198,9 +6311,64 @@
</span><span class="cx">          resetFocus: function() {
</span><span class="cx">                  this.$( '.caption textarea' ).focus();
</span><span class="cx">                  this.$( '.embed-image-settings' ).scrollTop( 0 );
</span><ins>+               },
+
+               updateUrl: function() {
+                       this.$( '.thumbnail img' ).attr( 'src', this.model.get('url' ) );
+                       this.$( '.url' ).val( this.model.get('url' ) );
+               },
+
+               editAttachment: function( event ) {
+                       event.preventDefault();
+                       this.controller.setState( 'edit-image' );
</ins><span class="cx">           }
</span><span class="cx">  });
</span><span class="cx"> 
</span><ins>+
+       media.view.EditImage = media.View.extend({
+
+               className: 'image-editor',
+               template: media.template('image-editor'),
+
+               initialize: function( options ) {
+                       this.editor = window.imageEdit;
+                       this.controller = options.controller;
+                       media.View.prototype.initialize.apply( this, arguments );
+               },
+
+               prepare: function() {
+                       return this.model.toJSON();
+               },
+
+               render: function() {
+                       media.View.prototype.render.apply( this, arguments );
+                       return this;
+               },
+
+               loadEditor: function() {
+                       this.editor.open( this.model.get('id'), this.model.get('nonces').edit, this );
+               },
+
+               back: function() {
+                       var lastState = this.controller.lastState();
+                       this.controller.setState( lastState );
+               },
+
+               refresh: function() {
+                       this.model.fetch();
+               },
+
+               save: function() {
+                       var self = this,
+                               lastState = this.controller.lastState();
+
+                       this.model.fetch().done( function() {
+                               self.controller.setState( lastState );
+                       });
+               }
+
+       });
+
</ins><span class="cx">   /**
</span><span class="cx">   * wp.media.view.AudioDetails
</span><span class="cx">   *
</span></span></pre></div>
<a id="trunksrcwpincludesmediatemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/media-template.php (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/media-template.php 2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/media-template.php    2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -559,6 +559,9 @@
</span><span class="cx">                          <div class="thumbnail">
</span><span class="cx">                                  <img src="{{ data.model.url }}" draggable="false" />
</span><span class="cx">                          </div>
</span><ins>+                               <# if ( data.attachment ) { #>
+                                       <input type="button" class="edit-attachment button" value="<?php esc_attr_e( 'Edit Image' ); ?>" />
+                               <# } #>
</ins><span class="cx"> 
</span><span class="cx">                          <div class="setting url">
</span><span class="cx">                                  <?php // might want to make the url editable if it isn't an attachment ?>
</span><span class="lines">@@ -649,6 +652,11 @@
</span><span class="cx">          </div>
</span><span class="cx">  </script>
</span><span class="cx"> 
</span><ins>+       <script type="text/html" id="tmpl-image-editor">
+               <div id="media-head-{{{ data.id }}}"></div>
+               <div id="image-editor-{{{ data.id }}}"></div>
+       </script>
+
</ins><span class="cx">   <script type="text/html" id="tmpl-audio-details">
</span><span class="cx">          <?php // reusing .media-embed to pick up the styles for now
</span><span class="cx">          ?><# var rendered = false; #>
</span></span></pre></div>
<a id="trunksrcwpincludesmediaphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/media.php (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/media.php  2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/media.php     2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -2175,12 +2175,14 @@
</span><span class="cx">          'nonces'      => array(
</span><span class="cx">                  'update' => false,
</span><span class="cx">                  'delete' => false,
</span><ins>+                       'edit'   => false
</ins><span class="cx">           ),
</span><span class="cx">          'editLink'   => false,
</span><span class="cx">  );
</span><span class="cx"> 
</span><span class="cx">  if ( current_user_can( 'edit_post', $attachment->ID ) ) {
</span><span class="cx">          $response['nonces']['update'] = wp_create_nonce( 'update-post_' . $attachment->ID );
</span><ins>+               $response['nonces']['edit'] = wp_create_nonce( 'image_editor-' . $attachment->ID );
</ins><span class="cx">           $response['editLink'] = get_edit_post_link( $attachment->ID, 'raw' );
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="lines">@@ -2335,6 +2337,7 @@
</span><span class="cx">          'cancel'      => __( 'Cancel' ),
</span><span class="cx">          'update'      => __( 'Update' ),
</span><span class="cx">          'replace'     => __( 'Replace' ),
</span><ins>+               'back'     => __( 'Back' ),
</ins><span class="cx">           /* translators: This is a would-be plural string used in the media manager.
</span><span class="cx">             If there is not a word you can use in your language to avoid issues with the
</span><span class="cx">             lack of plural support here, turn it into "selected: %d" then translate it.
</span><span class="lines">@@ -2380,6 +2383,7 @@
</span><span class="cx">          'imageDetailsTitle'     => __( 'Image Details' ),
</span><span class="cx">          'imageReplaceTitle'     => __( 'Replace Image' ),
</span><span class="cx">          'imageDetailsCancel'    => __( 'Cancel Edit' ),
</span><ins>+               'editImage'             => __( 'Edit Image' ),
</ins><span class="cx"> 
</span><span class="cx">          // Edit Image
</span><span class="cx">          'audioDetailsTitle'     => __( 'Audio Details' ),
</span><span class="lines">@@ -2421,6 +2425,7 @@
</span><span class="cx"> 
</span><span class="cx">  wp_enqueue_script( 'media-editor' );
</span><span class="cx">  wp_enqueue_style( 'media-views' );
</span><ins>+       wp_enqueue_style( 'imgareaselect' );
</ins><span class="cx">   wp_plupload_default_settings();
</span><span class="cx"> 
</span><span class="cx">  require_once ABSPATH . WPINC . '/media-template.php';
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/script-loader.php (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/script-loader.php  2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/script-loader.php     2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -389,7 +389,7 @@
</span><span class="cx"> 
</span><span class="cx">  // To enqueue media-views or media-editor, call wp_enqueue_media().
</span><span class="cx">  // Both rely on numerous settings, styles, and templates to operate correctly.
</span><del>-       $scripts->add( 'media-views',  "/wp-includes/js/media-views$suffix.js",  array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement' ), false, 1 );
</del><ins>+        $scripts->add( 'media-views',  "/wp-includes/js/media-views$suffix.js",  array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement', 'image-edit' ), false, 1 );
</ins><span class="cx">   $scripts->add( 'media-editor', "/wp-includes/js/media-editor$suffix.js", array( 'shortcode', 'media-views' ), false, 1 );
</span><span class="cx">  $scripts->add( 'mce-view', "/wp-includes/js/mce-view$suffix.js", array( 'shortcode', 'media-models' ), false, 1 );
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpincludesversionphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/version.php (27444 => 27445)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/version.php        2014-03-06 22:51:53 UTC (rev 27444)
+++ trunk/src/wp-includes/version.php   2014-03-06 22:54:32 UTC (rev 27445)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx">  *
</span><span class="cx">  * @global string $wp_version
</span><span class="cx">  */
</span><del>-$wp_version = '3.9-alpha-27368-src';
</del><ins>+$wp_version = '3.9-alpha-27445-src';
</ins><span class="cx"> 
</span><span class="cx"> /**
</span><span class="cx">  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
</span></span></pre>
</div>
</div>

</body>
</html>