Make WordPress Core

Changeset 23006


Ignore:
Timestamp:
12/04/2012 01:26:03 AM (12 years ago)
Author:
ryan
Message:

Final round of media UX improvements.

Props koopersmith
fixes #21390 #22502

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/js/custom-background.js

    r22979 r23006  
    3838            });
    3939
    40             frame.toolbar.on( 'activate:select', function() {
    41                 frame.toolbar.view().set({
     40            frame.on( 'toolbar:render:select', function( view ) {
     41                view.set({
    4242                    select: {
    4343                        style: 'primary',
  • trunk/wp-admin/js/custom-header.js

    r22979 r23006  
    2525            });
    2626
    27             frame.toolbar.on( 'activate:select', function() {
    28                 frame.toolbar.view().set({
     27            frame.on( 'toolbar:render:select', function( view ) {
     28                view.set({
    2929                    select: {
    3030                        style: 'primary',
  • trunk/wp-includes/css/media-views-rtl.css

    r22983 r23006  
    22 * Modal
    33 */
    4 .media-modal-title {
    5     left: auto;
    6     right: 0;
    7 }
    8 
    94.media-modal-close {
    105    right: auto;
    11     left: 0;
     6    left: 7px;
    127}
    138
     
    1510 * Toolbar
    1611 */
    17 .media-frame-toolbar > .media-toolbar {
    18     left: 0;
    19     right: 200px;
    20 }
    21 
    22 
    2312.media-toolbar-primary {
    2413    float: left;
     
    9483 */
    9584.media-menu {
    96     left: auto;
    97     right: 0;
    9885    border-right: 0;
    9986    border-left: 1px solid #d9d9d9;
     
    10289
    10390/**
     91 * Router
     92 */
     93.media-router > a {
     94    float: right;
     95    border-right: 0;
     96    border-left: 1px solid #dfdfdf;
     97}
     98.media-router > a:last-child {
     99    border-left: 0;
     100}
     101
     102/**
    104103 * Frame
    105104 */
    106 .media-frame .region-content {
     105.media-frame-menu {
     106    left: auto;
     107    right: 0;
     108}
     109
     110.media-frame-title,
     111.media-frame-router,
     112.media-frame-content,
     113.media-frame-toolbar {
    107114    left: 0;
    108115    right: 200px;
     116}
     117
     118.media-frame.hide-menu .media-frame-title,
     119.media-frame.hide-menu .media-frame-router,
     120.media-frame.hide-menu .media-frame-toolbar,
     121.media-frame.hide-menu .media-frame-content {
     122    right: 0;
     123}
     124
     125.media-frame.hide-menu .media-frame-menu {
     126    left: auto;
     127    right: -200px;
    109128}
    110129
     
    227246
    228247/**
    229  * Selection Preview
    230  */
    231 .selected-img {
    232     float: right;
    233     margin-right: 0;
    234     margin-left: 14px;
    235 }
    236 
    237 .selection-preview img {
    238     float: right;
    239     margin-left: 0;
    240     margin-right: 1px;
    241 }
    242 
    243 .selection-preview .count {
    244     right: auto;
    245     left: 0;
    246 }
    247 
    248 .selection-preview .clear-selection {
    249     float: right;
    250 }
    251 
    252 /**
    253248 * Attachment Details
    254249 */
     
    290285 */
    291286@media only screen and (max-width: 900px) {
    292     .media-frame .region-content,
    293     .media-frame-toolbar > .media-toolbar {
     287    .media-frame-title,
     288    .media-frame-router,
     289    .media-frame-content,
     290    .media-frame-toolbar {
    294291        left: 0;
    295292        right: 140px;
  • trunk/wp-includes/css/media-views.css

    r22998 r23006  
    8181.media-modal {
    8282    position: fixed;
    83     top: 60px;
    84     left: 40px;
    85     right: 40px;
    86     bottom: 40px;
     83    top: 30px;
     84    left: 30px;
     85    right: 30px;
     86    bottom: 30px;
    8787    z-index: 160000;
    8888}
     
    9595    bottom: 0;
    9696    background: #000;
    97     opacity: 0.8;
     97    opacity: 0.7;
    9898    z-index: 159900;
    9999}
    100100
    101 .media-modal-backdrop div,
    102 .uploader-window-content {
    103     position: absolute;
    104     top: 10px;
    105     left: 10px;
    106     right: 10px;
    107     bottom: 10px;
    108     border: 1px dashed rgba( 255, 255, 255, 0.5 );
    109 }
    110 
    111 .media-modal-title {
    112     position: absolute;
    113     top: -40px;
    114     left: 0;
    115     height: 40px;
    116     padding: 0;
    117     margin: 0;
    118 
    119     line-height: 40px;
    120     color: #fff;
    121     font-size: 16px;
    122     font-weight: 200;
    123     text-shadow: 0 0 16px rgba( 0, 0, 0, 0.6 );
    124 }
    125 
    126101.media-modal-close {
    127102    position: absolute;
    128     top: -27px;
    129     right: 0;
     103    top: 7px;
     104    right: 7px;
     105    width: 30px;
     106    height: 30px;
     107    z-index: 1000;
     108}
     109.media-modal-close span {
     110    display: block;
     111    margin: 8px auto 0;
     112    width: 15px;
    130113    height: 15px;
    131     width: 15px;
    132     background-position: -80px 0;
     114    background-position: -100px 0;
    133115}
    134116
     
    164146    padding: 0 16px;
    165147    border: 0 solid #dfdfdf;
    166 }
    167 
    168 .media-frame-toolbar > .media-toolbar {
    169     top: auto;
    170     left: 200px;
    171     bottom: 0;
    172     border-width: 1px 0 0 0;
    173     box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 );
    174 }
    175 
    176 .hide-toolbar .media-frame-toolbar > .media-toolbar {
    177     bottom: -61px;
    178148}
    179149
     
    237207    display: block;
    238208    width: 100%;
    239 }
    240 
    241 .media-sidebar .selection-preview {
    242     display: block;
    243     padding-top: 5px;
    244209}
    245210
     
    362327    top: 0;
    363328    left: 0;
    364     bottom: 0;
    365     width: 199px;
     329    right: 0;
     330    bottom: 0;
    366331    margin: 0;
    367332    padding: 16px 0;
    368     z-index: 200;
    369333    border-right: 1px solid #d9d9d9;
    370334    box-shadow: inset -6px 0 6px -6px rgba( 0, 0, 0, 0.2 );
     
    375339}
    376340
    377 .media-menu li {
     341.media-menu > a {
     342    display: block;
    378343    position: relative;
    379344    padding: 4px 20px;
     
    383348    color: #21759B;
    384349    text-shadow: 0 1px 0 #fff;
    385 }
    386 
    387 .media-menu-item {
    388     cursor: pointer;
    389 }
    390 
    391 .media-menu li:hover {
     350    text-decoration: none;
     351}
     352
     353.media-menu > a:hover {
     354    color: #21759B;
    392355    background: rgba( 0, 0, 0, 0.04 );
     356}
     357
     358.media-menu > a:active {
     359    outline: none;
    393360}
    394361
     
    408375
    409376/**
     377 * Menu
     378 */
     379.media-router {
     380    position: relative;
     381    padding: 0 6px;
     382    margin: 0;
     383    clear: both;
     384    -webkit-user-select: none;
     385    -moz-user-select: none;
     386    -ms-user-select: none;
     387    user-select: none;
     388}
     389
     390.media-router > a {
     391    position: relative;
     392    float: left;
     393    padding: 2px 10px;
     394    margin: 0;
     395    height: 18px;
     396    line-height: 18px;
     397    font-size: 14px;
     398    border-right: 1px solid #dfdfdf;
     399    text-shadow: 0 1px 0 #fff;
     400    text-decoration: none;
     401}
     402
     403.media-router > a:last-child {
     404    border-right: 0;
     405}
     406
     407.media-router > a:active,
     408.media-router > a:focus {
     409    outline: none;
     410}
     411
     412.media-router .active,
     413.media-router .active:hover {
     414    color: #333;
     415}
     416
     417.media-router .active:after {
     418    content: '';
     419    display: block;
     420    margin: -100px auto 0;
     421    width: 7px;
     422    height: 7px;
     423    background: #fff;
     424    box-shadow: 1px 1px 1px rgba( 0, 0, 0, 0.2 );
     425    z-index: 300;
     426
     427    -webkit-transform: rotate( 45deg ) translate( 75px, 75px );
     428    -moz-transform:    rotate( 45deg ) translate( 75px, 75px );
     429    -ms-transform:     rotate( 45deg ) translate( 75px, 75px );
     430    -o-transform:      rotate( 45deg ) translate( 75px, 75px );
     431    transform:         rotate( 45deg ) translate( 75px, 75px );
     432}
     433
     434/**
    410435 * Frame
    411436 */
     
    419444}
    420445
    421 .media-frame .region-content {
    422     position: absolute;
    423     top: 0;
     446.media-frame-menu {
     447    position: absolute;
     448    top: 0;
     449    left: 0;
     450    bottom: 0;
     451    width: 199px;
     452    z-index: 150;
     453}
     454
     455.media-frame-title {
     456    position: absolute;
     457    top: 0;
     458    left: 200px;
     459    right: 0;
     460    height: 45px;
     461    z-index: 200;
     462}
     463
     464.media-frame-router {
     465    position: absolute;
     466    top: 45px;
     467    left: 200px;
     468    right: 0;
     469    height: 30px;
     470    z-index: 200;
     471    border-bottom: 1px solid #dfdfdf;
     472    box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
     473}
     474
     475.media-frame-content {
     476    position: absolute;
     477    top: 75px;
    424478    left: 200px;
    425479    right: 0;
     
    431485}
    432486
    433 .media-frame.hide-toolbar .region-content {
    434     bottom: 0;
     487.media-frame-toolbar {
     488    position: absolute;
     489    left: 200px;
     490    right: 0;
     491    bottom: 0;
     492    height: 60px;
     493    z-index: 100;
     494    border: 0 solid #dfdfdf;
     495    border-width: 1px 0 0 0;
     496    box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 );
     497}
     498
     499.media-frame.hide-menu .media-frame-title,
     500.media-frame.hide-menu .media-frame-router,
     501.media-frame.hide-menu .media-frame-toolbar,
     502.media-frame.hide-menu .media-frame-content {
     503    left: 0;
     504}
     505
     506.media-frame.hide-menu .media-frame-menu {
     507    left: -200px;
     508}
     509
     510.media-frame.hide-toolbar .media-frame-content {
     511    bottom: 0;
     512}
     513
     514.media-frame.hide-toolbar .media-frame-toolbar {
     515    bottom: -61px;
     516}
     517
     518.media-frame.hide-router .media-frame-content {
     519    top: 45px;
     520}
     521
     522.media-frame.hide-router .media-frame-router {
     523    display: none;
     524}
     525
     526.media-frame.hide-router .media-frame-title {
     527    border-bottom: 1px solid #dfdfdf;
     528    box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    435529}
    436530
    437531.media-frame .media-toolbar .add-to-gallery {
    438532    display: none;
     533}
     534
     535.media-frame-title h1 {
     536    padding: 0 16px;
     537    font-size: 22px;
     538    font-weight: 200;
     539    line-height: 45px;
     540    margin: 0;
    439541}
    440542
     
    722824 */
    723825.media-frame .attachments-browser {
     826    position: relative;
     827    width: 100%;
     828    height: 100%;
    724829    overflow: hidden;
    725830}
     
    9041009
    9051010.uploader-window-content {
    906     border-color: #fff;
     1011    position: absolute;
     1012    top: 10px;
     1013    left: 10px;
     1014    right: 10px;
     1015    bottom: 10px;
     1016    border: 1px dashed #fff;
    9071017}
    9081018
     
    9571067}
    9581068
     1069.uploader-inline .has-upload-message .upload-ui {
     1070    margin: 0 0 4em;
     1071}
     1072
    9591073.uploader-inline h3 {
    9601074    font-size: 20px;
     
    9621076    font-weight: 200;
    9631077    margin-bottom: 1.6em;
     1078}
     1079
     1080.uploader-inline .has-upload-message .upload-instructions {
     1081    font-size: 14px;
     1082    color: #464646;
     1083    font-weight: normal;
    9641084}
    9651085
     
    10591179}
    10601180
    1061 .media-selection .attachment img {
     1181.media-selection .attachment .icon {
    10621182    width: 50%;
    10631183}
     
    10961216.media-selection .attachment .filename {
    10971217    display: none;
    1098 }
    1099 
    1100 /**
    1101  * Selection Preview
    1102  */
    1103 .selection-preview {
    1104     position: relative;
    1105     height: 60px;
    1106     overflow: hidden;
    1107 }
    1108 
    1109 .selected-img {
    1110     float: left;
    1111     position: relative;
    1112     margin-right: 14px;
    1113 }
    1114 
    1115 .selection-preview img {
    1116     max-width: 40px;
    1117     max-height: 40px;
    1118     float: left;
    1119     margin-top: 6px;
    1120     margin-left: 1px;
    1121     border: 2px solid white;
    1122     box-shadow:
    1123         0 0 0 1px #ccc,
    1124         3px 3px 0 0 #fff,
    1125         3px 3px 0 1px #ccc,
    1126         6px 6px 0 0 #fff,
    1127         6px 6px 0 1px #ccc;
    1128 }
    1129 
    1130 .selection-preview .selected-count-1 img {
    1131     margin-top: 8px;
    1132     box-shadow: 0 0 0 1px #ccc;
    1133 }
    1134 
    1135 .selection-preview .selected-count-2 img {
    1136     margin-top: 7px;
    1137     box-shadow:
    1138         0 0 0 1px #ccc,
    1139         3px 3px 0 0 #fff,
    1140         3px 3px 0 1px #ccc;
    1141 }
    1142 
    1143 .selection-preview .count {
    1144     position: absolute;
    1145     bottom: 0;
    1146     right: 0;
    1147     height: 16px;
    1148     min-width: 8px;
    1149     padding: 0 4px;
    1150     font-size: 12px;
    1151     text-align: center;
    1152     font-weight: bold;
    1153     color: #999;
    1154     background: #fff;
    1155     box-shadow: -1px -1px 2px -1px rgba( 0, 0, 0, 0.2 );
    1156 }
    1157 
    1158 .selection-preview .clear-selection {
    1159     float: left;
    1160     line-height: 60px;
    11611218}
    11621219
     
    12981355    display: block;
    12991356    position: relative;
    1300     height: 75px;
    1301     padding: 16px 16px;
     1357    height: 40px;
     1358    padding: 0 16px 16px;
    13021359    margin: 0;
    1303     z-index: 50;
     1360    z-index: 250;
     1361    background: #fff;
    13041362    border-bottom: 1px solid #dfdfdf;
    13051363    box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    13061364    font-size: 18px;
    13071365    font-weight: 200;
    1308 }
    1309 
    1310 .embed-url span {
    1311     display: block;
    1312     padding: 4px 0 6px 2px;
    13131366}
    13141367
     
    13251378    position: absolute;
    13261379    background: #f5f5f5;
    1327     top: 108px;
     1380    top: 57px;
    13281381    left: 0;
    13291382    right: 0;
     
    13891442 */
    13901443@media only screen and (max-width: 900px) {
    1391     .media-modal {
    1392         bottom: 20px;
    1393         left: 20px;
    1394         right: 20px;
    1395         top: 40px;
    1396     }
    1397 
    1398     .media-modal-title {
    1399         height: 30px;
    1400         line-height: 30px;
    1401         top: -30px;
    1402     }
    1403 
    1404     .media-modal-close {
    1405         top: -23px;
    1406     }
    1407 
    1408     .media-modal-backdrop div,
    1409     .uploader-window-content {
    1410         top: 5px;
    1411         left: 5px;
    1412         right: 5px;
    1413         bottom: 5px;
    1414     }
    1415 
    1416     .media-menu {
     1444    .media-frame-menu {
    14171445        width: 139px;
    14181446    }
    14191447
    1420     .media-menu li {
     1448    .media-menu > a {
    14211449        padding: 4px 10px;
    14221450    }
    14231451
    1424     .media-frame .region-content,
    1425     .media-frame-toolbar > .media-toolbar {
     1452    .media-frame-title,
     1453    .media-frame-router,
     1454    .media-frame-content,
     1455    .media-frame-toolbar {
    14261456        left: 140px;
    14271457    }
  • trunk/wp-includes/js/media-editor.js

    r22994 r23006  
    385385            workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
    386386                frame:    'post',
    387                 state:    'upload',
     387                state:    'insert',
    388388                title:    wp.media.view.l10n.addMedia,
    389389                multiple: true
     
    409409
    410410            workflow.state('embed').on( 'select', function() {
    411                 var embed = workflow.state().toJSON();
     411                var state = workflow.state(),
     412                    type = state.get('type'),
     413                    embed = state.props.toJSON();
    412414
    413415                embed.url = embed.url || '';
    414416
    415                 if ( 'link' === embed.type ) {
     417                if ( 'link' === type ) {
    416418                    _.defaults( embed, {
    417419                        title:   embed.url,
     
    421423                    this.send.link( embed );
    422424
    423                 } else if ( 'image' === embed.type ) {
     425                } else if ( 'image' === type ) {
    424426                    _.defaults( embed, {
    425427                        title:   embed.url,
  • trunk/wp-includes/js/media-views.js

    r22994 r23006  
    6969     */
    7070    media.controller.Region = function( options ) {
    71         _.extend( this, _.pick( options || {}, 'id', 'controller', 'selector' ) );
    72 
    73         this.on( 'activate:empty', this.empty, this );
    74         this.mode('empty');
     71        _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
    7572    };
    7673
     
    7875    media.controller.Region.extend = Backbone.Model.extend;
    7976
    80     _.extend( media.controller.Region.prototype, Backbone.Events, {
    81         trigger: (function() {
    82             var eventSplitter = /\s+/,
    83                 trigger = Backbone.Events.trigger;
    84 
    85             return function( events ) {
    86                 var mode = ':' + this._mode,
    87                     modeEvents = events.split( eventSplitter ).join( mode ) + mode;
    88 
    89                 trigger.apply( this, arguments );
    90                 trigger.apply( this, [ modeEvents ].concat( _.rest( arguments ) ) );
     77    _.extend( media.controller.Region.prototype, {
     78        mode: function( mode ) {
     79            if ( ! mode )
     80                return this._mode;
     81
     82            // Bail if we're trying to change to the current mode.
     83            if ( mode === this._mode )
    9184                return this;
    92             };
    93         }()),
    94 
    95         mode: function( mode ) {
    96             if ( mode ) {
    97                 this.trigger( 'deactivate', this );
    98                 this._mode = mode;
    99                 return this.trigger( 'activate', this );
    100             }
    101             return this._mode;
    102         },
    103 
    104         view: function( view ) {
    105             var previous = this._view,
    106                 mode = this._mode,
    107                 id = this.id;
    108 
    109             // If no argument is provided, return the current view.
    110             if ( ! view )
    111                 return previous;
    112 
    113             // If we're attempting to switch to the current view, bail.
    114             if ( view === previous )
     85
     86            this.trigger('deactivate');
     87            this._mode = mode;
     88            this.render( mode );
     89            this.trigger('activate');
     90            return this;
     91        },
     92
     93        render: function( mode ) {
     94            // If no mode is provided, just re-render the current mode.
     95            // If the provided mode isn't active, perform a full switch.
     96            if ( mode && mode !== this._mode )
     97                return this.mode( mode );
     98
     99            var set = { view: null },
     100                view;
     101
     102            this.trigger( 'create', set );
     103            view = set.view;
     104            this.trigger( 'render', view );
     105            if ( view )
     106                this.set( view );
     107            return this;
     108        },
     109
     110        get: function() {
     111            return this.view.views.first( this.selector );
     112        },
     113
     114        set: function( views, options ) {
     115            if ( options )
     116                options.add = false;
     117            return this.view.views.set( this.selector, views, options );
     118        },
     119
     120        trigger: function( event ) {
     121            var base;
     122            if ( ! this._mode )
    115123                return;
    116124
    117             // Add classes to the new view.
    118             if ( id )
    119                 view.$el.addClass( 'region-' + id );
    120 
    121             if ( mode )
    122                 view.$el.addClass( 'mode-' + mode );
    123 
    124             this.controller.views.set( this.selector, view );
    125             this._view = view;
    126         },
    127 
    128         empty: function() {
    129             this.view( new media.View() );
     125            var args = _.toArray( arguments );
     126            base = this.id + ':' + event;
     127
     128            // Trigger `region:action:mode` event.
     129            args[0] = base + ':' + this._mode;
     130            this.view.trigger.apply( this.view, args );
     131
     132            // Trigger `region:action` event.
     133            args[0] = base;
     134            this.view.trigger.apply( this.view, args );
     135            return this;
    130136        }
    131137    });
     
    209215    // ---------------------------
    210216    media.controller.State = Backbone.Model.extend({
    211         initialize: function() {
    212             this.on( 'activate', this._activate, this );
     217        constructor: function() {
     218            this.on( 'activate', this._preActivate, this );
    213219            this.on( 'activate', this.activate, this );
     220            this.on( 'activate', this._postActivate, this );
    214221            this.on( 'deactivate', this._deactivate, this );
    215222            this.on( 'deactivate', this.deactivate, this );
    216223            this.on( 'reset', this.reset, this );
    217         },
    218 
     224            this.on( 'ready', this._ready, this );
     225            this.on( 'ready', this.ready, this );
     226
     227            this.on( 'change:menu', this._updateMenu, this );
     228
     229            Backbone.Model.apply( this, arguments );
     230        },
     231
     232        ready: function() {},
    219233        activate: function() {},
    220         _activate: function() {
     234        deactivate: function() {},
     235        reset: function() {},
     236
     237        _ready: function() {
     238            this._updateMenu();
     239        },
     240
     241        _preActivate: function() {
    221242            this.active = true;
    222 
    223             this.menu();
    224             this.toolbar();
    225             this.content();
    226         },
    227 
    228         deactivate: function() {},
     243        },
     244
     245        _postActivate: function() {
     246            this.on( 'change:menu', this._menu, this );
     247            this.on( 'change:titleMode', this._title, this );
     248            this.on( 'change:content', this._content, this );
     249            this.on( 'change:toolbar', this._toolbar, this );
     250
     251            this.frame.on( 'title:render:default', this._renderTitle, this );
     252
     253            this._title();
     254            this._menu();
     255            this._toolbar();
     256            this._content();
     257            this._router();
     258        },
     259
     260
    229261        _deactivate: function() {
    230262            this.active = false;
    231         },
    232 
    233         reset: function() {},
    234 
    235         menu: function() {
     263
     264            this.frame.off( 'title:render:default', this._renderTitle, this );
     265
     266            this.off( 'change:menu', this._menu, this );
     267            this.off( 'change:titleMode', this._title, this );
     268            this.off( 'change:content', this._content, this );
     269            this.off( 'change:toolbar', this._toolbar, this );
     270        },
     271
     272        _title: function() {
     273            this.frame.title.render( this.get('titleMode') || 'default' );
     274        },
     275
     276        _renderTitle: function( view ) {
     277            view.$el.text( this.get('title') || '' );
     278        },
     279
     280        _router: function() {
     281            var router = this.frame.router,
     282                mode = this.get('router'),
     283                view;
     284
     285            this.frame.$el.toggleClass( 'hide-router', ! mode );
     286            if ( ! mode )
     287                return;
     288
     289            this.frame.router.render( mode );
     290
     291            view = router.get();
     292            if ( view.select )
     293                view.select( this.frame.content.mode() );
     294        },
     295
     296        _menu: function() {
    236297            var menu = this.frame.menu,
    237298                mode = this.get('menu'),
     
    241302                return;
    242303
    243             if ( menu.mode() !== mode )
    244                 menu.mode( mode );
    245 
    246             view = menu.view();
     304            menu.mode( mode );
     305
     306            view = menu.get();
    247307            if ( view.select )
    248308                view.select( this.id );
     309        },
     310
     311        _updateMenu: function() {
     312            var previous = this.previous('menu'),
     313                menu = this.get('menu');
     314
     315            if ( previous )
     316                this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
     317
     318            if ( menu )
     319                this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
     320        },
     321
     322        _renderMenu: function( view ) {
     323            var menuItem = this.get('menuItem'),
     324                title = this.get('title'),
     325                priority = this.get('priority');
     326
     327            if ( ! menuItem && title ) {
     328                menuItem = { text: title };
     329
     330                if ( priority )
     331                    menuItem.priority = priority;
     332            }
     333
     334            if ( ! menuItem )
     335                return;
     336
     337            view.set( this.id, menuItem );
    249338        }
    250339    });
    251340
    252341    _.each(['toolbar','content'], function( region ) {
    253         media.controller.State.prototype[ region ] = function() {
     342        media.controller.State.prototype[ '_' + region ] = function() {
    254343            var mode = this.get( region );
    255344            if ( mode )
    256                 this.frame[ region ].mode( mode );
     345                this.frame[ region ].render( mode );
    257346        };
    258347    });
     
    263352        defaults: {
    264353            id:         'library',
    265             multiple:   false,
     354            multiple:   false, // false, 'add', 'reset'
    266355            describe:   false,
    267             toolbar:    'main-attachments',
     356            toolbar:    'select',
    268357            sidebar:    'settings',
    269             content:    'browse',
     358            content:    'upload',
     359            router:     'browse',
    270360            searchable: true,
    271361            filterable: false,
    272             uploads:    true,
    273             sortable:   true
     362            sortable:   true,
     363            title:      l10n.mediaLibraryTitle,
     364
     365            // Uses a user setting to override the content mode.
     366            contentUserSetting: true,
     367
     368            // Sync the selection from the last state when 'multiple' matches.
     369            syncLastSelection: true
    274370        },
    275371
     
    291387
    292388            this.resetDisplays();
    293 
    294             media.controller.State.prototype.initialize.apply( this, arguments );
    295389        },
    296390
    297391        activate: function() {
    298392            var library = this.get('library'),
    299                 selection = this.get('selection');
     393                selection = this.get('selection'),
     394                mode;
     395
     396            if ( this.get('syncLastSelection') ) {
     397                this.getLastSelection();
     398            }
    300399
    301400            this._excludeStateLibrary();
     
    304403            this.on( 'change:excludeState', this._excludeState, this );
    305404
    306             // If we're in a workflow that supports multiple attachments,
    307             // automatically select any uploading attachments.
    308             if ( this.get('multiple') )
    309                 wp.Uploader.queue.on( 'add', this.selectUpload, this );
     405            wp.Uploader.queue.on( 'add', this.uploading, this );
    310406
    311407            selection.on( 'add remove reset', this.refreshSelection, this );
    312408
    313             this.refresh();
    314409            this.on( 'insert', this._insertDisplaySettings, this );
     410
     411            if ( this.get('contentUserSetting') ) {
     412                this.frame.on( 'content:activate', this.saveContentMode, this );
     413                this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
     414            }
    315415        },
    316416
    317417        deactivate: function() {
     418            this.frame.off( 'content:activate', this.saveContentMode, this );
     419
    318420            // Unbind all event handlers that use this state as the context
    319421            // from the selection.
     
    331433            this.get('selection').reset();
    332434            this.resetDisplays();
    333         },
    334 
    335         refresh: function() {
    336             this.content();
    337             this.refreshSelection();
     435            this.refreshContent();
    338436        },
    339437
     
    372470        },
    373471
     472        getLastSelection: function() {
     473            var selection = this.get('selection'),
     474                lastState = this.frame.lastState(),
     475                lastSelection = lastState && lastState.get('selection'),
     476                lastMultiple, thisMultiple;
     477
     478            if ( ! lastSelection )
     479                return;
     480
     481            // We don't care about the method of multiple selection the
     482            // selections use, just that they both support (or don't support)
     483            // multiple selection.
     484            lastMultiple = !! lastSelection.multiple;
     485            thisMultiple = !! selection.multiple;
     486
     487            if ( lastMultiple !== thisMultiple )
     488                return;
     489
     490            selection.reset( lastSelection.toArray() ).single( lastSelection.single() );
     491        },
     492
    374493        refreshSelection: function() {
     494            this.frame.toolbar.get().refresh();
     495            this.trigger( 'refresh:selection', this, this.get('selection') );
     496            this.refreshContent();
     497        },
     498
     499        refreshContent: function() {
    375500            var selection = this.get('selection'),
    376                 mode = this.frame.content.mode();
    377 
    378             this.frame.toolbar.view().refresh();
    379             this.trigger( 'refresh:selection', this, selection );
    380 
    381             if ( ! selection.length && 'browse' !== mode && 'upload' !== mode )
    382                 this.content();
    383         },
    384 
    385         selectUpload: function( attachment ) {
    386             this.get('selection').add( attachment );
     501                frame = this.frame,
     502                router = frame.router.get(),
     503                mode = frame.content.mode();
     504
     505            if ( this.active&& ! selection.length && ! router.get( mode ) )
     506                this.frame.content.render( this.get('content') );
     507        },
     508
     509        uploading: function( attachment ) {
     510            var content = this.frame.content;
     511
     512            // If the uploader was selected, navigate to the browser.
     513            if ( 'upload' === content.mode() )
     514                this.frame.content.mode('browse');
     515
     516            // If we're in a workflow that supports multiple attachments,
     517            // automatically select any uploading attachments.
     518            if ( this.get('multiple') )
     519                this.get('selection').add( attachment );
     520        },
     521
     522        saveContentMode: function() {
     523            // Only track the browse router on library states.
     524            if ( 'browse' !== this.get('router') )
     525                return;
     526
     527            var mode = this.frame.content.mode(),
     528                view = this.frame.router.get();
     529
     530            if ( view && view.get( mode ) )
     531                setUserSetting( 'libraryContent', mode );
    387532        },
    388533
     
    449594    });
    450595
    451 
    452     // wp.media.controller.Upload
    453     // ---------------------------
    454     media.controller.Upload = media.controller.State.extend({
    455         defaults: _.defaults({
    456             id:      'upload',
    457             content: 'upload',
    458             toolbar: 'empty',
    459             uploads: true,
    460 
    461             // The state to navigate to when files are uploading.
    462             libraryState: 'library'
    463         }, media.controller.State.prototype.defaults ),
    464 
    465         initialize: function() {
    466             media.controller.State.prototype.initialize.apply( this, arguments );
    467         },
    468 
    469         activate: function() {
    470             wp.Uploader.queue.on( 'add', this.uploading, this );
    471             media.controller.State.prototype.activate.apply( this, arguments );
    472         },
    473 
    474         deactivate: function() {
    475             wp.Uploader.queue.off( null, null, this );
    476             media.controller.State.prototype.deactivate.apply( this, arguments );
    477         },
    478 
    479         uploading: function( attachment ) {
    480             var library = this.get('libraryState');
    481 
    482             this.frame.state( library ).get('selection').add( attachment );
    483             this.frame.setState( library );
    484         }
    485     });
    486 
    487     // wp.media.controller.Gallery
    488     // ---------------------------
    489     media.controller.Gallery = media.controller.Library.extend({
     596    // wp.media.controller.GalleryEdit
     597    // -------------------------------
     598    media.controller.GalleryEdit = media.controller.Library.extend({
    490599        defaults: {
    491600            id:         'gallery-edit',
     
    497606            searchable: false,
    498607            toolbar:    'gallery-edit',
    499             content:    'browse'
     608            content:    'browse',
     609            title:      l10n.editGalleryTitle,
     610            priority:   60,
     611            dragInfo:   true
    500612        },
    501613
     
    520632            this.get('library').observe( wp.Uploader.queue );
    521633
    522             this.frame.content.on( 'activate:browse', this.gallerySettings, this );
     634            this.frame.on( 'content:render:browse', this.gallerySettings, this );
    523635
    524636            media.controller.Library.prototype.activate.apply( this, arguments );
     
    529641            this.get('library').unobserve( wp.Uploader.queue );
    530642
    531             this.frame.content.off( null, null, this );
     643            this.frame.off( 'content:render:browse', this.gallerySettings, this );
     644
    532645            media.controller.Library.prototype.deactivate.apply( this, arguments );
    533646        },
    534647
    535         gallerySettings: function() {
    536             var library = this.get('library'),
    537                 browser;
    538 
    539             if ( ! library )
     648        gallerySettings: function( browser ) {
     649            var library = this.get('library');
     650
     651            if ( ! library || ! browser )
    540652                return;
    541653
    542654            library.gallery = library.gallery || new Backbone.Model();
    543 
    544             browser = this.frame.content.view();
    545655
    546656            browser.sidebar.set({
     
    571681            multiple:   false,
    572682            menu:       'main',
    573             toolbar:    'featured-image'
     683            toolbar:    'featured-image',
     684            title:      l10n.featuredImageTitle,
     685            priority:   60
    574686        }, media.controller.Library.prototype.defaults ),
    575687
     
    630742            content: 'embed',
    631743            toolbar: 'main-embed',
    632             type:    'link'
     744            type:    'link',
     745
     746            title:    l10n.fromUrlTitle,
     747            priority: 120
    633748        },
    634749
     
    638753        initialize: function() {
    639754            this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
    640             this.on( 'change:url', this.debouncedScan, this );
     755            this.props = new Backbone.Model({ url: '' });
     756            this.props.on( 'change:url', this.debouncedScan, this );
    641757            this.on( 'scan', this.scanImage, this );
    642             media.controller.State.prototype.initialize.apply( this, arguments );
    643758        },
    644759
     
    653768            var frame = this.frame,
    654769                state = this,
    655                 url = this.get('url'),
     770                url = this.props.get('url'),
    656771                image = new Image();
    657772
    658773            image.onload = function() {
    659                 if ( state !== frame.state() || url !== state.get('url') )
     774                if ( state !== frame.state() || url !== state.props.get('url') )
    660775                    return;
    661776
     
    671786
    672787        reset: function() {
    673             _.each( _.difference( _.keys( this.attributes ), _.keys( this.defaults ) ), function( key ) {
    674                 this.unset( key );
    675             }, this );
    676 
    677             this.set( 'url', '' );
     788            this.props = new Backbone.Model({ url: '' });
    678789
    679790            if ( this.id === this.frame.state().id )
    680                 this.frame.toolbar.view().refresh();
     791                this.frame.toolbar.get().refresh();
    681792        }
    682793    });
     
    10231134        Views: media.Views,
    10241135
    1025         constructor: function() {
     1136        constructor: function( options ) {
    10261137            this.views = new this.Views( this, this.views );
    10271138            this.on( 'ready', this.ready, this );
     1139
     1140            if ( options && options.controller )
     1141                this.controller = options.controller;
     1142
    10281143            Backbone.View.apply( this, arguments );
    10291144        },
     
    10981213            _.each( this.regions, function( region ) {
    10991214                this[ region ] = new media.controller.Region({
    1100                     controller: this,
    1101                     id:         region,
    1102                     selector:   '.media-frame-' + region
     1215                    view:    this,
     1216                    id:       region,
     1217                    selector: '.media-frame-' + region
    11031218                });
    11041219            }, this );
     
    11141229            this.states.on( 'add', function( model ) {
    11151230                model.frame = this;
     1231                model.trigger('ready');
    11161232            }, this );
    11171233        },
     
    11321248        className: 'media-frame',
    11331249        template:  media.template('media-frame'),
    1134         regions:   ['menu','content','toolbar'],
     1250        regions:   ['menu','title','content','toolbar','router'],
    11351251
    11361252        initialize: function() {
     
    11741290
    11751291            this.on( 'attach', _.bind( this.views.ready, this.views ), this );
     1292
     1293            // Bind default title creation.
     1294            this.on( 'title:create:default', this.createTitle, this );
     1295            this.title.mode('default');
    11761296        },
    11771297
     
    11821302
    11831303            return media.view.Frame.prototype.render.apply( this, arguments );
     1304        },
     1305
     1306        createTitle: function( title ) {
     1307            title.view = new media.View({
     1308                controller: this,
     1309                tagName: 'h1'
     1310            });
     1311        },
     1312
     1313        createMenu: function( menu ) {
     1314            menu.view = new media.view.Menu({
     1315                controller: this
     1316            });
     1317        },
     1318
     1319        createToolbar: function( toolbar ) {
     1320            menu.view = new media.view.Toolbar({
     1321                controller: this
     1322            });
     1323        },
     1324
     1325        createRouter: function( router ) {
     1326            router.view = new media.view.Router({
     1327                controller: this
     1328            });
    11841329        },
    11851330
     
    12091354            }, this );
    12101355
    1211             this.content.on( 'activate:iframe', this.iframeContent, this );
    1212             this.menu.on( 'activate:main', this.iframeMenu, this );
     1356            this.on( 'content:create:iframe', this.iframeContent, this );
     1357            this.on( 'menu:render:main', this.iframeMenu, this );
    12131358            this.on( 'open', this.hijackThickbox, this );
    12141359            this.on( 'close', this.restoreThickbox, this );
    12151360        },
    12161361
    1217         iframeContent: function() {
     1362        iframeContent: function( content ) {
    12181363            this.$el.addClass('hide-toolbar');
    1219             this.content.view( new media.view.Iframe({
     1364            content.view = new media.view.Iframe({
    12201365                controller: this
    1221             }).render() );
    1222         },
    1223 
    1224         iframeMenu: function() {
     1366            });
     1367        },
     1368
     1369        iframeMenu: function( view ) {
    12251370            var views = {};
     1371
     1372            if ( ! view )
     1373                return;
    12261374
    12271375            _.each( media.view.settings.tabs, function( title, id ) {
     
    12321380            }, this );
    12331381
    1234             this.menu.view().set( views );
     1382            view.set( views );
    12351383        },
    12361384
     
    13061454                    selection: options.selection,
    13071455                    library:   media.query( options.library ),
    1308                     multiple:  this.options.multiple,
     1456                    multiple:  options.multiple,
    13091457                    menu:      'main',
    1310                     toolbar:   'select'
    1311                 }),
    1312 
    1313                 new media.controller.Upload({
    1314                     menu: 'main'
     1458                    title:     options.title,
     1459                    priority:  20
    13151460                })
    13161461            ]);
     
    13181463
    13191464        bindHandlers: function() {
    1320             this.menu.on( 'activate:main', this.mainMenu, this );
    1321             this.content.on( 'activate:browse', this.browseContent, this );
    1322             this.content.on( 'activate:upload', this.uploadContent, this );
    1323             this.toolbar.on( 'activate:select', this.selectToolbar, this );
     1465            this.on( 'menu:create:main', this.createMenu, this );
     1466            this.on( 'router:create:browse', this.createRouter, this );
     1467            this.on( 'router:render:browse', this.browseRouter, this );
     1468            this.on( 'content:create:browse', this.browseContent, this );
     1469            this.on( 'content:render:upload', this.uploadContent, this );
     1470            this.on( 'toolbar:create:select', this.createSelectToolbar, this );
    13241471
    13251472            this.on( 'refresh:selection', this.refreshSelectToolbar, this );
    13261473        },
    13271474
    1328         mainMenu: function( options ) {
    1329             this.menu.view( new media.view.Menu({
    1330                 controller: this,
    1331                 silent:     options && options.silent,
    1332 
    1333                 views: {
    1334                     upload: {
    1335                         text: l10n.uploadFilesTitle,
    1336                         priority: 20
    1337                     },
    1338                     library: {
    1339                         text: l10n.mediaLibraryTitle,
    1340                         priority: 40
    1341                     }
     1475        // Routers
     1476        browseRouter: function( view ) {
     1477            view.set({
     1478                upload: {
     1479                    text:     l10n.uploadFilesTitle,
     1480                    priority: 20
     1481                },
     1482                browse: {
     1483                    text:     l10n.mediaLibraryTitle,
     1484                    priority: 40
    13421485                }
    1343             }) );
     1486            });
    13441487        },
    13451488
    13461489        // Content
    1347         browseContent: function() {
     1490        browseContent: function( content ) {
    13481491            var state = this.state();
    13491492
     
    13511494
    13521495            // Browse our library of attachments.
    1353             this.content.view( new media.view.AttachmentsBrowser({
     1496            content.view = new media.view.AttachmentsBrowser({
    13541497                controller: this,
    13551498                collection: state.get('library'),
     
    13581501                sortable:   state.get('sortable'),
    13591502                search:     state.get('searchable'),
    1360                 uploads:    state.get('uploads'),
    13611503                filters:    state.get('filterable'),
    13621504                display:    state.get('displaySettings'),
     1505                dragInfo:   state.get('dragInfo'),
    13631506
    13641507                AttachmentView: state.get('AttachmentView')
    1365             }) );
     1508            });
    13661509        },
    13671510
    13681511        uploadContent: function() {
    1369             this.$el.addClass('hide-toolbar');
    1370 
    1371             this.content.view( new media.view.UploaderInline({
     1512            this.$el.removeClass('hide-toolbar');
     1513            this.content.set( new media.view.UploaderInline({
    13721514                controller: this
    13731515            }) );
     
    13751517
    13761518        // Toolbars
    1377         selectToolbar: function( options ) {
     1519        createSelectToolbar: function( toolbar, options ) {
    13781520            options = _.defaults( options || {}, {
    13791521                event:  'select',
     
    13821524            });
    13831525
    1384             this.toolbar.view( new media.view.Toolbar({
     1526            toolbar.view = new media.view.Toolbar({
    13851527                controller: this,
    13861528                silent:     options.silent,
     
    14031545                    }
    14041546                }
    1405             }) );
     1547            });
    14061548        },
    14071549
     
    14121554                return;
    14131555
    1414             this.toolbar.view().get('select').model.set( 'disabled', ! selection.length );
     1556            this.toolbar.get().get('select').model.set( 'disabled', ! selection.length );
    14151557        }
    14161558    });
     
    14311573
    14321574        createStates: function() {
    1433             var options = this.options;
     1575            var options = this.options,
     1576                selection = options.selection;
    14341577
    14351578            // Add the default states.
     
    14371580                // Main states.
    14381581                new media.controller.Library({
    1439                     selection:  options.selection,
     1582                    id:         'insert',
     1583                    title:      l10n.insertMediaTitle,
     1584                    priority:   20,
     1585                    menu:       'main',
     1586                    toolbar:    'main-insert',
     1587                    filterable: 'all',
    14401588                    library:    media.query( options.library ),
     1589                    selection:  selection,
     1590                    multiple:   options.multiple ? 'reset' : false,
    14411591                    editable:   true,
    1442                     filterable: 'all',
    1443                     multiple:   this.options.multiple,
    1444                     menu:       'main',
    14451592
    14461593                    // Show the attachment display settings.
     
    14511598                }),
    14521599
    1453                 new media.controller.Upload({
    1454                     menu: 'main'
     1600                new media.controller.Library({
     1601                    id:         'gallery',
     1602                    title:      l10n.createGalleryTitle,
     1603                    priority:   40,
     1604                    menu:       'main',
     1605                    toolbar:    'main-gallery',
     1606                    filterable: 'uploaded',
     1607                    multiple:   'add',
     1608                    editable:   true,
     1609
     1610                    library:  media.query( _.defaults({
     1611                        type: 'image'
     1612                    }, options.library ) ),
     1613
     1614                    selection: new media.model.Selection( selection.models, {
     1615                        multiple: 'add'
     1616                    })
    14551617                }),
    14561618
     
    14591621
    14601622                // Gallery states.
    1461                 new media.controller.Gallery({
     1623                new media.controller.GalleryEdit({
    14621624                    library: options.selection,
    14631625                    editing: options.editing,
     
    14691631                    library:      media.query({ type: 'image' }),
    14701632                    filterable:   'uploaded',
    1471                     multiple:     true,
     1633                    multiple:     'add',
    14721634                    menu:         'gallery',
    14731635                    toolbar:      'gallery-add',
    1474                     excludeState: 'gallery-edit'
    1475                 }),
    1476 
    1477                 new media.controller.Upload({
    1478                     id:           'gallery-upload',
    1479                     menu:         'gallery',
    1480                     libraryState: 'gallery-edit'
     1636                    excludeState: 'gallery-edit',
     1637                    title:        l10n.addToGalleryTitle,
     1638                    priority:     100
    14811639                })
    14821640            ]);
     
    14931651        bindHandlers: function() {
    14941652            media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
     1653            this.on( 'menu:create:gallery', this.createMenu, this );
     1654            this.on( 'toolbar:create:main-insert', this.createSelectionToolbar, this );
     1655            this.on( 'toolbar:create:main-gallery', this.createSelectionToolbar, this );
    14951656
    14961657            var handlers = {
    14971658                    menu: {
     1659                        'main':    'mainMenu',
    14981660                        'gallery': 'galleryMenu'
    14991661                    },
     
    15051667
    15061668                    toolbar: {
    1507                         'main-attachments': 'mainAttachmentsToolbar',
     1669                        'main-insert':      'mainInsertToolbar',
     1670                        'main-gallery':     'mainGalleryToolbar',
    15081671                        'main-embed':       'mainEmbedToolbar',
    15091672                        'featured-image':   'featuredImageToolbar',
     
    15151678            _.each( handlers, function( regionHandlers, region ) {
    15161679                _.each( regionHandlers, function( callback, handler ) {
    1517                     this[ region ].on( 'activate:' + handler, this[ callback ], this );
     1680                    this.on( region + ':render:' + handler, this[ callback ], this );
    15181681                }, this );
    15191682            }, this );
     
    15211684
    15221685        // Menus
    1523         mainMenu: function() {
    1524             media.view.MediaFrame.Select.prototype.mainMenu.call( this, { silent: true });
    1525 
    1526             this.menu.view().set({
     1686        mainMenu: function( view ) {
     1687            view.set({
    15271688                'library-separator': new media.View({
    15281689                    className: 'separator',
    1529                     priority: 60
    1530                 }),
    1531                 'embed': {
    1532                     text: l10n.fromUrlTitle,
    1533                     priority: 80
    1534                 }
    1535             });
    1536 
    1537             if ( media.view.settings.post.featuredImageId ) {
    1538                 this.menu.view().set( 'featured-image', {
    1539                     text: l10n.featuredImageTitle,
    15401690                    priority: 100
    1541                 });
    1542             }
    1543         },
    1544 
    1545         galleryMenu: function() {
     1691                })
     1692            });
     1693        },
     1694
     1695        galleryMenu: function( view ) {
    15461696            var lastState = this.lastState(),
    15471697                previous = lastState && lastState.id,
    15481698                frame = this;
    15491699
    1550             this.menu.view( new media.view.Menu({
    1551                 controller: this,
    1552                 views: {
    1553                     cancel: {
    1554                         text:     l10n.cancelGalleryTitle,
    1555                         priority: 20,
    1556                         click:    function() {
    1557                             if ( previous )
    1558                                 frame.setState( previous );
    1559                             else
    1560                                 frame.close();
    1561                         }
    1562                     },
    1563                     separateCancel: new media.View({
    1564                         className: 'separator',
    1565                         priority: 40
    1566                     }),
    1567                     'gallery-edit': {
    1568                         text: l10n.editGalleryTitle,
    1569                         priority: 60
    1570                     },
    1571                     'gallery-upload': {
    1572                         text: l10n.uploadImagesTitle,
    1573                         priority: 80
    1574                     },
    1575                     'gallery-library': {
    1576                         text: l10n.mediaLibraryTitle,
    1577                         priority: 100
     1700            view.set({
     1701                cancel: {
     1702                    text:     l10n.cancelGalleryTitle,
     1703                    priority: 20,
     1704                    click:    function() {
     1705                        if ( previous )
     1706                            frame.setState( previous );
     1707                        else
     1708                            frame.close();
    15781709                    }
    1579                 }
    1580             }) );
     1710                },
     1711                separateCancel: new media.View({
     1712                    className: 'separator',
     1713                    priority: 40
     1714                })
     1715            });
    15811716        },
    15821717
     
    15881723            }).render();
    15891724
    1590             this.content.view( view );
     1725            this.content.set( view );
    15911726            view.url.focus();
    15921727        },
     
    16041739                sortable:   true,
    16051740                search:     false,
     1741                dragInfo:   true,
    16061742
    16071743                AttachmentView: media.view.Attachment.EditSelection
     
    16181754
    16191755            // Browse our library of attachments.
    1620             this.content.view( view );
    1621         },
    1622 
    1623         // Sidebars
    1624         onSidebarGallerySettings: function( options ) {
    1625             var library = this.state().get('library');
    1626 
    1627             if ( ! library )
    1628                 return;
    1629 
    1630             library.gallery = library.gallery || new Backbone.Model();
    1631 
    1632             this.sidebar.view().set({
    1633                 gallery: new media.view.Settings.Gallery({
    1634                     controller: this,
    1635                     model:      library.gallery,
    1636                     priority:   40
    1637                 }).render()
    1638             }, options );
     1756            this.content.set( view );
    16391757        },
    16401758
    16411759        // Toolbars
    1642         mainAttachmentsToolbar: function() {
    1643             this.toolbar.view( new media.view.Toolbar.Insert({
     1760        createSelectionToolbar: function( toolbar ) {
     1761            toolbar.view = new media.view.Toolbar.Selection({
    16441762                controller: this,
    16451763                editable:   this.state().get('editable')
    1646             }) );
     1764            });
     1765        },
     1766
     1767        mainInsertToolbar: function( view ) {
     1768            var controller = this;
     1769
     1770            view.button = 'insert';
     1771            view.set( 'insert', {
     1772                style:    'primary',
     1773                priority: 80,
     1774                text:     l10n.insertIntoPost,
     1775
     1776                click: function() {
     1777                    var state = controller.state(),
     1778                        selection = state.get('selection');
     1779
     1780                    controller.close();
     1781                    state.trigger( 'insert', selection ).reset();
     1782                }
     1783            });
     1784        },
     1785
     1786        mainGalleryToolbar: function( view ) {
     1787            var controller = this;
     1788
     1789            view.button = 'gallery';
     1790            view.set( 'gallery', {
     1791                style:    'primary',
     1792                text:     l10n.createNewGallery,
     1793                priority: 60,
     1794
     1795                click: function() {
     1796                    var selection = controller.state().get('selection'),
     1797                        edit = controller.state('gallery-edit'),
     1798                        models = selection.where({ type: 'image' });
     1799
     1800                    edit.set( 'library', new media.model.Selection( models, {
     1801                        props:    selection.props.toJSON(),
     1802                        multiple: true
     1803                    }) );
     1804
     1805                    this.controller.setState('gallery-edit');
     1806                }
     1807            });
    16471808        },
    16481809
    16491810        featuredImageToolbar: function() {
    1650             this.toolbar.view( new media.view.Toolbar.Select({
     1811            this.toolbar.set( new media.view.Toolbar.Select({
    16511812                controller: this,
    16521813                text:       l10n.setFeaturedImage,
     
    16561817
    16571818        mainEmbedToolbar: function() {
    1658             this.toolbar.view( new media.view.Toolbar.Embed({
     1819            this.toolbar.set( new media.view.Toolbar.Embed({
    16591820                controller: this
    16601821            }) );
     
    16651826        galleryEditToolbar: function() {
    16661827            var editing = this.state().get('editing');
    1667             this.toolbar.view( new media.view.Toolbar({
     1828            this.toolbar.set( new media.view.Toolbar({
    16681829                controller: this,
    16691830                items: {
     
    16901851
    16911852        galleryAddToolbar: function() {
    1692             this.toolbar.view( new media.view.Toolbar({
     1853            this.toolbar.set( new media.view.Toolbar({
    16931854                controller: this,
    16941855                items: {
     
    17051866                            edit.get('library').add( state.get('selection').models );
    17061867                            state.trigger('reset');
    1707                             controller.state('gallery-edit');
     1868                            controller.setState('gallery-edit');
    17081869                        }
    17091870                    }
     
    17301891
    17311892        initialize: function() {
    1732             this.controller = this.options.controller;
    1733 
    17341893            _.defaults( this.options, {
    17351894                container: document.body,
     
    18391998            var uploader;
    18401999
    1841             this.controller = this.options.controller;
    1842 
    18432000            this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    18442001
     
    19072064
    19082065        initialize: function() {
    1909             this.controller = this.options.controller;
     2066            _.defaults( this.options, {
     2067                message: '',
     2068                status:  true
     2069            });
    19102070
    19112071            if ( ! this.options.$browser && this.controller.uploader )
     
    19152075                this.options.postId = media.view.settings.post.id;
    19162076
    1917             this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
    1918                 controller: this.controller
    1919             }) );
     2077            if ( this.options.status ) {
     2078                this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
     2079                    controller: this.controller
     2080                }) );
     2081            }
    19202082        },
    19212083
     
    19522114
    19532115        initialize: function() {
    1954             this.controller = this.options.controller;
    1955 
    19562116            this.queue = wp.Uploader.queue;
    19572117            this.queue.on( 'add remove reset', this.visibility, this );
     
    20612221
    20622222        initialize: function() {
    2063             this.controller = this.options.controller;
    2064 
    20652223            this._views     = {};
    20662224            this.$primary   = $('<div class="media-toolbar-primary" />').prependTo( this.$el );
     
    22002358    media.view.Toolbar.Embed = media.view.Toolbar.Select.extend({
    22012359        initialize: function() {
    2202             var controller = this.options.controller;
    2203 
    22042360            _.defaults( this.options, {
    22052361                text: l10n.insertIntoPost
     
    22072363
    22082364            media.view.Toolbar.Select.prototype.initialize.apply( this, arguments );
    2209             controller.on( 'change:url', this.refresh, this );
     2365            this.controller.state().props.on( 'change:url', this.refresh, this );
    22102366        },
    22112367
    22122368        refresh: function() {
    2213             var url = this.controller.state().get('url');
     2369            var url = this.controller.state().props.get('url');
    22142370            this.get('select').model.set( 'disabled', ! url || /^https?:\/\/$/.test(url) );
    22152371        }
    22162372    });
    22172373
    2218     // wp.media.view.Toolbar.Insert
    2219     // ----------------------------
    2220     media.view.Toolbar.Insert = media.view.Toolbar.extend({
     2374    // wp.media.view.Toolbar.Selection
     2375    // -------------------------------
     2376    media.view.Toolbar.Selection = media.view.Toolbar.extend({
     2377        button: 'insert',
     2378
    22212379        initialize: function() {
    2222             var controller = this.options.controller,
    2223                 selection = controller.state().get('selection'),
    2224                 selectionToLibrary;
    2225 
    2226             selectionToLibrary = function( state, filter ) {
    2227                 return function() {
    2228                     var controller = this.controller,
    2229                         selection = controller.state().get('selection'),
    2230                         edit = controller.state( state ),
    2231                         models = filter ? filter( selection ) : selection.models;
    2232 
    2233                     edit.set( 'library', new media.model.Selection( models, {
    2234                         props:    selection.props.toJSON(),
    2235                         multiple: true
    2236                     }) );
    2237 
    2238                     this.controller.setState( state );
    2239                 };
    2240             };
     2380            var controller = this.controller;
    22412381
    22422382            this.options.items = _.defaults( this.options.items || {}, {
    22432383                selection: new media.view.Selection({
    22442384                    controller: controller,
    2245                     collection: selection,
     2385                    collection: controller.state().get('selection'),
    22462386                    priority:   -40,
    22472387
     
    22512391                        this.controller.content.mode('edit-selection');
    22522392                    }
    2253                 }).render(),
    2254 
    2255                 insert: {
    2256                     style:    'primary',
    2257                     priority: 80,
    2258                     text:     l10n.insertIntoPost,
    2259 
    2260                     click: function() {
    2261                         controller.close();
    2262                         controller.state().trigger( 'insert', selection ).reset();
    2263                     }
    2264                 },
    2265 
    2266                 gallery: {
    2267                     text:     l10n.createNewGallery,
    2268                     priority: 40,
    2269                     click:    selectionToLibrary('gallery-edit', function( selection ) {
    2270                         return selection.where({ type: 'image' });
    2271                     })
    2272                 }
     2393                }).render()
    22732394            });
    22742395
     
    22782399        refresh: function() {
    22792400            var selection = this.controller.state().get('selection'),
    2280                 count = selection.length;
    2281 
    2282             this.get('insert').model.set( 'disabled', ! selection.length );
    2283 
    2284             // Check if any attachment in the selection is an image.
    2285             this.get('gallery').$el.toggle( count > 1 && selection.any( function( attachment ) {
    2286                 return 'image' === attachment.get('type');
    2287             }) );
     2401                button = this.get( this.button );
     2402
     2403            if ( ! button )
     2404                return;
     2405
     2406            button.model.set( 'disabled', ! selection.length );
    22882407        }
    22892408    });
     
    23892508
    23902509        initialize: function() {
    2391             this.controller = this.options.controller;
    2392             this._views     = {};
     2510            this._views = {};
    23932511
    23942512            this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
     
    24582576    });
    24592577
     2578    /**
     2579     * wp.media.view.MenuItem
     2580     */
     2581    media.view.MenuItem = media.View.extend({
     2582        tagName:   'a',
     2583        className: 'media-menu-item',
     2584
     2585        attributes: {
     2586            href: '#'
     2587        },
     2588
     2589        events: {
     2590            'click': '_click'
     2591        },
     2592
     2593        _click: function( event ) {
     2594            var clickOverride = this.options.click;
     2595
     2596            if ( event )
     2597                event.preventDefault();
     2598
     2599            if ( clickOverride )
     2600                clickOverride.call( this );
     2601            else
     2602                this.click();
     2603        },
     2604
     2605        click: function() {
     2606            var state = this.options.state;
     2607            if ( state )
     2608                this.controller.setState( state );
     2609        },
     2610
     2611        render: function() {
     2612            var options = this.options;
     2613
     2614            if ( options.text )
     2615                this.$el.text( options.text );
     2616            else if ( options.html )
     2617                this.$el.html( options.html );
     2618
     2619            return this;
     2620        }
     2621    });
    24602622
    24612623    /**
     
    24632625     */
    24642626    media.view.Menu = media.view.PriorityList.extend({
    2465         tagName:   'ul',
     2627        tagName:   'div',
    24662628        className: 'media-menu',
    2467 
    2468         toView: function( options, state ) {
     2629        property:  'state',
     2630        ItemView:  media.view.MenuItem,
     2631        region:    'menu',
     2632
     2633        toView: function( options, id ) {
    24692634            options = options || {};
    2470             options.state = options.state || state;
    2471             return new media.view.MenuItem( options ).render();
    2472         },
    2473 
    2474         select: function( state ) {
    2475             var view = this.get( state );
     2635            options[ this.property ] = options[ this.property ] || id;
     2636            return new this.ItemView( options ).render();
     2637        },
     2638
     2639        ready: function() {
     2640            media.view.PriorityList.prototype.ready.apply( this, arguments );
     2641            this.visibility();
     2642        },
     2643
     2644        set: function() {
     2645            media.view.PriorityList.prototype.set.apply( this, arguments );
     2646            this.visibility();
     2647        },
     2648
     2649        unset: function() {
     2650            media.view.PriorityList.prototype.unset.apply( this, arguments );
     2651            this.visibility();
     2652        },
     2653
     2654        visibility: function() {
     2655            var region = this.region,
     2656                view = this.controller[ region ].get(),
     2657                views = this.views.get(),
     2658                hide = ! views || views.length < 2;
     2659
     2660            if ( this === view )
     2661                this.controller.$el.toggleClass( 'hide-' + region, hide );
     2662        },
     2663
     2664        select: function( id ) {
     2665            var view = this.get( id );
    24762666
    24772667            if ( ! view )
     
    24872677    });
    24882678
    2489     media.view.MenuItem = media.View.extend({
    2490         tagName:   'li',
    2491         className: 'media-menu-item',
    2492 
    2493         events: {
    2494             'click': 'click'
    2495         },
    2496 
     2679    /**
     2680     * wp.media.view.RouterItem
     2681     */
     2682    media.view.RouterItem = media.view.MenuItem.extend({
    24972683        click: function() {
    2498             var options = this.options;
    2499 
    2500             if ( options.click )
    2501                 options.click.call( this );
    2502             else if ( options.state )
    2503                 this.controller.setState( options.state );
    2504         },
    2505 
    2506         render: function() {
    2507             var options = this.options;
    2508 
    2509             if ( options.text )
    2510                 this.$el.text( options.text );
    2511             else if ( options.html )
    2512                 this.$el.html( options.html );
    2513 
    2514             return this;
    2515         }
    2516     });
     2684            var contentMode = this.options.contentMode;
     2685            if ( contentMode )
     2686                this.controller.content.mode( contentMode );
     2687        }
     2688    });
     2689
     2690    /**
     2691     * wp.media.view.Router
     2692     */
     2693    media.view.Router = media.view.Menu.extend({
     2694        tagName:   'div',
     2695        className: 'media-router',
     2696        property:  'contentMode',
     2697        ItemView:  media.view.RouterItem,
     2698        region:    'router',
     2699
     2700        initialize: function() {
     2701            this.controller.on( 'content:render', this.update, this );
     2702            media.view.Menu.prototype.initialize.apply( this, arguments );
     2703        },
     2704
     2705        update: function() {
     2706            var mode = this.controller.content.mode();
     2707            if ( mode )
     2708                this.select( mode );
     2709        }
     2710    });
     2711
    25172712
    25182713    /**
     
    25322727
    25332728        events: {
    2534             'click .attachment-preview':      'toggleSelection',
     2729            'click .attachment-preview':      'toggleSelectionHandler',
    25352730            'change [data-setting]':          'updateSetting',
    25362731            'change [data-setting] input':    'updateSetting',
     
    25462741        initialize: function() {
    25472742            var selection = this.options.selection;
    2548 
    2549             this.controller = this.options.controller;
    25502743
    25512744            this.model.on( 'change:sizes change:uploading change:caption change:title', this.render, this );
     
    26242817        },
    26252818
    2626         toggleSelection: function( event ) {
    2627             var selection = this.options.selection,
    2628                 model = this.model;
     2819        toggleSelectionHandler: function( event ) {
     2820            var method;
     2821
     2822            if ( event.shiftKey )
     2823                method = 'between';
     2824            else if ( event.ctrlKey || event.metaKey )
     2825                method = 'toggle';
     2826
     2827            this.toggleSelection({
     2828                method: method
     2829            });
     2830        },
     2831
     2832        toggleSelection: function( options ) {
     2833            var collection = this.collection,
     2834                selection = this.options.selection,
     2835                model = this.model,
     2836                method = options && options.method,
     2837                single, between, models, singleIndex, modelIndex;
    26292838
    26302839            if ( ! selection )
    26312840                return;
     2841
     2842            single = selection.single();
     2843            method = _.isUndefined( method ) ? selection.multiple : method;
     2844
     2845            // If the `method` is set to `between`, select all models that
     2846            // exist between the current and the selected model.
     2847            if ( 'between' === method && single && selection.multiple ) {
     2848                // If the models are the same, short-circuit.
     2849                if ( single === model )
     2850                    return;
     2851
     2852                singleIndex = collection.indexOf( single );
     2853                modelIndex  = collection.indexOf( this.model );
     2854
     2855                if ( singleIndex < modelIndex )
     2856                    models = collection.models.slice( singleIndex, modelIndex + 1 );
     2857                else
     2858                    models = collection.models.slice( modelIndex, singleIndex + 1 );
     2859
     2860                selection.add( models ).single( model );
     2861                return;
     2862
     2863            // If the `method` is set to `toggle`, just flip the selection
     2864            // status, regardless of whether the model is the single model.
     2865            } else if ( 'toggle' === method ) {
     2866                selection[ this.selected() ? 'remove' : 'add' ]( model ).single( model );
     2867                return;
     2868            }
     2869
     2870            if ( method !== 'add' )
     2871                method = 'reset';
    26322872
    26332873            if ( this.selected() ) {
     
    26352875                // If it is not the same as the single model,
    26362876                // it now becomes the single model.
    2637                 selection[ selection.single() === model ? 'remove' : 'single' ]( model );
     2877                selection[ single === model ? 'remove' : 'single' ]( model );
    26382878            } else {
    2639                 selection.add( model ).single( model );
     2879                // If the model is not selected, run the `method` on the
     2880                // selection. By default, we `reset` the selection, but the
     2881                // `method` can be set to `add` the model to the selection.
     2882                selection[ method ]( model ).single( model );
    26402883            }
    26412884        },
     
    28393082
    28403083        initialize: function() {
    2841             this.controller = this.options.controller;
    28423084            this.el.id = _.uniqueId('__attachments-view-');
    28433085
     
    29513193                        silent: true
    29523194                    }).add( model, {
    2953                         at:     ui.item.index(),
    2954                         silent: true
     3195                        silent: true,
     3196                        at:     ui.item.index()
    29553197                    });
    29563198
    29573199                    // Restore the comparator.
    29583200                    collection.comparator = comparator;
     3201
     3202                    // Fire the `reset` event to ensure other collections sync.
     3203                    collection.trigger( 'reset', collection );
    29593204
    29603205                    // If the collection is sorted by menu order,
     
    32013446
    32023447        initialize: function() {
    3203             this.controller = this.options.controller;
    3204 
    32053448            _.defaults( this.options, {
    32063449                filters: false,
    32073450                search:  true,
    3208                 uploads: false,
    32093451                display: false,
    32103452
     
    32563498            }
    32573499
    3258             if ( this.options.sortable && ! this.options.filters ) {
     3500            if ( this.options.dragInfo ) {
    32593501                this.toolbar.set( 'dragInfo', new media.View({
    32603502                    el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
     
    32913533
    32923534            this.uploader = new media.view.UploaderInline({
    3293                 controller: this.controller
     3535                controller: this.controller,
     3536                status:     false,
     3537                message:    l10n.noItemsFound
    32943538            });
    32953539
     
    33233567            this.views.add( sidebar );
    33243568
    3325             if ( options.uploads && this.controller.uploader ) {
     3569            if ( this.controller.uploader ) {
    33263570                sidebar.set( 'uploads', new media.view.UploaderStatus({
    33273571                    controller: this.controller,
     
    33743618
    33753619    /**
    3376      * wp.media.view.SelectionPreview
    3377      */
    3378     media.view.SelectionPreview = media.View.extend({
    3379         tagName:   'div',
    3380         className: 'selection-preview',
    3381         template:  media.template('media-selection-preview'),
    3382 
    3383         events: {
    3384             'click .clear-selection': 'clear'
    3385         },
    3386 
    3387         initialize: function() {
    3388             _.defaults( this.options, {
    3389                 clearable: true
    3390             });
    3391 
    3392             this.controller = this.options.controller;
    3393             this.collection.on( 'add change:url remove', this.render, this );
    3394             this.render();
    3395         },
    3396 
    3397         render: function() {
    3398             var options = _.clone( this.options ),
    3399                 last, sizes, amount;
    3400 
    3401             // If nothing is selected, display nothing.
    3402             if ( ! this.collection.length ) {
    3403                 this.$el.empty();
    3404                 return this;
    3405             }
    3406 
    3407             options.count = this.collection.length;
    3408             last  = this.collection.last();
    3409             sizes = last.get('sizes');
    3410 
    3411             if ( 'image' === last.get('type') )
    3412                 options.thumbnail = ( sizes && sizes.thumbnail ) ? sizes.thumbnail.url : last.get('url');
    3413             else
    3414                 options.thumbnail =  last.get('icon');
    3415 
    3416             this.$el.html( this.template( options ) );
    3417             return this;
    3418         },
    3419 
    3420         clear: function( event ) {
    3421             event.preventDefault();
    3422             this.collection.reset();
    3423         }
    3424     });
    3425 
    3426     /**
    34273620     * wp.media.view.Selection
    34283621     */
     
    34433636            });
    34443637
    3445             this.controller = this.options.controller;
    34463638            this.attachments = new media.view.Attachments({
    34473639                controller: this.controller,
     
    36603852                attachment = this.options.attachment;
    36613853
    3662             if ( 'none' === linkTo ) {
     3854            if ( 'none' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
    36633855                $input.hide();
    36643856                return;
    36653857            }
    36663858
     3859            if ( attachment ) {
     3860                if ( 'post' === linkTo ) {
     3861                    $input.val( attachment.get('link') );
     3862                } else if ( 'file' === linkTo ) {
     3863                    $input.val( attachment.get('url') );
     3864                } else if ( ! this.model.get('linkUrl') ) {
     3865                    $input.val('http://');
     3866                }
     3867
     3868                $input.prop( 'readonly', 'custom' !== linkTo );
     3869            }
     3870
    36673871            $input.show();
    3668 
    3669             if ( 'post' === linkTo ) {
    3670                 $input.val( attachment.get('link') );
    3671             } else if ( 'file' === linkTo ) {
    3672                 $input.val( attachment.get('url') );
    3673             } else if ( ! this.model.get('linkUrl') ) {
    3674                 $input.val('http://');
    3675             }
    3676 
    3677             $input.prop( 'readonly', 'custom' !== linkTo );
    36783872
    36793873            // If the input is visible, focus and select its contents.
     
    37693963        className: 'media-iframe',
    37703964
    3771         initialize: function() {
    3772             this.controller = this.options.controller;
    3773         },
    3774 
    37753965        render: function() {
     3966            this.views.detach();
    37763967            this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
     3968            this.views.render();
    37773969            return this;
    37783970        }
     
    37863978
    37873979        initialize: function() {
    3788             this.controller = this.options.controller;
    3789 
    37903980            this.url = new media.view.EmbedUrl({
    37913981                controller: this.controller,
    3792                 model:      this.model
     3982                model:      this.model.props
    37933983            }).render();
    37943984
     
    38274017            this.settings( new constructor({
    38284018                controller: this.controller,
    3829                 model:      this.model,
     4019                model:      this.model.props,
    38304020                priority:   40
    38314021            }) );
     
    38474037
    38484038        initialize: function() {
    3849             this.label = this.make( 'span', null, this.options.label || l10n.url );
    38504039            this.input = this.make( 'input', {
    38514040                type:  'text',
     
    38534042            });
    38544043
    3855             this.$label = $( this.label );
    38564044            this.$input = $( this.input );
    3857             this.$el.append([ this.label, this.input ]);
     4045            this.$el.append( this.input );
    38584046
    38594047            this.model.on( 'change:url', this.render, this );
  • trunk/wp-includes/media.php

    r22994 r23006  
    14681468        // Library
    14691469        'mediaLibraryTitle'  => __( 'Media Library' ),
     1470        'insertMediaTitle'   => __( 'Insert Media' ),
    14701471        'createNewGallery'   => __( 'Create a new gallery' ),
    14711472        'returnToLibrary'    => __( '&#8592; Return to library' ),
    14721473        'allMediaItems'      => __( 'All media items' ),
     1474        'noItemsFound'       => __( 'No items found.' ),
    14731475        'insertIntoPost'     => $hier ? __( 'Insert into page' ) : __( 'Insert into post' ),
    14741476        'uploadedToThisPost' => $hier ? __( 'Uploaded to this page' ) : __( 'Uploaded to this post' ),
     
    14901492        'continueEditing'    => __( 'Continue editing' ),
    14911493        'addToGallery'       => __( 'Add to gallery' ),
     1494        'addToGalleryTitle'  => __( 'Add to Gallery' ),
    14921495        'reverseOrder'       => __( 'Reverse order' ),
    14931496    );
     
    15181521    <script type="text/html" id="tmpl-media-frame">
    15191522        <div class="media-frame-menu"></div>
     1523        <div class="media-frame-title"></div>
     1524        <div class="media-frame-router"></div>
    15201525        <div class="media-frame-content"></div>
    15211526        <div class="media-frame-toolbar"></div>
     
    15251530    <script type="text/html" id="tmpl-media-modal">
    15261531        <div class="media-modal wp-core-ui">
    1527             <h3 class="media-modal-title">{{ data.title }}</h3>
    1528             <a class="media-modal-close media-modal-icon" href="#" title="<?php esc_attr_e('Close'); ?>"></a>
     1532            <a class="media-modal-close" href="#" title="<?php esc_attr_e('Close'); ?>"><span class="media-modal-icon"></span></a>
    15291533            <div class="media-modal-content"></div>
    15301534        </div>
    1531         <div class="media-modal-backdrop">
    1532             <div></div>
    1533         </div>
     1535        <div class="media-modal-backdrop"></div>
    15341536    </script>
    15351537
     
    15411543
    15421544    <script type="text/html" id="tmpl-uploader-inline">
    1543         <div class="uploader-inline-content">
     1545        <# var messageClass = data.message ? 'has-upload-message' : 'no-upload-message'; #>
     1546        <div class="uploader-inline-content {{ messageClass }}">
     1547        <# if ( data.message ) { #>
     1548            <h3 class="upload-message">{{ data.message }}</h3>
     1549        <# } #>
    15441550        <?php if ( ! _device_can_upload() ) : ?>
    1545             <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>
     1551            <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>
    15461552        <?php elseif ( is_multisite() && ! is_upload_space_available() ) : ?>
    1547             <h3><?php _e( 'Upload Limit Exceeded' ); ?></h3>
     1553            <h3 class="upload-instructions"><?php _e( 'Upload Limit Exceeded' ); ?></h3>
    15481554            <?php do_action( 'upload_ui_over_quota' ); ?>
    15491555
    15501556        <?php else : ?>
    15511557            <div class="upload-ui">
    1552                 <h3 class="drop-instructions"><?php _e( 'Drop files anywhere to upload' ); ?></h3>
     1558                <h3 class="upload-instructions drop-instructions"><?php _e( 'Drop files anywhere to upload' ); ?></h3>
    15531559                <a href="#" class="browser button button-hero"><?php _e( 'Select Files' ); ?></a>
    15541560            </div>
     
    17451751    </script>
    17461752
    1747     <script type="text/html" id="tmpl-media-selection-preview">
    1748         <div class="selected-img selected-count-{{ data.count }}">
    1749             <# if ( data.thumbnail ) { #>
    1750                 <img src="{{ data.thumbnail }}" draggable="false" />
    1751             <# } #>
    1752 
    1753             <span class="count">{{ data.count }}</span>
    1754         </div>
    1755         <# if ( data.clearable ) { #>
    1756             <a class="clear-selection" href="#"><?php _e('Clear selection'); ?></a>
    1757         <# } #>
    1758     </script>
    1759 
    17601753    <script type="text/html" id="tmpl-attachment-display-settings">
    17611754        <h3><?php _e('Attachment Display Settings'); ?></h3>
Note: See TracChangeset for help on using the changeset viewer.