Ticket #21390: 21390.3.diff

File 21390.3.diff, 61.8 KB (added by koopersmith, 6 months ago)
Line 
1Index: wp-admin/js/custom-background.js
2===================================================================
3--- wp-admin/js/custom-background.js    (revision 22995)
4+++ wp-admin/js/custom-background.js    (working copy)
5@@ -37,8 +37,8 @@
6                                }
7                        });
8 
9-                       frame.toolbar.on( 'activate:select', function() {
10-                               frame.toolbar.view().set({
11+                       frame.on( 'toolbar:render:select', function( view ) {
12+                               view.set({
13                                        select: {
14                                                style: 'primary',
15                                                text:  $el.data('update'),
16Index: wp-admin/js/custom-header.js
17===================================================================
18--- wp-admin/js/custom-header.js        (revision 22995)
19+++ wp-admin/js/custom-header.js        (working copy)
20@@ -24,8 +24,8 @@
21                                }
22                        });
23 
24-                       frame.toolbar.on( 'activate:select', function() {
25-                               frame.toolbar.view().set({
26+                       frame.on( 'toolbar:render:select', function( view ) {
27+                               view.set({
28                                        select: {
29                                                style: 'primary',
30                                                text:  $el.data('update'),
31Index: wp-includes/css/media-views-rtl.css
32===================================================================
33--- wp-includes/css/media-views-rtl.css (revision 22995)
34+++ wp-includes/css/media-views-rtl.css (working copy)
35@@ -226,30 +226,6 @@
36 }
37 
38 /**
39- * Selection Preview
40- */
41-.selected-img {
42-       float: right;
43-       margin-right: 0;
44-       margin-left: 14px;
45-}
46-
47-.selection-preview img {
48-       float: right;
49-       margin-left: 0;
50-       margin-right: 1px;
51-}
52-
53-.selection-preview .count {
54-       right: auto;
55-       left: 0;
56-}
57-
58-.selection-preview .clear-selection {
59-       float: right;
60-}
61-
62-/**
63  * Attachment Details
64  */
65 .attachment-info .thumbnail {
66Index: wp-includes/css/media-views.css
67===================================================================
68--- wp-includes/css/media-views.css     (revision 22995)
69+++ wp-includes/css/media-views.css     (working copy)
70@@ -80,10 +80,10 @@
71  */
72 .media-modal {
73        position: fixed;
74-       top: 60px;
75-       left: 40px;
76-       right: 40px;
77-       bottom: 40px;
78+       top: 30px;
79+       left: 30px;
80+       right: 30px;
81+       bottom: 30px;
82        z-index: 160000;
83 }
84 
85@@ -94,42 +94,24 @@
86        right: 0;
87        bottom: 0;
88        background: #000;
89-       opacity: 0.8;
90+       opacity: 0.7;
91        z-index: 159900;
92 }
93 
94-.media-modal-backdrop div,
95-.uploader-window-content {
96+.media-modal-close {
97        position: absolute;
98-       top: 10px;
99-       left: 10px;
100-       right: 10px;
101-       bottom: 10px;
102-       border: 1px dashed rgba( 255, 255, 255, 0.5 );
103+       top: 7px;
104+       right: 7px;
105+       width: 30px;
106+       height: 30px;
107+       z-index: 1000;
108 }
109-
110-.media-modal-title {
111-       position: absolute;
112-       top: -40px;
113-       left: 0;
114-       height: 40px;
115-       padding: 0;
116-       margin: 0;
117-
118-       line-height: 40px;
119-       color: #fff;
120-       font-size: 16px;
121-       font-weight: 200;
122-       text-shadow: 0 0 16px rgba( 0, 0, 0, 0.6 );
123-}
124-
125-.media-modal-close {
126-       position: absolute;
127-       top: -27px;
128-       right: 0;
129+.media-modal-close span {
130+       display: block;
131+       margin: 8px auto 0;
132+       width: 15px;
133        height: 15px;
134-       width: 15px;
135-       background-position: -80px 0;
136+       background-position: -100px 0;
137 }
138 
139 .media-modal-close:active {
140@@ -165,18 +147,6 @@
141        border: 0 solid #dfdfdf;
142 }
143 
144-.media-frame-toolbar > .media-toolbar {
145-       top: auto;
146-       left: 200px;
147-       bottom: 0;
148-       border-width: 1px 0 0 0;
149-       box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 );
150-}
151-
152-.hide-toolbar .media-frame-toolbar > .media-toolbar {
153-       bottom: -61px;
154-}
155-
156 .media-toolbar-primary {
157        float: right;
158 }
159@@ -238,11 +208,6 @@
160        width: 100%;
161 }
162 
163-.media-sidebar .selection-preview {
164-       display: block;
165-       padding-top: 5px;
166-}
167-
168 .media-sidebar h3 {
169        position: relative;
170        font-weight: bold;
171@@ -361,11 +326,10 @@
172        position: absolute;
173        top: 0;
174        left: 0;
175+       right: 0;
176        bottom: 0;
177-       width: 199px;
178        margin: 0;
179        padding: 16px 0;
180-       z-index: 200;
181        border-right: 1px solid #d9d9d9;
182        box-shadow: inset -6px 0 6px -6px rgba( 0, 0, 0, 0.2 );
183        -webkit-user-select: none;
184@@ -374,7 +338,8 @@
185        user-select: none;
186 }
187 
188-.media-menu li {
189+.media-menu > a {
190+       display: block;
191        position: relative;
192        padding: 4px 20px;
193        margin: 0;
194@@ -382,14 +347,16 @@
195        font-size: 14px;
196        color: #21759B;
197        text-shadow: 0 1px 0 #fff;
198+       text-decoration: none;
199 }
200 
201-.media-menu-item {
202-       cursor: pointer;
203+.media-menu > a:hover {
204+       color: #21759B;
205+       background: rgba( 0, 0, 0, 0.04 );
206 }
207 
208-.media-menu li:hover {
209-       background: rgba( 0, 0, 0, 0.04 );
210+.media-menu > a:active {
211+       outline: none;
212 }
213 
214 .media-menu .active,
215@@ -407,6 +374,63 @@
216 }
217 
218 /**
219+ * Menu
220+ */
221+.media-router {
222+       position: relative;
223+       padding: 0 6px;
224+       margin: 0;
225+       clear: both;
226+       -webkit-user-select: none;
227+       -moz-user-select: none;
228+       -ms-user-select: none;
229+       user-select: none;
230+}
231+
232+.media-router > a {
233+       position: relative;
234+       float: left;
235+       padding: 2px 10px;
236+       margin: 0;
237+       height: 18px;
238+       line-height: 18px;
239+       font-size: 14px;
240+       border-right: 1px solid #dfdfdf;
241+       text-shadow: 0 1px 0 #fff;
242+       text-decoration: none;
243+}
244+
245+.media-router > a:last-child {
246+       border-right: 0;
247+}
248+
249+.media-router > a:active {
250+       outline: none;
251+}
252+
253+.media-router .active,
254+.media-router .active:hover {
255+       color: #333;
256+}
257+
258+.media-router .active:after {
259+       content: '';
260+       display: block;
261+       margin: -100px auto 0;
262+       width: 7px;
263+       height: 7px;
264+       background: #fff;
265+       box-shadow: 1px 1px 1px rgba( 0, 0, 0, 0.2 );
266+       z-index: 300;
267+
268+       -webkit-transform: rotate( 45deg ) translate( 75px, 75px );
269+       -moz-transform:    rotate( 45deg ) translate( 75px, 75px );
270+       -ms-transform:     rotate( 45deg ) translate( 75px, 75px );
271+       -o-transform:      rotate( 45deg ) translate( 75px, 75px );
272+       transform:         rotate( 45deg ) translate( 75px, 75px );
273+}
274+
275+/**
276  * Frame
277  */
278 .media-frame {
279@@ -418,11 +442,40 @@
280        bottom: 0;
281 }
282 
283-.media-frame .region-content {
284+.media-frame-menu {
285        position: absolute;
286        top: 0;
287+       left: 0;
288+       bottom: 0;
289+       width: 199px;
290+       z-index: 150;
291+}
292+
293+.media-frame-title {
294+       position: absolute;
295+       top: 0;
296        left: 200px;
297        right: 0;
298+       height: 45px;
299+       z-index: 200;
300+}
301+
302+.media-frame-router {
303+       position: absolute;
304+       top: 45px;
305+       left: 200px;
306+       right: 0;
307+       height: 30px;
308+       z-index: 200;
309+       border-bottom: 1px solid #dfdfdf;
310+       box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
311+}
312+
313+.media-frame-content {
314+       position: absolute;
315+       top: 75px;
316+       left: 200px;
317+       right: 0;
318        bottom: 61px;
319        height: auto;
320        width: auto;
321@@ -430,14 +483,62 @@
322        overflow: auto;
323 }
324 
325-.media-frame.hide-toolbar .region-content {
326+.media-frame-toolbar {
327+       position: absolute;
328+       left: 200px;
329+       right: 0;
330        bottom: 0;
331+       height: 60px;
332+       z-index: 100;
333+       border: 0 solid #dfdfdf;
334+       border-width: 1px 0 0 0;
335+       box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 );
336 }
337 
338+.media-frame.hide-menu .media-frame-title,
339+.media-frame.hide-menu .media-frame-router,
340+.media-frame.hide-menu .media-frame-toolbar,
341+.media-frame.hide-menu .media-frame-content {
342+       left: 0;
343+}
344+
345+.media-frame.hide-menu .media-frame-menu {
346+       left: -200px;
347+}
348+
349+.media-frame.hide-toolbar .media-frame-content {
350+       bottom: 0;
351+}
352+
353+.media-frame.hide-toolbar .media-frame-toolbar {
354+       bottom: -61px;
355+}
356+
357+.media-frame.hide-router .media-frame-content {
358+       top: 45px;
359+}
360+
361+.media-frame.hide-router .media-frame-router {
362+       display: none;
363+}
364+
365+.media-frame.hide-router .media-frame-title {
366+       border-bottom: 1px solid #dfdfdf;
367+       box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
368+}
369+
370 .media-frame .media-toolbar .add-to-gallery {
371        display: none;
372 }
373 
374+.media-frame-title h1 {
375+       padding: 0 16px;
376+       font-size: 22px;
377+       font-weight: 200;
378+       line-height: 45px;
379+       margin: 0;
380+}
381+
382 /**
383  * Iframes
384  */
385@@ -721,6 +822,9 @@
386  * Attachments Browser
387  */
388 .media-frame .attachments-browser {
389+       position: relative;
390+       width: 100%;
391+       height: 100%;
392        overflow: hidden;
393 }
394 
395@@ -903,7 +1007,12 @@
396 }
397 
398 .uploader-window-content {
399-       border-color: #fff;
400+       position: absolute;
401+       top: 10px;
402+       left: 10px;
403+       right: 10px;
404+       bottom: 10px;
405+       border: 1px dashed #fff;
406 }
407 
408 .uploader-window h3 {
409@@ -956,6 +1065,10 @@
410        margin: 4em 0;
411 }
412 
413+.uploader-inline .has-upload-message .upload-ui {
414+       margin: 0 0 4em;
415+}
416+
417 .uploader-inline h3 {
418        font-size: 20px;
419        line-height: 28px;
420@@ -963,6 +1076,12 @@
421        margin-bottom: 1.6em;
422 }
423 
424+.uploader-inline .has-upload-message .upload-instructions {
425+       font-size: 14px;
426+       color: #464646;
427+       font-weight: normal;
428+}
429+
430 .uploader-inline .drop-instructions {
431        display: none;
432 }
433@@ -1058,7 +1177,7 @@
434        vertical-align: top;
435 }
436 
437-.media-selection .attachment img {
438+.media-selection .attachment .icon {
439        width: 50%;
440 }
441 
442@@ -1098,69 +1217,6 @@
443 }
444 
445 /**
446- * Selection Preview
447- */
448-.selection-preview {
449-       position: relative;
450-       height: 60px;
451-       overflow: hidden;
452-}
453-
454-.selected-img {
455-       float: left;
456-       position: relative;
457-       margin-right: 14px;
458-}
459-
460-.selection-preview img {
461-       max-width: 40px;
462-       max-height: 40px;
463-       float: left;
464-       margin-top: 6px;
465-       margin-left: 1px;
466-       border: 2px solid white;
467-       box-shadow:
468-           0 0 0 1px #ccc,
469-           3px 3px 0 0 #fff,
470-           3px 3px 0 1px #ccc,
471-           6px 6px 0 0 #fff,
472-           6px 6px 0 1px #ccc;
473-}
474-
475-.selection-preview .selected-count-1 img {
476-       margin-top: 8px;
477-       box-shadow: 0 0 0 1px #ccc;
478-}
479-
480-.selection-preview .selected-count-2 img {
481-       margin-top: 7px;
482-       box-shadow:
483-           0 0 0 1px #ccc,
484-           3px 3px 0 0 #fff,
485-           3px 3px 0 1px #ccc;
486-}
487-
488-.selection-preview .count {
489-       position: absolute;
490-       bottom: 0;
491-       right: 0;
492-       height: 16px;
493-       min-width: 8px;
494-       padding: 0 4px;
495-       font-size: 12px;
496-       text-align: center;
497-       font-weight: bold;
498-       color: #999;
499-       background: #fff;
500-       box-shadow: -1px -1px 2px -1px rgba( 0, 0, 0, 0.2 );
501-}
502-
503-.selection-preview .clear-selection {
504-       float: left;
505-       line-height: 60px;
506-}
507-
508-/**
509  * Spinner
510  */
511 .media-sidebar .settings-save-status {
512@@ -1296,21 +1352,17 @@
513 .embed-url {
514        display: block;
515        position: relative;
516-       height: 75px;
517-       padding: 16px 16px;
518+       height: 40px;
519+       padding: 0 16px 16px;
520        margin: 0;
521-       z-index: 50;
522+       z-index: 250;
523+       background: #fff;
524        border-bottom: 1px solid #dfdfdf;
525        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
526        font-size: 18px;
527        font-weight: 200;
528 }
529 
530-.embed-url span {
531-       display: block;
532-       padding: 4px 0 6px 2px;
533-}
534-
535 .media-frame .embed-url input {
536        font-size: 18px;
537        padding: 12px 14px;
538@@ -1323,7 +1375,7 @@
539 .embed-image-settings {
540        position: absolute;
541        background: #f5f5f5;
542-       top: 108px;
543+       top: 57px;
544        left: 0;
545        right: 0;
546        bottom: 0;
547@@ -1387,32 +1439,7 @@
548  * Responsive layout
549  */
550 @media only screen and (max-width: 900px) {
551-       .media-modal {
552-               bottom: 20px;
553-               left: 20px;
554-               right: 20px;
555-               top: 40px;
556-       }
557-
558-       .media-modal-title {
559-               height: 30px;
560-               line-height: 30px;
561-               top: -30px;
562-       }
563-
564-       .media-modal-close {
565-               top: -23px;
566-       }
567-
568-       .media-modal-backdrop div,
569-       .uploader-window-content {
570-               top: 5px;
571-               left: 5px;
572-               right: 5px;
573-               bottom: 5px;
574-       }
575-
576-       .media-menu {
577+       .media-frame-menu {
578                width: 139px;
579        }
580 
581@@ -1420,8 +1447,8 @@
582                padding: 4px 10px;
583        }
584 
585-       .media-frame .region-content,
586-       .media-frame-toolbar > .media-toolbar {
587+       .media-frame-content,
588+       .media-frame-toolbar {
589                left: 140px;
590        }
591 
592Index: wp-includes/js/media-editor.js
593===================================================================
594--- wp-includes/js/media-editor.js      (revision 22995)
595+++ wp-includes/js/media-editor.js      (working copy)
596@@ -384,7 +384,7 @@
597 
598                        workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
599                                frame:    'post',
600-                               state:    'upload',
601+                               state:    'insert',
602                                title:    wp.media.view.l10n.addMedia,
603                                multiple: true
604                        } ) );
605@@ -408,11 +408,13 @@
606                        }, this );
607 
608                        workflow.state('embed').on( 'select', function() {
609-                               var embed = workflow.state().toJSON();
610+                               var state = workflow.state(),
611+                                       type = state.get('type'),
612+                                       embed = state.props.toJSON();
613 
614                                embed.url = embed.url || '';
615 
616-                               if ( 'link' === embed.type ) {
617+                               if ( 'link' === type ) {
618                                        _.defaults( embed, {
619                                                title:   embed.url,
620                                                linkUrl: embed.url
621@@ -420,7 +422,7 @@
622 
623                                        this.send.link( embed );
624 
625-                               } else if ( 'image' === embed.type ) {
626+                               } else if ( 'image' === type ) {
627                                        _.defaults( embed, {
628                                                title:   embed.url,
629                                                linkUrl: '',
630Index: wp-includes/js/media-views.js
631===================================================================
632--- wp-includes/js/media-views.js       (revision 22995)
633+++ wp-includes/js/media-views.js       (working copy)
634@@ -68,65 +68,71 @@
635         * wp.media.controller.Region
636         */
637        media.controller.Region = function( options ) {
638-               _.extend( this, _.pick( options || {}, 'id', 'controller', 'selector' ) );
639-
640-               this.on( 'activate:empty', this.empty, this );
641-               this.mode('empty');
642+               _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
643        };
644 
645        // Use Backbone's self-propagating `extend` inheritance method.
646        media.controller.Region.extend = Backbone.Model.extend;
647 
648-       _.extend( media.controller.Region.prototype, Backbone.Events, {
649-               trigger: (function() {
650-                       var eventSplitter = /\s+/,
651-                               trigger = Backbone.Events.trigger;
652+       _.extend( media.controller.Region.prototype, {
653+               mode: function( mode ) {
654+                       if ( ! mode )
655+                               return this._mode;
656 
657-                       return function( events ) {
658-                               var mode = ':' + this._mode,
659-                                       modeEvents = events.split( eventSplitter ).join( mode ) + mode;
660-
661-                               trigger.apply( this, arguments );
662-                               trigger.apply( this, [ modeEvents ].concat( _.rest( arguments ) ) );
663+                       // Bail if we're trying to change to the current mode.
664+                       if ( mode === this._mode )
665                                return this;
666-                       };
667-               }()),
668 
669-               mode: function( mode ) {
670-                       if ( mode ) {
671-                               this.trigger( 'deactivate', this );
672-                               this._mode = mode;
673-                               return this.trigger( 'activate', this );
674-                       }
675-                       return this._mode;
676+                       this.trigger('deactivate');
677+                       this._mode = mode;
678+                       this.render( mode );
679+                       this.trigger('activate');
680+                       return this;
681                },
682 
683-               view: function( view ) {
684-                       var previous = this._view,
685-                               mode = this._mode,
686-                               id = this.id;
687+               render: function( mode ) {
688+                       // If no mode is provided, just re-render the current mode.
689+                       // If the provided mode isn't active, perform a full switch.
690+                       if ( mode && mode !== this._mode )
691+                               return this.mode( mode );
692 
693-                       // If no argument is provided, return the current view.
694-                       if ( ! view )
695-                               return previous;
696+                       var set = { view: null },
697+                               view;
698 
699-                       // If we're attempting to switch to the current view, bail.
700-                       if ( view === previous )
701+                       this.trigger( 'create', set );
702+                       view = set.view;
703+                       this.trigger( 'render', view );
704+                       if ( view )
705+                               this.set( view );
706+                       return this;
707+               },
708+
709+               get: function() {
710+                       return this.view.views.first( this.selector );
711+               },
712+
713+               set: function( views, options ) {
714+                       if ( options )
715+                               options.add = false;
716+                       return this.view.views.set( this.selector, views, options );
717+               },
718+
719+               trigger: function( event ) {
720+                       var base;
721+                       if ( ! this._mode )
722                                return;
723 
724-                       // Add classes to the new view.
725-                       if ( id )
726-                               view.$el.addClass( 'region-' + id );
727+                       var args = _.toArray( arguments );
728+                       base = this.id + ':' + event;
729 
730-                       if ( mode )
731-                               view.$el.addClass( 'mode-' + mode );
732+                       // Trigger `region:action:mode` event.
733+                       args[0] = base + ':' + this._mode;
734+                       this.view.trigger.apply( this.view, args );
735 
736-                       this.controller.views.set( this.selector, view );
737-                       this._view = view;
738-               },
739-
740-               empty: function() {
741-                       this.view( new media.View() );
742+                       // Trigger `region:action` event.
743+                       args[0] = base;
744+                       this.view.trigger.apply( this.view, args );
745+                       return this;
746                }
747        });
748 
749@@ -208,31 +214,86 @@
750        // wp.media.controller.State
751        // ---------------------------
752        media.controller.State = Backbone.Model.extend({
753-               initialize: function() {
754-                       this.on( 'activate', this._activate, this );
755+               constructor: function() {
756+                       this.on( 'activate', this._preActivate, this );
757                        this.on( 'activate', this.activate, this );
758+                       this.on( 'activate', this._postActivate, this );
759                        this.on( 'deactivate', this._deactivate, this );
760                        this.on( 'deactivate', this.deactivate, this );
761                        this.on( 'reset', this.reset, this );
762+                       this.on( 'ready', this._ready, this );
763+                       this.on( 'ready', this.ready, this );
764+
765+                       this.on( 'change:menu', this._updateMenu, this );
766+
767+                       Backbone.Model.apply( this, arguments );
768                },
769 
770+               ready: function() {},
771                activate: function() {},
772-               _activate: function() {
773+               deactivate: function() {},
774+               reset: function() {},
775+
776+               _ready: function() {
777+                       this._updateMenu();
778+               },
779+
780+               _preActivate: function() {
781                        this.active = true;
782+               },
783 
784-                       this.menu();
785-                       this.toolbar();
786-                       this.content();
787+               _postActivate: function() {
788+                       this.on( 'change:menu', this._menu, this );
789+                       this.on( 'change:titleMode', this._title, this );
790+                       this.on( 'change:content', this._content, this );
791+                       this.on( 'change:toolbar', this._toolbar, this );
792+
793+                       this.frame.on( 'title:render:default', this._renderTitle, this );
794+
795+                       this._title();
796+                       this._menu();
797+                       this._toolbar();
798+                       this._content();
799+                       this._router();
800                },
801 
802-               deactivate: function() {},
803+
804                _deactivate: function() {
805                        this.active = false;
806+
807+                       this.frame.off( 'title:render:default', this._renderTitle, this );
808+
809+                       this.off( 'change:menu', this._menu, this );
810+                       this.off( 'change:titleMode', this._title, this );
811+                       this.off( 'change:content', this._content, this );
812+                       this.off( 'change:toolbar', this._toolbar, this );
813                },
814 
815-               reset: function() {},
816+               _title: function() {
817+                       this.frame.title.render( this.get('titleMode') || 'default' );
818+               },
819 
820-               menu: function() {
821+               _renderTitle: function( view ) {
822+                       view.$el.text( this.get('title') || '' );
823+               },
824+
825+               _router: function() {
826+                       var router = this.frame.router,
827+                               mode = this.get('router'),
828+                               view;
829+
830+                       this.frame.$el.toggleClass( 'hide-router', ! mode );
831+                       if ( ! mode )
832+                               return;
833+
834+                       this.frame.router.render( mode );
835+
836+                       view = router.get();
837+                       if ( view.select )
838+                               view.select( this.frame.content.mode() );
839+               },
840+
841+               _menu: function() {
842                        var menu = this.frame.menu,
843                                mode = this.get('menu'),
844                                view;
845@@ -240,20 +301,48 @@
846                        if ( ! mode )
847                                return;
848 
849-                       if ( menu.mode() !== mode )
850-                               menu.mode( mode );
851+                       menu.mode( mode );
852 
853-                       view = menu.view();
854+                       view = menu.get();
855                        if ( view.select )
856                                view.select( this.id );
857+               },
858+
859+               _updateMenu: function() {
860+                       var previous = this.previous('menu'),
861+                               menu = this.get('menu');
862+
863+                       if ( previous )
864+                               this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
865+
866+                       if ( menu )
867+                               this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
868+               },
869+
870+               _renderMenu: function( view ) {
871+                       var menuItem = this.get('menuItem'),
872+                               title = this.get('title'),
873+                               priority = this.get('priority');
874+
875+                       if ( ! menuItem && title ) {
876+                               menuItem = { text: title };
877+
878+                               if ( priority )
879+                                       menuItem.priority = priority;
880+                       }
881+
882+                       if ( ! menuItem )
883+                               return;
884+
885+                       view.set( this.id, menuItem );
886                }
887        });
888 
889        _.each(['toolbar','content'], function( region ) {
890-               media.controller.State.prototype[ region ] = function() {
891+               media.controller.State.prototype[ '_' + region ] = function() {
892                        var mode = this.get( region );
893                        if ( mode )
894-                               this.frame[ region ].mode( mode );
895+                               this.frame[ region ].render( mode );
896                };
897        });
898 
899@@ -262,15 +351,22 @@
900        media.controller.Library = media.controller.State.extend({
901                defaults: {
902                        id:         'library',
903-                       multiple:   false,
904+                       multiple:   false, // false, 'add', 'reset'
905                        describe:   false,
906-                       toolbar:    'main-attachments',
907+                       toolbar:    'select',
908                        sidebar:    'settings',
909-                       content:    'browse',
910+                       content:    'upload',
911+                       router:     'browse',
912                        searchable: true,
913                        filterable: false,
914-                       uploads:    true,
915-                       sortable:   true
916+                       sortable:   true,
917+                       title:      l10n.mediaLibraryTitle,
918+
919+                       // Uses a user setting to override the content mode.
920+                       contentUserSetting: true,
921+
922+                       // Sync the selection from the last state when 'multiple' matches.
923+                       syncLastSelection: true
924                },
925 
926                initialize: function() {
927@@ -290,31 +386,36 @@
928                                this.set( 'gutter', 8 );
929 
930                        this.resetDisplays();
931-
932-                       media.controller.State.prototype.initialize.apply( this, arguments );
933                },
934 
935                activate: function() {
936                        var library = this.get('library'),
937                                selection = this.get('selection');
938 
939+                       if ( this.get('syncLastSelection') ) {
940+                               this.getLastSelection();
941+                       }
942+
943                        this._excludeStateLibrary();
944                        this.buildComposite();
945                        this.on( 'change:library change:exclude', this.buildComposite, this );
946                        this.on( 'change:excludeState', this._excludeState, this );
947 
948-                       // If we're in a workflow that supports multiple attachments,
949-                       // automatically select any uploading attachments.
950-                       if ( this.get('multiple') )
951-                               wp.Uploader.queue.on( 'add', this.selectUpload, this );
952+                       wp.Uploader.queue.on( 'add', this.uploading, this );
953 
954                        selection.on( 'add remove reset', this.refreshSelection, this );
955 
956-                       this.refresh();
957                        this.on( 'insert', this._insertDisplaySettings, this );
958+
959+                       if ( this.get('contentUserSetting') ) {
960+                               this.frame.on( 'content:activate', this.saveContentMode, this );
961+                               this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
962+                       }
963                },
964 
965                deactivate: function() {
966+                       this.frame.off( 'content:activate', this.saveContentMode, this );
967+
968                        // Unbind all event handlers that use this state as the context
969                        // from the selection.
970                        this.get('selection').off( null, null, this );
971@@ -332,11 +433,6 @@
972                        this.resetDisplays();
973                },
974 
975-               refresh: function() {
976-                       this.content();
977-                       this.refreshSelection();
978-               },
979-
980                resetDisplays: function() {
981                        this._displays = [];
982                        this._defaultDisplaySettings = {
983@@ -371,21 +467,49 @@
984                        setUserSetting( 'urlbutton', display.link );
985                },
986 
987+               getLastSelection: function() {
988+                       var selection = this.get('selection'),
989+                               lastState = this.frame.lastState(),
990+                               lastSelection = lastState && lastState.get('selection');
991+
992+                       if ( ! lastSelection || lastSelection.multiple !== selection.multiple )
993+                               return;
994+
995+                       selection.reset( lastSelection.toArray() ).single( lastSelection.single() );
996+               },
997+
998                refreshSelection: function() {
999                        var selection = this.get('selection'),
1000                                mode = this.frame.content.mode();
1001 
1002-                       this.frame.toolbar.view().refresh();
1003+                       this.frame.toolbar.get().refresh();
1004                        this.trigger( 'refresh:selection', this, selection );
1005 
1006                        if ( ! selection.length && 'browse' !== mode && 'upload' !== mode )
1007-                               this.content();
1008+                               this.frame.content.render();
1009                },
1010 
1011-               selectUpload: function( attachment ) {
1012-                       this.get('selection').add( attachment );
1013+               uploading: function( attachment ) {
1014+                       var content = this.frame.content;
1015+
1016+                       // If the uploader was selected, navigate to the browser.
1017+                       if ( 'upload' === content.mode() )
1018+                               this.frame.content.mode('browse');
1019+
1020+                       // If we're in a workflow that supports multiple attachments,
1021+                       // automatically select any uploading attachments.
1022+                       if ( this.get('multiple') )
1023+                               this.get('selection').add( attachment );
1024                },
1025 
1026+               saveContentMode: function() {
1027+                       // Only track the browse router on library states.
1028+                       if ( 'browse' !== this.get('router') )
1029+                               return;
1030+
1031+                       setUserSetting( 'libraryContent', this.frame.content.mode() );
1032+               },
1033+
1034                buildComposite: function() {
1035                        var original = this.get('_library'),
1036                                exclude = this.get('exclude'),
1037@@ -448,45 +572,9 @@
1038                }
1039        });
1040 
1041-
1042-       // wp.media.controller.Upload
1043-       // ---------------------------
1044-       media.controller.Upload = media.controller.State.extend({
1045-               defaults: _.defaults({
1046-                       id:      'upload',
1047-                       content: 'upload',
1048-                       toolbar: 'empty',
1049-                       uploads: true,
1050-
1051-                       // The state to navigate to when files are uploading.
1052-                       libraryState: 'library'
1053-               }, media.controller.State.prototype.defaults ),
1054-
1055-               initialize: function() {
1056-                       media.controller.State.prototype.initialize.apply( this, arguments );
1057-               },
1058-
1059-               activate: function() {
1060-                       wp.Uploader.queue.on( 'add', this.uploading, this );
1061-                       media.controller.State.prototype.activate.apply( this, arguments );
1062-               },
1063-
1064-               deactivate: function() {
1065-                       wp.Uploader.queue.off( null, null, this );
1066-                       media.controller.State.prototype.deactivate.apply( this, arguments );
1067-               },
1068-
1069-               uploading: function( attachment ) {
1070-                       var library = this.get('libraryState');
1071-
1072-                       this.frame.state( library ).get('selection').add( attachment );
1073-                       this.frame.setState( library );
1074-               }
1075-       });
1076-
1077-       // wp.media.controller.Gallery
1078-       // ---------------------------
1079-       media.controller.Gallery = media.controller.Library.extend({
1080+       // wp.media.controller.GalleryEdit
1081+       // -------------------------------
1082+       media.controller.GalleryEdit = media.controller.Library.extend({
1083                defaults: {
1084                        id:         'gallery-edit',
1085                        multiple:   false,
1086@@ -496,7 +584,9 @@
1087                        sortable:   true,
1088                        searchable: false,
1089                        toolbar:    'gallery-edit',
1090-                       content:    'browse'
1091+                       content:    'browse',
1092+                       title:      l10n.editGalleryTitle,
1093+                       priority:   60
1094                },
1095 
1096                initialize: function() {
1097@@ -519,7 +609,7 @@
1098                        // Watch for uploaded attachments.
1099                        this.get('library').observe( wp.Uploader.queue );
1100 
1101-                       this.frame.content.on( 'activate:browse', this.gallerySettings, this );
1102+                       this.frame.on( 'content:render:browse', this.gallerySettings, this );
1103 
1104                        media.controller.Library.prototype.activate.apply( this, arguments );
1105                },
1106@@ -528,21 +618,19 @@
1107                        // Stop watching for uploaded attachments.
1108                        this.get('library').unobserve( wp.Uploader.queue );
1109 
1110-                       this.frame.content.off( null, null, this );
1111+                       this.frame.off( 'content:render:browse', this.gallerySettings, this );
1112+
1113                        media.controller.Library.prototype.deactivate.apply( this, arguments );
1114                },
1115 
1116-               gallerySettings: function() {
1117-                       var library = this.get('library'),
1118-                               browser;
1119+               gallerySettings: function( browser ) {
1120+                       var library = this.get('library');
1121 
1122-                       if ( ! library )
1123+                       if ( ! library || ! browser )
1124                                return;
1125 
1126                        library.gallery = library.gallery || new Backbone.Model();
1127 
1128-                       browser = this.frame.content.view();
1129-
1130                        browser.sidebar.set({
1131                                gallery: new media.view.Settings.Gallery({
1132                                        controller: this,
1133@@ -570,7 +658,9 @@
1134                        filterable: 'uploaded',
1135                        multiple:   false,
1136                        menu:       'main',
1137-                       toolbar:    'featured-image'
1138+                       toolbar:    'featured-image',
1139+                       title:      l10n.featuredImageTitle,
1140+                       priority:   60
1141                }, media.controller.Library.prototype.defaults ),
1142 
1143                initialize: function() {
1144@@ -629,7 +719,10 @@
1145                        menu:    'main',
1146                        content: 'embed',
1147                        toolbar: 'main-embed',
1148-                       type:    'link'
1149+                       type:    'link',
1150+
1151+                       title:    l10n.fromUrlTitle,
1152+                       priority: 120
1153                },
1154 
1155                // The amount of time used when debouncing the scan.
1156@@ -637,9 +730,9 @@
1157 
1158                initialize: function() {
1159                        this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
1160-                       this.on( 'change:url', this.debouncedScan, this );
1161+                       this.props = new Backbone.Model({ url: '' });
1162+                       this.props.on( 'change:url', this.debouncedScan, this );
1163                        this.on( 'scan', this.scanImage, this );
1164-                       media.controller.State.prototype.initialize.apply( this, arguments );
1165                },
1166 
1167                scan: function() {
1168@@ -652,11 +745,11 @@
1169                scanImage: function( attributes ) {
1170                        var frame = this.frame,
1171                                state = this,
1172-                               url = this.get('url'),
1173+                               url = this.props.get('url'),
1174                                image = new Image();
1175 
1176                        image.onload = function() {
1177-                               if ( state !== frame.state() || url !== state.get('url') )
1178+                               if ( state !== frame.state() || url !== state.props.get('url') )
1179                                        return;
1180 
1181                                state.set({
1182@@ -670,14 +763,10 @@
1183                },
1184 
1185                reset: function() {
1186-                       _.each( _.difference( _.keys( this.attributes ), _.keys( this.defaults ) ), function( key ) {
1187-                               this.unset( key );
1188-                       }, this );
1189+                       this.props = new Backbone.Model({ url: '' });
1190 
1191-                       this.set( 'url', '' );
1192-
1193                        if ( this.id === this.frame.state().id )
1194-                               this.frame.toolbar.view().refresh();
1195+                               this.frame.toolbar.get().refresh();
1196                }
1197        });
1198 
1199@@ -1022,9 +1111,13 @@
1200                // The constructor for the `Views` manager.
1201                Views: media.Views,
1202 
1203-               constructor: function() {
1204+               constructor: function( options ) {
1205                        this.views = new this.Views( this, this.views );
1206                        this.on( 'ready', this.ready, this );
1207+
1208+                       if ( options && options.controller )
1209+                               this.controller = options.controller;
1210+
1211                        Backbone.View.apply( this, arguments );
1212                },
1213 
1214@@ -1097,9 +1190,9 @@
1215                        // Initialize regions.
1216                        _.each( this.regions, function( region ) {
1217                                this[ region ] = new media.controller.Region({
1218-                                       controller: this,
1219-                                       id:         region,
1220-                                       selector:   '.media-frame-' + region
1221+                                       view:     this,
1222+                                       id:       region,
1223+                                       selector: '.media-frame-' + region
1224                                });
1225                        }, this );
1226                },
1227@@ -1113,6 +1206,7 @@
1228                        // Ensure states have a reference to the frame.
1229                        this.states.on( 'add', function( model ) {
1230                                model.frame = this;
1231+                               model.trigger('ready');
1232                        }, this );
1233                },
1234 
1235@@ -1131,7 +1225,7 @@
1236        media.view.MediaFrame = media.view.Frame.extend({
1237                className: 'media-frame',
1238                template:  media.template('media-frame'),
1239-               regions:   ['menu','content','toolbar'],
1240+               regions:   ['menu','title','content','toolbar','router'],
1241 
1242                initialize: function() {
1243                        media.view.Frame.prototype.initialize.apply( this, arguments );
1244@@ -1173,6 +1267,10 @@
1245                        }
1246 
1247                        this.on( 'attach', _.bind( this.views.ready, this.views ), this );
1248+
1249+                       // Bind default title creation.
1250+                       this.on( 'title:create:default', this.createTitle, this );
1251+                       this.title.mode('default');
1252                },
1253 
1254                render: function() {
1255@@ -1183,6 +1281,31 @@
1256                        return media.view.Frame.prototype.render.apply( this, arguments );
1257                },
1258 
1259+               createTitle: function( title ) {
1260+                       title.view = new media.View({
1261+                               controller: this,
1262+                               tagName: 'h1'
1263+                       });
1264+               },
1265+
1266+               createMenu: function( menu ) {
1267+                       menu.view = new media.view.Menu({
1268+                               controller: this
1269+                       });
1270+               },
1271+
1272+               createToolbar: function( toolbar ) {
1273+                       menu.view = new media.view.Toolbar({
1274+                               controller: this
1275+                       });
1276+               },
1277+
1278+               createRouter: function( router ) {
1279+                       router.view = new media.view.Router({
1280+                               controller: this
1281+                       });
1282+               },
1283+
1284                createIframeStates: function( options ) {
1285                        var settings = media.view.settings,
1286                                tabs = settings.tabs,
1287@@ -1208,22 +1331,25 @@
1288                                }, options ) );
1289                        }, this );
1290 
1291-                       this.content.on( 'activate:iframe', this.iframeContent, this );
1292-                       this.menu.on( 'activate:main', this.iframeMenu, this );
1293+                       this.on( 'content:create:iframe', this.iframeContent, this );
1294+                       this.on( 'menu:render:main', this.iframeMenu, this );
1295                        this.on( 'open', this.hijackThickbox, this );
1296                        this.on( 'close', this.restoreThickbox, this );
1297                },
1298 
1299-               iframeContent: function() {
1300+               iframeContent: function( content ) {
1301                        this.$el.addClass('hide-toolbar');
1302-                       this.content.view( new media.view.Iframe({
1303+                       content.view = new media.view.Iframe({
1304                                controller: this
1305-                       }).render() );
1306+                       });
1307                },
1308 
1309-               iframeMenu: function() {
1310+               iframeMenu: function( view ) {
1311                        var views = {};
1312 
1313+                       if ( ! view )
1314+                               return;
1315+
1316                        _.each( media.view.settings.tabs, function( title, id ) {
1317                                views[ 'iframe:' + id ] = {
1318                                        text: this.state( 'iframe:' + id ).get('title'),
1319@@ -1231,7 +1357,7 @@
1320                                };
1321                        }, this );
1322 
1323-                       this.menu.view().set( views );
1324+                       view.set( views );
1325                },
1326 
1327                hijackThickbox: function() {
1328@@ -1305,83 +1431,76 @@
1329                                new media.controller.Library({
1330                                        selection: options.selection,
1331                                        library:   media.query( options.library ),
1332-                                       multiple:  this.options.multiple,
1333+                                       multiple:  options.multiple,
1334                                        menu:      'main',
1335-                                       toolbar:   'select'
1336-                               }),
1337-
1338-                               new media.controller.Upload({
1339-                                       menu: 'main'
1340+                                       title:     options.title,
1341+                                       priority:  20
1342                                })
1343                        ]);
1344                },
1345 
1346                bindHandlers: function() {
1347-                       this.menu.on( 'activate:main', this.mainMenu, this );
1348-                       this.content.on( 'activate:browse', this.browseContent, this );
1349-                       this.content.on( 'activate:upload', this.uploadContent, this );
1350-                       this.toolbar.on( 'activate:select', this.selectToolbar, this );
1351+                       this.on( 'menu:create:main', this.createMenu, this );
1352+                       this.on( 'router:create:browse', this.createRouter, this );
1353+                       this.on( 'router:render:browse', this.browseRouter, this );
1354+                       this.on( 'content:create:browse', this.browseContent, this );
1355+                       this.on( 'content:render:upload', this.uploadContent, this );
1356+                       this.on( 'toolbar:create:select', this.createSelectToolbar, this );
1357 
1358                        this.on( 'refresh:selection', this.refreshSelectToolbar, this );
1359                },
1360 
1361-               mainMenu: function( options ) {
1362-                       this.menu.view( new media.view.Menu({
1363-                               controller: this,
1364-                               silent:     options && options.silent,
1365-
1366-                               views: {
1367-                                       upload: {
1368-                                               text: l10n.uploadFilesTitle,
1369-                                               priority: 20
1370-                                       },
1371-                                       library: {
1372-                                               text: l10n.mediaLibraryTitle,
1373-                                               priority: 40
1374-                                       }
1375+               // Routers
1376+               browseRouter: function( view ) {
1377+                       view.set({
1378+                               upload: {
1379+                                       text:     l10n.uploadFilesTitle,
1380+                                       priority: 20
1381+                               },
1382+                               browse: {
1383+                                       text:     l10n.mediaLibraryTitle,
1384+                                       priority: 40
1385                                }
1386-                       }) );
1387+                       });
1388                },
1389 
1390                // Content
1391-               browseContent: function() {
1392+               browseContent: function( content ) {
1393                        var state = this.state();
1394 
1395                        this.$el.removeClass('hide-toolbar');
1396 
1397                        // Browse our library of attachments.
1398-                       this.content.view( new media.view.AttachmentsBrowser({
1399+                       content.view = new media.view.AttachmentsBrowser({
1400                                controller: this,
1401                                collection: state.get('library'),
1402                                selection:  state.get('selection'),
1403                                model:      state,
1404                                sortable:   state.get('sortable'),
1405                                search:     state.get('searchable'),
1406-                               uploads:    state.get('uploads'),
1407                                filters:    state.get('filterable'),
1408                                display:    state.get('displaySettings'),
1409 
1410                                AttachmentView: state.get('AttachmentView')
1411-                       }) );
1412+                       });
1413                },
1414 
1415                uploadContent: function() {
1416-                       this.$el.addClass('hide-toolbar');
1417-
1418-                       this.content.view( new media.view.UploaderInline({
1419+                       this.$el.removeClass('hide-toolbar');
1420+                       this.content.set( new media.view.UploaderInline({
1421                                controller: this
1422                        }) );
1423                },
1424 
1425                // Toolbars
1426-               selectToolbar: function( options ) {
1427+               createSelectToolbar: function( toolbar, options ) {
1428                        options = _.defaults( options || {}, {
1429                                event:  'select',
1430                                silent: false,
1431                                state:  false
1432                        });
1433 
1434-                       this.toolbar.view( new media.view.Toolbar({
1435+                       toolbar.view = new media.view.Toolbar({
1436                                controller: this,
1437                                silent:     options.silent,
1438 
1439@@ -1402,7 +1521,7 @@
1440                                                }
1441                                        }
1442                                }
1443-                       }) );
1444+                       });
1445                },
1446 
1447                refreshSelectToolbar: function() {
1448@@ -1411,7 +1530,7 @@
1449                        if ( ! selection || 'select' !== this.toolbar.mode() )
1450                                return;
1451 
1452-                       this.toolbar.view().get('select').model.set( 'disabled', ! selection.length );
1453+                       this.toolbar.get().get('select').model.set( 'disabled', ! selection.length );
1454                }
1455        });
1456 
1457@@ -1430,35 +1549,55 @@
1458                },
1459 
1460                createStates: function() {
1461-                       var options = this.options;
1462+                       var options = this.options,
1463+                               selection = options.selection,
1464+                               library = {
1465+                                       editable: true,
1466+                                       menu:     'main',
1467 
1468-                       // Add the default states.
1469-                       this.states.add([
1470-                               // Main states.
1471-                               new media.controller.Library({
1472-                                       selection:  options.selection,
1473-                                       library:    media.query( options.library ),
1474-                                       editable:   true,
1475-                                       filterable: 'all',
1476-                                       multiple:   this.options.multiple,
1477-                                       menu:       'main',
1478-
1479                                        // Show the attachment display settings.
1480                                        displaySettings: true,
1481                                        // Update user settings when users adjust the
1482                                        // attachment display settings.
1483                                        displayUserSettings: true
1484-                               }),
1485+                               };
1486 
1487-                               new media.controller.Upload({
1488-                                       menu: 'main'
1489-                               }),
1490+                       // Add the default states.
1491+                       this.states.add([
1492+                               // Main states.
1493+                               new media.controller.Library( _.defaults({
1494+                                       id:         'insert',
1495+                                       title:      l10n.insertMediaTitle,
1496+                                       priority:   20,
1497+                                       toolbar:    'main-insert',
1498+                                       filterable: 'all',
1499+                                       library:    media.query( options.library ),
1500+                                       selection:  selection,
1501+                                       multiple:   options.multiple ? 'reset' : false
1502+                               }, library ) ),
1503 
1504+                               new media.controller.Library( _.defaults({
1505+                                       id:         'gallery',
1506+                                       title:      l10n.createGalleryTitle,
1507+                                       priority:   40,
1508+                                       toolbar:    'main-gallery',
1509+                                       filterable: 'uploaded',
1510+                                       multiple:   'add',
1511+
1512+                                       library:  media.query( _.defaults({
1513+                                               type: 'image'
1514+                                       }, options.library ) ),
1515+
1516+                                       selection: new media.model.Selection( selection.models, {
1517+                                               multiple: 'add'
1518+                                       })
1519+                               }, library ) ),
1520+
1521                                // Embed states.
1522                                new media.controller.Embed(),
1523 
1524                                // Gallery states.
1525-                               new media.controller.Gallery({
1526+                               new media.controller.GalleryEdit({
1527                                        library: options.selection,
1528                                        editing: options.editing,
1529                                        menu:    'gallery'
1530@@ -1471,13 +1610,9 @@
1531                                        multiple:     true,
1532                                        menu:         'gallery',
1533                                        toolbar:      'gallery-add',
1534-                                       excludeState: 'gallery-edit'
1535-                               }),
1536-
1537-                               new media.controller.Upload({
1538-                                       id:           'gallery-upload',
1539-                                       menu:         'gallery',
1540-                                       libraryState: 'gallery-edit'
1541+                                       excludeState: 'gallery-edit',
1542+                                       title:        l10n.addToGalleryTitle,
1543+                                       priority:     100
1544                                })
1545                        ]);
1546 
1547@@ -1492,9 +1627,13 @@
1548 
1549                bindHandlers: function() {
1550                        media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
1551+                       this.on( 'menu:create:gallery', this.createMenu, this );
1552+                       this.on( 'toolbar:create:main-insert', this.createSelectionToolbar, this );
1553+                       this.on( 'toolbar:create:main-gallery', this.createSelectionToolbar, this );
1554 
1555                        var handlers = {
1556                                        menu: {
1557+                                               'main':    'mainMenu',
1558                                                'gallery': 'galleryMenu'
1559                                        },
1560 
1561@@ -1504,7 +1643,8 @@
1562                                        },
1563 
1564                                        toolbar: {
1565-                                               'main-attachments': 'mainAttachmentsToolbar',
1566+                                               'main-insert':      'mainInsertToolbar',
1567+                                               'main-gallery':     'mainGalleryToolbar',
1568                                                'main-embed':       'mainEmbedToolbar',
1569                                                'featured-image':   'featuredImageToolbar',
1570                                                'gallery-edit':     'galleryEditToolbar',
1571@@ -1514,70 +1654,42 @@
1572 
1573                        _.each( handlers, function( regionHandlers, region ) {
1574                                _.each( regionHandlers, function( callback, handler ) {
1575-                                       this[ region ].on( 'activate:' + handler, this[ callback ], this );
1576+                                       this.on( region + ':render:' + handler, this[ callback ], this );
1577                                }, this );
1578                        }, this );
1579                },
1580 
1581                // Menus
1582-               mainMenu: function() {
1583-                       media.view.MediaFrame.Select.prototype.mainMenu.call( this, { silent: true });
1584-
1585-                       this.menu.view().set({
1586+               mainMenu: function( view ) {
1587+                       view.set({
1588                                'library-separator': new media.View({
1589                                        className: 'separator',
1590-                                       priority: 60
1591-                               }),
1592-                               'embed': {
1593-                                       text: l10n.fromUrlTitle,
1594-                                       priority: 80
1595-                               }
1596+                                       priority: 100
1597+                               })
1598                        });
1599-
1600-                       if ( media.view.settings.post.featuredImageId ) {
1601-                               this.menu.view().set( 'featured-image', {
1602-                                       text: l10n.featuredImageTitle,
1603-                                       priority: 100
1604-                               });
1605-                       }
1606                },
1607 
1608-               galleryMenu: function() {
1609+               galleryMenu: function( view ) {
1610                        var lastState = this.lastState(),
1611                                previous = lastState && lastState.id,
1612                                frame = this;
1613 
1614-                       this.menu.view( new media.view.Menu({
1615-                               controller: this,
1616-                               views: {
1617-                                       cancel: {
1618-                                               text:     l10n.cancelGalleryTitle,
1619-                                               priority: 20,
1620-                                               click:    function() {
1621-                                                       if ( previous )
1622-                                                               frame.setState( previous );
1623-                                                       else
1624-                                                               frame.close();
1625-                                               }
1626-                                       },
1627-                                       separateCancel: new media.View({
1628-                                               className: 'separator',
1629-                                               priority: 40
1630-                                       }),
1631-                                       'gallery-edit': {
1632-                                               text: l10n.editGalleryTitle,
1633-                                               priority: 60
1634-                                       },
1635-                                       'gallery-upload': {
1636-                                               text: l10n.uploadImagesTitle,
1637-                                               priority: 80
1638-                                       },
1639-                                       'gallery-library': {
1640-                                               text: l10n.mediaLibraryTitle,
1641-                                               priority: 100
1642+                       view.set({
1643+                               cancel: {
1644+                                       text:     l10n.cancelGalleryTitle,
1645+                                       priority: 20,
1646+                                       click:    function() {
1647+                                               if ( previous )
1648+                                                       frame.setState( previous );
1649+                                               else
1650+                                                       frame.close();
1651                                        }
1652-                               }
1653-                       }) );
1654+                               },
1655+                               separateCancel: new media.View({
1656+                                       className: 'separator',
1657+                                       priority: 40
1658+                               })
1659+                       });
1660                },
1661 
1662                // Content
1663@@ -1587,7 +1699,7 @@
1664                                model:      this.state()
1665                        }).render();
1666 
1667-                       this.content.view( view );
1668+                       this.content.set( view );
1669                        view.url.focus();
1670                },
1671 
1672@@ -1617,37 +1729,62 @@
1673                        });
1674 
1675                        // Browse our library of attachments.
1676-                       this.content.view( view );
1677+                       this.content.set( view );
1678                },
1679 
1680-               // Sidebars
1681-               onSidebarGallerySettings: function( options ) {
1682-                       var library = this.state().get('library');
1683+               // Toolbars
1684+               createSelectionToolbar: function( toolbar ) {
1685+                       toolbar.view = new media.view.Toolbar.Selection({
1686+                               controller: this,
1687+                               editable:   this.state().get('editable')
1688+                       });
1689+               },
1690 
1691-                       if ( ! library )
1692-                               return;
1693+               mainInsertToolbar: function( view ) {
1694+                       var controller = this;
1695 
1696-                       library.gallery = library.gallery || new Backbone.Model();
1697+                       view.button = 'insert';
1698+                       view.set( 'insert', {
1699+                               style:    'primary',
1700+                               priority: 80,
1701+                               text:     l10n.insertIntoPost,
1702 
1703-                       this.sidebar.view().set({
1704-                               gallery: new media.view.Settings.Gallery({
1705-                                       controller: this,
1706-                                       model:      library.gallery,
1707-                                       priority:   40
1708-                               }).render()
1709-                       }, options );
1710+                               click: function() {
1711+                                       var state = controller.state(),
1712+                                               selection = state.get('selection');
1713+
1714+                                       controller.close();
1715+                                       state.trigger( 'insert', selection ).reset();
1716+                               }
1717+                       });
1718                },
1719 
1720-               // Toolbars
1721-               mainAttachmentsToolbar: function() {
1722-                       this.toolbar.view( new media.view.Toolbar.Insert({
1723-                               controller: this,
1724-                               editable:   this.state().get('editable')
1725-                       }) );
1726+               mainGalleryToolbar: function( view ) {
1727+                       var controller = this;
1728+
1729+                       view.button = 'gallery';
1730+                       view.set( 'gallery', {
1731+                               style:    'primary',
1732+                               text:     l10n.createNewGallery,
1733+                               priority: 60,
1734+
1735+                               click: function() {
1736+                                       var selection = controller.state().get('selection'),
1737+                                               edit = controller.state('gallery-edit'),
1738+                                               models = selection.where({ type: 'image' });
1739+
1740+                                       edit.set( 'library', new media.model.Selection( models, {
1741+                                               props:    selection.props.toJSON(),
1742+                                               multiple: true
1743+                                       }) );
1744+
1745+                                       this.controller.setState('gallery-edit');
1746+                               }
1747+                       });
1748                },
1749 
1750                featuredImageToolbar: function() {
1751-                       this.toolbar.view( new media.view.Toolbar.Select({
1752+                       this.toolbar.set( new media.view.Toolbar.Select({
1753                                controller: this,
1754                                text:       l10n.setFeaturedImage,
1755                                state:      this.options.state || 'upload'
1756@@ -1655,7 +1792,7 @@
1757                },
1758 
1759                mainEmbedToolbar: function() {
1760-                       this.toolbar.view( new media.view.Toolbar.Embed({
1761+                       this.toolbar.set( new media.view.Toolbar.Embed({
1762                                controller: this
1763                        }) );
1764 
1765@@ -1664,7 +1801,7 @@
1766 
1767                galleryEditToolbar: function() {
1768                        var editing = this.state().get('editing');
1769-                       this.toolbar.view( new media.view.Toolbar({
1770+                       this.toolbar.set( new media.view.Toolbar({
1771                                controller: this,
1772                                items: {
1773                                        insert: {
1774@@ -1689,7 +1826,7 @@
1775                },
1776 
1777                galleryAddToolbar: function() {
1778-                       this.toolbar.view( new media.view.Toolbar({
1779+                       this.toolbar.set( new media.view.Toolbar({
1780                                controller: this,
1781                                items: {
1782                                        insert: {
1783@@ -1704,7 +1841,7 @@
1784 
1785                                                        edit.get('library').add( state.get('selection').models );
1786                                                        state.trigger('reset');
1787-                                                       controller.state('gallery-edit');
1788+                                                       controller.setState('gallery-edit');
1789                                                }
1790                                        }
1791                                }
1792@@ -1729,8 +1866,6 @@
1793                },
1794 
1795                initialize: function() {
1796-                       this.controller = this.options.controller;
1797-
1798                        _.defaults( this.options, {
1799                                container: document.body,
1800                                title:     '',
1801@@ -1838,8 +1973,6 @@
1802                initialize: function() {
1803                        var uploader;
1804 
1805-                       this.controller = this.options.controller;
1806-
1807                        this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
1808 
1809                        uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
1810@@ -1906,7 +2039,10 @@
1811                template:  media.template('uploader-inline'),
1812 
1813                initialize: function() {
1814-                       this.controller = this.options.controller;
1815+                       _.defaults( this.options, {
1816+                               message: '',
1817+                               status:  true
1818+                       });
1819 
1820                        if ( ! this.options.$browser && this.controller.uploader )
1821                                this.options.$browser = this.controller.uploader.$browser;
1822@@ -1914,9 +2050,11 @@
1823                        if ( _.isUndefined( this.options.postId ) )
1824                                this.options.postId = media.view.settings.post.id;
1825 
1826-                       this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
1827-                               controller: this.controller
1828-                       }) );
1829+                       if ( this.options.status ) {
1830+                               this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
1831+                                       controller: this.controller
1832+                               }) );
1833+                       }
1834                },
1835 
1836                ready: function() {
1837@@ -1951,8 +2089,6 @@
1838                },
1839 
1840                initialize: function() {
1841-                       this.controller = this.options.controller;
1842-
1843                        this.queue = wp.Uploader.queue;
1844                        this.queue.on( 'add remove reset', this.visibility, this );
1845                        this.queue.on( 'add remove reset change:percent', this.progress, this );
1846@@ -2060,8 +2196,6 @@
1847                className: 'media-toolbar',
1848 
1849                initialize: function() {
1850-                       this.controller = this.options.controller;
1851-
1852                        this._views     = {};
1853                        this.$primary   = $('<div class="media-toolbar-primary" />').prependTo( this.$el );
1854                        this.$secondary = $('<div class="media-toolbar-secondary" />').prependTo( this.$el );
1855@@ -2199,50 +2333,32 @@
1856        // ---------------------------
1857        media.view.Toolbar.Embed = media.view.Toolbar.Select.extend({
1858                initialize: function() {
1859-                       var controller = this.options.controller;
1860-
1861                        _.defaults( this.options, {
1862                                text: l10n.insertIntoPost
1863                        });
1864 
1865                        media.view.Toolbar.Select.prototype.initialize.apply( this, arguments );
1866-                       controller.on( 'change:url', this.refresh, this );
1867+                       this.controller.state().props.on( 'change:url', this.refresh, this );
1868                },
1869 
1870                refresh: function() {
1871-                       var url = this.controller.state().get('url');
1872+                       var url = this.controller.state().props.get('url');
1873                        this.get('select').model.set( 'disabled', ! url || /^https?:\/\/$/.test(url) );
1874                }
1875        });
1876 
1877-       // wp.media.view.Toolbar.Insert
1878-       // ----------------------------
1879-       media.view.Toolbar.Insert = media.view.Toolbar.extend({
1880+       // wp.media.view.Toolbar.Selection
1881+       // -------------------------------
1882+       media.view.Toolbar.Selection = media.view.Toolbar.extend({
1883+               button: 'insert',
1884+
1885                initialize: function() {
1886-                       var controller = this.options.controller,
1887-                               selection = controller.state().get('selection'),
1888-                               selectionToLibrary;
1889+                       var controller = this.controller;
1890 
1891-                       selectionToLibrary = function( state, filter ) {
1892-                               return function() {
1893-                                       var controller = this.controller,
1894-                                               selection = controller.state().get('selection'),
1895-                                               edit = controller.state( state ),
1896-                                               models = filter ? filter( selection ) : selection.models;
1897-
1898-                                       edit.set( 'library', new media.model.Selection( models, {
1899-                                               props:    selection.props.toJSON(),
1900-                                               multiple: true
1901-                                       }) );
1902-
1903-                                       this.controller.setState( state );
1904-                               };
1905-                       };
1906-
1907                        this.options.items = _.defaults( this.options.items || {}, {
1908                                selection: new media.view.Selection({
1909                                        controller: controller,
1910-                                       collection: selection,
1911+                                       collection: controller.state().get('selection'),
1912                                        priority:   -40,
1913 
1914                                        // If the selection is editable, pass the callback to
1915@@ -2250,26 +2366,7 @@
1916                                        editable: this.options.editable && function() {
1917                                                this.controller.content.mode('edit-selection');
1918                                        }
1919-                               }).render(),
1920-
1921-                               insert: {
1922-                                       style:    'primary',
1923-                                       priority: 80,
1924-                                       text:     l10n.insertIntoPost,
1925-
1926-                                       click: function() {
1927-                                               controller.close();
1928-                                               controller.state().trigger( 'insert', selection ).reset();
1929-                                       }
1930-                               },
1931-
1932-                               gallery: {
1933-                                       text:     l10n.createNewGallery,
1934-                                       priority: 40,
1935-                                       click:    selectionToLibrary('gallery-edit', function( selection ) {
1936-                                               return selection.where({ type: 'image' });
1937-                                       })
1938-                               }
1939+                               }).render()
1940                        });
1941 
1942                        media.view.Toolbar.prototype.initialize.apply( this, arguments );
1943@@ -2277,14 +2374,12 @@
1944 
1945                refresh: function() {
1946                        var selection = this.controller.state().get('selection'),
1947-                               count = selection.length;
1948+                               button = this.get( this.button );
1949 
1950-                       this.get('insert').model.set( 'disabled', ! selection.length );
1951+                       if ( ! button )
1952+                               return;
1953 
1954-                       // Check if any attachment in the selection is an image.
1955-                       this.get('gallery').$el.toggle( count > 1 && selection.any( function( attachment ) {
1956-                               return 'image' === attachment.get('type');
1957-                       }) );
1958+                       button.model.set( 'disabled', ! selection.length );
1959                }
1960        });
1961 
1962@@ -2388,8 +2483,7 @@
1963                tagName:   'div',
1964 
1965                initialize: function() {
1966-                       this.controller = this.options.controller;
1967-                       this._views     = {};
1968+                       this._views = {};
1969 
1970                        this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
1971                        delete this.options.views;
1972@@ -2457,23 +2551,94 @@
1973                }
1974        });
1975 
1976+       /**
1977+        * wp.media.view.MenuItem
1978+        */
1979+       media.view.MenuItem = media.View.extend({
1980+               tagName:   'a',
1981+               className: 'media-menu-item',
1982 
1983+               attributes: {
1984+                       href: '#'
1985+               },
1986+
1987+               events: {
1988+                       'click': '_click'
1989+               },
1990+
1991+               _click: function( event ) {
1992+                       var clickOverride = this.options.click;
1993+
1994+                       event.preventDefault();
1995+
1996+                       if ( clickOverride )
1997+                               clickOverride.call( this );
1998+                       else
1999+                               this.click();
2000+               },
2001+
2002+               click: function() {
2003+                       var state = this.options.state;
2004+                       if ( state )
2005+                               this.controller.setState( state );
2006+               },
2007+
2008+               render: function() {
2009+                       var options = this.options;
2010+
2011+                       if ( options.text )
2012+                               this.$el.text( options.text );
2013+                       else if ( options.html )
2014+                               this.$el.html( options.html );
2015+
2016+                       return this;
2017+               }
2018+       });
2019+
2020        /**
2021         * wp.media.view.Menu
2022         */
2023        media.view.Menu = media.view.PriorityList.extend({
2024-               tagName:   'ul',
2025+               tagName:   'div',
2026                className: 'media-menu',
2027+               property:  'state',
2028+               ItemView:  media.view.MenuItem,
2029+               region:    'menu',
2030 
2031-               toView: function( options, state ) {
2032+               toView: function( options, id ) {
2033                        options = options || {};
2034-                       options.state = options.state || state;
2035-                       return new media.view.MenuItem( options ).render();
2036+                       options[ this.property ] = options[ this.property ] || id;
2037+                       return new this.ItemView( options ).render();
2038                },
2039 
2040-               select: function( state ) {
2041-                       var view = this.get( state );
2042+               ready: function() {
2043+                       media.view.PriorityList.prototype.ready.apply( this, arguments );
2044+                       this.visibility();
2045+               },
2046 
2047+               set: function() {
2048+                       media.view.PriorityList.prototype.set.apply( this, arguments );
2049+                       this.visibility();
2050+               },
2051+
2052+               unset: function() {
2053+                       media.view.PriorityList.prototype.unset.apply( this, arguments );
2054+                       this.visibility();
2055+               },
2056+
2057+               visibility: function() {
2058+                       var region = this.region,
2059+                               view = this.controller[ region ].get(),
2060+                               views = this.views.get(),
2061+                               hide = ! views || views.length < 2;
2062+
2063+                       if ( this === view )
2064+                               this.controller.$el.toggleClass( 'hide-' + region, hide );
2065+               },
2066+
2067+               select: function( id ) {
2068+                       var view = this.get( id );
2069+
2070                        if ( ! view )
2071                                return;
2072 
2073@@ -2486,35 +2651,40 @@
2074                }
2075        });
2076 
2077-       media.view.MenuItem = media.View.extend({
2078-               tagName:   'li',
2079-               className: 'media-menu-item',
2080+       /**
2081+        * wp.media.view.RouterItem
2082+        */
2083+       media.view.RouterItem = media.view.MenuItem.extend({
2084+               click: function() {
2085+                       var contentMode = this.options.contentMode;
2086+                       if ( contentMode )
2087+                               this.controller.content.mode( contentMode );
2088+               }
2089+       });
2090 
2091-               events: {
2092-                       'click': 'click'
2093-               },
2094+       /**
2095+        * wp.media.view.Router
2096+        */
2097+       media.view.Router = media.view.Menu.extend({
2098+               tagName:   'div',
2099+               className: 'media-router',
2100+               property:  'contentMode',
2101+               ItemView:  media.view.RouterItem,
2102+               region:    'router',
2103 
2104-               click: function() {
2105-                       var options = this.options;
2106-
2107-                       if ( options.click )
2108-                               options.click.call( this );
2109-                       else if ( options.state )
2110-                               this.controller.setState( options.state );
2111+               initialize: function() {
2112+                       this.controller.on( 'content:render', this.update, this );
2113+                       media.view.Menu.prototype.initialize.apply( this, arguments );
2114                },
2115 
2116-               render: function() {
2117-                       var options = this.options;
2118-
2119-                       if ( options.text )
2120-                               this.$el.text( options.text );
2121-                       else if ( options.html )
2122-                               this.$el.html( options.html );
2123-
2124-                       return this;
2125+               update: function() {
2126+                       var mode = this.controller.content.mode();
2127+                       if ( mode )
2128+                               this.select( mode );
2129                }
2130        });
2131 
2132+
2133        /**
2134         * wp.media.view.Sidebar
2135         */
2136@@ -2531,7 +2701,7 @@
2137                template:  media.template('attachment'),
2138 
2139                events: {
2140-                       'click .attachment-preview':      'toggleSelection',
2141+                       'click .attachment-preview':      'toggleSelectionHandler',
2142                        'change [data-setting]':          'updateSetting',
2143                        'change [data-setting] input':    'updateSetting',
2144                        'change [data-setting] select':   'updateSetting',
2145@@ -2546,8 +2716,6 @@
2146                initialize: function() {
2147                        var selection = this.options.selection;
2148 
2149-                       this.controller = this.options.controller;
2150-
2151                        this.model.on( 'change:sizes change:uploading change:caption change:title', this.render, this );
2152                        this.model.on( 'change:percent', this.progress, this );
2153 
2154@@ -2623,20 +2791,70 @@
2155                                this.$bar.width( this.model.get('percent') + '%' );
2156                },
2157 
2158-               toggleSelection: function( event ) {
2159-                       var selection = this.options.selection,
2160-                               model = this.model;
2161+               toggleSelectionHandler: function( event ) {
2162+                       var method;
2163 
2164+                       if ( event.shiftKey )
2165+                               method = 'between';
2166+                       else if ( event.ctrlKey || event.metaKey )
2167+                               method = 'toggle';
2168+
2169+                       this.toggleSelection({
2170+                               method: method
2171+                       });
2172+               },
2173+
2174+               toggleSelection: function( options ) {
2175+                       var collection = this.collection,
2176+                               selection = this.options.selection,
2177+                               model = this.model,
2178+                               method = options && options.method,
2179+                               single, between, models, singleIndex, modelIndex;
2180+
2181                        if ( ! selection )
2182                                return;
2183 
2184+                       single = selection.single();
2185+                       method = _.isUndefined( method ) ? selection.multiple : method;
2186+
2187+                       // If the `method` is set to `between`, select all models that
2188+                       // exist between the current and the selected model.
2189+                       if ( 'between' === method && single && selection.multiple ) {
2190+                               // If the models are the same, short-circuit.
2191+                               if ( single === model )
2192+                                       return;
2193+
2194+                               singleIndex = collection.indexOf( single );
2195+                               modelIndex  = collection.indexOf( this.model );
2196+
2197+                               if ( singleIndex < modelIndex )
2198+                                       models = collection.models.slice( singleIndex, modelIndex + 1 );
2199+                               else
2200+                                       models = collection.models.slice( modelIndex, singleIndex + 1 );
2201+
2202+                               selection.add( models ).single( model );
2203+                               return;
2204+
2205+                       // If the `method` is set to `toggle`, just flip the selection
2206+                       // status, regardless of whether the model is the single model.
2207+                       } else if ( 'toggle' === method ) {
2208+                               selection[ this.selected() ? 'remove' : 'add' ]( model ).single( model );
2209+                               return;
2210+                       }
2211+
2212+                       if ( method !== 'add' )
2213+                               method = 'reset';
2214+
2215                        if ( this.selected() ) {
2216                                // If the model is the single model, remove it.
2217                                // If it is not the same as the single model,
2218                                // it now becomes the single model.
2219-                               selection[ selection.single() === model ? 'remove' : 'single' ]( model );
2220+                               selection[ single === model ? 'remove' : 'single' ]( model );
2221                        } else {
2222-                               selection.add( model ).single( model );
2223+                               // If the model is not selected, run the `method` on the
2224+                               // selection. By default, we `reset` the selection, but the
2225+                               // `method` can be set to `add` the model to the selection.
2226+                               selection[ method ]( model ).single( model );
2227                        }
2228                },
2229 
2230@@ -2838,7 +3056,6 @@
2231                },
2232 
2233                initialize: function() {
2234-                       this.controller = this.options.controller;
2235                        this.el.id = _.uniqueId('__attachments-view-');
2236 
2237                        _.defaults( this.options, {
2238@@ -3200,12 +3417,9 @@
2239                className: 'attachments-browser',
2240 
2241                initialize: function() {
2242-                       this.controller = this.options.controller;
2243-
2244                        _.defaults( this.options, {
2245                                filters: false,
2246                                search:  true,
2247-                               uploads: false,
2248                                display: false,
2249 
2250                                AttachmentView: media.view.Attachment.Library
2251@@ -3290,7 +3504,9 @@
2252                        this.removeContent();
2253 
2254                        this.uploader = new media.view.UploaderInline({
2255-                               controller: this.controller
2256+                               controller: this.controller,
2257+                               status:     false,
2258+                               message:    l10n.noItemsFound
2259                        });
2260 
2261                        this.views.add( this.uploader );
2262@@ -3322,7 +3538,7 @@
2263 
2264                        this.views.add( sidebar );
2265 
2266-                       if ( options.uploads && this.controller.uploader ) {
2267+                       if ( this.controller.uploader ) {
2268                                sidebar.set( 'uploads', new media.view.UploaderStatus({
2269                                        controller: this.controller,
2270                                        priority:   40
2271@@ -3373,57 +3589,6 @@
2272        });
2273 
2274        /**
2275-        * wp.media.view.SelectionPreview
2276-        */
2277-       media.view.SelectionPreview = media.View.extend({
2278-               tagName:   'div',
2279-               className: 'selection-preview',
2280-               template:  media.template('media-selection-preview'),
2281-
2282-               events: {
2283-                       'click .clear-selection': 'clear'
2284-               },
2285-
2286-               initialize: function() {
2287-                       _.defaults( this.options, {
2288-                               clearable: true
2289-                       });
2290-
2291-                       this.controller = this.options.controller;
2292-                       this.collection.on( 'add change:url remove', this.render, this );
2293-                       this.render();
2294-               },
2295-
2296-               render: function() {
2297-                       var options = _.clone( this.options ),
2298-                               last, sizes, amount;
2299-
2300-                       // If nothing is selected, display nothing.
2301-                       if ( ! this.collection.length ) {
2302-                               this.$el.empty();
2303-                               return this;
2304-                       }
2305-
2306-                       options.count = this.collection.length;
2307-                       last  = this.collection.last();
2308-                       sizes = last.get('sizes');
2309-
2310-                       if ( 'image' === last.get('type') )
2311-                               options.thumbnail = ( sizes && sizes.thumbnail ) ? sizes.thumbnail.url : last.get('url');
2312-                       else
2313-                               options.thumbnail =  last.get('icon');
2314-
2315-                       this.$el.html( this.template( options ) );
2316-                       return this;
2317-               },
2318-
2319-               clear: function( event ) {
2320-                       event.preventDefault();
2321-                       this.collection.reset();
2322-               }
2323-       });
2324-
2325-       /**
2326         * wp.media.view.Selection
2327         */
2328        media.view.Selection = media.View.extend({
2329@@ -3442,7 +3607,6 @@
2330                                clearable: true
2331                        });
2332 
2333-                       this.controller = this.options.controller;
2334                        this.attachments = new media.view.Attachments({
2335                                controller: this.controller,
2336                                collection: this.collection,
2337@@ -3768,12 +3932,10 @@
2338        media.view.Iframe = media.View.extend({
2339                className: 'media-iframe',
2340 
2341-               initialize: function() {
2342-                       this.controller = this.options.controller;
2343-               },
2344-
2345                render: function() {
2346+                       this.views.detach();
2347                        this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
2348+                       this.views.render();
2349                        return this;
2350                }
2351        });
2352@@ -3785,11 +3947,9 @@
2353                className: 'media-embed',
2354 
2355                initialize: function() {
2356-                       this.controller = this.options.controller;
2357-
2358                        this.url = new media.view.EmbedUrl({
2359                                controller: this.controller,
2360-                               model:      this.model
2361+                               model:      this.model.props
2362                        }).render();
2363 
2364                        this._settings = new media.View();
2365@@ -3826,7 +3986,7 @@
2366 
2367                        this.settings( new constructor({
2368                                controller: this.controller,
2369-                               model:      this.model,
2370+                               model:      this.model.props,
2371                                priority:   40
2372                        }) );
2373                }
2374@@ -3846,15 +4006,13 @@
2375                },
2376 
2377                initialize: function() {
2378-                       this.label = this.make( 'span', null, this.options.label || l10n.url );
2379                        this.input = this.make( 'input', {
2380                                type:  'text',
2381                                value: this.model.get('url') || ''
2382                        });
2383 
2384-                       this.$label = $( this.label );
2385                        this.$input = $( this.input );
2386-                       this.$el.append([ this.label, this.input ]);
2387+                       this.$el.append( this.input );
2388 
2389                        this.model.on( 'change:url', this.render, this );
2390                },
2391Index: wp-includes/media.php
2392===================================================================
2393--- wp-includes/media.php       (revision 22995)
2394+++ wp-includes/media.php       (working copy)
2395@@ -1467,9 +1467,11 @@
2396 
2397                // Library
2398                'mediaLibraryTitle'  => __( 'Media Library' ),
2399+               'insertMediaTitle'   => __( 'Insert Media' ),
2400                'createNewGallery'   => __( 'Create a new gallery' ),
2401                'returnToLibrary'    => __( '&#8592; Return to library' ),
2402                'allMediaItems'      => __( 'All media items' ),
2403+               'noItemsFound'       => __( 'No items found.' ),
2404                'insertIntoPost'     => $hier ? __( 'Insert into page' ) : __( 'Insert into post' ),
2405                'uploadedToThisPost' => $hier ? __( 'Uploaded to this page' ) : __( 'Uploaded to this post' ),
2406                'warnDelete' =>      __( "You are about to permanently delete this item.\n  'Cancel' to stop, 'OK' to delete." ),
2407@@ -1489,6 +1491,7 @@
2408                'updateGallery'      => __( 'Update gallery' ),
2409                'continueEditing'    => __( 'Continue editing' ),
2410                'addToGallery'       => __( 'Add to gallery' ),
2411+               'addToGalleryTitle'  => __( 'Add to Gallery' ),
2412                'reverseOrder'       => __( 'Reverse order' ),
2413        );
2414 
2415@@ -1517,6 +1520,8 @@
2416        ?>
2417        <script type="text/html" id="tmpl-media-frame">
2418                <div class="media-frame-menu"></div>
2419+               <div class="media-frame-title"></div>
2420+               <div class="media-frame-router"></div>
2421                <div class="media-frame-content"></div>
2422                <div class="media-frame-toolbar"></div>
2423                <div class="media-frame-uploader"></div>
2424@@ -1524,13 +1529,10 @@
2425 
2426        <script type="text/html" id="tmpl-media-modal">
2427                <div class="media-modal wp-core-ui">
2428-                       <h3 class="media-modal-title">{{ data.title }}</h3>
2429-                       <a class="media-modal-close media-modal-icon" href="#" title="<?php esc_attr_e('Close'); ?>"></a>
2430+                       <a class="media-modal-close" href="#" title="<?php esc_attr_e('Close'); ?>"><span class="media-modal-icon"></span></a>
2431                        <div class="media-modal-content"></div>
2432                </div>
2433-               <div class="media-modal-backdrop">
2434-                       <div></div>
2435-               </div>
2436+               <div class="media-modal-backdrop"></div>
2437        </script>
2438 
2439        <script type="text/html" id="tmpl-uploader-window">
2440@@ -1540,16 +1542,20 @@
2441        </script>
2442 
2443        <script type="text/html" id="tmpl-uploader-inline">
2444-               <div class="uploader-inline-content">
2445+               <# var messageClass = data.message ? 'has-upload-message' : 'no-upload-message'; #>
2446+               <div class="uploader-inline-content {{ messageClass }}">
2447+               <# if ( data.message ) { #>
2448+                       <h3 class="upload-message">{{ data.message }}</h3>
2449+               <# } #>
2450                <?php if ( ! _device_can_upload() ) : ?>
2451-                       <h3><?php _e('The web browser on your device cannot be used to upload files. You may be able to use the <a href="http://wordpress.org/extend/mobile/">native app for your device</a> instead.'); ?></h3>
2452+                       <h3 class="upload-instructions"><?php _e('The web browser on your device cannot be used to upload files. You may be able to use the <a href="http://wordpress.org/extend/mobile/">native app for your device</a> instead.'); ?></h3>
2453                <?php elseif ( is_multisite() && ! is_upload_space_available() ) : ?>
2454-                       <h3><?php _e( 'Upload Limit Exceeded' ); ?></h3>
2455+                       <h3 class="upload-instructions"><?php _e( 'Upload Limit Exceeded' ); ?></h3>
2456                        <?php do_action( 'upload_ui_over_quota' ); ?>
2457 
2458                <?php else : ?>
2459                        <div class="upload-ui">
2460-                               <h3 class="drop-instructions"><?php _e( 'Drop files anywhere to upload' ); ?></h3>
2461+                               <h3 class="upload-instructions drop-instructions"><?php _e( 'Drop files anywhere to upload' ); ?></h3>
2462                                <a href="#" class="browser button button-hero"><?php _e( 'Select Files' ); ?></a>
2463                        </div>
2464 
2465@@ -1744,19 +1750,6 @@
2466                <div class="selection-view"></div>
2467        </script>
2468 
2469-       <script type="text/html" id="tmpl-media-selection-preview">
2470-               <div class="selected-img selected-count-{{ data.count }}">
2471-                       <# if ( data.thumbnail ) { #>
2472-                               <img src="{{ data.thumbnail }}" draggable="false" />
2473-                       <# } #>
2474-
2475-                       <span class="count">{{ data.count }}</span>
2476-               </div>
2477-               <# if ( data.clearable ) { #>
2478-                       <a class="clear-selection" href="#"><?php _e('Clear selection'); ?></a>
2479-               <# } #>
2480-       </script>
2481-
2482        <script type="text/html" id="tmpl-attachment-display-settings">
2483                <h3><?php _e('Attachment Display Settings'); ?></h3>
2484