Make WordPress Core

Ticket #21390: 21390.8.diff

File 21390.8.diff, 65.5 KB (added by koopersmith, 12 years ago)
  • wp-admin/js/custom-background.js

     
    3737                                }
    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',
    4444                                                text:  $el.data('update'),
  • wp-admin/js/custom-header.js

     
    2424                                }
    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',
    3131                                                text:  $el.data('update'),
  • wp-includes/css/media-views-rtl.css

     
    11/**
    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
    149/**
    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;
    2514}
     
    9382 * Menu
    9483 */
    9584.media-menu {
    96         left: auto;
    97         right: 0;
    9885        border-right: 0;
    9986        border-left: 1px solid #d9d9d9;
    10087        box-shadow: inset 6px 0 6px -6px rgba( 0, 0, 0, 0.2 )
    10188}
    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;
    109116}
    110117
     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;
     128}
     129
    111130/**
    112131 * Attachment Browser Filters
    113132 */
     
    226245}
    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 */
    255250.attachment-info .thumbnail {
     
    289284 * Responsive layout
    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;
    296293        }
  • wp-includes/css/media-views.css

     
    8080 */
    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}
    8989
     
    9494        right: 0;
    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 {
     101.media-modal-close {
    103102        position: absolute;
    104         top: 10px;
    105         left: 10px;
    106         right: 10px;
    107         bottom: 10px;
    108         border: 1px dashed rgba( 255, 255, 255, 0.5 );
     103        top: 7px;
     104        right: 7px;
     105        width: 30px;
     106        height: 30px;
     107        z-index: 1000;
    109108}
    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 
    126 .media-modal-close {
    127         position: absolute;
    128         top: -27px;
    129         right: 0;
     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
    135117.media-modal-close:active {
     
    165147        border: 0 solid #dfdfdf;
    166148}
    167149
    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;
    178 }
    179 
    180150.media-toolbar-primary {
    181151        float: right;
    182152}
     
    238208        width: 100%;
    239209}
    240210
    241 .media-sidebar .selection-preview {
    242         display: block;
    243         padding-top: 5px;
    244 }
    245 
    246211.media-sidebar h3 {
    247212        position: relative;
    248213        font-weight: bold;
     
    361326        position: absolute;
    362327        top: 0;
    363328        left: 0;
     329        right: 0;
    364330        bottom: 0;
    365         width: 199px;
    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 );
    371335        -webkit-user-select: none;
     
    374338        user-select: none;
    375339}
    376340
    377 .media-menu li {
     341.media-menu > a {
     342        display: block;
    378343        position: relative;
    379344        padding: 4px 20px;
    380345        margin: 0;
     
    382347        font-size: 14px;
    383348        color: #21759B;
    384349        text-shadow: 0 1px 0 #fff;
     350        text-decoration: none;
    385351}
    386352
    387 .media-menu-item {
    388         cursor: pointer;
     353.media-menu > a:hover {
     354        color: #21759B;
     355        background: rgba( 0, 0, 0, 0.04 );
    389356}
    390357
    391 .media-menu li:hover {
    392         background: rgba( 0, 0, 0, 0.04 );
     358.media-menu > a:active {
     359        outline: none;
    393360}
    394361
    395362.media-menu .active,
     
    407374}
    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        outline: none;
     409}
     410
     411.media-router .active,
     412.media-router .active:hover {
     413        color: #333;
     414}
     415
     416.media-router .active:after {
     417        content: '';
     418        display: block;
     419        margin: -100px auto 0;
     420        width: 7px;
     421        height: 7px;
     422        background: #fff;
     423        box-shadow: 1px 1px 1px rgba( 0, 0, 0, 0.2 );
     424        z-index: 300;
     425
     426        -webkit-transform: rotate( 45deg ) translate( 75px, 75px );
     427        -moz-transform:    rotate( 45deg ) translate( 75px, 75px );
     428        -ms-transform:     rotate( 45deg ) translate( 75px, 75px );
     429        -o-transform:      rotate( 45deg ) translate( 75px, 75px );
     430        transform:         rotate( 45deg ) translate( 75px, 75px );
     431}
     432
     433/**
    410434 * Frame
    411435 */
    412436.media-frame {
     
    418442        bottom: 0;
    419443}
    420444
    421 .media-frame .region-content {
     445.media-frame-menu {
    422446        position: absolute;
    423447        top: 0;
     448        left: 0;
     449        bottom: 0;
     450        width: 199px;
     451        z-index: 150;
     452}
     453
     454.media-frame-title {
     455        position: absolute;
     456        top: 0;
    424457        left: 200px;
    425458        right: 0;
     459        height: 45px;
     460        z-index: 200;
     461}
     462
     463.media-frame-router {
     464        position: absolute;
     465        top: 45px;
     466        left: 200px;
     467        right: 0;
     468        height: 30px;
     469        z-index: 200;
     470        border-bottom: 1px solid #dfdfdf;
     471        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
     472}
     473
     474.media-frame-content {
     475        position: absolute;
     476        top: 75px;
     477        left: 200px;
     478        right: 0;
    426479        bottom: 61px;
    427480        height: auto;
    428481        width: auto;
     
    430483        overflow: auto;
    431484}
    432485
    433 .media-frame.hide-toolbar .region-content {
     486.media-frame-toolbar {
     487        position: absolute;
     488        left: 200px;
     489        right: 0;
    434490        bottom: 0;
     491        height: 60px;
     492        z-index: 100;
     493        border: 0 solid #dfdfdf;
     494        border-width: 1px 0 0 0;
     495        box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 );
    435496}
    436497
     498.media-frame.hide-menu .media-frame-title,
     499.media-frame.hide-menu .media-frame-router,
     500.media-frame.hide-menu .media-frame-toolbar,
     501.media-frame.hide-menu .media-frame-content {
     502        left: 0;
     503}
     504
     505.media-frame.hide-menu .media-frame-menu {
     506        left: -200px;
     507}
     508
     509.media-frame.hide-toolbar .media-frame-content {
     510        bottom: 0;
     511}
     512
     513.media-frame.hide-toolbar .media-frame-toolbar {
     514        bottom: -61px;
     515}
     516
     517.media-frame.hide-router .media-frame-content {
     518        top: 45px;
     519}
     520
     521.media-frame.hide-router .media-frame-router {
     522        display: none;
     523}
     524
     525.media-frame.hide-router .media-frame-title {
     526        border-bottom: 1px solid #dfdfdf;
     527        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
     528}
     529
    437530.media-frame .media-toolbar .add-to-gallery {
    438531        display: none;
    439532}
    440533
     534.media-frame-title h1 {
     535        padding: 0 16px;
     536        font-size: 22px;
     537        font-weight: 200;
     538        line-height: 45px;
     539        margin: 0;
     540}
     541
    441542/**
    442543 * Iframes
    443544 */
     
    721822 * Attachments Browser
    722823 */
    723824.media-frame .attachments-browser {
     825        position: relative;
     826        width: 100%;
     827        height: 100%;
    724828        overflow: hidden;
    725829}
    726830
     
    9031007}
    9041008
    9051009.uploader-window-content {
    906         border-color: #fff;
     1010        position: absolute;
     1011        top: 10px;
     1012        left: 10px;
     1013        right: 10px;
     1014        bottom: 10px;
     1015        border: 1px dashed #fff;
    9071016}
    9081017
    9091018.uploader-window h3 {
     
    9561065        margin: 4em 0;
    9571066}
    9581067
     1068.uploader-inline .has-upload-message .upload-ui {
     1069        margin: 0 0 4em;
     1070}
     1071
    9591072.uploader-inline h3 {
    9601073        font-size: 20px;
    9611074        line-height: 28px;
     
    9631076        margin-bottom: 1.6em;
    9641077}
    9651078
     1079.uploader-inline .has-upload-message .upload-instructions {
     1080        font-size: 14px;
     1081        color: #464646;
     1082        font-weight: normal;
     1083}
     1084
    9661085.uploader-inline .drop-instructions {
    9671086        display: none;
    9681087}
     
    10581177        vertical-align: top;
    10591178}
    10601179
    1061 .media-selection .attachment img {
     1180.media-selection .attachment .icon {
    10621181        width: 50%;
    10631182}
    10641183
     
    10981217}
    10991218
    11001219/**
    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;
    1161 }
    1162 
    1163 /**
    11641220 * Spinner
    11651221 */
    11661222.media-sidebar .settings-save-status {
     
    12971353.embed-url {
    12981354        display: block;
    12991355        position: relative;
    1300         height: 75px;
    1301         padding: 16px 16px;
     1356        height: 40px;
     1357        padding: 0 16px 16px;
    13021358        margin: 0;
    1303         z-index: 50;
     1359        z-index: 250;
     1360        background: #fff;
    13041361        border-bottom: 1px solid #dfdfdf;
    13051362        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    13061363        font-size: 18px;
    13071364        font-weight: 200;
    13081365}
    13091366
    1310 .embed-url span {
    1311         display: block;
    1312         padding: 4px 0 6px 2px;
    1313 }
    1314 
    13151367.media-frame .embed-url input {
    13161368        font-size: 18px;
    13171369        padding: 12px 14px;
     
    13241376.embed-image-settings {
    13251377        position: absolute;
    13261378        background: #f5f5f5;
    1327         top: 108px;
     1379        top: 57px;
    13281380        left: 0;
    13291381        right: 0;
    13301382        bottom: 0;
     
    13881440 * Responsive layout
    13891441 */
    13901442@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 {
     1443        .media-frame-menu {
    14171444                width: 139px;
    14181445        }
    14191446
     
    14211448                padding: 4px 10px;
    14221449        }
    14231450
    1424         .media-frame .region-content,
    1425         .media-frame-toolbar > .media-toolbar {
     1451        .media-frame-title,
     1452        .media-frame-router,
     1453        .media-frame-content,
     1454        .media-frame-toolbar {
    14261455                left: 140px;
    14271456        }
    14281457
  • wp-includes/js/media-editor.js

     
    384384
    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
    390390                        } ) );
     
    408408                        }, this );
    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,
    418420                                                linkUrl: embed.url
     
    420422
    421423                                        this.send.link( embed );
    422424
    423                                 } else if ( 'image' === embed.type ) {
     425                                } else if ( 'image' === type ) {
    424426                                        _.defaults( embed, {
    425427                                                title:   embed.url,
    426428                                                linkUrl: '',
  • wp-includes/js/media-views.js

     
    6868         * wp.media.controller.Region
    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
    7774        // Use Backbone's self-propagating `extend` inheritance method.
    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;
     77        _.extend( media.controller.Region.prototype, {
     78                mode: function( mode ) {
     79                        if ( ! mode )
     80                                return this._mode;
    8481
    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 ) ) );
     82                        // Bail if we're trying to change to the current mode.
     83                        if ( mode === this._mode )
    9184                                return this;
    92                         };
    93                 }()),
    9485
    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;
     86                        this.trigger('deactivate');
     87                        this._mode = mode;
     88                        this.render( mode );
     89                        this.trigger('activate');
     90                        return this;
    10291                },
    10392
    104                 view: function( view ) {
    105                         var previous = this._view,
    106                                 mode = this._mode,
    107                                 id = this.id;
     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 );
    10898
    109                         // If no argument is provided, return the current view.
    110                         if ( ! view )
    111                                 return previous;
     99                        var set = { view: null },
     100                                view;
    112101
    113                         // If we're attempting to switch to the current view, bail.
    114                         if ( view === previous )
     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 );
     125                        var args = _.toArray( arguments );
     126                        base = this.id + ':' + event;
    120127
    121                         if ( mode )
    122                                 view.$el.addClass( 'mode-' + mode );
     128                        // Trigger `region:action:mode` event.
     129                        args[0] = base + ':' + this._mode;
     130                        this.view.trigger.apply( this.view, args );
    123131
    124                         this.controller.views.set( this.selector, view );
    125                         this._view = view;
    126                 },
    127 
    128                 empty: function() {
    129                         this.view( new media.View() );
     132                        // Trigger `region:action` event.
     133                        args[0] = base;
     134                        this.view.trigger.apply( this.view, args );
     135                        return this;
    130136                }
    131137        });
    132138
     
    208214        // wp.media.controller.State
    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 );
     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 );
    217230                },
    218231
     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;
     243                },
    222244
    223                         this.menu();
    224                         this.toolbar();
    225                         this.content();
     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();
    226258                },
    227259
    228                 deactivate: function() {},
     260
    229261                _deactivate: function() {
    230262                        this.active = false;
     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 );
    231270                },
    232271
    233                 reset: function() {},
     272                _title: function() {
     273                        this.frame.title.render( this.get('titleMode') || 'default' );
     274                },
    234275
    235                 menu: function() {
     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'),
    238299                                view;
     
    240301                        if ( ! mode )
    241302                                return;
    242303
    243                         if ( menu.mode() !== mode )
    244                                 menu.mode( mode );
     304                        menu.mode( mode );
    245305
    246                         view = menu.view();
     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        });
    259348
     
    262351        media.controller.Library = media.controller.State.extend({
    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
    276372                initialize: function() {
     
    290386                                this.set( 'gutter', 8 );
    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;
    300395
     396                        if ( this.get('syncLastSelection') ) {
     397                                this.getLastSelection();
     398                        }
     399
    301400                        this._excludeStateLibrary();
    302401                        this.buildComposite();
    303402                        this.on( 'change:library change:exclude', this.buildComposite, this );
    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.
    320422                        this.get('selection').off( null, null, this );
     
    330432                reset: function() {
    331433                        this.get('selection').reset();
    332434                        this.resetDisplays();
     435                        this.refreshContent();
    333436                },
    334437
    335                 refresh: function() {
    336                         this.content();
    337                         this.refreshSelection();
    338                 },
    339 
    340438                resetDisplays: function() {
    341439                        this._displays = [];
    342440                        this._defaultDisplaySettings = {
     
    371469                        setUserSetting( 'urlbutton', display.link );
    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();
     501                                frame = this.frame,
     502                                router = frame.router.get(),
     503                                mode = frame.content.mode();
    377504
    378                         this.frame.toolbar.view().refresh();
    379                         this.trigger( 'refresh:selection', this, selection );
     505                        if ( this.active&& ! selection.length && ! router.get( mode ) )
     506                                this.frame.content.render( this.get('content') );
     507                },
    380508
    381                         if ( ! selection.length && 'browse' !== mode && 'upload' !== mode )
    382                                 this.content();
     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 );
    383520                },
    384521
    385                 selectUpload: function( attachment ) {
    386                         this.get('selection').add( attachment );
     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
    389534                buildComposite: function() {
     
    448593                }
    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',
    492601                        multiple:   false,
     
    496605                        sortable:   true,
    497606                        searchable: false,
    498607                        toolbar:    'gallery-edit',
    499                         content:    'browse'
     608                        content:    'browse',
     609                        title:      l10n.editGalleryTitle,
     610                        priority:   60
    500611                },
    501612
    502613                initialize: function() {
     
    519630                        // Watch for uploaded attachments.
    520631                        this.get('library').observe( wp.Uploader.queue );
    521632
    522                         this.frame.content.on( 'activate:browse', this.gallerySettings, this );
     633                        this.frame.on( 'content:render:browse', this.gallerySettings, this );
    523634
    524635                        media.controller.Library.prototype.activate.apply( this, arguments );
    525636                },
     
    528639                        // Stop watching for uploaded attachments.
    529640                        this.get('library').unobserve( wp.Uploader.queue );
    530641
    531                         this.frame.content.off( null, null, this );
     642                        this.frame.off( 'content:render:browse', this.gallerySettings, this );
     643
    532644                        media.controller.Library.prototype.deactivate.apply( this, arguments );
    533645                },
    534646
    535                 gallerySettings: function() {
    536                         var library = this.get('library'),
    537                                 browser;
     647                gallerySettings: function( browser ) {
     648                        var library = this.get('library');
    538649
    539                         if ( ! library )
     650                        if ( ! library || ! browser )
    540651                                return;
    541652
    542653                        library.gallery = library.gallery || new Backbone.Model();
    543654
    544                         browser = this.frame.content.view();
    545 
    546655                        browser.sidebar.set({
    547656                                gallery: new media.view.Settings.Gallery({
    548657                                        controller: this,
     
    570679                        filterable: 'uploaded',
    571680                        multiple:   false,
    572681                        menu:       'main',
    573                         toolbar:    'featured-image'
     682                        toolbar:    'featured-image',
     683                        title:      l10n.featuredImageTitle,
     684                        priority:   60
    574685                }, media.controller.Library.prototype.defaults ),
    575686
    576687                initialize: function() {
     
    629740                        menu:    'main',
    630741                        content: 'embed',
    631742                        toolbar: 'main-embed',
    632                         type:    'link'
     743                        type:    'link',
     744
     745                        title:    l10n.fromUrlTitle,
     746                        priority: 120
    633747                },
    634748
    635749                // The amount of time used when debouncing the scan.
     
    637751
    638752                initialize: function() {
    639753                        this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
    640                         this.on( 'change:url', this.debouncedScan, this );
     754                        this.props = new Backbone.Model({ url: '' });
     755                        this.props.on( 'change:url', this.debouncedScan, this );
    641756                        this.on( 'scan', this.scanImage, this );
    642                         media.controller.State.prototype.initialize.apply( this, arguments );
    643757                },
    644758
    645759                scan: function() {
     
    652766                scanImage: function( attributes ) {
    653767                        var frame = this.frame,
    654768                                state = this,
    655                                 url = this.get('url'),
     769                                url = this.props.get('url'),
    656770                                image = new Image();
    657771
    658772                        image.onload = function() {
    659                                 if ( state !== frame.state() || url !== state.get('url') )
     773                                if ( state !== frame.state() || url !== state.props.get('url') )
    660774                                        return;
    661775
    662776                                state.set({
     
    670784                },
    671785
    672786                reset: function() {
    673                         _.each( _.difference( _.keys( this.attributes ), _.keys( this.defaults ) ), function( key ) {
    674                                 this.unset( key );
    675                         }, this );
     787                        this.props = new Backbone.Model({ url: '' });
    676788
    677                         this.set( 'url', '' );
    678 
    679789                        if ( this.id === this.frame.state().id )
    680                                 this.frame.toolbar.view().refresh();
     790                                this.frame.toolbar.get().refresh();
    681791                }
    682792        });
    683793
     
    10221132                // The constructor for the `Views` manager.
    10231133                Views: media.Views,
    10241134
    1025                 constructor: function() {
     1135                constructor: function( options ) {
    10261136                        this.views = new this.Views( this, this.views );
    10271137                        this.on( 'ready', this.ready, this );
     1138
     1139                        if ( options && options.controller )
     1140                                this.controller = options.controller;
     1141
    10281142                        Backbone.View.apply( this, arguments );
    10291143                },
    10301144
     
    10971211                        // Initialize regions.
    10981212                        _.each( this.regions, function( region ) {
    10991213                                this[ region ] = new media.controller.Region({
    1100                                         controller: this,
    1101                                         id:         region,
    1102                                         selector:   '.media-frame-' + region
     1214                                        view:    this,
     1215                                        id:       region,
     1216                                        selector: '.media-frame-' + region
    11031217                                });
    11041218                        }, this );
    11051219                },
     
    11131227                        // Ensure states have a reference to the frame.
    11141228                        this.states.on( 'add', function( model ) {
    11151229                                model.frame = this;
     1230                                model.trigger('ready');
    11161231                        }, this );
    11171232                },
    11181233
     
    11311246        media.view.MediaFrame = media.view.Frame.extend({
    11321247                className: 'media-frame',
    11331248                template:  media.template('media-frame'),
    1134                 regions:   ['menu','content','toolbar'],
     1249                regions:   ['menu','title','content','toolbar','router'],
    11351250
    11361251                initialize: function() {
    11371252                        media.view.Frame.prototype.initialize.apply( this, arguments );
     
    11731288                        }
    11741289
    11751290                        this.on( 'attach', _.bind( this.views.ready, this.views ), this );
     1291
     1292                        // Bind default title creation.
     1293                        this.on( 'title:create:default', this.createTitle, this );
     1294                        this.title.mode('default');
    11761295                },
    11771296
    11781297                render: function() {
     
    11831302                        return media.view.Frame.prototype.render.apply( this, arguments );
    11841303                },
    11851304
     1305                createTitle: function( title ) {
     1306                        title.view = new media.View({
     1307                                controller: this,
     1308                                tagName: 'h1'
     1309                        });
     1310                },
     1311
     1312                createMenu: function( menu ) {
     1313                        menu.view = new media.view.Menu({
     1314                                controller: this
     1315                        });
     1316                },
     1317
     1318                createToolbar: function( toolbar ) {
     1319                        menu.view = new media.view.Toolbar({
     1320                                controller: this
     1321                        });
     1322                },
     1323
     1324                createRouter: function( router ) {
     1325                        router.view = new media.view.Router({
     1326                                controller: this
     1327                        });
     1328                },
     1329
    11861330                createIframeStates: function( options ) {
    11871331                        var settings = media.view.settings,
    11881332                                tabs = settings.tabs,
     
    12081352                                }, options ) );
    12091353                        }, this );
    12101354
    1211                         this.content.on( 'activate:iframe', this.iframeContent, this );
    1212                         this.menu.on( 'activate:main', this.iframeMenu, this );
     1355                        this.on( 'content:create:iframe', this.iframeContent, this );
     1356                        this.on( 'menu:render:main', this.iframeMenu, this );
    12131357                        this.on( 'open', this.hijackThickbox, this );
    12141358                        this.on( 'close', this.restoreThickbox, this );
    12151359                },
    12161360
    1217                 iframeContent: function() {
     1361                iframeContent: function( content ) {
    12181362                        this.$el.addClass('hide-toolbar');
    1219                         this.content.view( new media.view.Iframe({
     1363                        content.view = new media.view.Iframe({
    12201364                                controller: this
    1221                         }).render() );
     1365                        });
    12221366                },
    12231367
    1224                 iframeMenu: function() {
     1368                iframeMenu: function( view ) {
    12251369                        var views = {};
    12261370
     1371                        if ( ! view )
     1372                                return;
     1373
    12271374                        _.each( media.view.settings.tabs, function( title, id ) {
    12281375                                views[ 'iframe:' + id ] = {
    12291376                                        text: this.state( 'iframe:' + id ).get('title'),
     
    12311378                                };
    12321379                        }, this );
    12331380
    1234                         this.menu.view().set( views );
     1381                        view.set( views );
    12351382                },
    12361383
    12371384                hijackThickbox: function() {
     
    13051452                                new media.controller.Library({
    13061453                                        selection: options.selection,
    13071454                                        library:   media.query( options.library ),
    1308                                         multiple:  this.options.multiple,
     1455                                        multiple:  options.multiple,
    13091456                                        menu:      'main',
    1310                                         toolbar:   'select'
    1311                                 }),
    1312 
    1313                                 new media.controller.Upload({
    1314                                         menu: 'main'
     1457                                        title:     options.title,
     1458                                        priority:  20
    13151459                                })
    13161460                        ]);
    13171461                },
    13181462
    13191463                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 );
     1464                        this.on( 'menu:create:main', this.createMenu, this );
     1465                        this.on( 'router:create:browse', this.createRouter, this );
     1466                        this.on( 'router:render:browse', this.browseRouter, this );
     1467                        this.on( 'content:create:browse', this.browseContent, this );
     1468                        this.on( 'content:render:upload', this.uploadContent, this );
     1469                        this.on( 'toolbar:create:select', this.createSelectToolbar, this );
    13241470
    13251471                        this.on( 'refresh:selection', this.refreshSelectToolbar, this );
    13261472                },
    13271473
    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                                         }
     1474                // Routers
     1475                browseRouter: function( view ) {
     1476                        view.set({
     1477                                upload: {
     1478                                        text:     l10n.uploadFilesTitle,
     1479                                        priority: 20
     1480                                },
     1481                                browse: {
     1482                                        text:     l10n.mediaLibraryTitle,
     1483                                        priority: 40
    13421484                                }
    1343                         }) );
     1485                        });
    13441486                },
    13451487
    13461488                // Content
    1347                 browseContent: function() {
     1489                browseContent: function( content ) {
    13481490                        var state = this.state();
    13491491
    13501492                        this.$el.removeClass('hide-toolbar');
    13511493
    13521494                        // Browse our library of attachments.
    1353                         this.content.view( new media.view.AttachmentsBrowser({
     1495                        content.view = new media.view.AttachmentsBrowser({
    13541496                                controller: this,
    13551497                                collection: state.get('library'),
    13561498                                selection:  state.get('selection'),
    13571499                                model:      state,
    13581500                                sortable:   state.get('sortable'),
    13591501                                search:     state.get('searchable'),
    1360                                 uploads:    state.get('uploads'),
    13611502                                filters:    state.get('filterable'),
    13621503                                display:    state.get('displaySettings'),
    13631504
    13641505                                AttachmentView: state.get('AttachmentView')
    1365                         }) );
     1506                        });
    13661507                },
    13671508
    13681509                uploadContent: function() {
    1369                         this.$el.addClass('hide-toolbar');
    1370 
    1371                         this.content.view( new media.view.UploaderInline({
     1510                        this.$el.removeClass('hide-toolbar');
     1511                        this.content.set( new media.view.UploaderInline({
    13721512                                controller: this
    13731513                        }) );
    13741514                },
    13751515
    13761516                // Toolbars
    1377                 selectToolbar: function( options ) {
     1517                createSelectToolbar: function( toolbar, options ) {
    13781518                        options = _.defaults( options || {}, {
    13791519                                event:  'select',
    13801520                                silent: false,
    13811521                                state:  false
    13821522                        });
    13831523
    1384                         this.toolbar.view( new media.view.Toolbar({
     1524                        toolbar.view = new media.view.Toolbar({
    13851525                                controller: this,
    13861526                                silent:     options.silent,
    13871527
     
    14021542                                                }
    14031543                                        }
    14041544                                }
    1405                         }) );
     1545                        });
    14061546                },
    14071547
    14081548                refreshSelectToolbar: function() {
     
    14111551                        if ( ! selection || 'select' !== this.toolbar.mode() )
    14121552                                return;
    14131553
    1414                         this.toolbar.view().get('select').model.set( 'disabled', ! selection.length );
     1554                        this.toolbar.get().get('select').model.set( 'disabled', ! selection.length );
    14151555                }
    14161556        });
    14171557
     
    14301570                },
    14311571
    14321572                createStates: function() {
    1433                         var options = this.options;
     1573                        var options = this.options,
     1574                                selection = options.selection;
    14341575
    14351576                        // Add the default states.
    14361577                        this.states.add([
    14371578                                // Main states.
    14381579                                new media.controller.Library({
    1439                                         selection:  options.selection,
     1580                                        id:         'insert',
     1581                                        title:      l10n.insertMediaTitle,
     1582                                        priority:   20,
     1583                                        menu:       'main',
     1584                                        toolbar:    'main-insert',
     1585                                        filterable: 'all',
    14401586                                        library:    media.query( options.library ),
     1587                                        selection:  selection,
     1588                                        multiple:   options.multiple ? 'reset' : false,
    14411589                                        editable:   true,
    1442                                         filterable: 'all',
    1443                                         multiple:   this.options.multiple,
    1444                                         menu:       'main',
    14451590
    14461591                                        // Show the attachment display settings.
    14471592                                        displaySettings: true,
     
    14501595                                        displayUserSettings: true
    14511596                                }),
    14521597
    1453                                 new media.controller.Upload({
    1454                                         menu: 'main'
     1598                                new media.controller.Library({
     1599                                        id:         'gallery',
     1600                                        title:      l10n.createGalleryTitle,
     1601                                        priority:   40,
     1602                                        menu:       'main',
     1603                                        toolbar:    'main-gallery',
     1604                                        filterable: 'uploaded',
     1605                                        multiple:   'add',
     1606                                        editable:   true,
     1607
     1608                                        library:  media.query( _.defaults({
     1609                                                type: 'image'
     1610                                        }, options.library ) ),
     1611
     1612                                        selection: new media.model.Selection( selection.models, {
     1613                                                multiple: 'add'
     1614                                        })
    14551615                                }),
    14561616
    14571617                                // Embed states.
    14581618                                new media.controller.Embed(),
    14591619
    14601620                                // Gallery states.
    1461                                 new media.controller.Gallery({
     1621                                new media.controller.GalleryEdit({
    14621622                                        library: options.selection,
    14631623                                        editing: options.editing,
    14641624                                        menu:    'gallery'
     
    14681628                                        id:           'gallery-library',
    14691629                                        library:      media.query({ type: 'image' }),
    14701630                                        filterable:   'uploaded',
    1471                                         multiple:     true,
     1631                                        multiple:     'add',
    14721632                                        menu:         'gallery',
    14731633                                        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'
     1634                                        excludeState: 'gallery-edit',
     1635                                        title:        l10n.addToGalleryTitle,
     1636                                        priority:     100
    14811637                                })
    14821638                        ]);
    14831639
     
    14921648
    14931649                bindHandlers: function() {
    14941650                        media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
     1651                        this.on( 'menu:create:gallery', this.createMenu, this );
     1652                        this.on( 'toolbar:create:main-insert', this.createSelectionToolbar, this );
     1653                        this.on( 'toolbar:create:main-gallery', this.createSelectionToolbar, this );
    14951654
    14961655                        var handlers = {
    14971656                                        menu: {
     1657                                                'main':    'mainMenu',
    14981658                                                'gallery': 'galleryMenu'
    14991659                                        },
    15001660
     
    15041664                                        },
    15051665
    15061666                                        toolbar: {
    1507                                                 'main-attachments': 'mainAttachmentsToolbar',
     1667                                                'main-insert':      'mainInsertToolbar',
     1668                                                'main-gallery':     'mainGalleryToolbar',
    15081669                                                'main-embed':       'mainEmbedToolbar',
    15091670                                                'featured-image':   'featuredImageToolbar',
    15101671                                                'gallery-edit':     'galleryEditToolbar',
     
    15141675
    15151676                        _.each( handlers, function( regionHandlers, region ) {
    15161677                                _.each( regionHandlers, function( callback, handler ) {
    1517                                         this[ region ].on( 'activate:' + handler, this[ callback ], this );
     1678                                        this.on( region + ':render:' + handler, this[ callback ], this );
    15181679                                }, this );
    15191680                        }, this );
    15201681                },
    15211682
    15221683                // Menus
    1523                 mainMenu: function() {
    1524                         media.view.MediaFrame.Select.prototype.mainMenu.call( this, { silent: true });
    1525 
    1526                         this.menu.view().set({
     1684                mainMenu: function( view ) {
     1685                        view.set({
    15271686                                'library-separator': new media.View({
    15281687                                        className: 'separator',
    1529                                         priority: 60
    1530                                 }),
    1531                                 'embed': {
    1532                                         text: l10n.fromUrlTitle,
    1533                                         priority: 80
    1534                                 }
     1688                                        priority: 100
     1689                                })
    15351690                        });
    1536 
    1537                         if ( media.view.settings.post.featuredImageId ) {
    1538                                 this.menu.view().set( 'featured-image', {
    1539                                         text: l10n.featuredImageTitle,
    1540                                         priority: 100
    1541                                 });
    1542                         }
    15431691                },
    15441692
    1545                 galleryMenu: function() {
     1693                galleryMenu: function( view ) {
    15461694                        var lastState = this.lastState(),
    15471695                                previous = lastState && lastState.id,
    15481696                                frame = this;
    15491697
    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
     1698                        view.set({
     1699                                cancel: {
     1700                                        text:     l10n.cancelGalleryTitle,
     1701                                        priority: 20,
     1702                                        click:    function() {
     1703                                                if ( previous )
     1704                                                        frame.setState( previous );
     1705                                                else
     1706                                                        frame.close();
    15781707                                        }
    1579                                 }
    1580                         }) );
     1708                                },
     1709                                separateCancel: new media.View({
     1710                                        className: 'separator',
     1711                                        priority: 40
     1712                                })
     1713                        });
    15811714                },
    15821715
    15831716                // Content
     
    15871720                                model:      this.state()
    15881721                        }).render();
    15891722
    1590                         this.content.view( view );
     1723                        this.content.set( view );
    15911724                        view.url.focus();
    15921725                },
    15931726
     
    16171750                        });
    16181751
    16191752                        // Browse our library of attachments.
    1620                         this.content.view( view );
     1753                        this.content.set( view );
    16211754                },
    16221755
    1623                 // Sidebars
    1624                 onSidebarGallerySettings: function( options ) {
    1625                         var library = this.state().get('library');
     1756                // Toolbars
     1757                createSelectionToolbar: function( toolbar ) {
     1758                        toolbar.view = new media.view.Toolbar.Selection({
     1759                                controller: this,
     1760                                editable:   this.state().get('editable')
     1761                        });
     1762                },
    16261763
    1627                         if ( ! library )
    1628                                 return;
     1764                mainInsertToolbar: function( view ) {
     1765                        var controller = this;
    16291766
    1630                         library.gallery = library.gallery || new Backbone.Model();
     1767                        view.button = 'insert';
     1768                        view.set( 'insert', {
     1769                                style:    'primary',
     1770                                priority: 80,
     1771                                text:     l10n.insertIntoPost,
    16311772
    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 );
     1773                                click: function() {
     1774                                        var state = controller.state(),
     1775                                                selection = state.get('selection');
     1776
     1777                                        controller.close();
     1778                                        state.trigger( 'insert', selection ).reset();
     1779                                }
     1780                        });
    16391781                },
    16401782
    1641                 // Toolbars
    1642                 mainAttachmentsToolbar: function() {
    1643                         this.toolbar.view( new media.view.Toolbar.Insert({
    1644                                 controller: this,
    1645                                 editable:   this.state().get('editable')
    1646                         }) );
     1783                mainGalleryToolbar: function( view ) {
     1784                        var controller = this;
     1785
     1786                        view.button = 'gallery';
     1787                        view.set( 'gallery', {
     1788                                style:    'primary',
     1789                                text:     l10n.createNewGallery,
     1790                                priority: 60,
     1791
     1792                                click: function() {
     1793                                        var selection = controller.state().get('selection'),
     1794                                                edit = controller.state('gallery-edit'),
     1795                                                models = selection.where({ type: 'image' });
     1796
     1797                                        edit.set( 'library', new media.model.Selection( models, {
     1798                                                props:    selection.props.toJSON(),
     1799                                                multiple: true
     1800                                        }) );
     1801
     1802                                        this.controller.setState('gallery-edit');
     1803                                }
     1804                        });
    16471805                },
    16481806
    16491807                featuredImageToolbar: function() {
    1650                         this.toolbar.view( new media.view.Toolbar.Select({
     1808                        this.toolbar.set( new media.view.Toolbar.Select({
    16511809                                controller: this,
    16521810                                text:       l10n.setFeaturedImage,
    16531811                                state:      this.options.state || 'upload'
     
    16551813                },
    16561814
    16571815                mainEmbedToolbar: function() {
    1658                         this.toolbar.view( new media.view.Toolbar.Embed({
     1816                        this.toolbar.set( new media.view.Toolbar.Embed({
    16591817                                controller: this
    16601818                        }) );
    16611819
     
    16641822
    16651823                galleryEditToolbar: function() {
    16661824                        var editing = this.state().get('editing');
    1667                         this.toolbar.view( new media.view.Toolbar({
     1825                        this.toolbar.set( new media.view.Toolbar({
    16681826                                controller: this,
    16691827                                items: {
    16701828                                        insert: {
     
    16891847                },
    16901848
    16911849                galleryAddToolbar: function() {
    1692                         this.toolbar.view( new media.view.Toolbar({
     1850                        this.toolbar.set( new media.view.Toolbar({
    16931851                                controller: this,
    16941852                                items: {
    16951853                                        insert: {
     
    17041862
    17051863                                                        edit.get('library').add( state.get('selection').models );
    17061864                                                        state.trigger('reset');
    1707                                                         controller.state('gallery-edit');
     1865                                                        controller.setState('gallery-edit');
    17081866                                                }
    17091867                                        }
    17101868                                }
     
    17291887                },
    17301888
    17311889                initialize: function() {
    1732                         this.controller = this.options.controller;
    1733 
    17341890                        _.defaults( this.options, {
    17351891                                container: document.body,
    17361892                                title:     '',
     
    18381994                initialize: function() {
    18391995                        var uploader;
    18401996
    1841                         this.controller = this.options.controller;
    1842 
    18431997                        this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    18441998
    18451999                        uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
     
    19062060                template:  media.template('uploader-inline'),
    19072061
    19082062                initialize: function() {
    1909                         this.controller = this.options.controller;
     2063                        _.defaults( this.options, {
     2064                                message: '',
     2065                                status:  true
     2066                        });
    19102067
    19112068                        if ( ! this.options.$browser && this.controller.uploader )
    19122069                                this.options.$browser = this.controller.uploader.$browser;
     
    19142071                        if ( _.isUndefined( this.options.postId ) )
    19152072                                this.options.postId = media.view.settings.post.id;
    19162073
    1917                         this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
    1918                                 controller: this.controller
    1919                         }) );
     2074                        if ( this.options.status ) {
     2075                                this.views.set( '.upload-inline-status', new media.view.UploaderStatus({
     2076                                        controller: this.controller
     2077                                }) );
     2078                        }
    19202079                },
    19212080
    19222081                ready: function() {
     
    19512110                },
    19522111
    19532112                initialize: function() {
    1954                         this.controller = this.options.controller;
    1955 
    19562113                        this.queue = wp.Uploader.queue;
    19572114                        this.queue.on( 'add remove reset', this.visibility, this );
    19582115                        this.queue.on( 'add remove reset change:percent', this.progress, this );
     
    20602217                className: 'media-toolbar',
    20612218
    20622219                initialize: function() {
    2063                         this.controller = this.options.controller;
    2064 
    20652220                        this._views     = {};
    20662221                        this.$primary   = $('<div class="media-toolbar-primary" />').prependTo( this.$el );
    20672222                        this.$secondary = $('<div class="media-toolbar-secondary" />').prependTo( this.$el );
     
    21992354        // ---------------------------
    22002355        media.view.Toolbar.Embed = media.view.Toolbar.Select.extend({
    22012356                initialize: function() {
    2202                         var controller = this.options.controller;
    2203 
    22042357                        _.defaults( this.options, {
    22052358                                text: l10n.insertIntoPost
    22062359                        });
    22072360
    22082361                        media.view.Toolbar.Select.prototype.initialize.apply( this, arguments );
    2209                         controller.on( 'change:url', this.refresh, this );
     2362                        this.controller.state().props.on( 'change:url', this.refresh, this );
    22102363                },
    22112364
    22122365                refresh: function() {
    2213                         var url = this.controller.state().get('url');
     2366                        var url = this.controller.state().props.get('url');
    22142367                        this.get('select').model.set( 'disabled', ! url || /^https?:\/\/$/.test(url) );
    22152368                }
    22162369        });
    22172370
    2218         // wp.media.view.Toolbar.Insert
    2219         // ----------------------------
    2220         media.view.Toolbar.Insert = media.view.Toolbar.extend({
     2371        // wp.media.view.Toolbar.Selection
     2372        // -------------------------------
     2373        media.view.Toolbar.Selection = media.view.Toolbar.extend({
     2374                button: 'insert',
     2375
    22212376                initialize: function() {
    2222                         var controller = this.options.controller,
    2223                                 selection = controller.state().get('selection'),
    2224                                 selectionToLibrary;
     2377                        var controller = this.controller;
    22252378
    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                         };
    2241 
    22422379                        this.options.items = _.defaults( this.options.items || {}, {
    22432380                                selection: new media.view.Selection({
    22442381                                        controller: controller,
    2245                                         collection: selection,
     2382                                        collection: controller.state().get('selection'),
    22462383                                        priority:   -40,
    22472384
    22482385                                        // If the selection is editable, pass the callback to
     
    22502387                                        editable: this.options.editable && function() {
    22512388                                                this.controller.content.mode('edit-selection');
    22522389                                        }
    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                                 }
     2390                                }).render()
    22732391                        });
    22742392
    22752393                        media.view.Toolbar.prototype.initialize.apply( this, arguments );
     
    22772395
    22782396                refresh: function() {
    22792397                        var selection = this.controller.state().get('selection'),
    2280                                 count = selection.length;
     2398                                button = this.get( this.button );
    22812399
    2282                         this.get('insert').model.set( 'disabled', ! selection.length );
     2400                        if ( ! button )
     2401                                return;
    22832402
    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                         }) );
     2403                        button.model.set( 'disabled', ! selection.length );
    22882404                }
    22892405        });
    22902406
     
    23882504                tagName:   'div',
    23892505
    23902506                initialize: function() {
    2391                         this.controller = this.options.controller;
    2392                         this._views     = {};
     2507                        this._views = {};
    23932508
    23942509                        this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
    23952510                        delete this.options.views;
     
    24572572                }
    24582573        });
    24592574
     2575        /**
     2576         * wp.media.view.MenuItem
     2577         */
     2578        media.view.MenuItem = media.View.extend({
     2579                tagName:   'a',
     2580                className: 'media-menu-item',
    24602581
     2582                attributes: {
     2583                        href: '#'
     2584                },
     2585
     2586                events: {
     2587                        'click': '_click'
     2588                },
     2589
     2590                _click: function( event ) {
     2591                        var clickOverride = this.options.click;
     2592
     2593                        if ( event )
     2594                                event.preventDefault();
     2595
     2596                        if ( clickOverride )
     2597                                clickOverride.call( this );
     2598                        else
     2599                                this.click();
     2600                },
     2601
     2602                click: function() {
     2603                        var state = this.options.state;
     2604                        if ( state )
     2605                                this.controller.setState( state );
     2606                },
     2607
     2608                render: function() {
     2609                        var options = this.options;
     2610
     2611                        if ( options.text )
     2612                                this.$el.text( options.text );
     2613                        else if ( options.html )
     2614                                this.$el.html( options.html );
     2615
     2616                        return this;
     2617                }
     2618        });
     2619
    24612620        /**
    24622621         * wp.media.view.Menu
    24632622         */
    24642623        media.view.Menu = media.view.PriorityList.extend({
    2465                 tagName:   'ul',
     2624                tagName:   'div',
    24662625                className: 'media-menu',
     2626                property:  'state',
     2627                ItemView:  media.view.MenuItem,
     2628                region:    'menu',
    24672629
    2468                 toView: function( options, state ) {
     2630                toView: function( options, id ) {
    24692631                        options = options || {};
    2470                         options.state = options.state || state;
    2471                         return new media.view.MenuItem( options ).render();
     2632                        options[ this.property ] = options[ this.property ] || id;
     2633                        return new this.ItemView( options ).render();
    24722634                },
    24732635
    2474                 select: function( state ) {
    2475                         var view = this.get( state );
     2636                ready: function() {
     2637                        media.view.PriorityList.prototype.ready.apply( this, arguments );
     2638                        this.visibility();
     2639                },
    24762640
     2641                set: function() {
     2642                        media.view.PriorityList.prototype.set.apply( this, arguments );
     2643                        this.visibility();
     2644                },
     2645
     2646                unset: function() {
     2647                        media.view.PriorityList.prototype.unset.apply( this, arguments );
     2648                        this.visibility();
     2649                },
     2650
     2651                visibility: function() {
     2652                        var region = this.region,
     2653                                view = this.controller[ region ].get(),
     2654                                views = this.views.get(),
     2655                                hide = ! views || views.length < 2;
     2656
     2657                        if ( this === view )
     2658                                this.controller.$el.toggleClass( 'hide-' + region, hide );
     2659                },
     2660
     2661                select: function( id ) {
     2662                        var view = this.get( id );
     2663
    24772664                        if ( ! view )
    24782665                                return;
    24792666
     
    24862673                }
    24872674        });
    24882675
    2489         media.view.MenuItem = media.View.extend({
    2490                 tagName:   'li',
    2491                 className: 'media-menu-item',
     2676        /**
     2677         * wp.media.view.RouterItem
     2678         */
     2679        media.view.RouterItem = media.view.MenuItem.extend({
     2680                click: function() {
     2681                        var contentMode = this.options.contentMode;
     2682                        if ( contentMode )
     2683                                this.controller.content.mode( contentMode );
     2684                }
     2685        });
    24922686
    2493                 events: {
    2494                         'click': 'click'
    2495                 },
     2687        /**
     2688         * wp.media.view.Router
     2689         */
     2690        media.view.Router = media.view.Menu.extend({
     2691                tagName:   'div',
     2692                className: 'media-router',
     2693                property:  'contentMode',
     2694                ItemView:  media.view.RouterItem,
     2695                region:    'router',
    24962696
    2497                 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 );
     2697                initialize: function() {
     2698                        this.controller.on( 'content:render', this.update, this );
     2699                        media.view.Menu.prototype.initialize.apply( this, arguments );
    25042700                },
    25052701
    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;
     2702                update: function() {
     2703                        var mode = this.controller.content.mode();
     2704                        if ( mode )
     2705                                this.select( mode );
    25152706                }
    25162707        });
    25172708
     2709
    25182710        /**
    25192711         * wp.media.view.Sidebar
    25202712         */
     
    25312723                template:  media.template('attachment'),
    25322724
    25332725                events: {
    2534                         'click .attachment-preview':      'toggleSelection',
     2726                        'click .attachment-preview':      'toggleSelectionHandler',
    25352727                        'change [data-setting]':          'updateSetting',
    25362728                        'change [data-setting] input':    'updateSetting',
    25372729                        'change [data-setting] select':   'updateSetting',
     
    25462738                initialize: function() {
    25472739                        var selection = this.options.selection;
    25482740
    2549                         this.controller = this.options.controller;
    2550 
    25512741                        this.model.on( 'change:sizes change:uploading change:caption change:title', this.render, this );
    25522742                        this.model.on( 'change:percent', this.progress, this );
    25532743
     
    26232813                                this.$bar.width( this.model.get('percent') + '%' );
    26242814                },
    26252815
    2626                 toggleSelection: function( event ) {
    2627                         var selection = this.options.selection,
    2628                                 model = this.model;
     2816                toggleSelectionHandler: function( event ) {
     2817                        var method;
    26292818
     2819                        if ( event.shiftKey )
     2820                                method = 'between';
     2821                        else if ( event.ctrlKey || event.metaKey )
     2822                                method = 'toggle';
     2823
     2824                        this.toggleSelection({
     2825                                method: method
     2826                        });
     2827                },
     2828
     2829                toggleSelection: function( options ) {
     2830                        var collection = this.collection,
     2831                                selection = this.options.selection,
     2832                                model = this.model,
     2833                                method = options && options.method,
     2834                                single, between, models, singleIndex, modelIndex;
     2835
    26302836                        if ( ! selection )
    26312837                                return;
    26322838
     2839                        single = selection.single();
     2840                        method = _.isUndefined( method ) ? selection.multiple : method;
     2841
     2842                        // If the `method` is set to `between`, select all models that
     2843                        // exist between the current and the selected model.
     2844                        if ( 'between' === method && single && selection.multiple ) {
     2845                                // If the models are the same, short-circuit.
     2846                                if ( single === model )
     2847                                        return;
     2848
     2849                                singleIndex = collection.indexOf( single );
     2850                                modelIndex  = collection.indexOf( this.model );
     2851
     2852                                if ( singleIndex < modelIndex )
     2853                                        models = collection.models.slice( singleIndex, modelIndex + 1 );
     2854                                else
     2855                                        models = collection.models.slice( modelIndex, singleIndex + 1 );
     2856
     2857                                selection.add( models ).single( model );
     2858                                return;
     2859
     2860                        // If the `method` is set to `toggle`, just flip the selection
     2861                        // status, regardless of whether the model is the single model.
     2862                        } else if ( 'toggle' === method ) {
     2863                                selection[ this.selected() ? 'remove' : 'add' ]( model ).single( model );
     2864                                return;
     2865                        }
     2866
     2867                        if ( method !== 'add' )
     2868                                method = 'reset';
     2869
    26332870                        if ( this.selected() ) {
    26342871                                // If the model is the single model, remove it.
    26352872                                // If it is not the same as the single model,
    26362873                                // it now becomes the single model.
    2637                                 selection[ selection.single() === model ? 'remove' : 'single' ]( model );
     2874                                selection[ single === model ? 'remove' : 'single' ]( model );
    26382875                        } else {
    2639                                 selection.add( model ).single( model );
     2876                                // If the model is not selected, run the `method` on the
     2877                                // selection. By default, we `reset` the selection, but the
     2878                                // `method` can be set to `add` the model to the selection.
     2879                                selection[ method ]( model ).single( model );
    26402880                        }
    26412881                },
    26422882
     
    28383078                },
    28393079
    28403080                initialize: function() {
    2841                         this.controller = this.options.controller;
    28423081                        this.el.id = _.uniqueId('__attachments-view-');
    28433082
    28443083                        _.defaults( this.options, {
     
    29503189                                        collection.remove( model, {
    29513190                                                silent: true
    29523191                                        }).add( model, {
    2953                                                 at:     ui.item.index(),
    2954                                                 silent: true
     3192                                                silent: true,
     3193                                                at:     ui.item.index()
    29553194                                        });
    29563195
    29573196                                        // Restore the comparator.
    29583197                                        collection.comparator = comparator;
    29593198
     3199                                        // Fire the `reset` event to ensure other collections sync.
     3200                                        collection.trigger( 'reset', collection );
     3201
    29603202                                        // If the collection is sorted by menu order,
    29613203                                        // update the menu order.
    29623204                                        collection.saveMenuOrder();
     
    32003442                className: 'attachments-browser',
    32013443
    32023444                initialize: function() {
    3203                         this.controller = this.options.controller;
    3204 
    32053445                        _.defaults( this.options, {
    32063446                                filters: false,
    32073447                                search:  true,
    3208                                 uploads: false,
    32093448                                display: false,
    32103449
    32113450                                AttachmentView: media.view.Attachment.Library
     
    32903529                        this.removeContent();
    32913530
    32923531                        this.uploader = new media.view.UploaderInline({
    3293                                 controller: this.controller
     3532                                controller: this.controller,
     3533                                status:     false,
     3534                                message:    l10n.noItemsFound
    32943535                        });
    32953536
    32963537                        this.views.add( this.uploader );
     
    33223563
    33233564                        this.views.add( sidebar );
    33243565
    3325                         if ( options.uploads && this.controller.uploader ) {
     3566                        if ( this.controller.uploader ) {
    33263567                                sidebar.set( 'uploads', new media.view.UploaderStatus({
    33273568                                        controller: this.controller,
    33283569                                        priority:   40
     
    33733614        });
    33743615
    33753616        /**
    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         /**
    34273617         * wp.media.view.Selection
    34283618         */
    34293619        media.view.Selection = media.View.extend({
     
    34423632                                clearable: true
    34433633                        });
    34443634
    3445                         this.controller = this.options.controller;
    34463635                        this.attachments = new media.view.Attachments({
    34473636                                controller: this.controller,
    34483637                                collection: this.collection,
     
    36593848                                $input = this.$('.link-to-custom'),
    36603849                                attachment = this.options.attachment;
    36613850
    3662                         if ( 'none' === linkTo ) {
     3851                        if ( 'none' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
    36633852                                $input.hide();
    36643853                                return;
    36653854                        }
    36663855
    3667                         $input.show();
     3856                        if ( attachment ) {
     3857                                if ( 'post' === linkTo ) {
     3858                                        $input.val( attachment.get('link') );
     3859                                } else if ( 'file' === linkTo ) {
     3860                                        $input.val( attachment.get('url') );
     3861                                } else if ( ! this.model.get('linkUrl') ) {
     3862                                        $input.val('http://');
     3863                                }
    36683864
    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://');
     3865                                $input.prop( 'readonly', 'custom' !== linkTo );
    36753866                        }
    36763867
    3677                         $input.prop( 'readonly', 'custom' !== linkTo );
     3868                        $input.show();
    36783869
    36793870                        // If the input is visible, focus and select its contents.
    36803871                        if ( $input.is(':visible') )
     
    37683959        media.view.Iframe = media.View.extend({
    37693960                className: 'media-iframe',
    37703961
    3771                 initialize: function() {
    3772                         this.controller = this.options.controller;
    3773                 },
    3774 
    37753962                render: function() {
     3963                        this.views.detach();
    37763964                        this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
     3965                        this.views.render();
    37773966                        return this;
    37783967                }
    37793968        });
     
    37853974                className: 'media-embed',
    37863975
    37873976                initialize: function() {
    3788                         this.controller = this.options.controller;
    3789 
    37903977                        this.url = new media.view.EmbedUrl({
    37913978                                controller: this.controller,
    3792                                 model:      this.model
     3979                                model:      this.model.props
    37933980                        }).render();
    37943981
    37953982                        this._settings = new media.View();
     
    38264013
    38274014                        this.settings( new constructor({
    38284015                                controller: this.controller,
    3829                                 model:      this.model,
     4016                                model:      this.model.props,
    38304017                                priority:   40
    38314018                        }) );
    38324019                }
     
    38464033                },
    38474034
    38484035                initialize: function() {
    3849                         this.label = this.make( 'span', null, this.options.label || l10n.url );
    38504036                        this.input = this.make( 'input', {
    38514037                                type:  'text',
    38524038                                value: this.model.get('url') || ''
    38534039                        });
    38544040
    3855                         this.$label = $( this.label );
    38564041                        this.$input = $( this.input );
    3857                         this.$el.append([ this.label, this.input ]);
     4042                        this.$el.append( this.input );
    38584043
    38594044                        this.model.on( 'change:url', this.render, this );
    38604045                },
  • wp-includes/media.php

     
    14671467
    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' ),
    14751477                'warnDelete' =>      __( "You are about to permanently delete this item.\n  'Cancel' to stop, 'OK' to delete." ),
     
    14891491                'updateGallery'      => __( 'Update gallery' ),
    14901492                'continueEditing'    => __( 'Continue editing' ),
    14911493                'addToGallery'       => __( 'Add to gallery' ),
     1494                'addToGalleryTitle'  => __( 'Add to Gallery' ),
    14921495                'reverseOrder'       => __( 'Reverse order' ),
    14931496        );
    14941497
     
    15171520        ?>
    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>
    15221527                <div class="media-frame-uploader"></div>
     
    15241529
    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
    15361538        <script type="text/html" id="tmpl-uploader-window">
     
    15401542        </script>
    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>
    15551561
     
    17441750                <div class="selection-view"></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>
    17621755