Make WordPress Core

Ticket #28510: 28510.2.diff

File 28510.2.diff, 141.7 KB (added by wonderboymusic, 10 years ago)
  • src/wp-includes/js/media/controllers/edit-attachment-metadata.js

     
    99 * @augments wp.media.controller.State
    1010 * @augments Backbone.Model
    1111 */
    12 var State = require( './state.js' ),
     12var State = wp.media.controller.State,
    1313        l10n = wp.media.view.l10n,
    1414        EditAttachmentMetadata;
    1515
  • src/wp-includes/js/media/grid.js

     
    1010 * @augments wp.media.controller.State
    1111 * @augments Backbone.Model
    1212 */
    13 var State = require( './state.js' ),
     13var State = wp.media.controller.State,
    1414        l10n = wp.media.view.l10n,
    1515        EditAttachmentMetadata;
    1616
     
    2929
    3030module.exports = EditAttachmentMetadata;
    3131
    32 },{"./state.js":6}],2:[function(require,module,exports){
     32},{}],2:[function(require,module,exports){
    3333/*globals wp */
    3434
    35 /**
    36  * wp.media.controller.EditImage
    37  *
    38  * A state for editing (cropping, etc.) an image.
    39  *
    40  * @class
    41  * @augments wp.media.controller.State
    42  * @augments Backbone.Model
    43  *
    44  * @param {object}                    attributes                      The attributes hash passed to the state.
    45  * @param {wp.media.model.Attachment} attributes.model                The attachment.
    46  * @param {string}                    [attributes.id=edit-image]      Unique identifier.
    47  * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
    48  * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
    49  * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
    50  * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
    51  * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
    52  */
    53 var State = require( './state.js' ),
    54         ToolbarView = require( '../views/toolbar.js' ),
    55         l10n = wp.media.view.l10n,
    56         EditImage;
    57 
    58 EditImage = State.extend({
    59         defaults: {
    60                 id:      'edit-image',
    61                 title:   l10n.editImage,
    62                 menu:    false,
    63                 toolbar: 'edit-image',
    64                 content: 'edit-image',
    65                 url:     ''
    66         },
    67 
    68         /**
    69          * @since 3.9.0
    70          */
    71         activate: function() {
    72                 this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
    73         },
    74 
    75         /**
    76          * @since 3.9.0
    77          */
    78         deactivate: function() {
    79                 this.stopListening( this.frame );
    80         },
    81 
    82         /**
    83          * @since 3.9.0
    84          */
    85         toolbar: function() {
    86                 var frame = this.frame,
    87                         lastState = frame.lastState(),
    88                         previous = lastState && lastState.id;
    89 
    90                 frame.toolbar.set( new ToolbarView({
    91                         controller: frame,
    92                         items: {
    93                                 back: {
    94                                         style: 'primary',
    95                                         text:     l10n.back,
    96                                         priority: 20,
    97                                         click:    function() {
    98                                                 if ( previous ) {
    99                                                         frame.setState( previous );
    100                                                 } else {
    101                                                         frame.close();
    102                                                 }
    103                                         }
    104                                 }
    105                         }
    106                 }) );
    107         }
    108 });
    109 
    110 module.exports = EditImage;
    111 
    112 },{"../views/toolbar.js":44,"./state.js":6}],3:[function(require,module,exports){
    113 /*globals wp, _, Backbone */
    114 
    115 /**
    116  * wp.media.controller.Library
    117  *
    118  * A state for choosing an attachment or group of attachments from the media library.
    119  *
    120  * @class
    121  * @augments wp.media.controller.State
    122  * @augments Backbone.Model
    123  * @mixes media.selectionSync
    124  *
    125  * @param {object}                          [attributes]                         The attributes hash passed to the state.
    126  * @param {string}                          [attributes.id=library]              Unique identifier.
    127  * @param {string}                          [attributes.title=Media library]     Title for the state. Displays in the media menu and the frame's title region.
    128  * @param {wp.media.model.Attachments}      [attributes.library]                 The attachments collection to browse.
    129  *                                                                               If one is not supplied, a collection of all attachments will be created.
    130  * @param {wp.media.model.Selection|object} [attributes.selection]               A collection to contain attachment selections within the state.
    131  *                                                                               If the 'selection' attribute is a plain JS object,
    132  *                                                                               a Selection will be created using its values as the selection instance's `props` model.
    133  *                                                                               Otherwise, it will copy the library's `props` model.
    134  * @param {boolean}                         [attributes.multiple=false]          Whether multi-select is enabled.
    135  * @param {string}                          [attributes.content=upload]          Initial mode for the content region.
    136  *                                                                               Overridden by persistent user setting if 'contentUserSetting' is true.
    137  * @param {string}                          [attributes.menu=default]            Initial mode for the menu region.
    138  * @param {string}                          [attributes.router=browse]           Initial mode for the router region.
    139  * @param {string}                          [attributes.toolbar=select]          Initial mode for the toolbar region.
    140  * @param {boolean}                         [attributes.searchable=true]         Whether the library is searchable.
    141  * @param {boolean|string}                  [attributes.filterable=false]        Whether the library is filterable, and if so what filters should be shown.
    142  *                                                                               Accepts 'all', 'uploaded', or 'unattached'.
    143  * @param {boolean}                         [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    144  * @param {boolean}                         [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
    145  * @param {boolean}                         [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
    146  * @param {boolean}                         [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
    147  * @param {boolean}                         [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
    148  */
    149 var selectionSync = require( '../utils/selection-sync.js' ),
    150         State = require( './state.js' ),
    151         l10n = wp.media.view.l10n,
    152         getUserSetting = window.getUserSetting,
    153         setUserSetting = window.setUserSetting,
    154         Library;
    155 
    156 Library = State.extend({
    157         defaults: {
    158                 id:                 'library',
    159                 title:              l10n.mediaLibraryTitle,
    160                 multiple:           false,
    161                 content:            'upload',
    162                 menu:               'default',
    163                 router:             'browse',
    164                 toolbar:            'select',
    165                 searchable:         true,
    166                 filterable:         false,
    167                 sortable:           true,
    168                 autoSelect:         true,
    169                 describe:           false,
    170                 contentUserSetting: true,
    171                 syncSelection:      true
    172         },
    173 
    174         /**
    175          * If a library isn't provided, query all media items.
    176          * If a selection instance isn't provided, create one.
    177          *
    178          * @since 3.5.0
    179          */
    180         initialize: function() {
    181                 var selection = this.get('selection'),
    182                         props;
    183 
    184                 if ( ! this.get('library') ) {
    185                         this.set( 'library', wp.media.query() );
    186                 }
    187 
    188                 if ( ! ( selection instanceof wp.media.model.Selection ) ) {
    189                         props = selection;
    190 
    191                         if ( ! props ) {
    192                                 props = this.get('library').props.toJSON();
    193                                 props = _.omit( props, 'orderby', 'query' );
    194                         }
    195 
    196                         this.set( 'selection', new wp.media.model.Selection( null, {
    197                                 multiple: this.get('multiple'),
    198                                 props: props
    199                         }) );
    200                 }
    201 
    202                 this.resetDisplays();
    203         },
    204 
    205         /**
    206          * @since 3.5.0
    207          */
    208         activate: function() {
    209                 this.syncSelection();
    210 
    211                 wp.Uploader.queue.on( 'add', this.uploading, this );
    212 
    213                 this.get('selection').on( 'add remove reset', this.refreshContent, this );
    214 
    215                 if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
    216                         this.frame.on( 'content:activate', this.saveContentMode, this );
    217                         this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
    218                 }
    219         },
    220 
    221         /**
    222          * @since 3.5.0
    223          */
    224         deactivate: function() {
    225                 this.recordSelection();
    226 
    227                 this.frame.off( 'content:activate', this.saveContentMode, this );
    228 
    229                 // Unbind all event handlers that use this state as the context
    230                 // from the selection.
    231                 this.get('selection').off( null, null, this );
    232 
    233                 wp.Uploader.queue.off( null, null, this );
    234         },
    235 
    236         /**
    237          * Reset the library to its initial state.
    238          *
    239          * @since 3.5.0
    240          */
    241         reset: function() {
    242                 this.get('selection').reset();
    243                 this.resetDisplays();
    244                 this.refreshContent();
    245         },
    246 
    247         /**
    248          * Reset the attachment display settings defaults to the site options.
    249          *
    250          * If site options don't define them, fall back to a persistent user setting.
    251          *
    252          * @since 3.5.0
    253          */
    254         resetDisplays: function() {
    255                 var defaultProps = wp.media.view.settings.defaultProps;
    256                 this._displays = [];
    257                 this._defaultDisplaySettings = {
    258                         align: defaultProps.align || getUserSetting( 'align', 'none' ),
    259                         size:  defaultProps.size  || getUserSetting( 'imgsize', 'medium' ),
    260                         link:  defaultProps.link  || getUserSetting( 'urlbutton', 'file' )
    261                 };
    262         },
    263 
    264         /**
    265          * Create a model to represent display settings (alignment, etc.) for an attachment.
    266          *
    267          * @since 3.5.0
    268          *
    269          * @param {wp.media.model.Attachment} attachment
    270          * @returns {Backbone.Model}
    271          */
    272         display: function( attachment ) {
    273                 var displays = this._displays;
    274 
    275                 if ( ! displays[ attachment.cid ] ) {
    276                         displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
    277                 }
    278                 return displays[ attachment.cid ];
    279         },
    280 
    281         /**
    282          * Given an attachment, create attachment display settings properties.
    283          *
    284          * @since 3.6.0
    285          *
    286          * @param {wp.media.model.Attachment} attachment
    287          * @returns {Object}
    288          */
    289         defaultDisplaySettings: function( attachment ) {
    290                 var settings = this._defaultDisplaySettings;
    291                 if ( settings.canEmbed = this.canEmbed( attachment ) ) {
    292                         settings.link = 'embed';
    293                 }
    294                 return settings;
    295         },
    296 
    297         /**
    298          * Whether an attachment can be embedded (audio or video).
    299          *
    300          * @since 3.6.0
    301          *
    302          * @param {wp.media.model.Attachment} attachment
    303          * @returns {Boolean}
    304          */
    305         canEmbed: function( attachment ) {
    306                 // If uploading, we know the filename but not the mime type.
    307                 if ( ! attachment.get('uploading') ) {
    308                         var type = attachment.get('type');
    309                         if ( type !== 'audio' && type !== 'video' ) {
    310                                 return false;
    311                         }
    312                 }
    313 
    314                 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
    315         },
    316 
    317 
    318         /**
    319          * If the state is active, no items are selected, and the current
    320          * content mode is not an option in the state's router (provided
    321          * the state has a router), reset the content mode to the default.
    322          *
    323          * @since 3.5.0
    324          */
    325         refreshContent: function() {
    326                 var selection = this.get('selection'),
    327                         frame = this.frame,
    328                         router = frame.router.get(),
    329                         mode = frame.content.mode();
    330 
    331                 if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
    332                         this.frame.content.render( this.get('content') );
    333                 }
    334         },
    335 
    336         /**
    337          * Callback handler when an attachment is uploaded.
    338          *
    339          * Switch to the Media Library if uploaded from the 'Upload Files' tab.
    340          *
    341          * Adds any uploading attachments to the selection.
    342          *
    343          * If the state only supports one attachment to be selected and multiple
    344          * attachments are uploaded, the last attachment in the upload queue will
    345          * be selected.
    346          *
    347          * @since 3.5.0
    348          *
    349          * @param {wp.media.model.Attachment} attachment
    350          */
    351         uploading: function( attachment ) {
    352                 var content = this.frame.content;
    353 
    354                 if ( 'upload' === content.mode() ) {
    355                         this.frame.content.mode('browse');
    356                 }
    357 
    358                 if ( this.get( 'autoSelect' ) ) {
    359                         this.get('selection').add( attachment );
    360                         this.frame.trigger( 'library:selection:add' );
    361                 }
    362         },
    363 
    364         /**
    365          * Persist the mode of the content region as a user setting.
    366          *
    367          * @since 3.5.0
    368          */
    369         saveContentMode: function() {
    370                 if ( 'browse' !== this.get('router') ) {
    371                         return;
    372                 }
    373 
    374                 var mode = this.frame.content.mode(),
    375                         view = this.frame.router.get();
    376 
    377                 if ( view && view.get( mode ) ) {
    378                         setUserSetting( 'libraryContent', mode );
    379                 }
    380         }
    381 });
    382 
    383 // Make selectionSync available on any Media Library state.
    384 _.extend( Library.prototype, selectionSync );
    385 
    386 module.exports = Library;
    387 
    388 },{"../utils/selection-sync.js":9,"./state.js":6}],4:[function(require,module,exports){
    389 /*globals Backbone, _ */
    390 
    391 /**
    392  * wp.media.controller.Region
    393  *
    394  * A region is a persistent application layout area.
    395  *
    396  * A region assumes one mode at any time, and can be switched to another.
    397  *
    398  * When mode changes, events are triggered on the region's parent view.
    399  * The parent view will listen to specific events and fill the region with an
    400  * appropriate view depending on mode. For example, a frame listens for the
    401  * 'browse' mode t be activated on the 'content' view and then fills the region
    402  * with an AttachmentsBrowser view.
    403  *
    404  * @class
    405  *
    406  * @param {object}        options          Options hash for the region.
    407  * @param {string}        options.id       Unique identifier for the region.
    408  * @param {Backbone.View} options.view     A parent view the region exists within.
    409  * @param {string}        options.selector jQuery selector for the region within the parent view.
    410  */
    411 var Region = function( options ) {
    412         _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
    413 };
    414 
    415 // Use Backbone's self-propagating `extend` inheritance method.
    416 Region.extend = Backbone.Model.extend;
    417 
    418 _.extend( Region.prototype, {
    419         /**
    420          * Activate a mode.
    421          *
    422          * @since 3.5.0
    423          *
    424          * @param {string} mode
    425          *
    426          * @fires this.view#{this.id}:activate:{this._mode}
    427          * @fires this.view#{this.id}:activate
    428          * @fires this.view#{this.id}:deactivate:{this._mode}
    429          * @fires this.view#{this.id}:deactivate
    430          *
    431          * @returns {wp.media.controller.Region} Returns itself to allow chaining.
    432          */
    433         mode: function( mode ) {
    434                 if ( ! mode ) {
    435                         return this._mode;
    436                 }
    437                 // Bail if we're trying to change to the current mode.
    438                 if ( mode === this._mode ) {
    439                         return this;
    440                 }
    441 
    442                 /**
    443                  * Region mode deactivation event.
    444                  *
    445                  * @event this.view#{this.id}:deactivate:{this._mode}
    446                  * @event this.view#{this.id}:deactivate
    447                  */
    448                 this.trigger('deactivate');
    449 
    450                 this._mode = mode;
    451                 this.render( mode );
    452 
    453                 /**
    454                  * Region mode activation event.
    455                  *
    456                  * @event this.view#{this.id}:activate:{this._mode}
    457                  * @event this.view#{this.id}:activate
    458                  */
    459                 this.trigger('activate');
    460                 return this;
    461         },
    462         /**
    463          * Render a mode.
    464          *
    465          * @since 3.5.0
    466          *
    467          * @param {string} mode
    468          *
    469          * @fires this.view#{this.id}:create:{this._mode}
    470          * @fires this.view#{this.id}:create
    471          * @fires this.view#{this.id}:render:{this._mode}
    472          * @fires this.view#{this.id}:render
    473          *
    474          * @returns {wp.media.controller.Region} Returns itself to allow chaining
    475          */
    476         render: function( mode ) {
    477                 // If the mode isn't active, activate it.
    478                 if ( mode && mode !== this._mode ) {
    479                         return this.mode( mode );
    480                 }
    481 
    482                 var set = { view: null },
    483                         view;
    484 
    485                 /**
    486                  * Create region view event.
    487                  *
    488                  * Region view creation takes place in an event callback on the frame.
    489                  *
    490                  * @event this.view#{this.id}:create:{this._mode}
    491                  * @event this.view#{this.id}:create
    492                  */
    493                 this.trigger( 'create', set );
    494                 view = set.view;
    495 
    496                 /**
    497                  * Render region view event.
    498                  *
    499                  * Region view creation takes place in an event callback on the frame.
    500                  *
    501                  * @event this.view#{this.id}:create:{this._mode}
    502                  * @event this.view#{this.id}:create
    503                  */
    504                 this.trigger( 'render', view );
    505                 if ( view ) {
    506                         this.set( view );
    507                 }
    508                 return this;
    509         },
    510 
    511         /**
    512          * Get the region's view.
    513          *
    514          * @since 3.5.0
    515          *
    516          * @returns {wp.media.View}
    517          */
    518         get: function() {
    519                 return this.view.views.first( this.selector );
    520         },
    521 
    522         /**
    523          * Set the region's view as a subview of the frame.
    524          *
    525          * @since 3.5.0
    526          *
    527          * @param {Array|Object} views
    528          * @param {Object} [options={}]
    529          * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
    530          */
    531         set: function( views, options ) {
    532                 if ( options ) {
    533                         options.add = false;
    534                 }
    535                 return this.view.views.set( this.selector, views, options );
    536         },
    537 
    538         /**
    539          * Trigger regional view events on the frame.
    540          *
    541          * @since 3.5.0
    542          *
    543          * @param {string} event
    544          * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
    545          */
    546         trigger: function( event ) {
    547                 var base, args;
    548 
    549                 if ( ! this._mode ) {
    550                         return;
    551                 }
    552 
    553                 args = _.toArray( arguments );
    554                 base = this.id + ':' + event;
    555 
    556                 // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
    557                 args[0] = base + ':' + this._mode;
    558                 this.view.trigger.apply( this.view, args );
    559 
    560                 // Trigger `{this.id}:{event}` event on the frame.
    561                 args[0] = base;
    562                 this.view.trigger.apply( this.view, args );
    563                 return this;
    564         }
    565 });
    566 
    567 module.exports = Region;
    568 
    569 },{}],5:[function(require,module,exports){
    570 /*globals _, Backbone */
    571 
    572 /**
    573  * wp.media.controller.StateMachine
    574  *
    575  * A state machine keeps track of state. It is in one state at a time,
    576  * and can change from one state to another.
    577  *
    578  * States are stored as models in a Backbone collection.
    579  *
    580  * @since 3.5.0
    581  *
    582  * @class
    583  * @augments Backbone.Model
    584  * @mixin
    585  * @mixes Backbone.Events
    586  *
    587  * @param {Array} states
    588  */
    589 var StateMachine = function( states ) {
    590         // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
    591         this.states = new Backbone.Collection( states );
    592 };
    593 
    594 // Use Backbone's self-propagating `extend` inheritance method.
    595 StateMachine.extend = Backbone.Model.extend;
    596 
    597 _.extend( StateMachine.prototype, Backbone.Events, {
    598         /**
    599          * Fetch a state.
    600          *
    601          * If no `id` is provided, returns the active state.
    602          *
    603          * Implicitly creates states.
    604          *
    605          * Ensure that the `states` collection exists so the `StateMachine`
    606          *   can be used as a mixin.
    607          *
    608          * @since 3.5.0
    609          *
    610          * @param {string} id
    611          * @returns {wp.media.controller.State} Returns a State model
    612          *   from the StateMachine collection
    613          */
    614         state: function( id ) {
    615                 this.states = this.states || new Backbone.Collection();
    616 
    617                 // Default to the active state.
    618                 id = id || this._state;
    619 
    620                 if ( id && ! this.states.get( id ) ) {
    621                         this.states.add({ id: id });
    622                 }
    623                 return this.states.get( id );
    624         },
    625 
    626         /**
    627          * Sets the active state.
    628          *
    629          * Bail if we're trying to select the current state, if we haven't
    630          * created the `states` collection, or are trying to select a state
    631          * that does not exist.
    632          *
    633          * @since 3.5.0
    634          *
    635          * @param {string} id
    636          *
    637          * @fires wp.media.controller.State#deactivate
    638          * @fires wp.media.controller.State#activate
    639          *
    640          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
    641          */
    642         setState: function( id ) {
    643                 var previous = this.state();
    644 
    645                 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
    646                         return this;
    647                 }
    648 
    649                 if ( previous ) {
    650                         previous.trigger('deactivate');
    651                         this._lastState = previous.id;
    652                 }
    653 
    654                 this._state = id;
    655                 this.state().trigger('activate');
    656 
    657                 return this;
    658         },
    659 
    660         /**
    661          * Returns the previous active state.
    662          *
    663          * Call the `state()` method with no parameters to retrieve the current
    664          * active state.
    665          *
    666          * @since 3.5.0
    667          *
    668          * @returns {wp.media.controller.State} Returns a State model
    669          *    from the StateMachine collection
    670          */
    671         lastState: function() {
    672                 if ( this._lastState ) {
    673                         return this.state( this._lastState );
    674                 }
    675         }
    676 });
    677 
    678 // Map all event binding and triggering on a StateMachine to its `states` collection.
    679 _.each([ 'on', 'off', 'trigger' ], function( method ) {
    680         /**
    681          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
    682          */
    683         StateMachine.prototype[ method ] = function() {
    684                 // Ensure that the `states` collection exists so the `StateMachine`
    685                 // can be used as a mixin.
    686                 this.states = this.states || new Backbone.Collection();
    687                 // Forward the method to the `states` collection.
    688                 this.states[ method ].apply( this.states, arguments );
    689                 return this;
    690         };
    691 });
    692 
    693 module.exports = StateMachine;
    694 
    695 },{}],6:[function(require,module,exports){
    696 /*globals _, Backbone */
    697 
    698 /**
    699  * wp.media.controller.State
    700  *
    701  * A state is a step in a workflow that when set will trigger the controllers
    702  * for the regions to be updated as specified in the frame.
    703  *
    704  * A state has an event-driven lifecycle:
    705  *
    706  *     'ready'      triggers when a state is added to a state machine's collection.
    707  *     'activate'   triggers when a state is activated by a state machine.
    708  *     'deactivate' triggers when a state is deactivated by a state machine.
    709  *     'reset'      is not triggered automatically. It should be invoked by the
    710  *                  proper controller to reset the state to its default.
    711  *
    712  * @class
    713  * @augments Backbone.Model
    714  */
    715 var State = Backbone.Model.extend({
    716         /**
    717          * Constructor.
    718          *
    719          * @since 3.5.0
    720          */
    721         constructor: function() {
    722                 this.on( 'activate', this._preActivate, this );
    723                 this.on( 'activate', this.activate, this );
    724                 this.on( 'activate', this._postActivate, this );
    725                 this.on( 'deactivate', this._deactivate, this );
    726                 this.on( 'deactivate', this.deactivate, this );
    727                 this.on( 'reset', this.reset, this );
    728                 this.on( 'ready', this._ready, this );
    729                 this.on( 'ready', this.ready, this );
    730                 /**
    731                  * Call parent constructor with passed arguments
    732                  */
    733                 Backbone.Model.apply( this, arguments );
    734                 this.on( 'change:menu', this._updateMenu, this );
    735         },
    736         /**
    737          * Ready event callback.
    738          *
    739          * @abstract
    740          * @since 3.5.0
    741          */
    742         ready: function() {},
    743 
    744         /**
    745          * Activate event callback.
    746          *
    747          * @abstract
    748          * @since 3.5.0
    749          */
    750         activate: function() {},
    751 
    752         /**
    753          * Deactivate event callback.
    754          *
    755          * @abstract
    756          * @since 3.5.0
    757          */
    758         deactivate: function() {},
    759 
    760         /**
    761          * Reset event callback.
    762          *
    763          * @abstract
    764          * @since 3.5.0
    765          */
    766         reset: function() {},
    767 
    768         /**
    769          * @access private
    770          * @since 3.5.0
    771          */
    772         _ready: function() {
    773                 this._updateMenu();
    774         },
    775 
    776         /**
    777          * @access private
    778          * @since 3.5.0
    779         */
    780         _preActivate: function() {
    781                 this.active = true;
    782         },
    783 
    784         /**
    785          * @access private
    786          * @since 3.5.0
    787          */
    788         _postActivate: function() {
    789                 this.on( 'change:menu', this._menu, this );
    790                 this.on( 'change:titleMode', this._title, this );
    791                 this.on( 'change:content', this._content, this );
    792                 this.on( 'change:toolbar', this._toolbar, this );
    793 
    794                 this.frame.on( 'title:render:default', this._renderTitle, this );
    795 
    796                 this._title();
    797                 this._menu();
    798                 this._toolbar();
    799                 this._content();
    800                 this._router();
    801         },
    802 
    803         /**
    804          * @access private
    805          * @since 3.5.0
    806          */
    807         _deactivate: function() {
    808                 this.active = false;
    809 
    810                 this.frame.off( 'title:render:default', this._renderTitle, this );
    811 
    812                 this.off( 'change:menu', this._menu, this );
    813                 this.off( 'change:titleMode', this._title, this );
    814                 this.off( 'change:content', this._content, this );
    815                 this.off( 'change:toolbar', this._toolbar, this );
    816         },
    817 
    818         /**
    819          * @access private
    820          * @since 3.5.0
    821          */
    822         _title: function() {
    823                 this.frame.title.render( this.get('titleMode') || 'default' );
    824         },
    825 
    826         /**
    827          * @access private
    828          * @since 3.5.0
    829          */
    830         _renderTitle: function( view ) {
    831                 view.$el.text( this.get('title') || '' );
    832         },
    833 
    834         /**
    835          * @access private
    836          * @since 3.5.0
    837          */
    838         _router: function() {
    839                 var router = this.frame.router,
    840                         mode = this.get('router'),
    841                         view;
    842 
    843                 this.frame.$el.toggleClass( 'hide-router', ! mode );
    844                 if ( ! mode ) {
    845                         return;
    846                 }
    847 
    848                 this.frame.router.render( mode );
    849 
    850                 view = router.get();
    851                 if ( view && view.select ) {
    852                         view.select( this.frame.content.mode() );
    853                 }
    854         },
    855 
    856         /**
    857          * @access private
    858          * @since 3.5.0
    859          */
    860         _menu: function() {
    861                 var menu = this.frame.menu,
    862                         mode = this.get('menu'),
    863                         view;
    864 
    865                 this.frame.$el.toggleClass( 'hide-menu', ! mode );
    866                 if ( ! mode ) {
    867                         return;
    868                 }
    869 
    870                 menu.mode( mode );
    871 
    872                 view = menu.get();
    873                 if ( view && view.select ) {
    874                         view.select( this.id );
    875                 }
    876         },
    877 
    878         /**
    879          * @access private
    880          * @since 3.5.0
    881          */
    882         _updateMenu: function() {
    883                 var previous = this.previous('menu'),
    884                         menu = this.get('menu');
    885 
    886                 if ( previous ) {
    887                         this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
    888                 }
    889 
    890                 if ( menu ) {
    891                         this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
    892                 }
    893         },
    894 
    895         /**
    896          * Create a view in the media menu for the state.
    897          *
    898          * @access private
    899          * @since 3.5.0
    900          *
    901          * @param {media.view.Menu} view The menu view.
    902          */
    903         _renderMenu: function( view ) {
    904                 var menuItem = this.get('menuItem'),
    905                         title = this.get('title'),
    906                         priority = this.get('priority');
    907 
    908                 if ( ! menuItem && title ) {
    909                         menuItem = { text: title };
    910 
    911                         if ( priority ) {
    912                                 menuItem.priority = priority;
    913                         }
    914                 }
    915 
    916                 if ( ! menuItem ) {
    917                         return;
    918                 }
    919 
    920                 view.set( this.id, menuItem );
    921         }
    922 });
    923 
    924 _.each(['toolbar','content'], function( region ) {
    925         /**
    926          * @access private
    927          */
    928         State.prototype[ '_' + region ] = function() {
    929                 var mode = this.get( region );
    930                 if ( mode ) {
    931                         this.frame[ region ].render( mode );
    932                 }
    933         };
    934 });
    935 
    936 module.exports = State;
    937 
    938 },{}],7:[function(require,module,exports){
    939 /*globals wp */
    940 
    94135var media = wp.media;
    94236
    94337media.controller.EditAttachmentMetadata = require( './controllers/edit-attachment-metadata.js' );
     
    95044media.view.DeleteSelectedButton = require( './views/button/delete-selected.js' );
    95145media.view.DeleteSelectedPermanentlyButton = require( './views/button/delete-selected-permanently.js' );
    95246
    953 },{"./controllers/edit-attachment-metadata.js":1,"./routers/manage.js":8,"./views/attachment/details-two-column.js":16,"./views/button/delete-selected-permanently.js":22,"./views/button/delete-selected.js":23,"./views/button/select-mode-toggle.js":24,"./views/edit-image-details.js":25,"./views/frame/edit-attachments.js":28,"./views/frame/manage.js":29}],8:[function(require,module,exports){
     47},{"./controllers/edit-attachment-metadata.js":1,"./routers/manage.js":3,"./views/attachment/details-two-column.js":4,"./views/button/delete-selected-permanently.js":5,"./views/button/delete-selected.js":6,"./views/button/select-mode-toggle.js":7,"./views/edit-image-details.js":8,"./views/frame/edit-attachments.js":9,"./views/frame/manage.js":10}],3:[function(require,module,exports){
    95448/*globals wp, Backbone */
    95549
    95650/**
     
    100094
    100195module.exports = Router;
    100296
    1003 },{}],9:[function(require,module,exports){
    1004 /*globals _ */
    1005 
    1006 /**
    1007  * wp.media.selectionSync
    1008  *
    1009  * Sync an attachments selection in a state with another state.
    1010  *
    1011  * Allows for selecting multiple images in the Insert Media workflow, and then
    1012  * switching to the Insert Gallery workflow while preserving the attachments selection.
    1013  *
    1014  * @mixin
    1015  */
    1016 var selectionSync = {
    1017         /**
    1018          * @since 3.5.0
    1019          */
    1020         syncSelection: function() {
    1021                 var selection = this.get('selection'),
    1022                         manager = this.frame._selection;
    1023 
    1024                 if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    1025                         return;
    1026                 }
    1027 
    1028                 // If the selection supports multiple items, validate the stored
    1029                 // attachments based on the new selection's conditions. Record
    1030                 // the attachments that are not included; we'll maintain a
    1031                 // reference to those. Other attachments are considered in flux.
    1032                 if ( selection.multiple ) {
    1033                         selection.reset( [], { silent: true });
    1034                         selection.validateAll( manager.attachments );
    1035                         manager.difference = _.difference( manager.attachments.models, selection.models );
    1036                 }
    1037 
    1038                 // Sync the selection's single item with the master.
    1039                 selection.single( manager.single );
    1040         },
    1041 
    1042         /**
    1043          * Record the currently active attachments, which is a combination
    1044          * of the selection's attachments and the set of selected
    1045          * attachments that this specific selection considered invalid.
    1046          * Reset the difference and record the single attachment.
    1047          *
    1048          * @since 3.5.0
    1049          */
    1050         recordSelection: function() {
    1051                 var selection = this.get('selection'),
    1052                         manager = this.frame._selection;
    1053 
    1054                 if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    1055                         return;
    1056                 }
    1057 
    1058                 if ( selection.multiple ) {
    1059                         manager.attachments.reset( selection.toArray().concat( manager.difference ) );
    1060                         manager.difference = [];
    1061                 } else {
    1062                         manager.attachments.add( selection.toArray() );
    1063                 }
    1064 
    1065                 manager.single = selection._single;
    1066         }
    1067 };
    1068 
    1069 module.exports = selectionSync;
    1070 
    1071 },{}],10:[function(require,module,exports){
    1072 /*globals _ */
    1073 
    1074 /**
    1075  * wp.media.view.AttachmentCompat
    1076  *
    1077  * A view to display fields added via the `attachment_fields_to_edit` filter.
    1078  *
    1079  * @class
    1080  * @augments wp.media.View
    1081  * @augments wp.Backbone.View
    1082  * @augments Backbone.View
    1083  */
    1084 var View = require( './view.js' ),
    1085         AttachmentCompat;
    1086 
    1087 AttachmentCompat = View.extend({
    1088         tagName:   'form',
    1089         className: 'compat-item',
    1090 
    1091         events: {
    1092                 'submit':          'preventDefault',
    1093                 'change input':    'save',
    1094                 'change select':   'save',
    1095                 'change textarea': 'save'
    1096         },
    1097 
    1098         initialize: function() {
    1099                 this.listenTo( this.model, 'change:compat', this.render );
    1100         },
    1101         /**
    1102          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    1103          */
    1104         dispose: function() {
    1105                 if ( this.$(':focus').length ) {
    1106                         this.save();
    1107                 }
    1108                 /**
    1109                  * call 'dispose' directly on the parent class
    1110                  */
    1111                 return View.prototype.dispose.apply( this, arguments );
    1112         },
    1113         /**
    1114          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    1115          */
    1116         render: function() {
    1117                 var compat = this.model.get('compat');
    1118                 if ( ! compat || ! compat.item ) {
    1119                         return;
    1120                 }
    1121 
    1122                 this.views.detach();
    1123                 this.$el.html( compat.item );
    1124                 this.views.render();
    1125                 return this;
    1126         },
    1127         /**
    1128          * @param {Object} event
    1129          */
    1130         preventDefault: function( event ) {
    1131                 event.preventDefault();
    1132         },
    1133         /**
    1134          * @param {Object} event
    1135          */
    1136         save: function( event ) {
    1137                 var data = {};
    1138 
    1139                 if ( event ) {
    1140                         event.preventDefault();
    1141                 }
    1142 
    1143                 _.each( this.$el.serializeArray(), function( pair ) {
    1144                         data[ pair.name ] = pair.value;
    1145                 });
    1146 
    1147                 this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
    1148                 this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
    1149         },
    1150 
    1151         postSave: function() {
    1152                 this.controller.trigger( 'attachment:compat:ready', ['ready'] );
    1153         }
    1154 });
    1155 
    1156 module.exports = AttachmentCompat;
    1157 
    1158 },{"./view.js":49}],11:[function(require,module,exports){
    1159 /*globals _, jQuery */
    1160 
    1161 /**
    1162  * wp.media.view.AttachmentFilters
    1163  *
    1164  * @class
    1165  * @augments wp.media.View
    1166  * @augments wp.Backbone.View
    1167  * @augments Backbone.View
    1168  */
    1169 var View = require( './view.js' ),
    1170         $ = jQuery,
    1171         AttachmentFilters;
    1172 
    1173 AttachmentFilters = View.extend({
    1174         tagName:   'select',
    1175         className: 'attachment-filters',
    1176         id:        'media-attachment-filters',
    1177 
    1178         events: {
    1179                 change: 'change'
    1180         },
    1181 
    1182         keys: [],
    1183 
    1184         initialize: function() {
    1185                 this.createFilters();
    1186                 _.extend( this.filters, this.options.filters );
    1187 
    1188                 // Build `<option>` elements.
    1189                 this.$el.html( _.chain( this.filters ).map( function( filter, value ) {
    1190                         return {
    1191                                 el: $( '<option></option>' ).val( value ).html( filter.text )[0],
    1192                                 priority: filter.priority || 50
    1193                         };
    1194                 }, this ).sortBy('priority').pluck('el').value() );
    1195 
    1196                 this.listenTo( this.model, 'change', this.select );
    1197                 this.select();
    1198         },
    1199 
    1200         /**
    1201          * @abstract
    1202          */
    1203         createFilters: function() {
    1204                 this.filters = {};
    1205         },
    1206 
    1207         /**
    1208          * When the selected filter changes, update the Attachment Query properties to match.
    1209          */
    1210         change: function() {
    1211                 var filter = this.filters[ this.el.value ];
    1212                 if ( filter ) {
    1213                         this.model.set( filter.props );
    1214                 }
    1215         },
    1216 
    1217         select: function() {
    1218                 var model = this.model,
    1219                         value = 'all',
    1220                         props = model.toJSON();
    1221 
    1222                 _.find( this.filters, function( filter, id ) {
    1223                         var equal = _.all( filter.props, function( prop, key ) {
    1224                                 return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] );
    1225                         });
    1226 
    1227                         if ( equal ) {
    1228                                 return value = id;
    1229                         }
    1230                 });
    1231 
    1232                 this.$el.val( value );
    1233         }
    1234 });
    1235 
    1236 module.exports = AttachmentFilters;
    1237 
    1238 },{"./view.js":49}],12:[function(require,module,exports){
     97},{}],4:[function(require,module,exports){
    123998/*globals wp */
    124099
    1241100/**
    1242  * wp.media.view.AttachmentFilters.All
    1243  *
    1244  * @class
    1245  * @augments wp.media.view.AttachmentFilters
    1246  * @augments wp.media.View
    1247  * @augments wp.Backbone.View
    1248  * @augments Backbone.View
    1249  */
    1250 var AttachmentFilters = require( '../attachment-filters.js' ),
    1251         l10n = wp.media.view.l10n,
    1252         All;
    1253 
    1254 All = AttachmentFilters.extend({
    1255         createFilters: function() {
    1256                 var filters = {};
    1257 
    1258                 _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
    1259                         filters[ key ] = {
    1260                                 text: text,
    1261                                 props: {
    1262                                         status:  null,
    1263                                         type:    key,
    1264                                         uploadedTo: null,
    1265                                         orderby: 'date',
    1266                                         order:   'DESC'
    1267                                 }
    1268                         };
    1269                 });
    1270 
    1271                 filters.all = {
    1272                         text:  l10n.allMediaItems,
    1273                         props: {
    1274                                 status:  null,
    1275                                 type:    null,
    1276                                 uploadedTo: null,
    1277                                 orderby: 'date',
    1278                                 order:   'DESC'
    1279                         },
    1280                         priority: 10
    1281                 };
    1282 
    1283                 if ( wp.media.view.settings.post.id ) {
    1284                         filters.uploaded = {
    1285                                 text:  l10n.uploadedToThisPost,
    1286                                 props: {
    1287                                         status:  null,
    1288                                         type:    null,
    1289                                         uploadedTo: wp.media.view.settings.post.id,
    1290                                         orderby: 'menuOrder',
    1291                                         order:   'ASC'
    1292                                 },
    1293                                 priority: 20
    1294                         };
    1295                 }
    1296 
    1297                 filters.unattached = {
    1298                         text:  l10n.unattached,
    1299                         props: {
    1300                                 status:     null,
    1301                                 uploadedTo: 0,
    1302                                 type:       null,
    1303                                 orderby:    'menuOrder',
    1304                                 order:      'ASC'
    1305                         },
    1306                         priority: 50
    1307                 };
    1308 
    1309                 if ( wp.media.view.settings.mediaTrash &&
    1310                         this.controller.isModeActive( 'grid' ) ) {
    1311 
    1312                         filters.trash = {
    1313                                 text:  l10n.trash,
    1314                                 props: {
    1315                                         uploadedTo: null,
    1316                                         status:     'trash',
    1317                                         type:       null,
    1318                                         orderby:    'date',
    1319                                         order:      'DESC'
    1320                                 },
    1321                                 priority: 50
    1322                         };
    1323                 }
    1324 
    1325                 this.filters = filters;
    1326         }
    1327 });
    1328 
    1329 module.exports = All;
    1330 
    1331 },{"../attachment-filters.js":11}],13:[function(require,module,exports){
    1332 /*globals wp, _ */
    1333 
    1334 /**
    1335  * A filter dropdown for month/dates.
    1336  *
    1337  * @class
    1338  * @augments wp.media.view.AttachmentFilters
    1339  * @augments wp.media.View
    1340  * @augments wp.Backbone.View
    1341  * @augments Backbone.View
    1342  */
    1343 var AttachmentFilters = require( '../attachment-filters.js' ),
    1344         l10n = wp.media.view.l10n,
    1345         DateFilter;
    1346 
    1347 DateFilter = AttachmentFilters.extend({
    1348         id: 'media-attachment-date-filters',
    1349 
    1350         createFilters: function() {
    1351                 var filters = {};
    1352                 _.each( wp.media.view.settings.months || {}, function( value, index ) {
    1353                         filters[ index ] = {
    1354                                 text: value.text,
    1355                                 props: {
    1356                                         year: value.year,
    1357                                         monthnum: value.month
    1358                                 }
    1359                         };
    1360                 });
    1361                 filters.all = {
    1362                         text:  l10n.allDates,
    1363                         props: {
    1364                                 monthnum: false,
    1365                                 year:  false
    1366                         },
    1367                         priority: 10
    1368                 };
    1369                 this.filters = filters;
    1370         }
    1371 });
    1372 
    1373 module.exports = DateFilter;
    1374 
    1375 },{"../attachment-filters.js":11}],14:[function(require,module,exports){
    1376 /*globals wp */
    1377 
    1378 /**
    1379  * wp.media.view.AttachmentFilters.Uploaded
    1380  *
    1381  * @class
    1382  * @augments wp.media.view.AttachmentFilters
    1383  * @augments wp.media.View
    1384  * @augments wp.Backbone.View
    1385  * @augments Backbone.View
    1386  */
    1387 var AttachmentFilters = require( '../attachment-filters.js' ),
    1388         l10n = wp.media.view.l10n,
    1389         Uploaded;
    1390 
    1391 Uploaded = AttachmentFilters.extend({
    1392         createFilters: function() {
    1393                 var type = this.model.get('type'),
    1394                         types = wp.media.view.settings.mimeTypes,
    1395                         text;
    1396 
    1397                 if ( types && type ) {
    1398                         text = types[ type ];
    1399                 }
    1400 
    1401                 this.filters = {
    1402                         all: {
    1403                                 text:  text || l10n.allMediaItems,
    1404                                 props: {
    1405                                         uploadedTo: null,
    1406                                         orderby: 'date',
    1407                                         order:   'DESC'
    1408                                 },
    1409                                 priority: 10
    1410                         },
    1411 
    1412                         uploaded: {
    1413                                 text:  l10n.uploadedToThisPost,
    1414                                 props: {
    1415                                         uploadedTo: wp.media.view.settings.post.id,
    1416                                         orderby: 'menuOrder',
    1417                                         order:   'ASC'
    1418                                 },
    1419                                 priority: 20
    1420                         },
    1421 
    1422                         unattached: {
    1423                                 text:  l10n.unattached,
    1424                                 props: {
    1425                                         uploadedTo: 0,
    1426                                         orderby: 'menuOrder',
    1427                                         order:   'ASC'
    1428                                 },
    1429                                 priority: 50
    1430                         }
    1431                 };
    1432         }
    1433 });
    1434 
    1435 module.exports = Uploaded;
    1436 
    1437 },{"../attachment-filters.js":11}],15:[function(require,module,exports){
    1438 /*globals wp, _, jQuery */
    1439 
    1440 /**
    1441  * wp.media.view.Attachment
    1442  *
    1443  * @class
    1444  * @augments wp.media.View
    1445  * @augments wp.Backbone.View
    1446  * @augments Backbone.View
    1447  */
    1448 var View = require( './view.js' ),
    1449         $ = jQuery,
    1450         Attachment;
    1451 
    1452 Attachment = View.extend({
    1453         tagName:   'li',
    1454         className: 'attachment',
    1455         template:  wp.template('attachment'),
    1456 
    1457         attributes: function() {
    1458                 return {
    1459                         'tabIndex':     0,
    1460                         'role':         'checkbox',
    1461                         'aria-label':   this.model.get( 'title' ),
    1462                         'aria-checked': false,
    1463                         'data-id':      this.model.get( 'id' )
    1464                 };
    1465         },
    1466 
    1467         events: {
    1468                 'click .js--select-attachment':   'toggleSelectionHandler',
    1469                 'change [data-setting]':          'updateSetting',
    1470                 'change [data-setting] input':    'updateSetting',
    1471                 'change [data-setting] select':   'updateSetting',
    1472                 'change [data-setting] textarea': 'updateSetting',
    1473                 'click .close':                   'removeFromLibrary',
    1474                 'click .check':                   'checkClickHandler',
    1475                 'click a':                        'preventDefault',
    1476                 'keydown .close':                 'removeFromLibrary',
    1477                 'keydown':                        'toggleSelectionHandler'
    1478         },
    1479 
    1480         buttons: {},
    1481 
    1482         initialize: function() {
    1483                 var selection = this.options.selection,
    1484                         options = _.defaults( this.options, {
    1485                                 rerenderOnModelChange: true
    1486                         } );
    1487 
    1488                 if ( options.rerenderOnModelChange ) {
    1489                         this.listenTo( this.model, 'change', this.render );
    1490                 } else {
    1491                         this.listenTo( this.model, 'change:percent', this.progress );
    1492                 }
    1493                 this.listenTo( this.model, 'change:title', this._syncTitle );
    1494                 this.listenTo( this.model, 'change:caption', this._syncCaption );
    1495                 this.listenTo( this.model, 'change:artist', this._syncArtist );
    1496                 this.listenTo( this.model, 'change:album', this._syncAlbum );
    1497 
    1498                 // Update the selection.
    1499                 this.listenTo( this.model, 'add', this.select );
    1500                 this.listenTo( this.model, 'remove', this.deselect );
    1501                 if ( selection ) {
    1502                         selection.on( 'reset', this.updateSelect, this );
    1503                         // Update the model's details view.
    1504                         this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
    1505                         this.details( this.model, this.controller.state().get('selection') );
    1506                 }
    1507 
    1508                 this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
    1509         },
    1510         /**
    1511          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1512          */
    1513         dispose: function() {
    1514                 var selection = this.options.selection;
    1515 
    1516                 // Make sure all settings are saved before removing the view.
    1517                 this.updateAll();
    1518 
    1519                 if ( selection ) {
    1520                         selection.off( null, null, this );
    1521                 }
    1522                 /**
    1523                  * call 'dispose' directly on the parent class
    1524                  */
    1525                 View.prototype.dispose.apply( this, arguments );
    1526                 return this;
    1527         },
    1528         /**
    1529          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1530          */
    1531         render: function() {
    1532                 var options = _.defaults( this.model.toJSON(), {
    1533                                 orientation:   'landscape',
    1534                                 uploading:     false,
    1535                                 type:          '',
    1536                                 subtype:       '',
    1537                                 icon:          '',
    1538                                 filename:      '',
    1539                                 caption:       '',
    1540                                 title:         '',
    1541                                 dateFormatted: '',
    1542                                 width:         '',
    1543                                 height:        '',
    1544                                 compat:        false,
    1545                                 alt:           '',
    1546                                 description:   ''
    1547                         }, this.options );
    1548 
    1549                 options.buttons  = this.buttons;
    1550                 options.describe = this.controller.state().get('describe');
    1551 
    1552                 if ( 'image' === options.type ) {
    1553                         options.size = this.imageSize();
    1554                 }
    1555 
    1556                 options.can = {};
    1557                 if ( options.nonces ) {
    1558                         options.can.remove = !! options.nonces['delete'];
    1559                         options.can.save = !! options.nonces.update;
    1560                 }
    1561 
    1562                 if ( this.controller.state().get('allowLocalEdits') ) {
    1563                         options.allowLocalEdits = true;
    1564                 }
    1565 
    1566                 if ( options.uploading && ! options.percent ) {
    1567                         options.percent = 0;
    1568                 }
    1569 
    1570                 this.views.detach();
    1571                 this.$el.html( this.template( options ) );
    1572 
    1573                 this.$el.toggleClass( 'uploading', options.uploading );
    1574 
    1575                 if ( options.uploading ) {
    1576                         this.$bar = this.$('.media-progress-bar div');
    1577                 } else {
    1578                         delete this.$bar;
    1579                 }
    1580 
    1581                 // Check if the model is selected.
    1582                 this.updateSelect();
    1583 
    1584                 // Update the save status.
    1585                 this.updateSave();
    1586 
    1587                 this.views.render();
    1588 
    1589                 return this;
    1590         },
    1591 
    1592         progress: function() {
    1593                 if ( this.$bar && this.$bar.length ) {
    1594                         this.$bar.width( this.model.get('percent') + '%' );
    1595                 }
    1596         },
    1597 
    1598         /**
    1599          * @param {Object} event
    1600          */
    1601         toggleSelectionHandler: function( event ) {
    1602                 var method;
    1603 
    1604                 // Don't do anything inside inputs.
    1605                 if ( 'INPUT' === event.target.nodeName ) {
    1606                         return;
    1607                 }
    1608 
    1609                 // Catch arrow events
    1610                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    1611                         this.controller.trigger( 'attachment:keydown:arrow', event );
    1612                         return;
    1613                 }
    1614 
    1615                 // Catch enter and space events
    1616                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    1617                         return;
    1618                 }
    1619 
    1620                 event.preventDefault();
    1621 
    1622                 // In the grid view, bubble up an edit:attachment event to the controller.
    1623                 if ( this.controller.isModeActive( 'grid' ) ) {
    1624                         if ( this.controller.isModeActive( 'edit' ) ) {
    1625                                 // Pass the current target to restore focus when closing
    1626                                 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
    1627                                 return;
    1628                         }
    1629 
    1630                         if ( this.controller.isModeActive( 'select' ) ) {
    1631                                 method = 'toggle';
    1632                         }
    1633                 }
    1634 
    1635                 if ( event.shiftKey ) {
    1636                         method = 'between';
    1637                 } else if ( event.ctrlKey || event.metaKey ) {
    1638                         method = 'toggle';
    1639                 }
    1640 
    1641                 this.toggleSelection({
    1642                         method: method
    1643                 });
    1644 
    1645                 this.controller.trigger( 'selection:toggle' );
    1646         },
    1647         /**
    1648          * @param {Object} options
    1649          */
    1650         toggleSelection: function( options ) {
    1651                 var collection = this.collection,
    1652                         selection = this.options.selection,
    1653                         model = this.model,
    1654                         method = options && options.method,
    1655                         single, models, singleIndex, modelIndex;
    1656 
    1657                 if ( ! selection ) {
    1658                         return;
    1659                 }
    1660 
    1661                 single = selection.single();
    1662                 method = _.isUndefined( method ) ? selection.multiple : method;
    1663 
    1664                 // If the `method` is set to `between`, select all models that
    1665                 // exist between the current and the selected model.
    1666                 if ( 'between' === method && single && selection.multiple ) {
    1667                         // If the models are the same, short-circuit.
    1668                         if ( single === model ) {
    1669                                 return;
    1670                         }
    1671 
    1672                         singleIndex = collection.indexOf( single );
    1673                         modelIndex  = collection.indexOf( this.model );
    1674 
    1675                         if ( singleIndex < modelIndex ) {
    1676                                 models = collection.models.slice( singleIndex, modelIndex + 1 );
    1677                         } else {
    1678                                 models = collection.models.slice( modelIndex, singleIndex + 1 );
    1679                         }
    1680 
    1681                         selection.add( models );
    1682                         selection.single( model );
    1683                         return;
    1684 
    1685                 // If the `method` is set to `toggle`, just flip the selection
    1686                 // status, regardless of whether the model is the single model.
    1687                 } else if ( 'toggle' === method ) {
    1688                         selection[ this.selected() ? 'remove' : 'add' ]( model );
    1689                         selection.single( model );
    1690                         return;
    1691                 } else if ( 'add' === method ) {
    1692                         selection.add( model );
    1693                         selection.single( model );
    1694                         return;
    1695                 }
    1696 
    1697                 // Fixes bug that loses focus when selecting a featured image
    1698                 if ( ! method ) {
    1699                         method = 'add';
    1700                 }
    1701 
    1702                 if ( method !== 'add' ) {
    1703                         method = 'reset';
    1704                 }
    1705 
    1706                 if ( this.selected() ) {
    1707                         // If the model is the single model, remove it.
    1708                         // If it is not the same as the single model,
    1709                         // it now becomes the single model.
    1710                         selection[ single === model ? 'remove' : 'single' ]( model );
    1711                 } else {
    1712                         // If the model is not selected, run the `method` on the
    1713                         // selection. By default, we `reset` the selection, but the
    1714                         // `method` can be set to `add` the model to the selection.
    1715                         selection[ method ]( model );
    1716                         selection.single( model );
    1717                 }
    1718         },
    1719 
    1720         updateSelect: function() {
    1721                 this[ this.selected() ? 'select' : 'deselect' ]();
    1722         },
    1723         /**
    1724          * @returns {unresolved|Boolean}
    1725          */
    1726         selected: function() {
    1727                 var selection = this.options.selection;
    1728                 if ( selection ) {
    1729                         return !! selection.get( this.model.cid );
    1730                 }
    1731         },
    1732         /**
    1733          * @param {Backbone.Model} model
    1734          * @param {Backbone.Collection} collection
    1735          */
    1736         select: function( model, collection ) {
    1737                 var selection = this.options.selection,
    1738                         controller = this.controller;
    1739 
    1740                 // Check if a selection exists and if it's the collection provided.
    1741                 // If they're not the same collection, bail; we're in another
    1742                 // selection's event loop.
    1743                 if ( ! selection || ( collection && collection !== selection ) ) {
    1744                         return;
    1745                 }
    1746 
    1747                 // Bail if the model is already selected.
    1748                 if ( this.$el.hasClass( 'selected' ) ) {
    1749                         return;
    1750                 }
    1751 
    1752                 // Add 'selected' class to model, set aria-checked to true.
    1753                 this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
    1754                 //  Make the checkbox tabable, except in media grid (bulk select mode).
    1755                 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
    1756                         this.$( '.check' ).attr( 'tabindex', '0' );
    1757                 }
    1758         },
    1759         /**
    1760          * @param {Backbone.Model} model
    1761          * @param {Backbone.Collection} collection
    1762          */
    1763         deselect: function( model, collection ) {
    1764                 var selection = this.options.selection;
    1765 
    1766                 // Check if a selection exists and if it's the collection provided.
    1767                 // If they're not the same collection, bail; we're in another
    1768                 // selection's event loop.
    1769                 if ( ! selection || ( collection && collection !== selection ) ) {
    1770                         return;
    1771                 }
    1772                 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
    1773                         .find( '.check' ).attr( 'tabindex', '-1' );
    1774         },
    1775         /**
    1776          * @param {Backbone.Model} model
    1777          * @param {Backbone.Collection} collection
    1778          */
    1779         details: function( model, collection ) {
    1780                 var selection = this.options.selection,
    1781                         details;
    1782 
    1783                 if ( selection !== collection ) {
    1784                         return;
    1785                 }
    1786 
    1787                 details = selection.single();
    1788                 this.$el.toggleClass( 'details', details === this.model );
    1789         },
    1790         /**
    1791          * @param {Object} event
    1792          */
    1793         preventDefault: function( event ) {
    1794                 event.preventDefault();
    1795         },
    1796         /**
    1797          * @param {string} size
    1798          * @returns {Object}
    1799          */
    1800         imageSize: function( size ) {
    1801                 var sizes = this.model.get('sizes'), matched = false;
    1802 
    1803                 size = size || 'medium';
    1804 
    1805                 // Use the provided image size if possible.
    1806                 if ( sizes ) {
    1807                         if ( sizes[ size ] ) {
    1808                                 matched = sizes[ size ];
    1809                         } else if ( sizes.large ) {
    1810                                 matched = sizes.large;
    1811                         } else if ( sizes.thumbnail ) {
    1812                                 matched = sizes.thumbnail;
    1813                         } else if ( sizes.full ) {
    1814                                 matched = sizes.full;
    1815                         }
    1816 
    1817                         if ( matched ) {
    1818                                 return _.clone( matched );
    1819                         }
    1820                 }
    1821 
    1822                 return {
    1823                         url:         this.model.get('url'),
    1824                         width:       this.model.get('width'),
    1825                         height:      this.model.get('height'),
    1826                         orientation: this.model.get('orientation')
    1827                 };
    1828         },
    1829         /**
    1830          * @param {Object} event
    1831          */
    1832         updateSetting: function( event ) {
    1833                 var $setting = $( event.target ).closest('[data-setting]'),
    1834                         setting, value;
    1835 
    1836                 if ( ! $setting.length ) {
    1837                         return;
    1838                 }
    1839 
    1840                 setting = $setting.data('setting');
    1841                 value   = event.target.value;
    1842 
    1843                 if ( this.model.get( setting ) !== value ) {
    1844                         this.save( setting, value );
    1845                 }
    1846         },
    1847 
    1848         /**
    1849          * Pass all the arguments to the model's save method.
    1850          *
    1851          * Records the aggregate status of all save requests and updates the
    1852          * view's classes accordingly.
    1853          */
    1854         save: function() {
    1855                 var view = this,
    1856                         save = this._save = this._save || { status: 'ready' },
    1857                         request = this.model.save.apply( this.model, arguments ),
    1858                         requests = save.requests ? $.when( request, save.requests ) : request;
    1859 
    1860                 // If we're waiting to remove 'Saved.', stop.
    1861                 if ( save.savedTimer ) {
    1862                         clearTimeout( save.savedTimer );
    1863                 }
    1864 
    1865                 this.updateSave('waiting');
    1866                 save.requests = requests;
    1867                 requests.always( function() {
    1868                         // If we've performed another request since this one, bail.
    1869                         if ( save.requests !== requests ) {
    1870                                 return;
    1871                         }
    1872 
    1873                         view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
    1874                         save.savedTimer = setTimeout( function() {
    1875                                 view.updateSave('ready');
    1876                                 delete save.savedTimer;
    1877                         }, 2000 );
    1878                 });
    1879         },
    1880         /**
    1881          * @param {string} status
    1882          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1883          */
    1884         updateSave: function( status ) {
    1885                 var save = this._save = this._save || { status: 'ready' };
    1886 
    1887                 if ( status && status !== save.status ) {
    1888                         this.$el.removeClass( 'save-' + save.status );
    1889                         save.status = status;
    1890                 }
    1891 
    1892                 this.$el.addClass( 'save-' + save.status );
    1893                 return this;
    1894         },
    1895 
    1896         updateAll: function() {
    1897                 var $settings = this.$('[data-setting]'),
    1898                         model = this.model,
    1899                         changed;
    1900 
    1901                 changed = _.chain( $settings ).map( function( el ) {
    1902                         var $input = $('input, textarea, select, [value]', el ),
    1903                                 setting, value;
    1904 
    1905                         if ( ! $input.length ) {
    1906                                 return;
    1907                         }
    1908 
    1909                         setting = $(el).data('setting');
    1910                         value = $input.val();
    1911 
    1912                         // Record the value if it changed.
    1913                         if ( model.get( setting ) !== value ) {
    1914                                 return [ setting, value ];
    1915                         }
    1916                 }).compact().object().value();
    1917 
    1918                 if ( ! _.isEmpty( changed ) ) {
    1919                         model.save( changed );
    1920                 }
    1921         },
    1922         /**
    1923          * @param {Object} event
    1924          */
    1925         removeFromLibrary: function( event ) {
    1926                 // Catch enter and space events
    1927                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    1928                         return;
    1929                 }
    1930 
    1931                 // Stop propagation so the model isn't selected.
    1932                 event.stopPropagation();
    1933 
    1934                 this.collection.remove( this.model );
    1935         },
    1936 
    1937         /**
    1938          * Add the model if it isn't in the selection, if it is in the selection,
    1939          * remove it.
    1940          *
    1941          * @param  {[type]} event [description]
    1942          * @return {[type]}       [description]
    1943          */
    1944         checkClickHandler: function ( event ) {
    1945                 var selection = this.options.selection;
    1946                 if ( ! selection ) {
    1947                         return;
    1948                 }
    1949                 event.stopPropagation();
    1950                 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
    1951                         selection.remove( this.model );
    1952                         // Move focus back to the attachment tile (from the check).
    1953                         this.$el.focus();
    1954                 } else {
    1955                         selection.add( this.model );
    1956                 }
    1957         }
    1958 });
    1959 
    1960 // Ensure settings remain in sync between attachment views.
    1961 _.each({
    1962         caption: '_syncCaption',
    1963         title:   '_syncTitle',
    1964         artist:  '_syncArtist',
    1965         album:   '_syncAlbum'
    1966 }, function( method, setting ) {
    1967         /**
    1968          * @param {Backbone.Model} model
    1969          * @param {string} value
    1970          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1971          */
    1972         Attachment.prototype[ method ] = function( model, value ) {
    1973                 var $setting = this.$('[data-setting="' + setting + '"]');
    1974 
    1975                 if ( ! $setting.length ) {
    1976                         return this;
    1977                 }
    1978 
    1979                 // If the updated value is in sync with the value in the DOM, there
    1980                 // is no need to re-render. If we're currently editing the value,
    1981                 // it will automatically be in sync, suppressing the re-render for
    1982                 // the view we're editing, while updating any others.
    1983                 if ( value === $setting.find('input, textarea, select, [value]').val() ) {
    1984                         return this;
    1985                 }
    1986 
    1987                 return this.render();
    1988         };
    1989 });
    1990 
    1991 module.exports = Attachment;
    1992 
    1993 },{"./view.js":49}],16:[function(require,module,exports){
    1994 /*globals wp */
    1995 
    1996 /**
    1997101 * wp.media.view.Attachment.Details.TwoColumn
    1998102 *
    1999103 * A similar view to media.view.Attachment.Details
     
    2006110 * @augments wp.Backbone.View
    2007111 * @augments Backbone.View
    2008112 */
    2009 var Details = require( './details.js' ),
     113var Details = wp.media.view.Attachment.Details,
    2010114        TwoColumn;
    2011115
    2012116TwoColumn = Details.extend({
     
    2035139
    2036140module.exports = TwoColumn;
    2037141
    2038 },{"./details.js":17}],17:[function(require,module,exports){
    2039 /*globals wp, _ */
    2040 
     142},{}],5:[function(require,module,exports){
    2041143/**
    2042  * wp.media.view.Attachment.Details
    2043  *
    2044  * @class
    2045  * @augments wp.media.view.Attachment
    2046  * @augments wp.media.View
    2047  * @augments wp.Backbone.View
    2048  * @augments Backbone.View
    2049  */
    2050 var Attachment = require( '../attachment.js' ),
    2051         l10n = wp.media.view.l10n,
    2052         Details;
    2053 
    2054 Details = Attachment.extend({
    2055         tagName:   'div',
    2056         className: 'attachment-details',
    2057         template:  wp.template('attachment-details'),
    2058 
    2059         attributes: function() {
    2060                 return {
    2061                         'tabIndex':     0,
    2062                         'data-id':      this.model.get( 'id' )
    2063                 };
    2064         },
    2065 
    2066         events: {
    2067                 'change [data-setting]':          'updateSetting',
    2068                 'change [data-setting] input':    'updateSetting',
    2069                 'change [data-setting] select':   'updateSetting',
    2070                 'change [data-setting] textarea': 'updateSetting',
    2071                 'click .delete-attachment':       'deleteAttachment',
    2072                 'click .trash-attachment':        'trashAttachment',
    2073                 'click .untrash-attachment':      'untrashAttachment',
    2074                 'click .edit-attachment':         'editAttachment',
    2075                 'click .refresh-attachment':      'refreshAttachment',
    2076                 'keydown':                        'toggleSelectionHandler'
    2077         },
    2078 
    2079         initialize: function() {
    2080                 this.options = _.defaults( this.options, {
    2081                         rerenderOnModelChange: false
    2082                 });
    2083 
    2084                 this.on( 'ready', this.initialFocus );
    2085                 // Call 'initialize' directly on the parent class.
    2086                 Attachment.prototype.initialize.apply( this, arguments );
    2087         },
    2088 
    2089         initialFocus: function() {
    2090                 if ( ! wp.media.isTouchDevice ) {
    2091                         this.$( ':input' ).eq( 0 ).focus();
    2092                 }
    2093         },
    2094         /**
    2095          * @param {Object} event
    2096          */
    2097         deleteAttachment: function( event ) {
    2098                 event.preventDefault();
    2099 
    2100                 if ( window.confirm( l10n.warnDelete ) ) {
    2101                         this.model.destroy();
    2102                         // Keep focus inside media modal
    2103                         // after image is deleted
    2104                         this.controller.modal.focusManager.focus();
    2105                 }
    2106         },
    2107         /**
    2108          * @param {Object} event
    2109          */
    2110         trashAttachment: function( event ) {
    2111                 var library = this.controller.library;
    2112                 event.preventDefault();
    2113 
    2114                 if ( wp.media.view.settings.mediaTrash &&
    2115                         'edit-metadata' === this.controller.content.mode() ) {
    2116 
    2117                         this.model.set( 'status', 'trash' );
    2118                         this.model.save().done( function() {
    2119                                 library._requery( true );
    2120                         } );
    2121                 }  else {
    2122                         this.model.destroy();
    2123                 }
    2124         },
    2125         /**
    2126          * @param {Object} event
    2127          */
    2128         untrashAttachment: function( event ) {
    2129                 var library = this.controller.library;
    2130                 event.preventDefault();
    2131 
    2132                 this.model.set( 'status', 'inherit' );
    2133                 this.model.save().done( function() {
    2134                         library._requery( true );
    2135                 } );
    2136         },
    2137         /**
    2138          * @param {Object} event
    2139          */
    2140         editAttachment: function( event ) {
    2141                 var editState = this.controller.states.get( 'edit-image' );
    2142                 if ( window.imageEdit && editState ) {
    2143                         event.preventDefault();
    2144 
    2145                         editState.set( 'image', this.model );
    2146                         this.controller.setState( 'edit-image' );
    2147                 } else {
    2148                         this.$el.addClass('needs-refresh');
    2149                 }
    2150         },
    2151         /**
    2152          * @param {Object} event
    2153          */
    2154         refreshAttachment: function( event ) {
    2155                 this.$el.removeClass('needs-refresh');
    2156                 event.preventDefault();
    2157                 this.model.fetch();
    2158         },
    2159         /**
    2160          * When reverse tabbing(shift+tab) out of the right details panel, deliver
    2161          * the focus to the item in the list that was being edited.
    2162          *
    2163          * @param {Object} event
    2164          */
    2165         toggleSelectionHandler: function( event ) {
    2166                 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
    2167                         this.controller.trigger( 'attachment:details:shift-tab', event );
    2168                         return false;
    2169                 }
    2170 
    2171                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    2172                         this.controller.trigger( 'attachment:keydown:arrow', event );
    2173                         return;
    2174                 }
    2175         }
    2176 });
    2177 
    2178 module.exports = Details;
    2179 
    2180 },{"../attachment.js":15}],18:[function(require,module,exports){
    2181 /**
    2182  * wp.media.view.Attachment.Library
    2183  *
    2184  * @class
    2185  * @augments wp.media.view.Attachment
    2186  * @augments wp.media.View
    2187  * @augments wp.Backbone.View
    2188  * @augments Backbone.View
    2189  */
    2190 var Attachment = require( '../attachment.js' ),
    2191         Library;
    2192 
    2193 Library = Attachment.extend({
    2194         buttons: {
    2195                 check: true
    2196         }
    2197 });
    2198 
    2199 module.exports = Library;
    2200 
    2201 },{"../attachment.js":15}],19:[function(require,module,exports){
    2202 /*globals wp, _, jQuery */
    2203 
    2204 /**
    2205  * wp.media.view.Attachments
    2206  *
    2207  * @class
    2208  * @augments wp.media.View
    2209  * @augments wp.Backbone.View
    2210  * @augments Backbone.View
    2211  */
    2212 var View = require( './view.js' ),
    2213         Attachment = require( './attachment.js' ),
    2214         $ = jQuery,
    2215         Attachments;
    2216 
    2217 Attachments = View.extend({
    2218         tagName:   'ul',
    2219         className: 'attachments',
    2220 
    2221         attributes: {
    2222                 tabIndex: -1
    2223         },
    2224 
    2225         initialize: function() {
    2226                 this.el.id = _.uniqueId('__attachments-view-');
    2227 
    2228                 _.defaults( this.options, {
    2229                         refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
    2230                         refreshThreshold:   3,
    2231                         AttachmentView:     Attachment,
    2232                         sortable:           false,
    2233                         resize:             true,
    2234                         idealColumnWidth:   $( window ).width() < 640 ? 135 : 150
    2235                 });
    2236 
    2237                 this._viewsByCid = {};
    2238                 this.$window = $( window );
    2239                 this.resizeEvent = 'resize.media-modal-columns';
    2240 
    2241                 this.collection.on( 'add', function( attachment ) {
    2242                         this.views.add( this.createAttachmentView( attachment ), {
    2243                                 at: this.collection.indexOf( attachment )
    2244                         });
    2245                 }, this );
    2246 
    2247                 this.collection.on( 'remove', function( attachment ) {
    2248                         var view = this._viewsByCid[ attachment.cid ];
    2249                         delete this._viewsByCid[ attachment.cid ];
    2250 
    2251                         if ( view ) {
    2252                                 view.remove();
    2253                         }
    2254                 }, this );
    2255 
    2256                 this.collection.on( 'reset', this.render, this );
    2257 
    2258                 this.listenTo( this.controller, 'library:selection:add',    this.attachmentFocus );
    2259 
    2260                 // Throttle the scroll handler and bind this.
    2261                 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
    2262 
    2263                 this.options.scrollElement = this.options.scrollElement || this.el;
    2264                 $( this.options.scrollElement ).on( 'scroll', this.scroll );
    2265 
    2266                 this.initSortable();
    2267 
    2268                 _.bindAll( this, 'setColumns' );
    2269 
    2270                 if ( this.options.resize ) {
    2271                         this.on( 'ready', this.bindEvents );
    2272                         this.controller.on( 'open', this.setColumns );
    2273 
    2274                         // Call this.setColumns() after this view has been rendered in the DOM so
    2275                         // attachments get proper width applied.
    2276                         _.defer( this.setColumns, this );
    2277                 }
    2278         },
    2279 
    2280         bindEvents: function() {
    2281                 this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
    2282         },
    2283 
    2284         attachmentFocus: function() {
    2285                 this.$( 'li:first' ).focus();
    2286         },
    2287 
    2288         restoreFocus: function() {
    2289                 this.$( 'li.selected:first' ).focus();
    2290         },
    2291 
    2292         arrowEvent: function( event ) {
    2293                 var attachments = this.$el.children( 'li' ),
    2294                         perRow = this.columns,
    2295                         index = attachments.filter( ':focus' ).index(),
    2296                         row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
    2297 
    2298                 if ( index === -1 ) {
    2299                         return;
    2300                 }
    2301 
    2302                 // Left arrow
    2303                 if ( 37 === event.keyCode ) {
    2304                         if ( 0 === index ) {
    2305                                 return;
    2306                         }
    2307                         attachments.eq( index - 1 ).focus();
    2308                 }
    2309 
    2310                 // Up arrow
    2311                 if ( 38 === event.keyCode ) {
    2312                         if ( 1 === row ) {
    2313                                 return;
    2314                         }
    2315                         attachments.eq( index - perRow ).focus();
    2316                 }
    2317 
    2318                 // Right arrow
    2319                 if ( 39 === event.keyCode ) {
    2320                         if ( attachments.length === index ) {
    2321                                 return;
    2322                         }
    2323                         attachments.eq( index + 1 ).focus();
    2324                 }
    2325 
    2326                 // Down arrow
    2327                 if ( 40 === event.keyCode ) {
    2328                         if ( Math.ceil( attachments.length / perRow ) === row ) {
    2329                                 return;
    2330                         }
    2331                         attachments.eq( index + perRow ).focus();
    2332                 }
    2333         },
    2334 
    2335         dispose: function() {
    2336                 this.collection.props.off( null, null, this );
    2337                 if ( this.options.resize ) {
    2338                         this.$window.off( this.resizeEvent );
    2339                 }
    2340 
    2341                 /**
    2342                  * call 'dispose' directly on the parent class
    2343                  */
    2344                 View.prototype.dispose.apply( this, arguments );
    2345         },
    2346 
    2347         setColumns: function() {
    2348                 var prev = this.columns,
    2349                         width = this.$el.width();
    2350 
    2351                 if ( width ) {
    2352                         this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
    2353 
    2354                         if ( ! prev || prev !== this.columns ) {
    2355                                 this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
    2356                         }
    2357                 }
    2358         },
    2359 
    2360         initSortable: function() {
    2361                 var collection = this.collection;
    2362 
    2363                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    2364                         return;
    2365                 }
    2366 
    2367                 this.$el.sortable( _.extend({
    2368                         // If the `collection` has a `comparator`, disable sorting.
    2369                         disabled: !! collection.comparator,
    2370 
    2371                         // Change the position of the attachment as soon as the
    2372                         // mouse pointer overlaps a thumbnail.
    2373                         tolerance: 'pointer',
    2374 
    2375                         // Record the initial `index` of the dragged model.
    2376                         start: function( event, ui ) {
    2377                                 ui.item.data('sortableIndexStart', ui.item.index());
    2378                         },
    2379 
    2380                         // Update the model's index in the collection.
    2381                         // Do so silently, as the view is already accurate.
    2382                         update: function( event, ui ) {
    2383                                 var model = collection.at( ui.item.data('sortableIndexStart') ),
    2384                                         comparator = collection.comparator;
    2385 
    2386                                 // Temporarily disable the comparator to prevent `add`
    2387                                 // from re-sorting.
    2388                                 delete collection.comparator;
    2389 
    2390                                 // Silently shift the model to its new index.
    2391                                 collection.remove( model, {
    2392                                         silent: true
    2393                                 });
    2394                                 collection.add( model, {
    2395                                         silent: true,
    2396                                         at:     ui.item.index()
    2397                                 });
    2398 
    2399                                 // Restore the comparator.
    2400                                 collection.comparator = comparator;
    2401 
    2402                                 // Fire the `reset` event to ensure other collections sync.
    2403                                 collection.trigger( 'reset', collection );
    2404 
    2405                                 // If the collection is sorted by menu order,
    2406                                 // update the menu order.
    2407                                 collection.saveMenuOrder();
    2408                         }
    2409                 }, this.options.sortable ) );
    2410 
    2411                 // If the `orderby` property is changed on the `collection`,
    2412                 // check to see if we have a `comparator`. If so, disable sorting.
    2413                 collection.props.on( 'change:orderby', function() {
    2414                         this.$el.sortable( 'option', 'disabled', !! collection.comparator );
    2415                 }, this );
    2416 
    2417                 this.collection.props.on( 'change:orderby', this.refreshSortable, this );
    2418                 this.refreshSortable();
    2419         },
    2420 
    2421         refreshSortable: function() {
    2422                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    2423                         return;
    2424                 }
    2425 
    2426                 // If the `collection` has a `comparator`, disable sorting.
    2427                 var collection = this.collection,
    2428                         orderby = collection.props.get('orderby'),
    2429                         enabled = 'menuOrder' === orderby || ! collection.comparator;
    2430 
    2431                 this.$el.sortable( 'option', 'disabled', ! enabled );
    2432         },
    2433 
    2434         /**
    2435          * @param {wp.media.model.Attachment} attachment
    2436          * @returns {wp.media.View}
    2437          */
    2438         createAttachmentView: function( attachment ) {
    2439                 var view = new this.options.AttachmentView({
    2440                         controller:           this.controller,
    2441                         model:                attachment,
    2442                         collection:           this.collection,
    2443                         selection:            this.options.selection
    2444                 });
    2445 
    2446                 return this._viewsByCid[ attachment.cid ] = view;
    2447         },
    2448 
    2449         prepare: function() {
    2450                 // Create all of the Attachment views, and replace
    2451                 // the list in a single DOM operation.
    2452                 if ( this.collection.length ) {
    2453                         this.views.set( this.collection.map( this.createAttachmentView, this ) );
    2454 
    2455                 // If there are no elements, clear the views and load some.
    2456                 } else {
    2457                         this.views.unset();
    2458                         this.collection.more().done( this.scroll );
    2459                 }
    2460         },
    2461 
    2462         ready: function() {
    2463                 // Trigger the scroll event to check if we're within the
    2464                 // threshold to query for additional attachments.
    2465                 this.scroll();
    2466         },
    2467 
    2468         scroll: function() {
    2469                 var view = this,
    2470                         el = this.options.scrollElement,
    2471                         scrollTop = el.scrollTop,
    2472                         toolbar;
    2473 
    2474                 // The scroll event occurs on the document, but the element
    2475                 // that should be checked is the document body.
    2476                 if ( el === document ) {
    2477                         el = document.body;
    2478                         scrollTop = $(document).scrollTop();
    2479                 }
    2480 
    2481                 if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
    2482                         return;
    2483                 }
    2484 
    2485                 toolbar = this.views.parent.toolbar;
    2486 
    2487                 // Show the spinner only if we are close to the bottom.
    2488                 if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
    2489                         toolbar.get('spinner').show();
    2490                 }
    2491 
    2492                 if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
    2493                         this.collection.more().done(function() {
    2494                                 view.scroll();
    2495                                 toolbar.get('spinner').hide();
    2496                         });
    2497                 }
    2498         }
    2499 });
    2500 
    2501 module.exports = Attachments;
    2502 
    2503 },{"./attachment.js":15,"./view.js":49}],20:[function(require,module,exports){
    2504 /*globals wp, _, jQuery */
    2505 
    2506 /**
    2507  * wp.media.view.AttachmentsBrowser
    2508  *
    2509  * @class
    2510  * @augments wp.media.View
    2511  * @augments wp.Backbone.View
    2512  * @augments Backbone.View
    2513  *
    2514  * @param {object}      options
    2515  * @param {object}      [options.filters=false] Which filters to show in the browser's toolbar.
    2516  *                                              Accepts 'uploaded' and 'all'.
    2517  * @param {object}      [options.search=true]   Whether to show the search interface in the
    2518  *                                              browser's toolbar.
    2519  * @param {object}      [options.date=true]     Whether to show the date filter in the
    2520  *                                              browser's toolbar.
    2521  * @param {object}      [options.display=false] Whether to show the attachments display settings
    2522  *                                              view in the sidebar.
    2523  * @param {bool|string} [options.sidebar=true]  Whether to create a sidebar for the browser.
    2524  *                                              Accepts true, false, and 'errors'.
    2525  */
    2526 var View = require( '../view.js' ),
    2527         Library = require( '../attachment/library.js' ),
    2528         Toolbar = require( '../toolbar.js' ),
    2529         Spinner = require( '../spinner.js' ),
    2530         Search = require( '../search.js' ),
    2531         Label = require( '../label.js' ),
    2532         Uploaded = require( '../attachment-filters/uploaded.js' ),
    2533         All = require( '../attachment-filters/all.js' ),
    2534         DateFilter = require( '../attachment-filters/date.js' ),
    2535         UploaderInline = require( '../uploader/inline.js' ),
    2536         Attachments = require( '../attachments.js' ),
    2537         Sidebar = require( '../sidebar.js' ),
    2538         UploaderStatus = require( '../uploader/status.js' ),
    2539         Details = require( '../attachment/details.js' ),
    2540         AttachmentCompat = require( '../attachment-compat.js' ),
    2541         AttachmentDisplay = require( '../settings/attachment-display.js' ),
    2542         mediaTrash = wp.media.view.settings.mediaTrash,
    2543         l10n = wp.media.view.l10n,
    2544         $ = jQuery,
    2545         AttachmentsBrowser;
    2546 
    2547 AttachmentsBrowser = View.extend({
    2548         tagName:   'div',
    2549         className: 'attachments-browser',
    2550 
    2551         initialize: function() {
    2552                 _.defaults( this.options, {
    2553                         filters: false,
    2554                         search:  true,
    2555                         date:    true,
    2556                         display: false,
    2557                         sidebar: true,
    2558                         AttachmentView: Library
    2559                 });
    2560 
    2561                 this.listenTo( this.controller, 'toggle:upload:attachment', _.bind( this.toggleUploader, this ) );
    2562                 this.controller.on( 'edit:selection', this.editSelection );
    2563                 this.createToolbar();
    2564                 if ( this.options.sidebar ) {
    2565                         this.createSidebar();
    2566                 }
    2567                 this.createUploader();
    2568                 this.createAttachments();
    2569                 this.updateContent();
    2570 
    2571                 if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) {
    2572                         this.$el.addClass( 'hide-sidebar' );
    2573 
    2574                         if ( 'errors' === this.options.sidebar ) {
    2575                                 this.$el.addClass( 'sidebar-for-errors' );
    2576                         }
    2577                 }
    2578 
    2579                 this.collection.on( 'add remove reset', this.updateContent, this );
    2580         },
    2581 
    2582         editSelection: function( modal ) {
    2583                 modal.$( '.media-button-backToLibrary' ).focus();
    2584         },
    2585 
    2586         /**
    2587          * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining
    2588          */
    2589         dispose: function() {
    2590                 this.options.selection.off( null, null, this );
    2591                 View.prototype.dispose.apply( this, arguments );
    2592                 return this;
    2593         },
    2594 
    2595         createToolbar: function() {
    2596                 var LibraryViewSwitcher, Filters, toolbarOptions;
    2597 
    2598                 toolbarOptions = {
    2599                         controller: this.controller
    2600                 };
    2601 
    2602                 if ( this.controller.isModeActive( 'grid' ) ) {
    2603                         toolbarOptions.className = 'media-toolbar wp-filter';
    2604                 }
    2605 
    2606                 /**
    2607                 * @member {wp.media.view.Toolbar}
    2608                 */
    2609                 this.toolbar = new Toolbar( toolbarOptions );
    2610 
    2611                 this.views.add( this.toolbar );
    2612 
    2613                 this.toolbar.set( 'spinner', new Spinner({
    2614                         priority: -60
    2615                 }) );
    2616 
    2617                 if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) {
    2618                         // "Filters" will return a <select>, need to render
    2619                         // screen reader text before
    2620                         this.toolbar.set( 'filtersLabel', new Label({
    2621                                 value: l10n.filterByType,
    2622                                 attributes: {
    2623                                         'for':  'media-attachment-filters'
    2624                                 },
    2625                                 priority:   -80
    2626                         }).render() );
    2627 
    2628                         if ( 'uploaded' === this.options.filters ) {
    2629                                 this.toolbar.set( 'filters', new Uploaded({
    2630                                         controller: this.controller,
    2631                                         model:      this.collection.props,
    2632                                         priority:   -80
    2633                                 }).render() );
    2634                         } else {
    2635                                 Filters = new All({
    2636                                         controller: this.controller,
    2637                                         model:      this.collection.props,
    2638                                         priority:   -80
    2639                                 });
    2640 
    2641                                 this.toolbar.set( 'filters', Filters.render() );
    2642                         }
    2643                 }
    2644 
    2645                 // Feels odd to bring the global media library switcher into the Attachment
    2646                 // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
    2647                 // which the controller can tap into and add this view?
    2648                 if ( this.controller.isModeActive( 'grid' ) ) {
    2649                         LibraryViewSwitcher = View.extend({
    2650                                 className: 'view-switch media-grid-view-switch',
    2651                                 template: wp.template( 'media-library-view-switcher')
    2652                         });
    2653 
    2654                         this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({
    2655                                 controller: this.controller,
    2656                                 priority: -90
    2657                         }).render() );
    2658 
    2659                         // DateFilter is a <select>, screen reader text needs to be rendered before
    2660                         this.toolbar.set( 'dateFilterLabel', new Label({
    2661                                 value: l10n.filterByDate,
    2662                                 attributes: {
    2663                                         'for': 'media-attachment-date-filters'
    2664                                 },
    2665                                 priority: -75
    2666                         }).render() );
    2667                         this.toolbar.set( 'dateFilter', new DateFilter({
    2668                                 controller: this.controller,
    2669                                 model:      this.collection.props,
    2670                                 priority: -75
    2671                         }).render() );
    2672 
    2673                         // BulkSelection is a <div> with subviews, including screen reader text
    2674                         this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({
    2675                                 text: l10n.bulkSelect,
    2676                                 controller: this.controller,
    2677                                 priority: -70
    2678                         }).render() );
    2679 
    2680                         this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({
    2681                                 filters: Filters,
    2682                                 style: 'primary',
    2683                                 disabled: true,
    2684                                 text: mediaTrash ? l10n.trashSelected : l10n.deleteSelected,
    2685                                 controller: this.controller,
    2686                                 priority: -60,
    2687                                 click: function() {
    2688                                         var changed = [], removed = [],
    2689                                                 selection = this.controller.state().get( 'selection' ),
    2690                                                 library = this.controller.state().get( 'library' );
    2691 
    2692                                         if ( ! selection.length ) {
    2693                                                 return;
    2694                                         }
    2695 
    2696                                         if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) {
    2697                                                 return;
    2698                                         }
    2699 
    2700                                         if ( mediaTrash &&
    2701                                                 'trash' !== selection.at( 0 ).get( 'status' ) &&
    2702                                                 ! window.confirm( l10n.warnBulkTrash ) ) {
    2703 
    2704                                                 return;
    2705                                         }
    2706 
    2707                                         selection.each( function( model ) {
    2708                                                 if ( ! model.get( 'nonces' )['delete'] ) {
    2709                                                         removed.push( model );
    2710                                                         return;
    2711                                                 }
    2712 
    2713                                                 if ( mediaTrash && 'trash' === model.get( 'status' ) ) {
    2714                                                         model.set( 'status', 'inherit' );
    2715                                                         changed.push( model.save() );
    2716                                                         removed.push( model );
    2717                                                 } else if ( mediaTrash ) {
    2718                                                         model.set( 'status', 'trash' );
    2719                                                         changed.push( model.save() );
    2720                                                         removed.push( model );
    2721                                                 } else {
    2722                                                         model.destroy({wait: true});
    2723                                                 }
    2724                                         } );
    2725 
    2726                                         if ( changed.length ) {
    2727                                                 selection.remove( removed );
    2728 
    2729                                                 $.when.apply( null, changed ).then( _.bind( function() {
    2730                                                         library._requery( true );
    2731                                                         this.controller.trigger( 'selection:action:done' );
    2732                                                 }, this ) );
    2733                                         } else {
    2734                                                 this.controller.trigger( 'selection:action:done' );
    2735                                         }
    2736                                 }
    2737                         }).render() );
    2738 
    2739                         if ( mediaTrash ) {
    2740                                 this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({
    2741                                         filters: Filters,
    2742                                         style: 'primary',
    2743                                         disabled: true,
    2744                                         text: l10n.deleteSelected,
    2745                                         controller: this.controller,
    2746                                         priority: -55,
    2747                                         click: function() {
    2748                                                 var removed = [], selection = this.controller.state().get( 'selection' );
    2749 
    2750                                                 if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) {
    2751                                                         return;
    2752                                                 }
    2753 
    2754                                                 selection.each( function( model ) {
    2755                                                         if ( ! model.get( 'nonces' )['delete'] ) {
    2756                                                                 removed.push( model );
    2757                                                                 return;
    2758                                                         }
    2759 
    2760                                                         model.destroy();
    2761                                                 } );
    2762 
    2763                                                 selection.remove( removed );
    2764                                                 this.controller.trigger( 'selection:action:done' );
    2765                                         }
    2766                                 }).render() );
    2767                         }
    2768 
    2769                 } else if ( this.options.date ) {
    2770                         // DateFilter is a <select>, screen reader text needs to be rendered before
    2771                         this.toolbar.set( 'dateFilterLabel', new Label({
    2772                                 value: l10n.filterByDate,
    2773                                 attributes: {
    2774                                         'for': 'media-attachment-date-filters'
    2775                                 },
    2776                                 priority: -75
    2777                         }).render() );
    2778                         this.toolbar.set( 'dateFilter', new DateFilter({
    2779                                 controller: this.controller,
    2780                                 model:      this.collection.props,
    2781                                 priority: -75
    2782                         }).render() );
    2783                 }
    2784 
    2785                 if ( this.options.search ) {
    2786                         // Search is an input, screen reader text needs to be rendered before
    2787                         this.toolbar.set( 'searchLabel', new Label({
    2788                                 value: l10n.searchMediaLabel,
    2789                                 attributes: {
    2790                                         'for': 'media-search-input'
    2791                                 },
    2792                                 priority:   60
    2793                         }).render() );
    2794                         this.toolbar.set( 'search', new Search({
    2795                                 controller: this.controller,
    2796                                 model:      this.collection.props,
    2797                                 priority:   60
    2798                         }).render() );
    2799                 }
    2800 
    2801                 if ( this.options.dragInfo ) {
    2802                         this.toolbar.set( 'dragInfo', new View({
    2803                                 el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
    2804                                 priority: -40
    2805                         }) );
    2806                 }
    2807 
    2808                 if ( this.options.suggestedWidth && this.options.suggestedHeight ) {
    2809                         this.toolbar.set( 'suggestedDimensions', new View({
    2810                                 el: $( '<div class="instructions">' + l10n.suggestedDimensions + ' ' + this.options.suggestedWidth + ' &times; ' + this.options.suggestedHeight + '</div>' )[0],
    2811                                 priority: -40
    2812                         }) );
    2813                 }
    2814         },
    2815 
    2816         updateContent: function() {
    2817                 var view = this,
    2818                         noItemsView;
    2819 
    2820                 if ( this.controller.isModeActive( 'grid' ) ) {
    2821                         noItemsView = view.attachmentsNoResults;
    2822                 } else {
    2823                         noItemsView = view.uploader;
    2824                 }
    2825 
    2826                 if ( ! this.collection.length ) {
    2827                         this.toolbar.get( 'spinner' ).show();
    2828                         this.dfd = this.collection.more().done( function() {
    2829                                 if ( ! view.collection.length ) {
    2830                                         noItemsView.$el.removeClass( 'hidden' );
    2831                                 } else {
    2832                                         noItemsView.$el.addClass( 'hidden' );
    2833                                 }
    2834                                 view.toolbar.get( 'spinner' ).hide();
    2835                         } );
    2836                 } else {
    2837                         noItemsView.$el.addClass( 'hidden' );
    2838                         view.toolbar.get( 'spinner' ).hide();
    2839                 }
    2840         },
    2841 
    2842         createUploader: function() {
    2843                 this.uploader = new UploaderInline({
    2844                         controller: this.controller,
    2845                         status:     false,
    2846                         message:    this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound,
    2847                         canClose:   this.controller.isModeActive( 'grid' )
    2848                 });
    2849 
    2850                 this.uploader.hide();
    2851                 this.views.add( this.uploader );
    2852         },
    2853 
    2854         toggleUploader: function() {
    2855                 if ( this.uploader.$el.hasClass( 'hidden' ) ) {
    2856                         this.uploader.show();
    2857                 } else {
    2858                         this.uploader.hide();
    2859                 }
    2860         },
    2861 
    2862         createAttachments: function() {
    2863                 this.attachments = new Attachments({
    2864                         controller:           this.controller,
    2865                         collection:           this.collection,
    2866                         selection:            this.options.selection,
    2867                         model:                this.model,
    2868                         sortable:             this.options.sortable,
    2869                         scrollElement:        this.options.scrollElement,
    2870                         idealColumnWidth:     this.options.idealColumnWidth,
    2871 
    2872                         // The single `Attachment` view to be used in the `Attachments` view.
    2873                         AttachmentView: this.options.AttachmentView
    2874                 });
    2875 
    2876                 // Add keydown listener to the instance of the Attachments view
    2877                 this.attachments.listenTo( this.controller, 'attachment:keydown:arrow',     this.attachments.arrowEvent );
    2878                 this.attachments.listenTo( this.controller, 'attachment:details:shift-tab', this.attachments.restoreFocus );
    2879 
    2880                 this.views.add( this.attachments );
    2881 
    2882 
    2883                 if ( this.controller.isModeActive( 'grid' ) ) {
    2884                         this.attachmentsNoResults = new View({
    2885                                 controller: this.controller,
    2886                                 tagName: 'p'
    2887                         });
    2888 
    2889                         this.attachmentsNoResults.$el.addClass( 'hidden no-media' );
    2890                         this.attachmentsNoResults.$el.html( l10n.noMedia );
    2891 
    2892                         this.views.add( this.attachmentsNoResults );
    2893                 }
    2894         },
    2895 
    2896         createSidebar: function() {
    2897                 var options = this.options,
    2898                         selection = options.selection,
    2899                         sidebar = this.sidebar = new Sidebar({
    2900                                 controller: this.controller
    2901                         });
    2902 
    2903                 this.views.add( sidebar );
    2904 
    2905                 if ( this.controller.uploader ) {
    2906                         sidebar.set( 'uploads', new UploaderStatus({
    2907                                 controller: this.controller,
    2908                                 priority:   40
    2909                         }) );
    2910                 }
    2911 
    2912                 selection.on( 'selection:single', this.createSingle, this );
    2913                 selection.on( 'selection:unsingle', this.disposeSingle, this );
    2914 
    2915                 if ( selection.single() ) {
    2916                         this.createSingle();
    2917                 }
    2918         },
    2919 
    2920         createSingle: function() {
    2921                 var sidebar = this.sidebar,
    2922                         single = this.options.selection.single();
    2923 
    2924                 sidebar.set( 'details', new Details({
    2925                         controller: this.controller,
    2926                         model:      single,
    2927                         priority:   80
    2928                 }) );
    2929 
    2930                 sidebar.set( 'compat', new AttachmentCompat({
    2931                         controller: this.controller,
    2932                         model:      single,
    2933                         priority:   120
    2934                 }) );
    2935 
    2936                 if ( this.options.display ) {
    2937                         sidebar.set( 'display', new AttachmentDisplay({
    2938                                 controller:   this.controller,
    2939                                 model:        this.model.display( single ),
    2940                                 attachment:   single,
    2941                                 priority:     160,
    2942                                 userSettings: this.model.get('displayUserSettings')
    2943                         }) );
    2944                 }
    2945 
    2946                 // Show the sidebar on mobile
    2947                 if ( this.model.id === 'insert' ) {
    2948                         sidebar.$el.addClass( 'visible' );
    2949                 }
    2950         },
    2951 
    2952         disposeSingle: function() {
    2953                 var sidebar = this.sidebar;
    2954                 sidebar.unset('details');
    2955                 sidebar.unset('compat');
    2956                 sidebar.unset('display');
    2957                 // Hide the sidebar on mobile
    2958                 sidebar.$el.removeClass( 'visible' );
    2959         }
    2960 });
    2961 
    2962 module.exports = AttachmentsBrowser;
    2963 
    2964 },{"../attachment-compat.js":10,"../attachment-filters/all.js":12,"../attachment-filters/date.js":13,"../attachment-filters/uploaded.js":14,"../attachment/details.js":17,"../attachment/library.js":18,"../attachments.js":19,"../label.js":31,"../search.js":39,"../settings/attachment-display.js":41,"../sidebar.js":42,"../spinner.js":43,"../toolbar.js":44,"../uploader/inline.js":45,"../uploader/status.js":47,"../view.js":49}],21:[function(require,module,exports){
    2965 /*globals _, Backbone */
    2966 
    2967 /**
    2968  * wp.media.view.Button
    2969  *
    2970  * @class
    2971  * @augments wp.media.View
    2972  * @augments wp.Backbone.View
    2973  * @augments Backbone.View
    2974  */
    2975 var View = require( './view.js' ),
    2976         Button;
    2977 
    2978 Button = View.extend({
    2979         tagName:    'a',
    2980         className:  'media-button',
    2981         attributes: { href: '#' },
    2982 
    2983         events: {
    2984                 'click': 'click'
    2985         },
    2986 
    2987         defaults: {
    2988                 text:     '',
    2989                 style:    '',
    2990                 size:     'large',
    2991                 disabled: false
    2992         },
    2993 
    2994         initialize: function() {
    2995                 /**
    2996                  * Create a model with the provided `defaults`.
    2997                  *
    2998                  * @member {Backbone.Model}
    2999                  */
    3000                 this.model = new Backbone.Model( this.defaults );
    3001 
    3002                 // If any of the `options` have a key from `defaults`, apply its
    3003                 // value to the `model` and remove it from the `options object.
    3004                 _.each( this.defaults, function( def, key ) {
    3005                         var value = this.options[ key ];
    3006                         if ( _.isUndefined( value ) ) {
    3007                                 return;
    3008                         }
    3009 
    3010                         this.model.set( key, value );
    3011                         delete this.options[ key ];
    3012                 }, this );
    3013 
    3014                 this.listenTo( this.model, 'change', this.render );
    3015         },
    3016         /**
    3017          * @returns {wp.media.view.Button} Returns itself to allow chaining
    3018          */
    3019         render: function() {
    3020                 var classes = [ 'button', this.className ],
    3021                         model = this.model.toJSON();
    3022 
    3023                 if ( model.style ) {
    3024                         classes.push( 'button-' + model.style );
    3025                 }
    3026 
    3027                 if ( model.size ) {
    3028                         classes.push( 'button-' + model.size );
    3029                 }
    3030 
    3031                 classes = _.uniq( classes.concat( this.options.classes ) );
    3032                 this.el.className = classes.join(' ');
    3033 
    3034                 this.$el.attr( 'disabled', model.disabled );
    3035                 this.$el.text( this.model.get('text') );
    3036 
    3037                 return this;
    3038         },
    3039         /**
    3040          * @param {Object} event
    3041          */
    3042         click: function( event ) {
    3043                 if ( '#' === this.attributes.href ) {
    3044                         event.preventDefault();
    3045                 }
    3046 
    3047                 if ( this.options.click && ! this.model.get('disabled') ) {
    3048                         this.options.click.apply( this, arguments );
    3049                 }
    3050         }
    3051 });
    3052 
    3053 module.exports = Button;
    3054 
    3055 },{"./view.js":49}],22:[function(require,module,exports){
    3056 /**
    3057144 * wp.media.view.DeleteSelectedPermanentlyButton
    3058145 *
    3059146 * When MEDIA_TRASH is true, a button that handles bulk Delete Permanently logic
     
    3065152 * @augments wp.Backbone.View
    3066153 * @augments Backbone.View
    3067154 */
    3068 var Button = require( '../button.js' ),
     155var Button = wp.media.view.Button,
    3069156        DeleteSelected = require( './delete-selected.js' ),
    3070157        DeleteSelectedPermanently;
    3071158
     
    3099186
    3100187module.exports = DeleteSelectedPermanently;
    3101188
    3102 },{"../button.js":21,"./delete-selected.js":23}],23:[function(require,module,exports){
     189},{"./delete-selected.js":6}],6:[function(require,module,exports){
    3103190/*globals wp */
    3104191
    3105192/**
     
    3113200 * @augments wp.Backbone.View
    3114201 * @augments Backbone.View
    3115202 */
    3116 var Button = require( '../button.js' ),
     203var Button = wp.media.view.Button,
    3117204        l10n = wp.media.view.l10n,
    3118205        DeleteSelected;
    3119206
     
    3154241
    3155242module.exports = DeleteSelected;
    3156243
    3157 },{"../button.js":21}],24:[function(require,module,exports){
     244},{}],7:[function(require,module,exports){
    3158245/*globals wp */
    3159246
    3160247/**
     
    3166253 * @augments wp.Backbone.View
    3167254 * @augments Backbone.View
    3168255 */
    3169 var Button = require( '../button.js' ),
     256var Button = wp.media.view.Button,
    3170257        l10n = wp.media.view.l10n,
    3171258        SelectModeToggle;
    3172259
     
    3220307
    3221308module.exports = SelectModeToggle;
    3222309
    3223 },{"../button.js":21}],25:[function(require,module,exports){
     310},{}],8:[function(require,module,exports){
    3224311/*globals wp, _ */
    3225312
    3226313/**
     
    3227314 * wp.media.view.EditImage.Details
    3228315 *
    3229316 * @class
    3230  * @augments wp.media.view.EditImage.Details
     317 * @augments wp.media.view.EditImage
    3231318 * @augments wp.media.View
    3232319 * @augments wp.Backbone.View
    3233320 * @augments Backbone.View
    3234321 */
    3235 var View = require( './view.js' ),
     322var View = wp.media.View,
    3236323        EditImage = wp.media.view.EditImage,
    3237324        Details;
    3238325
     
    3257344
    3258345module.exports = Details;
    3259346
    3260 },{"./view.js":49}],26:[function(require,module,exports){
    3261 /**
    3262  * wp.media.view.FocusManager
    3263  *
    3264  * @class
    3265  * @augments wp.media.View
    3266  * @augments wp.Backbone.View
    3267  * @augments Backbone.View
    3268  */
    3269 var View = require( './view.js' ),
    3270         FocusManager;
    3271 
    3272 FocusManager = View.extend({
    3273 
    3274         events: {
    3275                 'keydown': 'constrainTabbing'
    3276         },
    3277 
    3278         focus: function() { // Reset focus on first left menu item
    3279                 this.$('.media-menu-item').first().focus();
    3280         },
    3281         /**
    3282          * @param {Object} event
    3283          */
    3284         constrainTabbing: function( event ) {
    3285                 var tabbables;
    3286 
    3287                 // Look for the tab key.
    3288                 if ( 9 !== event.keyCode ) {
    3289                         return;
    3290                 }
    3291 
    3292                 // Skip the file input added by Plupload.
    3293                 tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
    3294 
    3295                 // Keep tab focus within media modal while it's open
    3296                 if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
    3297                         tabbables.first().focus();
    3298                         return false;
    3299                 } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
    3300                         tabbables.last().focus();
    3301                         return false;
    3302                 }
    3303         }
    3304 
    3305 });
    3306 
    3307 module.exports = FocusManager;
    3308 
    3309 },{"./view.js":49}],27:[function(require,module,exports){
    3310 /*globals _, Backbone */
    3311 
    3312 /**
    3313  * wp.media.view.Frame
    3314  *
    3315  * A frame is a composite view consisting of one or more regions and one or more
    3316  * states.
    3317  *
    3318  * @see wp.media.controller.State
    3319  * @see wp.media.controller.Region
    3320  *
    3321  * @class
    3322  * @augments wp.media.View
    3323  * @augments wp.Backbone.View
    3324  * @augments Backbone.View
    3325  * @mixes wp.media.controller.StateMachine
    3326  */
    3327 var StateMachine = require( '../controllers/state-machine.js' ),
    3328         State = require( '../controllers/state.js' ),
    3329         Region = require( '../controllers/region.js' ),
    3330         View = require( './view.js' ),
    3331         Frame;
    3332 
    3333 Frame = View.extend({
    3334         initialize: function() {
    3335                 _.defaults( this.options, {
    3336                         mode: [ 'select' ]
    3337                 });
    3338                 this._createRegions();
    3339                 this._createStates();
    3340                 this._createModes();
    3341         },
    3342 
    3343         _createRegions: function() {
    3344                 // Clone the regions array.
    3345                 this.regions = this.regions ? this.regions.slice() : [];
    3346 
    3347                 // Initialize regions.
    3348                 _.each( this.regions, function( region ) {
    3349                         this[ region ] = new Region({
    3350                                 view:     this,
    3351                                 id:       region,
    3352                                 selector: '.media-frame-' + region
    3353                         });
    3354                 }, this );
    3355         },
    3356         /**
    3357          * Create the frame's states.
    3358          *
    3359          * @see wp.media.controller.State
    3360          * @see wp.media.controller.StateMachine
    3361          *
    3362          * @fires wp.media.controller.State#ready
    3363          */
    3364         _createStates: function() {
    3365                 // Create the default `states` collection.
    3366                 this.states = new Backbone.Collection( null, {
    3367                         model: State
    3368                 });
    3369 
    3370                 // Ensure states have a reference to the frame.
    3371                 this.states.on( 'add', function( model ) {
    3372                         model.frame = this;
    3373                         model.trigger('ready');
    3374                 }, this );
    3375 
    3376                 if ( this.options.states ) {
    3377                         this.states.add( this.options.states );
    3378                 }
    3379         },
    3380 
    3381         /**
    3382          * A frame can be in a mode or multiple modes at one time.
    3383          *
    3384          * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
    3385          */
    3386         _createModes: function() {
    3387                 // Store active "modes" that the frame is in. Unrelated to region modes.
    3388                 this.activeModes = new Backbone.Collection();
    3389                 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
    3390 
    3391                 _.each( this.options.mode, function( mode ) {
    3392                         this.activateMode( mode );
    3393                 }, this );
    3394         },
    3395         /**
    3396          * Reset all states on the frame to their defaults.
    3397          *
    3398          * @returns {wp.media.view.Frame} Returns itself to allow chaining
    3399          */
    3400         reset: function() {
    3401                 this.states.invoke( 'trigger', 'reset' );
    3402                 return this;
    3403         },
    3404         /**
    3405          * Map activeMode collection events to the frame.
    3406          */
    3407         triggerModeEvents: function( model, collection, options ) {
    3408                 var collectionEvent,
    3409                         modeEventMap = {
    3410                                 add: 'activate',
    3411                                 remove: 'deactivate'
    3412                         },
    3413                         eventToTrigger;
    3414                 // Probably a better way to do this.
    3415                 _.each( options, function( value, key ) {
    3416                         if ( value ) {
    3417                                 collectionEvent = key;
    3418                         }
    3419                 } );
    3420 
    3421                 if ( ! _.has( modeEventMap, collectionEvent ) ) {
    3422                         return;
    3423                 }
    3424 
    3425                 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
    3426                 this.trigger( eventToTrigger );
    3427         },
    3428         /**
    3429          * Activate a mode on the frame.
    3430          *
    3431          * @param string mode Mode ID.
    3432          * @returns {this} Returns itself to allow chaining.
    3433          */
    3434         activateMode: function( mode ) {
    3435                 // Bail if the mode is already active.
    3436                 if ( this.isModeActive( mode ) ) {
    3437                         return;
    3438                 }
    3439                 this.activeModes.add( [ { id: mode } ] );
    3440                 // Add a CSS class to the frame so elements can be styled for the mode.
    3441                 this.$el.addClass( 'mode-' + mode );
    3442 
    3443                 return this;
    3444         },
    3445         /**
    3446          * Deactivate a mode on the frame.
    3447          *
    3448          * @param string mode Mode ID.
    3449          * @returns {this} Returns itself to allow chaining.
    3450          */
    3451         deactivateMode: function( mode ) {
    3452                 // Bail if the mode isn't active.
    3453                 if ( ! this.isModeActive( mode ) ) {
    3454                         return this;
    3455                 }
    3456                 this.activeModes.remove( this.activeModes.where( { id: mode } ) );
    3457                 this.$el.removeClass( 'mode-' + mode );
    3458                 /**
    3459                  * Frame mode deactivation event.
    3460                  *
    3461                  * @event this#{mode}:deactivate
    3462                  */
    3463                 this.trigger( mode + ':deactivate' );
    3464 
    3465                 return this;
    3466         },
    3467         /**
    3468          * Check if a mode is enabled on the frame.
    3469          *
    3470          * @param  string mode Mode ID.
    3471          * @return bool
    3472          */
    3473         isModeActive: function( mode ) {
    3474                 return Boolean( this.activeModes.where( { id: mode } ).length );
    3475         }
    3476 });
    3477 
    3478 // Make the `Frame` a `StateMachine`.
    3479 _.extend( Frame.prototype, StateMachine.prototype );
    3480 
    3481 module.exports = Frame;
    3482 
    3483 },{"../controllers/region.js":4,"../controllers/state-machine.js":5,"../controllers/state.js":6,"./view.js":49}],28:[function(require,module,exports){
     347},{}],9:[function(require,module,exports){
    3484348/*globals wp, _, jQuery */
    3485349
    3486350/**
     
    3499363 * @augments Backbone.View
    3500364 * @mixes wp.media.controller.StateMachine
    3501365 */
    3502 var Frame = require( '../frame.js' ),
    3503         MediaFrame = require( '../media-frame.js' ),
    3504         Modal = require( '../modal.js' ),
     366var Frame = wp.media.view.Frame,
     367        MediaFrame = wp.media.view.MediaFrame,
     368        Modal = wp.media.view.Modal,
     369        AttachmentCompat = wp.media.view.AttachmentCompat,
     370        EditImageController = wp.media.controller.EditImage,
     371
    3505372        EditAttachmentMetadata = require( '../../controllers/edit-attachment-metadata.js' ),
    3506373        TwoColumn = require( '../attachment/details-two-column.js' ),
    3507         AttachmentCompat = require( '../attachment-compat.js' ),
    3508         EditImageController = require( '../../controllers/edit-image.js' ),
    3509374        DetailsView = require( '../edit-image-details.js' ),
     375
    3510376        $ = jQuery,
    3511377        EditAttachments;
    3512378
     
    3730596
    3731597module.exports = EditAttachments;
    3732598
    3733 },{"../../controllers/edit-attachment-metadata.js":1,"../../controllers/edit-image.js":2,"../attachment-compat.js":10,"../attachment/details-two-column.js":16,"../edit-image-details.js":25,"../frame.js":27,"../media-frame.js":32,"../modal.js":35}],29:[function(require,module,exports){
     599},{"../../controllers/edit-attachment-metadata.js":1,"../attachment/details-two-column.js":4,"../edit-image-details.js":8}],10:[function(require,module,exports){
    3734600/*globals wp, _, jQuery, Backbone */
    3735601
    3736602/**
     
    3748614 * @augments Backbone.View
    3749615 * @mixes wp.media.controller.StateMachine
    3750616 */
    3751 var MediaFrame = require( '../media-frame.js' ),
    3752         UploaderWindow = require( '../uploader/window.js' ),
    3753         AttachmentsBrowser = require( '../attachments/browser.js' ),
     617var MediaFrame = wp.media.view.MediaFrame,
     618        UploaderWindow = wp.media.view.UploaderWindow,
     619        AttachmentsBrowser = wp.media.view.AttachmentsBrowser,
     620        Library = wp.media.controller.Library,
     621
    3754622        Router = require( '../../routers/manage.js' ),
    3755         Library = require( '../../controllers/library.js' ),
     623
    3756624        $ = jQuery,
    3757625        Manage;
    3758626
     
    3979847
    3980848module.exports = Manage;
    3981849
    3982 },{"../../controllers/library.js":3,"../../routers/manage.js":8,"../attachments/browser.js":20,"../media-frame.js":32,"../uploader/window.js":48}],30:[function(require,module,exports){
    3983 /**
    3984  * wp.media.view.Iframe
    3985  *
    3986  * @class
    3987  * @augments wp.media.View
    3988  * @augments wp.Backbone.View
    3989  * @augments Backbone.View
    3990  */
    3991 var View = require( './view.js' ),
    3992         Iframe;
    3993 
    3994 Iframe = View.extend({
    3995         className: 'media-iframe',
    3996         /**
    3997          * @returns {wp.media.view.Iframe} Returns itself to allow chaining
    3998          */
    3999         render: function() {
    4000                 this.views.detach();
    4001                 this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
    4002                 this.views.render();
    4003                 return this;
    4004         }
    4005 });
    4006 
    4007 module.exports = Iframe;
    4008 
    4009 },{"./view.js":49}],31:[function(require,module,exports){
    4010 /**
    4011  * wp.media.view.Label
    4012  *
    4013  * @class
    4014  * @augments wp.media.View
    4015  * @augments wp.Backbone.View
    4016  * @augments Backbone.View
    4017  */
    4018 var View = require( './view.js' ),
    4019         Label;
    4020 
    4021 Label = View.extend({
    4022         tagName: 'label',
    4023         className: 'screen-reader-text',
    4024 
    4025         initialize: function() {
    4026                 this.value = this.options.value;
    4027         },
    4028 
    4029         render: function() {
    4030                 this.$el.html( this.value );
    4031 
    4032                 return this;
    4033         }
    4034 });
    4035 
    4036 module.exports = Label;
    4037 
    4038 },{"./view.js":49}],32:[function(require,module,exports){
    4039 /*globals wp, _, jQuery */
    4040 
    4041 /**
    4042  * wp.media.view.MediaFrame
    4043  *
    4044  * The frame used to create the media modal.
    4045  *
    4046  * @class
    4047  * @augments wp.media.view.Frame
    4048  * @augments wp.media.View
    4049  * @augments wp.Backbone.View
    4050  * @augments Backbone.View
    4051  * @mixes wp.media.controller.StateMachine
    4052  */
    4053 var View = require( './view.js' ),
    4054         Frame = require( './frame.js' ),
    4055         Modal = require( './modal.js' ),
    4056         UploaderWindow = require( './uploader/window.js' ),
    4057         Menu = require( './menu.js' ),
    4058         Toolbar = require( './toolbar.js' ),
    4059         Router = require( './router.js' ),
    4060         Iframe = require( './iframe.js' ),
    4061         $ = jQuery,
    4062         MediaFrame;
    4063 
    4064 MediaFrame = Frame.extend({
    4065         className: 'media-frame',
    4066         template:  wp.template('media-frame'),
    4067         regions:   ['menu','title','content','toolbar','router'],
    4068 
    4069         events: {
    4070                 'click div.media-frame-title h1': 'toggleMenu'
    4071         },
    4072 
    4073         /**
    4074          * @global wp.Uploader
    4075          */
    4076         initialize: function() {
    4077                 Frame.prototype.initialize.apply( this, arguments );
    4078 
    4079                 _.defaults( this.options, {
    4080                         title:    '',
    4081                         modal:    true,
    4082                         uploader: true
    4083                 });
    4084 
    4085                 // Ensure core UI is enabled.
    4086                 this.$el.addClass('wp-core-ui');
    4087 
    4088                 // Initialize modal container view.
    4089                 if ( this.options.modal ) {
    4090                         this.modal = new Modal({
    4091                                 controller: this,
    4092                                 title:      this.options.title
    4093                         });
    4094 
    4095                         this.modal.content( this );
    4096                 }
    4097 
    4098                 // Force the uploader off if the upload limit has been exceeded or
    4099                 // if the browser isn't supported.
    4100                 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
    4101                         this.options.uploader = false;
    4102                 }
    4103 
    4104                 // Initialize window-wide uploader.
    4105                 if ( this.options.uploader ) {
    4106                         this.uploader = new UploaderWindow({
    4107                                 controller: this,
    4108                                 uploader: {
    4109                                         dropzone:  this.modal ? this.modal.$el : this.$el,
    4110                                         container: this.$el
    4111                                 }
    4112                         });
    4113                         this.views.set( '.media-frame-uploader', this.uploader );
    4114                 }
    4115 
    4116                 this.on( 'attach', _.bind( this.views.ready, this.views ), this );
    4117 
    4118                 // Bind default title creation.
    4119                 this.on( 'title:create:default', this.createTitle, this );
    4120                 this.title.mode('default');
    4121 
    4122                 this.on( 'title:render', function( view ) {
    4123                         view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' );
    4124                 });
    4125 
    4126                 // Bind default menu.
    4127                 this.on( 'menu:create:default', this.createMenu, this );
    4128         },
    4129         /**
    4130          * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    4131          */
    4132         render: function() {
    4133                 // Activate the default state if no active state exists.
    4134                 if ( ! this.state() && this.options.state ) {
    4135                         this.setState( this.options.state );
    4136                 }
    4137                 /**
    4138                  * call 'render' directly on the parent class
    4139                  */
    4140                 return Frame.prototype.render.apply( this, arguments );
    4141         },
    4142         /**
    4143          * @param {Object} title
    4144          * @this wp.media.controller.Region
    4145          */
    4146         createTitle: function( title ) {
    4147                 title.view = new View({
    4148                         controller: this,
    4149                         tagName: 'h1'
    4150                 });
    4151         },
    4152         /**
    4153          * @param {Object} menu
    4154          * @this wp.media.controller.Region
    4155          */
    4156         createMenu: function( menu ) {
    4157                 menu.view = new Menu({
    4158                         controller: this
    4159                 });
    4160         },
    4161 
    4162         toggleMenu: function() {
    4163                 this.$el.find( '.media-menu' ).toggleClass( 'visible' );
    4164         },
    4165 
    4166         /**
    4167          * @param {Object} toolbar
    4168          * @this wp.media.controller.Region
    4169          */
    4170         createToolbar: function( toolbar ) {
    4171                 toolbar.view = new Toolbar({
    4172                         controller: this
    4173                 });
    4174         },
    4175         /**
    4176          * @param {Object} router
    4177          * @this wp.media.controller.Region
    4178          */
    4179         createRouter: function( router ) {
    4180                 router.view = new Router({
    4181                         controller: this
    4182                 });
    4183         },
    4184         /**
    4185          * @param {Object} options
    4186          */
    4187         createIframeStates: function( options ) {
    4188                 var settings = wp.media.view.settings,
    4189                         tabs = settings.tabs,
    4190                         tabUrl = settings.tabUrl,
    4191                         $postId;
    4192 
    4193                 if ( ! tabs || ! tabUrl ) {
    4194                         return;
    4195                 }
    4196 
    4197                 // Add the post ID to the tab URL if it exists.
    4198                 $postId = $('#post_ID');
    4199                 if ( $postId.length ) {
    4200                         tabUrl += '&post_id=' + $postId.val();
    4201                 }
    4202 
    4203                 // Generate the tab states.
    4204                 _.each( tabs, function( title, id ) {
    4205                         this.state( 'iframe:' + id ).set( _.defaults({
    4206                                 tab:     id,
    4207                                 src:     tabUrl + '&tab=' + id,
    4208                                 title:   title,
    4209                                 content: 'iframe',
    4210                                 menu:    'default'
    4211                         }, options ) );
    4212                 }, this );
    4213 
    4214                 this.on( 'content:create:iframe', this.iframeContent, this );
    4215                 this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
    4216                 this.on( 'menu:render:default', this.iframeMenu, this );
    4217                 this.on( 'open', this.hijackThickbox, this );
    4218                 this.on( 'close', this.restoreThickbox, this );
    4219         },
    4220 
    4221         /**
    4222          * @param {Object} content
    4223          * @this wp.media.controller.Region
    4224          */
    4225         iframeContent: function( content ) {
    4226                 this.$el.addClass('hide-toolbar');
    4227                 content.view = new Iframe({
    4228                         controller: this
    4229                 });
    4230         },
    4231 
    4232         iframeContentCleanup: function() {
    4233                 this.$el.removeClass('hide-toolbar');
    4234         },
    4235 
    4236         iframeMenu: function( view ) {
    4237                 var views = {};
    4238 
    4239                 if ( ! view ) {
    4240                         return;
    4241                 }
    4242 
    4243                 _.each( wp.media.view.settings.tabs, function( title, id ) {
    4244                         views[ 'iframe:' + id ] = {
    4245                                 text: this.state( 'iframe:' + id ).get('title'),
    4246                                 priority: 200
    4247                         };
    4248                 }, this );
    4249 
    4250                 view.set( views );
    4251         },
    4252 
    4253         hijackThickbox: function() {
    4254                 var frame = this;
    4255 
    4256                 if ( ! window.tb_remove || this._tb_remove ) {
    4257                         return;
    4258                 }
    4259 
    4260                 this._tb_remove = window.tb_remove;
    4261                 window.tb_remove = function() {
    4262                         frame.close();
    4263                         frame.reset();
    4264                         frame.setState( frame.options.state );
    4265                         frame._tb_remove.call( window );
    4266                 };
    4267         },
    4268 
    4269         restoreThickbox: function() {
    4270                 if ( ! this._tb_remove ) {
    4271                         return;
    4272                 }
    4273 
    4274                 window.tb_remove = this._tb_remove;
    4275                 delete this._tb_remove;
    4276         }
    4277 });
    4278 
    4279 // Map some of the modal's methods to the frame.
    4280 _.each(['open','close','attach','detach','escape'], function( method ) {
    4281         /**
    4282          * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    4283          */
    4284         MediaFrame.prototype[ method ] = function() {
    4285                 if ( this.modal ) {
    4286                         this.modal[ method ].apply( this.modal, arguments );
    4287                 }
    4288                 return this;
    4289         };
    4290 });
    4291 
    4292 module.exports = MediaFrame;
    4293 
    4294 },{"./frame.js":27,"./iframe.js":30,"./menu.js":34,"./modal.js":35,"./router.js":38,"./toolbar.js":44,"./uploader/window.js":48,"./view.js":49}],33:[function(require,module,exports){
    4295 /*globals jQuery */
    4296 
    4297 /**
    4298  * wp.media.view.MenuItem
    4299  *
    4300  * @class
    4301  * @augments wp.media.View
    4302  * @augments wp.Backbone.View
    4303  * @augments Backbone.View
    4304  */
    4305 var View = require( './view.js' ),
    4306         $ = jQuery,
    4307         MenuItem;
    4308 
    4309 MenuItem = View.extend({
    4310         tagName:   'a',
    4311         className: 'media-menu-item',
    4312 
    4313         attributes: {
    4314                 href: '#'
    4315         },
    4316 
    4317         events: {
    4318                 'click': '_click'
    4319         },
    4320         /**
    4321          * @param {Object} event
    4322          */
    4323         _click: function( event ) {
    4324                 var clickOverride = this.options.click;
    4325 
    4326                 if ( event ) {
    4327                         event.preventDefault();
    4328                 }
    4329 
    4330                 if ( clickOverride ) {
    4331                         clickOverride.call( this );
    4332                 } else {
    4333                         this.click();
    4334                 }
    4335 
    4336                 // When selecting a tab along the left side,
    4337                 // focus should be transferred into the main panel
    4338                 if ( ! wp.media.isTouchDevice ) {
    4339                         $('.media-frame-content input').first().focus();
    4340                 }
    4341         },
    4342 
    4343         click: function() {
    4344                 var state = this.options.state;
    4345 
    4346                 if ( state ) {
    4347                         this.controller.setState( state );
    4348                         this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
    4349                 }
    4350         },
    4351         /**
    4352          * @returns {wp.media.view.MenuItem} returns itself to allow chaining
    4353          */
    4354         render: function() {
    4355                 var options = this.options;
    4356 
    4357                 if ( options.text ) {
    4358                         this.$el.text( options.text );
    4359                 } else if ( options.html ) {
    4360                         this.$el.html( options.html );
    4361                 }
    4362 
    4363                 return this;
    4364         }
    4365 });
    4366 
    4367 module.exports = MenuItem;
    4368 
    4369 },{"./view.js":49}],34:[function(require,module,exports){
    4370 /**
    4371  * wp.media.view.Menu
    4372  *
    4373  * @class
    4374  * @augments wp.media.view.PriorityList
    4375  * @augments wp.media.View
    4376  * @augments wp.Backbone.View
    4377  * @augments Backbone.View
    4378  */
    4379 var MenuItem = require( './menu-item.js' ),
    4380         PriorityList = require( './priority-list.js' ),
    4381         Menu;
    4382 
    4383 Menu = PriorityList.extend({
    4384         tagName:   'div',
    4385         className: 'media-menu',
    4386         property:  'state',
    4387         ItemView:  MenuItem,
    4388         region:    'menu',
    4389 
    4390         /* TODO: alternatively hide on any click anywhere
    4391         events: {
    4392                 'click': 'click'
    4393         },
    4394 
    4395         click: function() {
    4396                 this.$el.removeClass( 'visible' );
    4397         },
    4398         */
    4399 
    4400         /**
    4401          * @param {Object} options
    4402          * @param {string} id
    4403          * @returns {wp.media.View}
    4404          */
    4405         toView: function( options, id ) {
    4406                 options = options || {};
    4407                 options[ this.property ] = options[ this.property ] || id;
    4408                 return new this.ItemView( options ).render();
    4409         },
    4410 
    4411         ready: function() {
    4412                 /**
    4413                  * call 'ready' directly on the parent class
    4414                  */
    4415                 PriorityList.prototype.ready.apply( this, arguments );
    4416                 this.visibility();
    4417         },
    4418 
    4419         set: function() {
    4420                 /**
    4421                  * call 'set' directly on the parent class
    4422                  */
    4423                 PriorityList.prototype.set.apply( this, arguments );
    4424                 this.visibility();
    4425         },
    4426 
    4427         unset: function() {
    4428                 /**
    4429                  * call 'unset' directly on the parent class
    4430                  */
    4431                 PriorityList.prototype.unset.apply( this, arguments );
    4432                 this.visibility();
    4433         },
    4434 
    4435         visibility: function() {
    4436                 var region = this.region,
    4437                         view = this.controller[ region ].get(),
    4438                         views = this.views.get(),
    4439                         hide = ! views || views.length < 2;
    4440 
    4441                 if ( this === view ) {
    4442                         this.controller.$el.toggleClass( 'hide-' + region, hide );
    4443                 }
    4444         },
    4445         /**
    4446          * @param {string} id
    4447          */
    4448         select: function( id ) {
    4449                 var view = this.get( id );
    4450 
    4451                 if ( ! view ) {
    4452                         return;
    4453                 }
    4454 
    4455                 this.deselect();
    4456                 view.$el.addClass('active');
    4457         },
    4458 
    4459         deselect: function() {
    4460                 this.$el.children().removeClass('active');
    4461         },
    4462 
    4463         hide: function( id ) {
    4464                 var view = this.get( id );
    4465 
    4466                 if ( ! view ) {
    4467                         return;
    4468                 }
    4469 
    4470                 view.$el.addClass('hidden');
    4471         },
    4472 
    4473         show: function( id ) {
    4474                 var view = this.get( id );
    4475 
    4476                 if ( ! view ) {
    4477                         return;
    4478                 }
    4479 
    4480                 view.$el.removeClass('hidden');
    4481         }
    4482 });
    4483 
    4484 module.exports = Menu;
    4485 
    4486 },{"./menu-item.js":33,"./priority-list.js":36}],35:[function(require,module,exports){
    4487 /*globals wp, _, jQuery */
    4488 
    4489 /**
    4490  * wp.media.view.Modal
    4491  *
    4492  * A modal view, which the media modal uses as its default container.
    4493  *
    4494  * @class
    4495  * @augments wp.media.View
    4496  * @augments wp.Backbone.View
    4497  * @augments Backbone.View
    4498  */
    4499 var View = require( './view.js' ),
    4500         FocusManager = require( './focus-manager.js' ),
    4501         $ = jQuery,
    4502         Modal;
    4503 
    4504 Modal = View.extend({
    4505         tagName:  'div',
    4506         template: wp.template('media-modal'),
    4507 
    4508         attributes: {
    4509                 tabindex: 0
    4510         },
    4511 
    4512         events: {
    4513                 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
    4514                 'keydown': 'keydown'
    4515         },
    4516 
    4517         initialize: function() {
    4518                 _.defaults( this.options, {
    4519                         container: document.body,
    4520                         title:     '',
    4521                         propagate: true,
    4522                         freeze:    true
    4523                 });
    4524 
    4525                 this.focusManager = new FocusManager({
    4526                         el: this.el
    4527                 });
    4528         },
    4529         /**
    4530          * @returns {Object}
    4531          */
    4532         prepare: function() {
    4533                 return {
    4534                         title: this.options.title
    4535                 };
    4536         },
    4537 
    4538         /**
    4539          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4540          */
    4541         attach: function() {
    4542                 if ( this.views.attached ) {
    4543                         return this;
    4544                 }
    4545 
    4546                 if ( ! this.views.rendered ) {
    4547                         this.render();
    4548                 }
    4549 
    4550                 this.$el.appendTo( this.options.container );
    4551 
    4552                 // Manually mark the view as attached and trigger ready.
    4553                 this.views.attached = true;
    4554                 this.views.ready();
    4555 
    4556                 return this.propagate('attach');
    4557         },
    4558 
    4559         /**
    4560          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4561          */
    4562         detach: function() {
    4563                 if ( this.$el.is(':visible') ) {
    4564                         this.close();
    4565                 }
    4566 
    4567                 this.$el.detach();
    4568                 this.views.attached = false;
    4569                 return this.propagate('detach');
    4570         },
    4571 
    4572         /**
    4573          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4574          */
    4575         open: function() {
    4576                 var $el = this.$el,
    4577                         options = this.options,
    4578                         mceEditor;
    4579 
    4580                 if ( $el.is(':visible') ) {
    4581                         return this;
    4582                 }
    4583 
    4584                 if ( ! this.views.attached ) {
    4585                         this.attach();
    4586                 }
    4587 
    4588                 // If the `freeze` option is set, record the window's scroll position.
    4589                 if ( options.freeze ) {
    4590                         this._freeze = {
    4591                                 scrollTop: $( window ).scrollTop()
    4592                         };
    4593                 }
    4594 
    4595                 // Disable page scrolling.
    4596                 $( 'body' ).addClass( 'modal-open' );
    4597 
    4598                 $el.show();
    4599 
    4600                 // Try to close the onscreen keyboard
    4601                 if ( 'ontouchend' in document ) {
    4602                         if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor )  && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
    4603                                 mceEditor.iframeElement.focus();
    4604                                 mceEditor.iframeElement.blur();
    4605 
    4606                                 setTimeout( function() {
    4607                                         mceEditor.iframeElement.blur();
    4608                                 }, 100 );
    4609                         }
    4610                 }
    4611 
    4612                 this.$el.focus();
    4613 
    4614                 return this.propagate('open');
    4615         },
    4616 
    4617         /**
    4618          * @param {Object} options
    4619          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4620          */
    4621         close: function( options ) {
    4622                 var freeze = this._freeze;
    4623 
    4624                 if ( ! this.views.attached || ! this.$el.is(':visible') ) {
    4625                         return this;
    4626                 }
    4627 
    4628                 // Enable page scrolling.
    4629                 $( 'body' ).removeClass( 'modal-open' );
    4630 
    4631                 // Hide modal and remove restricted media modal tab focus once it's closed
    4632                 this.$el.hide().undelegate( 'keydown' );
    4633 
    4634                 // Put focus back in useful location once modal is closed
    4635                 $('#wpbody-content').focus();
    4636 
    4637                 this.propagate('close');
    4638 
    4639                 // If the `freeze` option is set, restore the container's scroll position.
    4640                 if ( freeze ) {
    4641                         $( window ).scrollTop( freeze.scrollTop );
    4642                 }
    4643 
    4644                 if ( options && options.escape ) {
    4645                         this.propagate('escape');
    4646                 }
    4647 
    4648                 return this;
    4649         },
    4650         /**
    4651          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4652          */
    4653         escape: function() {
    4654                 return this.close({ escape: true });
    4655         },
    4656         /**
    4657          * @param {Object} event
    4658          */
    4659         escapeHandler: function( event ) {
    4660                 event.preventDefault();
    4661                 this.escape();
    4662         },
    4663 
    4664         /**
    4665          * @param {Array|Object} content Views to register to '.media-modal-content'
    4666          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4667          */
    4668         content: function( content ) {
    4669                 this.views.set( '.media-modal-content', content );
    4670                 return this;
    4671         },
    4672 
    4673         /**
    4674          * Triggers a modal event and if the `propagate` option is set,
    4675          * forwards events to the modal's controller.
    4676          *
    4677          * @param {string} id
    4678          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4679          */
    4680         propagate: function( id ) {
    4681                 this.trigger( id );
    4682 
    4683                 if ( this.options.propagate ) {
    4684                         this.controller.trigger( id );
    4685                 }
    4686 
    4687                 return this;
    4688         },
    4689         /**
    4690          * @param {Object} event
    4691          */
    4692         keydown: function( event ) {
    4693                 // Close the modal when escape is pressed.
    4694                 if ( 27 === event.which && this.$el.is(':visible') ) {
    4695                         this.escape();
    4696                         event.stopImmediatePropagation();
    4697                 }
    4698         }
    4699 });
    4700 
    4701 module.exports = Modal;
    4702 
    4703 },{"./focus-manager.js":26,"./view.js":49}],36:[function(require,module,exports){
    4704 /*globals _, Backbone */
    4705 
    4706 /**
    4707  * wp.media.view.PriorityList
    4708  *
    4709  * @class
    4710  * @augments wp.media.View
    4711  * @augments wp.Backbone.View
    4712  * @augments Backbone.View
    4713  */
    4714 var View = require( './view.js' ),
    4715         PriorityList;
    4716 
    4717 PriorityList = View.extend({
    4718         tagName:   'div',
    4719 
    4720         initialize: function() {
    4721                 this._views = {};
    4722 
    4723                 this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
    4724                 delete this.options.views;
    4725 
    4726                 if ( ! this.options.silent ) {
    4727                         this.render();
    4728                 }
    4729         },
    4730         /**
    4731          * @param {string} id
    4732          * @param {wp.media.View|Object} view
    4733          * @param {Object} options
    4734          * @returns {wp.media.view.PriorityList} Returns itself to allow chaining
    4735          */
    4736         set: function( id, view, options ) {
    4737                 var priority, views, index;
    4738 
    4739                 options = options || {};
    4740 
    4741                 // Accept an object with an `id` : `view` mapping.
    4742                 if ( _.isObject( id ) ) {
    4743                         _.each( id, function( view, id ) {
    4744                                 this.set( id, view );
    4745                         }, this );
    4746                         return this;
    4747                 }
    4748 
    4749                 if ( ! (view instanceof Backbone.View) ) {
    4750                         view = this.toView( view, id, options );
    4751                 }
    4752                 view.controller = view.controller || this.controller;
    4753 
    4754                 this.unset( id );
    4755 
    4756                 priority = view.options.priority || 10;
    4757                 views = this.views.get() || [];
    4758 
    4759                 _.find( views, function( existing, i ) {
    4760                         if ( existing.options.priority > priority ) {
    4761                                 index = i;
    4762                                 return true;
    4763                         }
    4764                 });
    4765 
    4766                 this._views[ id ] = view;
    4767                 this.views.add( view, {
    4768                         at: _.isNumber( index ) ? index : views.length || 0
    4769                 });
    4770 
    4771                 return this;
    4772         },
    4773         /**
    4774          * @param {string} id
    4775          * @returns {wp.media.View}
    4776          */
    4777         get: function( id ) {
    4778                 return this._views[ id ];
    4779         },
    4780         /**
    4781          * @param {string} id
    4782          * @returns {wp.media.view.PriorityList}
    4783          */
    4784         unset: function( id ) {
    4785                 var view = this.get( id );
    4786 
    4787                 if ( view ) {
    4788                         view.remove();
    4789                 }
    4790 
    4791                 delete this._views[ id ];
    4792                 return this;
    4793         },
    4794         /**
    4795          * @param {Object} options
    4796          * @returns {wp.media.View}
    4797          */
    4798         toView: function( options ) {
    4799                 return new View( options );
    4800         }
    4801 });
    4802 
    4803 module.exports = PriorityList;
    4804 
    4805 },{"./view.js":49}],37:[function(require,module,exports){
    4806 /**
    4807  * wp.media.view.RouterItem
    4808  *
    4809  * @class
    4810  * @augments wp.media.view.MenuItem
    4811  * @augments wp.media.View
    4812  * @augments wp.Backbone.View
    4813  * @augments Backbone.View
    4814  */
    4815 var MenuItem = require( './menu-item.js' ),
    4816         RouterItem;
    4817 
    4818 RouterItem = MenuItem.extend({
    4819         /**
    4820          * On click handler to activate the content region's corresponding mode.
    4821          */
    4822         click: function() {
    4823                 var contentMode = this.options.contentMode;
    4824                 if ( contentMode ) {
    4825                         this.controller.content.mode( contentMode );
    4826                 }
    4827         }
    4828 });
    4829 
    4830 module.exports = RouterItem;
    4831 
    4832 },{"./menu-item.js":33}],38:[function(require,module,exports){
    4833 /**
    4834  * wp.media.view.Router
    4835  *
    4836  * @class
    4837  * @augments wp.media.view.Menu
    4838  * @augments wp.media.view.PriorityList
    4839  * @augments wp.media.View
    4840  * @augments wp.Backbone.View
    4841  * @augments Backbone.View
    4842  */
    4843 var Menu = require( './menu.js' ),
    4844         RouterItem = require( './router-item.js' ),
    4845         Router;
    4846 
    4847 Router = Menu.extend({
    4848         tagName:   'div',
    4849         className: 'media-router',
    4850         property:  'contentMode',
    4851         ItemView:  RouterItem,
    4852         region:    'router',
    4853 
    4854         initialize: function() {
    4855                 this.controller.on( 'content:render', this.update, this );
    4856                 // Call 'initialize' directly on the parent class.
    4857                 Menu.prototype.initialize.apply( this, arguments );
    4858         },
    4859 
    4860         update: function() {
    4861                 var mode = this.controller.content.mode();
    4862                 if ( mode ) {
    4863                         this.select( mode );
    4864                 }
    4865         }
    4866 });
    4867 
    4868 module.exports = Router;
    4869 
    4870 },{"./menu.js":34,"./router-item.js":37}],39:[function(require,module,exports){
    4871 /*globals wp */
    4872 
    4873 /**
    4874  * wp.media.view.Search
    4875  *
    4876  * @class
    4877  * @augments wp.media.View
    4878  * @augments wp.Backbone.View
    4879  * @augments Backbone.View
    4880  */
    4881 var View = require( './view.js' ),
    4882         l10n = wp.media.view.l10n,
    4883         Search;
    4884 
    4885 Search = View.extend({
    4886         tagName:   'input',
    4887         className: 'search',
    4888         id:        'media-search-input',
    4889 
    4890         attributes: {
    4891                 type:        'search',
    4892                 placeholder: l10n.search
    4893         },
    4894 
    4895         events: {
    4896                 'input':  'search',
    4897                 'keyup':  'search',
    4898                 'change': 'search',
    4899                 'search': 'search'
    4900         },
    4901 
    4902         /**
    4903          * @returns {wp.media.view.Search} Returns itself to allow chaining
    4904          */
    4905         render: function() {
    4906                 this.el.value = this.model.escape('search');
    4907                 return this;
    4908         },
    4909 
    4910         search: function( event ) {
    4911                 if ( event.target.value ) {
    4912                         this.model.set( 'search', event.target.value );
    4913                 } else {
    4914                         this.model.unset('search');
    4915                 }
    4916         }
    4917 });
    4918 
    4919 module.exports = Search;
    4920 
    4921 },{"./view.js":49}],40:[function(require,module,exports){
    4922 /*globals _, jQuery, Backbone */
    4923 
    4924 /**
    4925  * wp.media.view.Settings
    4926  *
    4927  * @class
    4928  * @augments wp.media.View
    4929  * @augments wp.Backbone.View
    4930  * @augments Backbone.View
    4931  */
    4932 var View = require( './view.js' ),
    4933         $ = jQuery,
    4934         Settings;
    4935 
    4936 Settings = View.extend({
    4937         events: {
    4938                 'click button':    'updateHandler',
    4939                 'change input':    'updateHandler',
    4940                 'change select':   'updateHandler',
    4941                 'change textarea': 'updateHandler'
    4942         },
    4943 
    4944         initialize: function() {
    4945                 this.model = this.model || new Backbone.Model();
    4946                 this.listenTo( this.model, 'change', this.updateChanges );
    4947         },
    4948 
    4949         prepare: function() {
    4950                 return _.defaults({
    4951                         model: this.model.toJSON()
    4952                 }, this.options );
    4953         },
    4954         /**
    4955          * @returns {wp.media.view.Settings} Returns itself to allow chaining
    4956          */
    4957         render: function() {
    4958                 View.prototype.render.apply( this, arguments );
    4959                 // Select the correct values.
    4960                 _( this.model.attributes ).chain().keys().each( this.update, this );
    4961                 return this;
    4962         },
    4963         /**
    4964          * @param {string} key
    4965          */
    4966         update: function( key ) {
    4967                 var value = this.model.get( key ),
    4968                         $setting = this.$('[data-setting="' + key + '"]'),
    4969                         $buttons, $value;
    4970 
    4971                 // Bail if we didn't find a matching setting.
    4972                 if ( ! $setting.length ) {
    4973                         return;
    4974                 }
    4975 
    4976                 // Attempt to determine how the setting is rendered and update
    4977                 // the selected value.
    4978 
    4979                 // Handle dropdowns.
    4980                 if ( $setting.is('select') ) {
    4981                         $value = $setting.find('[value="' + value + '"]');
    4982 
    4983                         if ( $value.length ) {
    4984                                 $setting.find('option').prop( 'selected', false );
    4985                                 $value.prop( 'selected', true );
    4986                         } else {
    4987                                 // If we can't find the desired value, record what *is* selected.
    4988                                 this.model.set( key, $setting.find(':selected').val() );
    4989                         }
    4990 
    4991                 // Handle button groups.
    4992                 } else if ( $setting.hasClass('button-group') ) {
    4993                         $buttons = $setting.find('button').removeClass('active');
    4994                         $buttons.filter( '[value="' + value + '"]' ).addClass('active');
    4995 
    4996                 // Handle text inputs and textareas.
    4997                 } else if ( $setting.is('input[type="text"], textarea') ) {
    4998                         if ( ! $setting.is(':focus') ) {
    4999                                 $setting.val( value );
    5000                         }
    5001                 // Handle checkboxes.
    5002                 } else if ( $setting.is('input[type="checkbox"]') ) {
    5003                         $setting.prop( 'checked', !! value && 'false' !== value );
    5004                 }
    5005         },
    5006         /**
    5007          * @param {Object} event
    5008          */
    5009         updateHandler: function( event ) {
    5010                 var $setting = $( event.target ).closest('[data-setting]'),
    5011                         value = event.target.value,
    5012                         userSetting;
    5013 
    5014                 event.preventDefault();
    5015 
    5016                 if ( ! $setting.length ) {
    5017                         return;
    5018                 }
    5019 
    5020                 // Use the correct value for checkboxes.
    5021                 if ( $setting.is('input[type="checkbox"]') ) {
    5022                         value = $setting[0].checked;
    5023                 }
    5024 
    5025                 // Update the corresponding setting.
    5026                 this.model.set( $setting.data('setting'), value );
    5027 
    5028                 // If the setting has a corresponding user setting,
    5029                 // update that as well.
    5030                 if ( userSetting = $setting.data('userSetting') ) {
    5031                         window.setUserSetting( userSetting, value );
    5032                 }
    5033         },
    5034 
    5035         updateChanges: function( model ) {
    5036                 if ( model.hasChanged() ) {
    5037                         _( model.changed ).chain().keys().each( this.update, this );
    5038                 }
    5039         }
    5040 });
    5041 
    5042 module.exports = Settings;
    5043 
    5044 },{"./view.js":49}],41:[function(require,module,exports){
    5045 /*globals wp, _ */
    5046 
    5047 /**
    5048  * wp.media.view.Settings.AttachmentDisplay
    5049  *
    5050  * @class
    5051  * @augments wp.media.view.Settings
    5052  * @augments wp.media.View
    5053  * @augments wp.Backbone.View
    5054  * @augments Backbone.View
    5055  */
    5056 var Settings = require( '../settings.js' ),
    5057         AttachmentDisplay;
    5058 
    5059 AttachmentDisplay = Settings.extend({
    5060         className: 'attachment-display-settings',
    5061         template:  wp.template('attachment-display-settings'),
    5062 
    5063         initialize: function() {
    5064                 var attachment = this.options.attachment;
    5065 
    5066                 _.defaults( this.options, {
    5067                         userSettings: false
    5068                 });
    5069                 // Call 'initialize' directly on the parent class.
    5070                 Settings.prototype.initialize.apply( this, arguments );
    5071                 this.listenTo( this.model, 'change:link', this.updateLinkTo );
    5072 
    5073                 if ( attachment ) {
    5074                         attachment.on( 'change:uploading', this.render, this );
    5075                 }
    5076         },
    5077 
    5078         dispose: function() {
    5079                 var attachment = this.options.attachment;
    5080                 if ( attachment ) {
    5081                         attachment.off( null, null, this );
    5082                 }
    5083                 /**
    5084                  * call 'dispose' directly on the parent class
    5085                  */
    5086                 Settings.prototype.dispose.apply( this, arguments );
    5087         },
    5088         /**
    5089          * @returns {wp.media.view.AttachmentDisplay} Returns itself to allow chaining
    5090          */
    5091         render: function() {
    5092                 var attachment = this.options.attachment;
    5093                 if ( attachment ) {
    5094                         _.extend( this.options, {
    5095                                 sizes: attachment.get('sizes'),
    5096                                 type:  attachment.get('type')
    5097                         });
    5098                 }
    5099                 /**
    5100                  * call 'render' directly on the parent class
    5101                  */
    5102                 Settings.prototype.render.call( this );
    5103                 this.updateLinkTo();
    5104                 return this;
    5105         },
    5106 
    5107         updateLinkTo: function() {
    5108                 var linkTo = this.model.get('link'),
    5109                         $input = this.$('.link-to-custom'),
    5110                         attachment = this.options.attachment;
    5111 
    5112                 if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
    5113                         $input.addClass( 'hidden' );
    5114                         return;
    5115                 }
    5116 
    5117                 if ( attachment ) {
    5118                         if ( 'post' === linkTo ) {
    5119                                 $input.val( attachment.get('link') );
    5120                         } else if ( 'file' === linkTo ) {
    5121                                 $input.val( attachment.get('url') );
    5122                         } else if ( ! this.model.get('linkUrl') ) {
    5123                                 $input.val('http://');
    5124                         }
    5125 
    5126                         $input.prop( 'readonly', 'custom' !== linkTo );
    5127                 }
    5128 
    5129                 $input.removeClass( 'hidden' );
    5130 
    5131                 // If the input is visible, focus and select its contents.
    5132                 if ( ! wp.media.isTouchDevice && $input.is(':visible') ) {
    5133                         $input.focus()[0].select();
    5134                 }
    5135         }
    5136 });
    5137 
    5138 module.exports = AttachmentDisplay;
    5139 
    5140 },{"../settings.js":40}],42:[function(require,module,exports){
    5141 /**
    5142  * wp.media.view.Sidebar
    5143  *
    5144  * @class
    5145  * @augments wp.media.view.PriorityList
    5146  * @augments wp.media.View
    5147  * @augments wp.Backbone.View
    5148  * @augments Backbone.View
    5149  */
    5150 var PriorityList = require( './priority-list.js' ),
    5151         Sidebar;
    5152 
    5153 Sidebar = PriorityList.extend({
    5154         className: 'media-sidebar'
    5155 });
    5156 
    5157 module.exports = Sidebar;
    5158 
    5159 },{"./priority-list.js":36}],43:[function(require,module,exports){
    5160 /*globals _ */
    5161 
    5162 /**
    5163  * wp.media.view.Spinner
    5164  *
    5165  * @class
    5166  * @augments wp.media.View
    5167  * @augments wp.Backbone.View
    5168  * @augments Backbone.View
    5169  */
    5170 var View = require( './view.js' ),
    5171         Spinner;
    5172 
    5173 Spinner = View.extend({
    5174         tagName:   'span',
    5175         className: 'spinner',
    5176         spinnerTimeout: false,
    5177         delay: 400,
    5178 
    5179         show: function() {
    5180                 if ( ! this.spinnerTimeout ) {
    5181                         this.spinnerTimeout = _.delay(function( $el ) {
    5182                                 $el.show();
    5183                         }, this.delay, this.$el );
    5184                 }
    5185 
    5186                 return this;
    5187         },
    5188 
    5189         hide: function() {
    5190                 this.$el.hide();
    5191                 this.spinnerTimeout = clearTimeout( this.spinnerTimeout );
    5192 
    5193                 return this;
    5194         }
    5195 });
    5196 
    5197 module.exports = Spinner;
    5198 
    5199 },{"./view.js":49}],44:[function(require,module,exports){
    5200 /*globals _, Backbone */
    5201 
    5202 /**
    5203  * wp.media.view.Toolbar
    5204  *
    5205  * A toolbar which consists of a primary and a secondary section. Each sections
    5206  * can be filled with views.
    5207  *
    5208  * @class
    5209  * @augments wp.media.View
    5210  * @augments wp.Backbone.View
    5211  * @augments Backbone.View
    5212  */
    5213 var View = require( './view.js' ),
    5214         Button = require( './button.js' ),
    5215         PriorityList = require( './priority-list.js' ),
    5216         Toolbar;
    5217 
    5218 Toolbar = View.extend({
    5219         tagName:   'div',
    5220         className: 'media-toolbar',
    5221 
    5222         initialize: function() {
    5223                 var state = this.controller.state(),
    5224                         selection = this.selection = state.get('selection'),
    5225                         library = this.library = state.get('library');
    5226 
    5227                 this._views = {};
    5228 
    5229                 // The toolbar is composed of two `PriorityList` views.
    5230                 this.primary   = new PriorityList();
    5231                 this.secondary = new PriorityList();
    5232                 this.primary.$el.addClass('media-toolbar-primary search-form');
    5233                 this.secondary.$el.addClass('media-toolbar-secondary');
    5234 
    5235                 this.views.set([ this.secondary, this.primary ]);
    5236 
    5237                 if ( this.options.items ) {
    5238                         this.set( this.options.items, { silent: true });
    5239                 }
    5240 
    5241                 if ( ! this.options.silent ) {
    5242                         this.render();
    5243                 }
    5244 
    5245                 if ( selection ) {
    5246                         selection.on( 'add remove reset', this.refresh, this );
    5247                 }
    5248 
    5249                 if ( library ) {
    5250                         library.on( 'add remove reset', this.refresh, this );
    5251                 }
    5252         },
    5253         /**
    5254          * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining
    5255          */
    5256         dispose: function() {
    5257                 if ( this.selection ) {
    5258                         this.selection.off( null, null, this );
    5259                 }
    5260 
    5261                 if ( this.library ) {
    5262                         this.library.off( null, null, this );
    5263                 }
    5264                 /**
    5265                  * call 'dispose' directly on the parent class
    5266                  */
    5267                 return View.prototype.dispose.apply( this, arguments );
    5268         },
    5269 
    5270         ready: function() {
    5271                 this.refresh();
    5272         },
    5273 
    5274         /**
    5275          * @param {string} id
    5276          * @param {Backbone.View|Object} view
    5277          * @param {Object} [options={}]
    5278          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    5279          */
    5280         set: function( id, view, options ) {
    5281                 var list;
    5282                 options = options || {};
    5283 
    5284                 // Accept an object with an `id` : `view` mapping.
    5285                 if ( _.isObject( id ) ) {
    5286                         _.each( id, function( view, id ) {
    5287                                 this.set( id, view, { silent: true });
    5288                         }, this );
    5289 
    5290                 } else {
    5291                         if ( ! ( view instanceof Backbone.View ) ) {
    5292                                 view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
    5293                                 view = new Button( view ).render();
    5294                         }
    5295 
    5296                         view.controller = view.controller || this.controller;
    5297 
    5298                         this._views[ id ] = view;
    5299 
    5300                         list = view.options.priority < 0 ? 'secondary' : 'primary';
    5301                         this[ list ].set( id, view, options );
    5302                 }
    5303 
    5304                 if ( ! options.silent ) {
    5305                         this.refresh();
    5306                 }
    5307 
    5308                 return this;
    5309         },
    5310         /**
    5311          * @param {string} id
    5312          * @returns {wp.media.view.Button}
    5313          */
    5314         get: function( id ) {
    5315                 return this._views[ id ];
    5316         },
    5317         /**
    5318          * @param {string} id
    5319          * @param {Object} options
    5320          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    5321          */
    5322         unset: function( id, options ) {
    5323                 delete this._views[ id ];
    5324                 this.primary.unset( id, options );
    5325                 this.secondary.unset( id, options );
    5326 
    5327                 if ( ! options || ! options.silent ) {
    5328                         this.refresh();
    5329                 }
    5330                 return this;
    5331         },
    5332 
    5333         refresh: function() {
    5334                 var state = this.controller.state(),
    5335                         library = state.get('library'),
    5336                         selection = state.get('selection');
    5337 
    5338                 _.each( this._views, function( button ) {
    5339                         if ( ! button.model || ! button.options || ! button.options.requires ) {
    5340                                 return;
    5341                         }
    5342 
    5343                         var requires = button.options.requires,
    5344                                 disabled = false;
    5345 
    5346                         // Prevent insertion of attachments if any of them are still uploading
    5347                         disabled = _.some( selection.models, function( attachment ) {
    5348                                 return attachment.get('uploading') === true;
    5349                         });
    5350 
    5351                         if ( requires.selection && selection && ! selection.length ) {
    5352                                 disabled = true;
    5353                         } else if ( requires.library && library && ! library.length ) {
    5354                                 disabled = true;
    5355                         }
    5356                         button.model.set( 'disabled', disabled );
    5357                 });
    5358         }
    5359 });
    5360 
    5361 module.exports = Toolbar;
    5362 
    5363 },{"./button.js":21,"./priority-list.js":36,"./view.js":49}],45:[function(require,module,exports){
    5364 /*globals wp, _ */
    5365 
    5366 /**
    5367  * wp.media.view.UploaderInline
    5368  *
    5369  * The inline uploader that shows up in the 'Upload Files' tab.
    5370  *
    5371  * @class
    5372  * @augments wp.media.View
    5373  * @augments wp.Backbone.View
    5374  * @augments Backbone.View
    5375  */
    5376 var View = require( '../view.js' ),
    5377         UploaderStatus = require( './status.js' ),
    5378         UploaderInline;
    5379 
    5380 UploaderInline = View.extend({
    5381         tagName:   'div',
    5382         className: 'uploader-inline',
    5383         template:  wp.template('uploader-inline'),
    5384 
    5385         events: {
    5386                 'click .close': 'hide'
    5387         },
    5388 
    5389         initialize: function() {
    5390                 _.defaults( this.options, {
    5391                         message: '',
    5392                         status:  true,
    5393                         canClose: false
    5394                 });
    5395 
    5396                 if ( ! this.options.$browser && this.controller.uploader ) {
    5397                         this.options.$browser = this.controller.uploader.$browser;
    5398                 }
    5399 
    5400                 if ( _.isUndefined( this.options.postId ) ) {
    5401                         this.options.postId = wp.media.view.settings.post.id;
    5402                 }
    5403 
    5404                 if ( this.options.status ) {
    5405                         this.views.set( '.upload-inline-status', new UploaderStatus({
    5406                                 controller: this.controller
    5407                         }) );
    5408                 }
    5409         },
    5410 
    5411         prepare: function() {
    5412                 var suggestedWidth = this.controller.state().get('suggestedWidth'),
    5413                         suggestedHeight = this.controller.state().get('suggestedHeight'),
    5414                         data = {};
    5415 
    5416                 data.message = this.options.message;
    5417                 data.canClose = this.options.canClose;
    5418 
    5419                 if ( suggestedWidth && suggestedHeight ) {
    5420                         data.suggestedWidth = suggestedWidth;
    5421                         data.suggestedHeight = suggestedHeight;
    5422                 }
    5423 
    5424                 return data;
    5425         },
    5426         /**
    5427          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
    5428          */
    5429         dispose: function() {
    5430                 if ( this.disposing ) {
    5431                         /**
    5432                          * call 'dispose' directly on the parent class
    5433                          */
    5434                         return View.prototype.dispose.apply( this, arguments );
    5435                 }
    5436 
    5437                 // Run remove on `dispose`, so we can be sure to refresh the
    5438                 // uploader with a view-less DOM. Track whether we're disposing
    5439                 // so we don't trigger an infinite loop.
    5440                 this.disposing = true;
    5441                 return this.remove();
    5442         },
    5443         /**
    5444          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
    5445          */
    5446         remove: function() {
    5447                 /**
    5448                  * call 'remove' directly on the parent class
    5449                  */
    5450                 var result = View.prototype.remove.apply( this, arguments );
    5451 
    5452                 _.defer( _.bind( this.refresh, this ) );
    5453                 return result;
    5454         },
    5455 
    5456         refresh: function() {
    5457                 var uploader = this.controller.uploader;
    5458 
    5459                 if ( uploader ) {
    5460                         uploader.refresh();
    5461                 }
    5462         },
    5463         /**
    5464          * @returns {wp.media.view.UploaderInline}
    5465          */
    5466         ready: function() {
    5467                 var $browser = this.options.$browser,
    5468                         $placeholder;
    5469 
    5470                 if ( this.controller.uploader ) {
    5471                         $placeholder = this.$('.browser');
    5472 
    5473                         // Check if we've already replaced the placeholder.
    5474                         if ( $placeholder[0] === $browser[0] ) {
    5475                                 return;
    5476                         }
    5477 
    5478                         $browser.detach().text( $placeholder.text() );
    5479                         $browser[0].className = $placeholder[0].className;
    5480                         $placeholder.replaceWith( $browser.show() );
    5481                 }
    5482 
    5483                 this.refresh();
    5484                 return this;
    5485         },
    5486         show: function() {
    5487                 this.$el.removeClass( 'hidden' );
    5488         },
    5489         hide: function() {
    5490                 this.$el.addClass( 'hidden' );
    5491         }
    5492 
    5493 });
    5494 
    5495 module.exports = UploaderInline;
    5496 
    5497 },{"../view.js":49,"./status.js":47}],46:[function(require,module,exports){
    5498 /*globals wp */
    5499 
    5500 /**
    5501  * wp.media.view.UploaderStatusError
    5502  *
    5503  * @class
    5504  * @augments wp.media.View
    5505  * @augments wp.Backbone.View
    5506  * @augments Backbone.View
    5507  */
    5508 var View = require( '../view.js' ),
    5509         UploaderStatusError;
    5510 
    5511 UploaderStatusError = View.extend({
    5512         className: 'upload-error',
    5513         template:  wp.template('uploader-status-error')
    5514 });
    5515 
    5516 module.exports = UploaderStatusError;
    5517 
    5518 },{"../view.js":49}],47:[function(require,module,exports){
    5519 /*globals wp, _ */
    5520 
    5521 /**
    5522  * wp.media.view.UploaderStatus
    5523  *
    5524  * An uploader status for on-going uploads.
    5525  *
    5526  * @class
    5527  * @augments wp.media.View
    5528  * @augments wp.Backbone.View
    5529  * @augments Backbone.View
    5530  */
    5531 var View = require( '../view.js' ),
    5532         UploaderStatusError = require( './status-error.js' ),
    5533         UploaderStatus;
    5534 
    5535 UploaderStatus = View.extend({
    5536         className: 'media-uploader-status',
    5537         template:  wp.template('uploader-status'),
    5538 
    5539         events: {
    5540                 'click .upload-dismiss-errors': 'dismiss'
    5541         },
    5542 
    5543         initialize: function() {
    5544                 this.queue = wp.Uploader.queue;
    5545                 this.queue.on( 'add remove reset', this.visibility, this );
    5546                 this.queue.on( 'add remove reset change:percent', this.progress, this );
    5547                 this.queue.on( 'add remove reset change:uploading', this.info, this );
    5548 
    5549                 this.errors = wp.Uploader.errors;
    5550                 this.errors.reset();
    5551                 this.errors.on( 'add remove reset', this.visibility, this );
    5552                 this.errors.on( 'add', this.error, this );
    5553         },
    5554         /**
    5555          * @global wp.Uploader
    5556          * @returns {wp.media.view.UploaderStatus}
    5557          */
    5558         dispose: function() {
    5559                 wp.Uploader.queue.off( null, null, this );
    5560                 /**
    5561                  * call 'dispose' directly on the parent class
    5562                  */
    5563                 View.prototype.dispose.apply( this, arguments );
    5564                 return this;
    5565         },
    5566 
    5567         visibility: function() {
    5568                 this.$el.toggleClass( 'uploading', !! this.queue.length );
    5569                 this.$el.toggleClass( 'errors', !! this.errors.length );
    5570                 this.$el.toggle( !! this.queue.length || !! this.errors.length );
    5571         },
    5572 
    5573         ready: function() {
    5574                 _.each({
    5575                         '$bar':      '.media-progress-bar div',
    5576                         '$index':    '.upload-index',
    5577                         '$total':    '.upload-total',
    5578                         '$filename': '.upload-filename'
    5579                 }, function( selector, key ) {
    5580                         this[ key ] = this.$( selector );
    5581                 }, this );
    5582 
    5583                 this.visibility();
    5584                 this.progress();
    5585                 this.info();
    5586         },
    5587 
    5588         progress: function() {
    5589                 var queue = this.queue,
    5590                         $bar = this.$bar;
    5591 
    5592                 if ( ! $bar || ! queue.length ) {
    5593                         return;
    5594                 }
    5595 
    5596                 $bar.width( ( queue.reduce( function( memo, attachment ) {
    5597                         if ( ! attachment.get('uploading') ) {
    5598                                 return memo + 100;
    5599                         }
    5600 
    5601                         var percent = attachment.get('percent');
    5602                         return memo + ( _.isNumber( percent ) ? percent : 100 );
    5603                 }, 0 ) / queue.length ) + '%' );
    5604         },
    5605 
    5606         info: function() {
    5607                 var queue = this.queue,
    5608                         index = 0, active;
    5609 
    5610                 if ( ! queue.length ) {
    5611                         return;
    5612                 }
    5613 
    5614                 active = this.queue.find( function( attachment, i ) {
    5615                         index = i;
    5616                         return attachment.get('uploading');
    5617                 });
    5618 
    5619                 this.$index.text( index + 1 );
    5620                 this.$total.text( queue.length );
    5621                 this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
    5622         },
    5623         /**
    5624          * @param {string} filename
    5625          * @returns {string}
    5626          */
    5627         filename: function( filename ) {
    5628                 return wp.media.truncate( _.escape( filename ), 24 );
    5629         },
    5630         /**
    5631          * @param {Backbone.Model} error
    5632          */
    5633         error: function( error ) {
    5634                 this.views.add( '.upload-errors', new UploaderStatusError({
    5635                         filename: this.filename( error.get('file').name ),
    5636                         message:  error.get('message')
    5637                 }), { at: 0 });
    5638         },
    5639 
    5640         /**
    5641          * @global wp.Uploader
    5642          *
    5643          * @param {Object} event
    5644          */
    5645         dismiss: function( event ) {
    5646                 var errors = this.views.get('.upload-errors');
    5647 
    5648                 event.preventDefault();
    5649 
    5650                 if ( errors ) {
    5651                         _.invoke( errors, 'remove' );
    5652                 }
    5653                 wp.Uploader.errors.reset();
    5654         }
    5655 });
    5656 
    5657 module.exports = UploaderStatus;
    5658 
    5659 },{"../view.js":49,"./status-error.js":46}],48:[function(require,module,exports){
    5660 /*globals wp, _, jQuery */
    5661 
    5662 /**
    5663  * wp.media.view.UploaderWindow
    5664  *
    5665  * An uploader window that allows for dragging and dropping media.
    5666  *
    5667  * @class
    5668  * @augments wp.media.View
    5669  * @augments wp.Backbone.View
    5670  * @augments Backbone.View
    5671  *
    5672  * @param {object} [options]                   Options hash passed to the view.
    5673  * @param {object} [options.uploader]          Uploader properties.
    5674  * @param {jQuery} [options.uploader.browser]
    5675  * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
    5676  * @param {object} [options.uploader.params]
    5677  */
    5678 var View = require( '../view.js' ),
    5679         $ = jQuery,
    5680         UploaderWindow;
    5681 
    5682 UploaderWindow = View.extend({
    5683         tagName:   'div',
    5684         className: 'uploader-window',
    5685         template:  wp.template('uploader-window'),
    5686 
    5687         initialize: function() {
    5688                 var uploader;
    5689 
    5690                 this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    5691 
    5692                 uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
    5693                         dropzone:  this.$el,
    5694                         browser:   this.$browser,
    5695                         params:    {}
    5696                 });
    5697 
    5698                 // Ensure the dropzone is a jQuery collection.
    5699                 if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
    5700                         uploader.dropzone = $( uploader.dropzone );
    5701                 }
    5702 
    5703                 this.controller.on( 'activate', this.refresh, this );
    5704 
    5705                 this.controller.on( 'detach', function() {
    5706                         this.$browser.remove();
    5707                 }, this );
    5708         },
    5709 
    5710         refresh: function() {
    5711                 if ( this.uploader ) {
    5712                         this.uploader.refresh();
    5713                 }
    5714         },
    5715 
    5716         ready: function() {
    5717                 var postId = wp.media.view.settings.post.id,
    5718                         dropzone;
    5719 
    5720                 // If the uploader already exists, bail.
    5721                 if ( this.uploader ) {
    5722                         return;
    5723                 }
    5724 
    5725                 if ( postId ) {
    5726                         this.options.uploader.params.post_id = postId;
    5727                 }
    5728                 this.uploader = new wp.Uploader( this.options.uploader );
    5729 
    5730                 dropzone = this.uploader.dropzone;
    5731                 dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
    5732                 dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
    5733 
    5734                 $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
    5735         },
    5736 
    5737         _ready: function() {
    5738                 this.controller.trigger( 'uploader:ready' );
    5739         },
    5740 
    5741         show: function() {
    5742                 var $el = this.$el.show();
    5743 
    5744                 // Ensure that the animation is triggered by waiting until
    5745                 // the transparent element is painted into the DOM.
    5746                 _.defer( function() {
    5747                         $el.css({ opacity: 1 });
    5748                 });
    5749         },
    5750 
    5751         hide: function() {
    5752                 var $el = this.$el.css({ opacity: 0 });
    5753 
    5754                 wp.media.transition( $el ).done( function() {
    5755                         // Transition end events are subject to race conditions.
    5756                         // Make sure that the value is set as intended.
    5757                         if ( '0' === $el.css('opacity') ) {
    5758                                 $el.hide();
    5759                         }
    5760                 });
    5761 
    5762                 // https://core.trac.wordpress.org/ticket/27341
    5763                 _.delay( function() {
    5764                         if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
    5765                                 $el.hide();
    5766                         }
    5767                 }, 500 );
    5768         }
    5769 });
    5770 
    5771 module.exports = UploaderWindow;
    5772 
    5773 },{"../view.js":49}],49:[function(require,module,exports){
    5774 /*globals wp */
    5775 
    5776 /**
    5777  * wp.media.View
    5778  *
    5779  * The base view class for media.
    5780  *
    5781  * Undelegating events, removing events from the model, and
    5782  * removing events from the controller mirror the code for
    5783  * `Backbone.View.dispose` in Backbone 0.9.8 development.
    5784  *
    5785  * This behavior has since been removed, and should not be used
    5786  * outside of the media manager.
    5787  *
    5788  * @class
    5789  * @augments wp.Backbone.View
    5790  * @augments Backbone.View
    5791  */
    5792 var View = wp.Backbone.View.extend({
    5793         constructor: function( options ) {
    5794                 if ( options && options.controller ) {
    5795                         this.controller = options.controller;
    5796                 }
    5797                 wp.Backbone.View.apply( this, arguments );
    5798         },
    5799         /**
    5800          * @todo The internal comment mentions this might have been a stop-gap
    5801          *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
    5802          *       care of this in Backbone.View now.
    5803          *
    5804          * @returns {wp.media.View} Returns itself to allow chaining
    5805          */
    5806         dispose: function() {
    5807                 // Undelegating events, removing events from the model, and
    5808                 // removing events from the controller mirror the code for
    5809                 // `Backbone.View.dispose` in Backbone 0.9.8 development.
    5810                 this.undelegateEvents();
    5811 
    5812                 if ( this.model && this.model.off ) {
    5813                         this.model.off( null, null, this );
    5814                 }
    5815 
    5816                 if ( this.collection && this.collection.off ) {
    5817                         this.collection.off( null, null, this );
    5818                 }
    5819 
    5820                 // Unbind controller events.
    5821                 if ( this.controller && this.controller.off ) {
    5822                         this.controller.off( null, null, this );
    5823                 }
    5824 
    5825                 return this;
    5826         },
    5827         /**
    5828          * @returns {wp.media.View} Returns itself to allow chaining
    5829          */
    5830         remove: function() {
    5831                 this.dispose();
    5832                 /**
    5833                  * call 'remove' directly on the parent class
    5834                  */
    5835                 return wp.Backbone.View.prototype.remove.apply( this, arguments );
    5836         }
    5837 });
    5838 
    5839 module.exports = View;
    5840 
    5841 },{}]},{},[7]);
     850},{"../../routers/manage.js":3}]},{},[2]);
  • src/wp-includes/js/media/views/attachment/details-two-column.js

     
    1313 * @augments wp.Backbone.View
    1414 * @augments Backbone.View
    1515 */
    16 var Details = require( './details.js' ),
     16var Details = wp.media.view.Attachment.Details,
    1717        TwoColumn;
    1818
    1919TwoColumn = Details.extend({
  • src/wp-includes/js/media/views/button/delete-selected-permanently.js

     
    1010 * @augments wp.Backbone.View
    1111 * @augments Backbone.View
    1212 */
    13 var Button = require( '../button.js' ),
     13var Button = wp.media.view.Button,
    1414        DeleteSelected = require( './delete-selected.js' ),
    1515        DeleteSelectedPermanently;
    1616
  • src/wp-includes/js/media/views/button/delete-selected.js

     
    1111 * @augments wp.Backbone.View
    1212 * @augments Backbone.View
    1313 */
    14 var Button = require( '../button.js' ),
     14var Button = wp.media.view.Button,
    1515        l10n = wp.media.view.l10n,
    1616        DeleteSelected;
    1717
  • src/wp-includes/js/media/views/button/select-mode-toggle.js

     
    99 * @augments wp.Backbone.View
    1010 * @augments Backbone.View
    1111 */
    12 var Button = require( '../button.js' ),
     12var Button = wp.media.view.Button,
    1313        l10n = wp.media.view.l10n,
    1414        SelectModeToggle;
    1515
  • src/wp-includes/js/media/views/edit-image-details.js

     
    44 * wp.media.view.EditImage.Details
    55 *
    66 * @class
    7  * @augments wp.media.view.EditImage.Details
     7 * @augments wp.media.view.EditImage
    88 * @augments wp.media.View
    99 * @augments wp.Backbone.View
    1010 * @augments Backbone.View
    1111 */
    12 var View = require( './view.js' ),
     12var View = wp.media.View,
    1313        EditImage = wp.media.view.EditImage,
    1414        Details;
    1515
  • src/wp-includes/js/media/views/frame/edit-attachments.js

     
    1616 * @augments Backbone.View
    1717 * @mixes wp.media.controller.StateMachine
    1818 */
    19 var Frame = require( '../frame.js' ),
    20         MediaFrame = require( '../media-frame.js' ),
    21         Modal = require( '../modal.js' ),
     19var Frame = wp.media.view.Frame,
     20        MediaFrame = wp.media.view.MediaFrame,
     21        Modal = wp.media.view.Modal,
     22        AttachmentCompat = wp.media.view.AttachmentCompat,
     23        EditImageController = wp.media.controller.EditImage,
     24
    2225        EditAttachmentMetadata = require( '../../controllers/edit-attachment-metadata.js' ),
    2326        TwoColumn = require( '../attachment/details-two-column.js' ),
    24         AttachmentCompat = require( '../attachment-compat.js' ),
    25         EditImageController = require( '../../controllers/edit-image.js' ),
    2627        DetailsView = require( '../edit-image-details.js' ),
     28
    2729        $ = jQuery,
    2830        EditAttachments;
    2931
  • src/wp-includes/js/media/views/frame/manage.js

     
    1515 * @augments Backbone.View
    1616 * @mixes wp.media.controller.StateMachine
    1717 */
    18 var MediaFrame = require( '../media-frame.js' ),
    19         UploaderWindow = require( '../uploader/window.js' ),
    20         AttachmentsBrowser = require( '../attachments/browser.js' ),
     18var MediaFrame = wp.media.view.MediaFrame,
     19        UploaderWindow = wp.media.view.UploaderWindow,
     20        AttachmentsBrowser = wp.media.view.AttachmentsBrowser,
     21        Library = wp.media.controller.Library,
     22
    2123        Router = require( '../../routers/manage.js' ),
    22         Library = require( '../../controllers/library.js' ),
     24
    2325        $ = jQuery,
    2426        Manage;
    2527
  • src/wp-includes/js/media/views.js

     
    21812181media.controller.Cropper = require( './controllers/cropper.js' );
    21822182
    21832183media.View = require( './views/view.js' );
    2184 media.view.Frame = require( './views/view.js' );
     2184media.view.Frame = require( './views/frame.js' );
    21852185media.view.MediaFrame = require( './views/media-frame.js' );
    21862186media.view.MediaFrame.Select = require( './views/frame/select.js' );
    21872187media.view.MediaFrame.Post = require( './views/frame/post.js' );
     
    22352235media.view.EditImage = require( './views/edit-image.js' );
    22362236media.view.Spinner = require( './views/spinner.js' );
    22372237
    2238 },{"./controllers/collection-add.js":1,"./controllers/collection-edit.js":2,"./controllers/cropper.js":3,"./controllers/edit-image.js":4,"./controllers/embed.js":5,"./controllers/featured-image.js":6,"./controllers/gallery-add.js":7,"./controllers/gallery-edit.js":8,"./controllers/image-details.js":9,"./controllers/library.js":10,"./controllers/media-library.js":11,"./controllers/region.js":12,"./controllers/replace-image.js":13,"./controllers/state-machine.js":14,"./controllers/state.js":15,"./utils/selection-sync.js":16,"./views/attachment-compat.js":18,"./views/attachment-filters.js":19,"./views/attachment-filters/all.js":20,"./views/attachment-filters/date.js":21,"./views/attachment-filters/uploaded.js":22,"./views/attachment.js":23,"./views/attachment/details.js":24,"./views/attachment/edit-library.js":25,"./views/attachment/edit-selection.js":26,"./views/attachment/library.js":27,"./views/attachment/selection.js":28,"./views/attachments.js":29,"./views/attachments/browser.js":30,"./views/attachments/selection.js":31,"./views/button-group.js":32,"./views/button.js":33,"./views/cropper.js":34,"./views/edit-image.js":35,"./views/embed.js":36,"./views/embed/image.js":37,"./views/embed/link.js":38,"./views/embed/url.js":39,"./views/focus-manager.js":40,"./views/frame/image-details.js":42,"./views/frame/post.js":43,"./views/frame/select.js":44,"./views/iframe.js":45,"./views/image-details.js":46,"./views/label.js":47,"./views/media-frame.js":48,"./views/menu-item.js":49,"./views/menu.js":50,"./views/modal.js":51,"./views/priority-list.js":52,"./views/router-item.js":53,"./views/router.js":54,"./views/search.js":55,"./views/selection.js":56,"./views/settings.js":57,"./views/settings/attachment-display.js":58,"./views/settings/gallery.js":59,"./views/settings/playlist.js":60,"./views/sidebar.js":61,"./views/spinner.js":62,"./views/toolbar.js":63,"./views/toolbar/embed.js":64,"./views/toolbar/select.js":65,"./views/uploader/editor.js":66,"./views/uploader/inline.js":67,"./views/uploader/status-error.js":68,"./views/uploader/status.js":69,"./views/uploader/window.js":70,"./views/view.js":71}],18:[function(require,module,exports){
     2238},{"./controllers/collection-add.js":1,"./controllers/collection-edit.js":2,"./controllers/cropper.js":3,"./controllers/edit-image.js":4,"./controllers/embed.js":5,"./controllers/featured-image.js":6,"./controllers/gallery-add.js":7,"./controllers/gallery-edit.js":8,"./controllers/image-details.js":9,"./controllers/library.js":10,"./controllers/media-library.js":11,"./controllers/region.js":12,"./controllers/replace-image.js":13,"./controllers/state-machine.js":14,"./controllers/state.js":15,"./utils/selection-sync.js":16,"./views/attachment-compat.js":18,"./views/attachment-filters.js":19,"./views/attachment-filters/all.js":20,"./views/attachment-filters/date.js":21,"./views/attachment-filters/uploaded.js":22,"./views/attachment.js":23,"./views/attachment/details.js":24,"./views/attachment/edit-library.js":25,"./views/attachment/edit-selection.js":26,"./views/attachment/library.js":27,"./views/attachment/selection.js":28,"./views/attachments.js":29,"./views/attachments/browser.js":30,"./views/attachments/selection.js":31,"./views/button-group.js":32,"./views/button.js":33,"./views/cropper.js":34,"./views/edit-image.js":35,"./views/embed.js":36,"./views/embed/image.js":37,"./views/embed/link.js":38,"./views/embed/url.js":39,"./views/focus-manager.js":40,"./views/frame.js":41,"./views/frame/image-details.js":42,"./views/frame/post.js":43,"./views/frame/select.js":44,"./views/iframe.js":45,"./views/image-details.js":46,"./views/label.js":47,"./views/media-frame.js":48,"./views/menu-item.js":49,"./views/menu.js":50,"./views/modal.js":51,"./views/priority-list.js":52,"./views/router-item.js":53,"./views/router.js":54,"./views/search.js":55,"./views/selection.js":56,"./views/settings.js":57,"./views/settings/attachment-display.js":58,"./views/settings/gallery.js":59,"./views/settings/playlist.js":60,"./views/sidebar.js":61,"./views/spinner.js":62,"./views/toolbar.js":63,"./views/toolbar/embed.js":64,"./views/toolbar/select.js":65,"./views/uploader/editor.js":66,"./views/uploader/inline.js":67,"./views/uploader/status-error.js":68,"./views/uploader/status.js":69,"./views/uploader/window.js":70,"./views/view.js":71}],18:[function(require,module,exports){
    22392239/*globals _ */
    22402240
    22412241/**
  • src/wp-includes/js/media/views.manifest.js

     
    9292media.controller.Cropper = require( './controllers/cropper.js' );