WordPress.org

Make WordPress Core

Changeset 23006


Ignore:
Timestamp:
12/04/12 01:26:03 (3 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.