Make WordPress Core

Ticket #28510: 28510.diff

File 28510.diff, 266.6 KB (added by wonderboymusic, 10 years ago)
  • src/wp-includes/js/media/audio-video.js

     
    217217media.view.AudioDetails = require( './views/audio-details.js' );
    218218media.view.VideoDetails = require( './views/video-details.js' );
    219219
    220 },{"./controllers/audio-details.js":2,"./controllers/video-details.js":8,"./models/post-media.js":9,"./views/audio-details.js":21,"./views/frame/audio-details.js":25,"./views/frame/media-details.js":26,"./views/frame/video-details.js":28,"./views/media-details.js":31,"./views/video-details.js":50}],2:[function(require,module,exports){
     220},{"./controllers/audio-details.js":2,"./controllers/video-details.js":3,"./models/post-media.js":4,"./views/audio-details.js":5,"./views/frame/audio-details.js":6,"./views/frame/media-details.js":7,"./views/frame/video-details.js":8,"./views/media-details.js":9,"./views/video-details.js":12}],2:[function(require,module,exports){
    221221/*globals wp */
    222222
    223223/**
     
    229229 * @augments wp.media.controller.State
    230230 * @augments Backbone.Model
    231231 */
    232 var State = require( './state.js' ),
     232var State = wp.media.controller.State,
    233233        l10n = wp.media.view.l10n,
    234234        AudioDetails;
    235235
     
    252252
    253253module.exports = AudioDetails;
    254254
    255 },{"./state.js":7}],3:[function(require,module,exports){
    256 /*globals wp, _, Backbone */
    257 
    258 /**
    259  * wp.media.controller.Library
    260  *
    261  * A state for choosing an attachment or group of attachments from the media library.
    262  *
    263  * @class
    264  * @augments wp.media.controller.State
    265  * @augments Backbone.Model
    266  * @mixes media.selectionSync
    267  *
    268  * @param {object}                          [attributes]                         The attributes hash passed to the state.
    269  * @param {string}                          [attributes.id=library]              Unique identifier.
    270  * @param {string}                          [attributes.title=Media library]     Title for the state. Displays in the media menu and the frame's title region.
    271  * @param {wp.media.model.Attachments}      [attributes.library]                 The attachments collection to browse.
    272  *                                                                               If one is not supplied, a collection of all attachments will be created.
    273  * @param {wp.media.model.Selection|object} [attributes.selection]               A collection to contain attachment selections within the state.
    274  *                                                                               If the 'selection' attribute is a plain JS object,
    275  *                                                                               a Selection will be created using its values as the selection instance's `props` model.
    276  *                                                                               Otherwise, it will copy the library's `props` model.
    277  * @param {boolean}                         [attributes.multiple=false]          Whether multi-select is enabled.
    278  * @param {string}                          [attributes.content=upload]          Initial mode for the content region.
    279  *                                                                               Overridden by persistent user setting if 'contentUserSetting' is true.
    280  * @param {string}                          [attributes.menu=default]            Initial mode for the menu region.
    281  * @param {string}                          [attributes.router=browse]           Initial mode for the router region.
    282  * @param {string}                          [attributes.toolbar=select]          Initial mode for the toolbar region.
    283  * @param {boolean}                         [attributes.searchable=true]         Whether the library is searchable.
    284  * @param {boolean|string}                  [attributes.filterable=false]        Whether the library is filterable, and if so what filters should be shown.
    285  *                                                                               Accepts 'all', 'uploaded', or 'unattached'.
    286  * @param {boolean}                         [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    287  * @param {boolean}                         [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
    288  * @param {boolean}                         [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
    289  * @param {boolean}                         [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
    290  * @param {boolean}                         [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
    291  */
    292 var selectionSync = require( '../utils/selection-sync.js' ),
    293         State = require( './state.js' ),
    294         l10n = wp.media.view.l10n,
    295         getUserSetting = window.getUserSetting,
    296         setUserSetting = window.setUserSetting,
    297         Library;
    298 
    299 Library = State.extend({
    300         defaults: {
    301                 id:                 'library',
    302                 title:              l10n.mediaLibraryTitle,
    303                 multiple:           false,
    304                 content:            'upload',
    305                 menu:               'default',
    306                 router:             'browse',
    307                 toolbar:            'select',
    308                 searchable:         true,
    309                 filterable:         false,
    310                 sortable:           true,
    311                 autoSelect:         true,
    312                 describe:           false,
    313                 contentUserSetting: true,
    314                 syncSelection:      true
    315         },
    316 
    317         /**
    318          * If a library isn't provided, query all media items.
    319          * If a selection instance isn't provided, create one.
    320          *
    321          * @since 3.5.0
    322          */
    323         initialize: function() {
    324                 var selection = this.get('selection'),
    325                         props;
    326 
    327                 if ( ! this.get('library') ) {
    328                         this.set( 'library', wp.media.query() );
    329                 }
    330 
    331                 if ( ! ( selection instanceof wp.media.model.Selection ) ) {
    332                         props = selection;
    333 
    334                         if ( ! props ) {
    335                                 props = this.get('library').props.toJSON();
    336                                 props = _.omit( props, 'orderby', 'query' );
    337                         }
    338 
    339                         this.set( 'selection', new wp.media.model.Selection( null, {
    340                                 multiple: this.get('multiple'),
    341                                 props: props
    342                         }) );
    343                 }
    344 
    345                 this.resetDisplays();
    346         },
    347 
    348         /**
    349          * @since 3.5.0
    350          */
    351         activate: function() {
    352                 this.syncSelection();
    353 
    354                 wp.Uploader.queue.on( 'add', this.uploading, this );
    355 
    356                 this.get('selection').on( 'add remove reset', this.refreshContent, this );
    357 
    358                 if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
    359                         this.frame.on( 'content:activate', this.saveContentMode, this );
    360                         this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
    361                 }
    362         },
    363 
    364         /**
    365          * @since 3.5.0
    366          */
    367         deactivate: function() {
    368                 this.recordSelection();
    369 
    370                 this.frame.off( 'content:activate', this.saveContentMode, this );
    371 
    372                 // Unbind all event handlers that use this state as the context
    373                 // from the selection.
    374                 this.get('selection').off( null, null, this );
    375 
    376                 wp.Uploader.queue.off( null, null, this );
    377         },
    378 
    379         /**
    380          * Reset the library to its initial state.
    381          *
    382          * @since 3.5.0
    383          */
    384         reset: function() {
    385                 this.get('selection').reset();
    386                 this.resetDisplays();
    387                 this.refreshContent();
    388         },
    389 
    390         /**
    391          * Reset the attachment display settings defaults to the site options.
    392          *
    393          * If site options don't define them, fall back to a persistent user setting.
    394          *
    395          * @since 3.5.0
    396          */
    397         resetDisplays: function() {
    398                 var defaultProps = wp.media.view.settings.defaultProps;
    399                 this._displays = [];
    400                 this._defaultDisplaySettings = {
    401                         align: defaultProps.align || getUserSetting( 'align', 'none' ),
    402                         size:  defaultProps.size  || getUserSetting( 'imgsize', 'medium' ),
    403                         link:  defaultProps.link  || getUserSetting( 'urlbutton', 'file' )
    404                 };
    405         },
    406 
    407         /**
    408          * Create a model to represent display settings (alignment, etc.) for an attachment.
    409          *
    410          * @since 3.5.0
    411          *
    412          * @param {wp.media.model.Attachment} attachment
    413          * @returns {Backbone.Model}
    414          */
    415         display: function( attachment ) {
    416                 var displays = this._displays;
    417 
    418                 if ( ! displays[ attachment.cid ] ) {
    419                         displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
    420                 }
    421                 return displays[ attachment.cid ];
    422         },
    423 
    424         /**
    425          * Given an attachment, create attachment display settings properties.
    426          *
    427          * @since 3.6.0
    428          *
    429          * @param {wp.media.model.Attachment} attachment
    430          * @returns {Object}
    431          */
    432         defaultDisplaySettings: function( attachment ) {
    433                 var settings = this._defaultDisplaySettings;
    434                 if ( settings.canEmbed = this.canEmbed( attachment ) ) {
    435                         settings.link = 'embed';
    436                 }
    437                 return settings;
    438         },
    439 
    440         /**
    441          * Whether an attachment can be embedded (audio or video).
    442          *
    443          * @since 3.6.0
    444          *
    445          * @param {wp.media.model.Attachment} attachment
    446          * @returns {Boolean}
    447          */
    448         canEmbed: function( attachment ) {
    449                 // If uploading, we know the filename but not the mime type.
    450                 if ( ! attachment.get('uploading') ) {
    451                         var type = attachment.get('type');
    452                         if ( type !== 'audio' && type !== 'video' ) {
    453                                 return false;
    454                         }
    455                 }
    456 
    457                 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
    458         },
    459 
    460 
    461         /**
    462          * If the state is active, no items are selected, and the current
    463          * content mode is not an option in the state's router (provided
    464          * the state has a router), reset the content mode to the default.
    465          *
    466          * @since 3.5.0
    467          */
    468         refreshContent: function() {
    469                 var selection = this.get('selection'),
    470                         frame = this.frame,
    471                         router = frame.router.get(),
    472                         mode = frame.content.mode();
    473 
    474                 if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
    475                         this.frame.content.render( this.get('content') );
    476                 }
    477         },
    478 
    479         /**
    480          * Callback handler when an attachment is uploaded.
    481          *
    482          * Switch to the Media Library if uploaded from the 'Upload Files' tab.
    483          *
    484          * Adds any uploading attachments to the selection.
    485          *
    486          * If the state only supports one attachment to be selected and multiple
    487          * attachments are uploaded, the last attachment in the upload queue will
    488          * be selected.
    489          *
    490          * @since 3.5.0
    491          *
    492          * @param {wp.media.model.Attachment} attachment
    493          */
    494         uploading: function( attachment ) {
    495                 var content = this.frame.content;
    496 
    497                 if ( 'upload' === content.mode() ) {
    498                         this.frame.content.mode('browse');
    499                 }
    500 
    501                 if ( this.get( 'autoSelect' ) ) {
    502                         this.get('selection').add( attachment );
    503                         this.frame.trigger( 'library:selection:add' );
    504                 }
    505         },
    506 
    507         /**
    508          * Persist the mode of the content region as a user setting.
    509          *
    510          * @since 3.5.0
    511          */
    512         saveContentMode: function() {
    513                 if ( 'browse' !== this.get('router') ) {
    514                         return;
    515                 }
    516 
    517                 var mode = this.frame.content.mode(),
    518                         view = this.frame.router.get();
    519 
    520                 if ( view && view.get( mode ) ) {
    521                         setUserSetting( 'libraryContent', mode );
    522                 }
    523         }
    524 });
    525 
    526 // Make selectionSync available on any Media Library state.
    527 _.extend( Library.prototype, selectionSync );
    528 
    529 module.exports = Library;
    530 
    531 },{"../utils/selection-sync.js":10,"./state.js":7}],4:[function(require,module,exports){
    532 /*globals wp, _ */
    533 
    534 /**
    535  * wp.media.controller.MediaLibrary
    536  *
    537  * @class
    538  * @augments wp.media.controller.Library
    539  * @augments wp.media.controller.State
    540  * @augments Backbone.Model
    541  */
    542 var Library = require( './library.js' ),
    543         MediaLibrary;
    544 
    545 MediaLibrary = Library.extend({
    546         defaults: _.defaults({
    547                 // Attachments browser defaults. @see media.view.AttachmentsBrowser
    548                 filterable:      'uploaded',
    549 
    550                 displaySettings: false,
    551                 priority:        80,
    552                 syncSelection:   false
    553         }, Library.prototype.defaults ),
    554 
    555         /**
    556          * @since 3.9.0
    557          *
    558          * @param options
    559          */
    560         initialize: function( options ) {
    561                 this.media = options.media;
    562                 this.type = options.type;
    563                 this.set( 'library', wp.media.query({ type: this.type }) );
    564 
    565                 Library.prototype.initialize.apply( this, arguments );
    566         },
    567 
    568         /**
    569          * @since 3.9.0
    570          */
    571         activate: function() {
    572                 // @todo this should use this.frame.
    573                 if ( wp.media.frame.lastMime ) {
    574                         this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
    575                         delete wp.media.frame.lastMime;
    576                 }
    577                 Library.prototype.activate.apply( this, arguments );
    578         }
    579 });
    580 
    581 module.exports = MediaLibrary;
    582 
    583 },{"./library.js":3}],5:[function(require,module,exports){
    584 /*globals Backbone, _ */
    585 
    586 /**
    587  * wp.media.controller.Region
    588  *
    589  * A region is a persistent application layout area.
    590  *
    591  * A region assumes one mode at any time, and can be switched to another.
    592  *
    593  * When mode changes, events are triggered on the region's parent view.
    594  * The parent view will listen to specific events and fill the region with an
    595  * appropriate view depending on mode. For example, a frame listens for the
    596  * 'browse' mode t be activated on the 'content' view and then fills the region
    597  * with an AttachmentsBrowser view.
    598  *
    599  * @class
    600  *
    601  * @param {object}        options          Options hash for the region.
    602  * @param {string}        options.id       Unique identifier for the region.
    603  * @param {Backbone.View} options.view     A parent view the region exists within.
    604  * @param {string}        options.selector jQuery selector for the region within the parent view.
    605  */
    606 var Region = function( options ) {
    607         _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
    608 };
    609 
    610 // Use Backbone's self-propagating `extend` inheritance method.
    611 Region.extend = Backbone.Model.extend;
    612 
    613 _.extend( Region.prototype, {
    614         /**
    615          * Activate a mode.
    616          *
    617          * @since 3.5.0
    618          *
    619          * @param {string} mode
    620          *
    621          * @fires this.view#{this.id}:activate:{this._mode}
    622          * @fires this.view#{this.id}:activate
    623          * @fires this.view#{this.id}:deactivate:{this._mode}
    624          * @fires this.view#{this.id}:deactivate
    625          *
    626          * @returns {wp.media.controller.Region} Returns itself to allow chaining.
    627          */
    628         mode: function( mode ) {
    629                 if ( ! mode ) {
    630                         return this._mode;
    631                 }
    632                 // Bail if we're trying to change to the current mode.
    633                 if ( mode === this._mode ) {
    634                         return this;
    635                 }
    636 
    637                 /**
    638                  * Region mode deactivation event.
    639                  *
    640                  * @event this.view#{this.id}:deactivate:{this._mode}
    641                  * @event this.view#{this.id}:deactivate
    642                  */
    643                 this.trigger('deactivate');
    644 
    645                 this._mode = mode;
    646                 this.render( mode );
    647 
    648                 /**
    649                  * Region mode activation event.
    650                  *
    651                  * @event this.view#{this.id}:activate:{this._mode}
    652                  * @event this.view#{this.id}:activate
    653                  */
    654                 this.trigger('activate');
    655                 return this;
    656         },
    657         /**
    658          * Render a mode.
    659          *
    660          * @since 3.5.0
    661          *
    662          * @param {string} mode
    663          *
    664          * @fires this.view#{this.id}:create:{this._mode}
    665          * @fires this.view#{this.id}:create
    666          * @fires this.view#{this.id}:render:{this._mode}
    667          * @fires this.view#{this.id}:render
    668          *
    669          * @returns {wp.media.controller.Region} Returns itself to allow chaining
    670          */
    671         render: function( mode ) {
    672                 // If the mode isn't active, activate it.
    673                 if ( mode && mode !== this._mode ) {
    674                         return this.mode( mode );
    675                 }
    676 
    677                 var set = { view: null },
    678                         view;
    679 
    680                 /**
    681                  * Create region view event.
    682                  *
    683                  * Region view creation takes place in an event callback on the frame.
    684                  *
    685                  * @event this.view#{this.id}:create:{this._mode}
    686                  * @event this.view#{this.id}:create
    687                  */
    688                 this.trigger( 'create', set );
    689                 view = set.view;
    690 
    691                 /**
    692                  * Render region view event.
    693                  *
    694                  * Region view creation takes place in an event callback on the frame.
    695                  *
    696                  * @event this.view#{this.id}:create:{this._mode}
    697                  * @event this.view#{this.id}:create
    698                  */
    699                 this.trigger( 'render', view );
    700                 if ( view ) {
    701                         this.set( view );
    702                 }
    703                 return this;
    704         },
    705 
    706         /**
    707          * Get the region's view.
    708          *
    709          * @since 3.5.0
    710          *
    711          * @returns {wp.media.View}
    712          */
    713         get: function() {
    714                 return this.view.views.first( this.selector );
    715         },
    716 
    717         /**
    718          * Set the region's view as a subview of the frame.
    719          *
    720          * @since 3.5.0
    721          *
    722          * @param {Array|Object} views
    723          * @param {Object} [options={}]
    724          * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
    725          */
    726         set: function( views, options ) {
    727                 if ( options ) {
    728                         options.add = false;
    729                 }
    730                 return this.view.views.set( this.selector, views, options );
    731         },
    732 
    733         /**
    734          * Trigger regional view events on the frame.
    735          *
    736          * @since 3.5.0
    737          *
    738          * @param {string} event
    739          * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
    740          */
    741         trigger: function( event ) {
    742                 var base, args;
    743 
    744                 if ( ! this._mode ) {
    745                         return;
    746                 }
    747 
    748                 args = _.toArray( arguments );
    749                 base = this.id + ':' + event;
    750 
    751                 // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
    752                 args[0] = base + ':' + this._mode;
    753                 this.view.trigger.apply( this.view, args );
    754 
    755                 // Trigger `{this.id}:{event}` event on the frame.
    756                 args[0] = base;
    757                 this.view.trigger.apply( this.view, args );
    758                 return this;
    759         }
    760 });
    761 
    762 module.exports = Region;
    763 
    764 },{}],6:[function(require,module,exports){
    765 /*globals _, Backbone */
    766 
    767 /**
    768  * wp.media.controller.StateMachine
    769  *
    770  * A state machine keeps track of state. It is in one state at a time,
    771  * and can change from one state to another.
    772  *
    773  * States are stored as models in a Backbone collection.
    774  *
    775  * @since 3.5.0
    776  *
    777  * @class
    778  * @augments Backbone.Model
    779  * @mixin
    780  * @mixes Backbone.Events
    781  *
    782  * @param {Array} states
    783  */
    784 var StateMachine = function( states ) {
    785         // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
    786         this.states = new Backbone.Collection( states );
    787 };
    788 
    789 // Use Backbone's self-propagating `extend` inheritance method.
    790 StateMachine.extend = Backbone.Model.extend;
    791 
    792 _.extend( StateMachine.prototype, Backbone.Events, {
    793         /**
    794          * Fetch a state.
    795          *
    796          * If no `id` is provided, returns the active state.
    797          *
    798          * Implicitly creates states.
    799          *
    800          * Ensure that the `states` collection exists so the `StateMachine`
    801          *   can be used as a mixin.
    802          *
    803          * @since 3.5.0
    804          *
    805          * @param {string} id
    806          * @returns {wp.media.controller.State} Returns a State model
    807          *   from the StateMachine collection
    808          */
    809         state: function( id ) {
    810                 this.states = this.states || new Backbone.Collection();
    811 
    812                 // Default to the active state.
    813                 id = id || this._state;
    814 
    815                 if ( id && ! this.states.get( id ) ) {
    816                         this.states.add({ id: id });
    817                 }
    818                 return this.states.get( id );
    819         },
    820 
    821         /**
    822          * Sets the active state.
    823          *
    824          * Bail if we're trying to select the current state, if we haven't
    825          * created the `states` collection, or are trying to select a state
    826          * that does not exist.
    827          *
    828          * @since 3.5.0
    829          *
    830          * @param {string} id
    831          *
    832          * @fires wp.media.controller.State#deactivate
    833          * @fires wp.media.controller.State#activate
    834          *
    835          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
    836          */
    837         setState: function( id ) {
    838                 var previous = this.state();
    839 
    840                 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
    841                         return this;
    842                 }
    843 
    844                 if ( previous ) {
    845                         previous.trigger('deactivate');
    846                         this._lastState = previous.id;
    847                 }
    848 
    849                 this._state = id;
    850                 this.state().trigger('activate');
    851 
    852                 return this;
    853         },
    854 
    855         /**
    856          * Returns the previous active state.
    857          *
    858          * Call the `state()` method with no parameters to retrieve the current
    859          * active state.
    860          *
    861          * @since 3.5.0
    862          *
    863          * @returns {wp.media.controller.State} Returns a State model
    864          *    from the StateMachine collection
    865          */
    866         lastState: function() {
    867                 if ( this._lastState ) {
    868                         return this.state( this._lastState );
    869                 }
    870         }
    871 });
    872 
    873 // Map all event binding and triggering on a StateMachine to its `states` collection.
    874 _.each([ 'on', 'off', 'trigger' ], function( method ) {
    875         /**
    876          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
    877          */
    878         StateMachine.prototype[ method ] = function() {
    879                 // Ensure that the `states` collection exists so the `StateMachine`
    880                 // can be used as a mixin.
    881                 this.states = this.states || new Backbone.Collection();
    882                 // Forward the method to the `states` collection.
    883                 this.states[ method ].apply( this.states, arguments );
    884                 return this;
    885         };
    886 });
    887 
    888 module.exports = StateMachine;
    889 
    890 },{}],7:[function(require,module,exports){
    891 /*globals _, Backbone */
    892 
    893 /**
    894  * wp.media.controller.State
    895  *
    896  * A state is a step in a workflow that when set will trigger the controllers
    897  * for the regions to be updated as specified in the frame.
    898  *
    899  * A state has an event-driven lifecycle:
    900  *
    901  *     'ready'      triggers when a state is added to a state machine's collection.
    902  *     'activate'   triggers when a state is activated by a state machine.
    903  *     'deactivate' triggers when a state is deactivated by a state machine.
    904  *     'reset'      is not triggered automatically. It should be invoked by the
    905  *                  proper controller to reset the state to its default.
    906  *
    907  * @class
    908  * @augments Backbone.Model
    909  */
    910 var State = Backbone.Model.extend({
    911         /**
    912          * Constructor.
    913          *
    914          * @since 3.5.0
    915          */
    916         constructor: function() {
    917                 this.on( 'activate', this._preActivate, this );
    918                 this.on( 'activate', this.activate, this );
    919                 this.on( 'activate', this._postActivate, this );
    920                 this.on( 'deactivate', this._deactivate, this );
    921                 this.on( 'deactivate', this.deactivate, this );
    922                 this.on( 'reset', this.reset, this );
    923                 this.on( 'ready', this._ready, this );
    924                 this.on( 'ready', this.ready, this );
    925                 /**
    926                  * Call parent constructor with passed arguments
    927                  */
    928                 Backbone.Model.apply( this, arguments );
    929                 this.on( 'change:menu', this._updateMenu, this );
    930         },
    931         /**
    932          * Ready event callback.
    933          *
    934          * @abstract
    935          * @since 3.5.0
    936          */
    937         ready: function() {},
    938 
    939         /**
    940          * Activate event callback.
    941          *
    942          * @abstract
    943          * @since 3.5.0
    944          */
    945         activate: function() {},
    946 
    947         /**
    948          * Deactivate event callback.
    949          *
    950          * @abstract
    951          * @since 3.5.0
    952          */
    953         deactivate: function() {},
    954 
    955         /**
    956          * Reset event callback.
    957          *
    958          * @abstract
    959          * @since 3.5.0
    960          */
    961         reset: function() {},
    962 
    963         /**
    964          * @access private
    965          * @since 3.5.0
    966          */
    967         _ready: function() {
    968                 this._updateMenu();
    969         },
    970 
    971         /**
    972          * @access private
    973          * @since 3.5.0
    974         */
    975         _preActivate: function() {
    976                 this.active = true;
    977         },
    978 
    979         /**
    980          * @access private
    981          * @since 3.5.0
    982          */
    983         _postActivate: function() {
    984                 this.on( 'change:menu', this._menu, this );
    985                 this.on( 'change:titleMode', this._title, this );
    986                 this.on( 'change:content', this._content, this );
    987                 this.on( 'change:toolbar', this._toolbar, this );
    988 
    989                 this.frame.on( 'title:render:default', this._renderTitle, this );
    990 
    991                 this._title();
    992                 this._menu();
    993                 this._toolbar();
    994                 this._content();
    995                 this._router();
    996         },
    997 
    998         /**
    999          * @access private
    1000          * @since 3.5.0
    1001          */
    1002         _deactivate: function() {
    1003                 this.active = false;
    1004 
    1005                 this.frame.off( 'title:render:default', this._renderTitle, this );
    1006 
    1007                 this.off( 'change:menu', this._menu, this );
    1008                 this.off( 'change:titleMode', this._title, this );
    1009                 this.off( 'change:content', this._content, this );
    1010                 this.off( 'change:toolbar', this._toolbar, this );
    1011         },
    1012 
    1013         /**
    1014          * @access private
    1015          * @since 3.5.0
    1016          */
    1017         _title: function() {
    1018                 this.frame.title.render( this.get('titleMode') || 'default' );
    1019         },
    1020 
    1021         /**
    1022          * @access private
    1023          * @since 3.5.0
    1024          */
    1025         _renderTitle: function( view ) {
    1026                 view.$el.text( this.get('title') || '' );
    1027         },
    1028 
    1029         /**
    1030          * @access private
    1031          * @since 3.5.0
    1032          */
    1033         _router: function() {
    1034                 var router = this.frame.router,
    1035                         mode = this.get('router'),
    1036                         view;
    1037 
    1038                 this.frame.$el.toggleClass( 'hide-router', ! mode );
    1039                 if ( ! mode ) {
    1040                         return;
    1041                 }
    1042 
    1043                 this.frame.router.render( mode );
    1044 
    1045                 view = router.get();
    1046                 if ( view && view.select ) {
    1047                         view.select( this.frame.content.mode() );
    1048                 }
    1049         },
    1050 
    1051         /**
    1052          * @access private
    1053          * @since 3.5.0
    1054          */
    1055         _menu: function() {
    1056                 var menu = this.frame.menu,
    1057                         mode = this.get('menu'),
    1058                         view;
    1059 
    1060                 this.frame.$el.toggleClass( 'hide-menu', ! mode );
    1061                 if ( ! mode ) {
    1062                         return;
    1063                 }
    1064 
    1065                 menu.mode( mode );
    1066 
    1067                 view = menu.get();
    1068                 if ( view && view.select ) {
    1069                         view.select( this.id );
    1070                 }
    1071         },
    1072 
    1073         /**
    1074          * @access private
    1075          * @since 3.5.0
    1076          */
    1077         _updateMenu: function() {
    1078                 var previous = this.previous('menu'),
    1079                         menu = this.get('menu');
    1080 
    1081                 if ( previous ) {
    1082                         this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
    1083                 }
    1084 
    1085                 if ( menu ) {
    1086                         this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
    1087                 }
    1088         },
    1089 
    1090         /**
    1091          * Create a view in the media menu for the state.
    1092          *
    1093          * @access private
    1094          * @since 3.5.0
    1095          *
    1096          * @param {media.view.Menu} view The menu view.
    1097          */
    1098         _renderMenu: function( view ) {
    1099                 var menuItem = this.get('menuItem'),
    1100                         title = this.get('title'),
    1101                         priority = this.get('priority');
    1102 
    1103                 if ( ! menuItem && title ) {
    1104                         menuItem = { text: title };
    1105 
    1106                         if ( priority ) {
    1107                                 menuItem.priority = priority;
    1108                         }
    1109                 }
    1110 
    1111                 if ( ! menuItem ) {
    1112                         return;
    1113                 }
    1114 
    1115                 view.set( this.id, menuItem );
    1116         }
    1117 });
    1118 
    1119 _.each(['toolbar','content'], function( region ) {
    1120         /**
    1121          * @access private
    1122          */
    1123         State.prototype[ '_' + region ] = function() {
    1124                 var mode = this.get( region );
    1125                 if ( mode ) {
    1126                         this.frame[ region ].render( mode );
    1127                 }
    1128         };
    1129 });
    1130 
    1131 module.exports = State;
    1132 
    1133 },{}],8:[function(require,module,exports){
     255},{}],3:[function(require,module,exports){
    1134256/*globals wp */
    1135257
    1136258/**
     
    1142264 * @augments wp.media.controller.State
    1143265 * @augments Backbone.Model
    1144266 */
    1145 var State = require( './state.js' ),
     267var State = wp.media.controller.State,
    1146268        l10n = wp.media.view.l10n,
    1147269        VideoDetails;
    1148270
     
    1165287
    1166288module.exports = VideoDetails;
    1167289
    1168 },{"./state.js":7}],9:[function(require,module,exports){
     290},{}],4:[function(require,module,exports){
    1169291/*globals wp, Backbone, _ */
    1170292
    1171293/**
     
    1209331
    1210332module.exports = PostMedia;
    1211333
    1212 },{}],10:[function(require,module,exports){
    1213 /*globals _ */
    1214 
    1215 /**
    1216  * wp.media.selectionSync
    1217  *
    1218  * Sync an attachments selection in a state with another state.
    1219  *
    1220  * Allows for selecting multiple images in the Insert Media workflow, and then
    1221  * switching to the Insert Gallery workflow while preserving the attachments selection.
    1222  *
    1223  * @mixin
    1224  */
    1225 var selectionSync = {
    1226         /**
    1227          * @since 3.5.0
    1228          */
    1229         syncSelection: function() {
    1230                 var selection = this.get('selection'),
    1231                         manager = this.frame._selection;
    1232 
    1233                 if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    1234                         return;
    1235                 }
    1236 
    1237                 // If the selection supports multiple items, validate the stored
    1238                 // attachments based on the new selection's conditions. Record
    1239                 // the attachments that are not included; we'll maintain a
    1240                 // reference to those. Other attachments are considered in flux.
    1241                 if ( selection.multiple ) {
    1242                         selection.reset( [], { silent: true });
    1243                         selection.validateAll( manager.attachments );
    1244                         manager.difference = _.difference( manager.attachments.models, selection.models );
    1245                 }
    1246 
    1247                 // Sync the selection's single item with the master.
    1248                 selection.single( manager.single );
    1249         },
    1250 
    1251         /**
    1252          * Record the currently active attachments, which is a combination
    1253          * of the selection's attachments and the set of selected
    1254          * attachments that this specific selection considered invalid.
    1255          * Reset the difference and record the single attachment.
    1256          *
    1257          * @since 3.5.0
    1258          */
    1259         recordSelection: function() {
    1260                 var selection = this.get('selection'),
    1261                         manager = this.frame._selection;
    1262 
    1263                 if ( ! this.get('syncSelection') || ! manager || ! selection ) {
    1264                         return;
    1265                 }
    1266 
    1267                 if ( selection.multiple ) {
    1268                         manager.attachments.reset( selection.toArray().concat( manager.difference ) );
    1269                         manager.difference = [];
    1270                 } else {
    1271                         manager.attachments.add( selection.toArray() );
    1272                 }
    1273 
    1274                 manager.single = selection._single;
    1275         }
    1276 };
    1277 
    1278 module.exports = selectionSync;
    1279 
    1280 },{}],11:[function(require,module,exports){
    1281 /*globals _ */
    1282 
    1283 /**
    1284  * wp.media.view.AttachmentCompat
    1285  *
    1286  * A view to display fields added via the `attachment_fields_to_edit` filter.
    1287  *
    1288  * @class
    1289  * @augments wp.media.View
    1290  * @augments wp.Backbone.View
    1291  * @augments Backbone.View
    1292  */
    1293 var View = require( './view.js' ),
    1294         AttachmentCompat;
    1295 
    1296 AttachmentCompat = View.extend({
    1297         tagName:   'form',
    1298         className: 'compat-item',
    1299 
    1300         events: {
    1301                 'submit':          'preventDefault',
    1302                 'change input':    'save',
    1303                 'change select':   'save',
    1304                 'change textarea': 'save'
    1305         },
    1306 
    1307         initialize: function() {
    1308                 this.listenTo( this.model, 'change:compat', this.render );
    1309         },
    1310         /**
    1311          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    1312          */
    1313         dispose: function() {
    1314                 if ( this.$(':focus').length ) {
    1315                         this.save();
    1316                 }
    1317                 /**
    1318                  * call 'dispose' directly on the parent class
    1319                  */
    1320                 return View.prototype.dispose.apply( this, arguments );
    1321         },
    1322         /**
    1323          * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
    1324          */
    1325         render: function() {
    1326                 var compat = this.model.get('compat');
    1327                 if ( ! compat || ! compat.item ) {
    1328                         return;
    1329                 }
    1330 
    1331                 this.views.detach();
    1332                 this.$el.html( compat.item );
    1333                 this.views.render();
    1334                 return this;
    1335         },
    1336         /**
    1337          * @param {Object} event
    1338          */
    1339         preventDefault: function( event ) {
    1340                 event.preventDefault();
    1341         },
    1342         /**
    1343          * @param {Object} event
    1344          */
    1345         save: function( event ) {
    1346                 var data = {};
    1347 
    1348                 if ( event ) {
    1349                         event.preventDefault();
    1350                 }
    1351 
    1352                 _.each( this.$el.serializeArray(), function( pair ) {
    1353                         data[ pair.name ] = pair.value;
    1354                 });
    1355 
    1356                 this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
    1357                 this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
    1358         },
    1359 
    1360         postSave: function() {
    1361                 this.controller.trigger( 'attachment:compat:ready', ['ready'] );
    1362         }
    1363 });
    1364 
    1365 module.exports = AttachmentCompat;
    1366 
    1367 },{"./view.js":51}],12:[function(require,module,exports){
    1368 /*globals _, jQuery */
    1369 
    1370 /**
    1371  * wp.media.view.AttachmentFilters
    1372  *
    1373  * @class
    1374  * @augments wp.media.View
    1375  * @augments wp.Backbone.View
    1376  * @augments Backbone.View
    1377  */
    1378 var View = require( './view.js' ),
    1379         $ = jQuery,
    1380         AttachmentFilters;
    1381 
    1382 AttachmentFilters = View.extend({
    1383         tagName:   'select',
    1384         className: 'attachment-filters',
    1385         id:        'media-attachment-filters',
    1386 
    1387         events: {
    1388                 change: 'change'
    1389         },
    1390 
    1391         keys: [],
    1392 
    1393         initialize: function() {
    1394                 this.createFilters();
    1395                 _.extend( this.filters, this.options.filters );
    1396 
    1397                 // Build `<option>` elements.
    1398                 this.$el.html( _.chain( this.filters ).map( function( filter, value ) {
    1399                         return {
    1400                                 el: $( '<option></option>' ).val( value ).html( filter.text )[0],
    1401                                 priority: filter.priority || 50
    1402                         };
    1403                 }, this ).sortBy('priority').pluck('el').value() );
    1404 
    1405                 this.listenTo( this.model, 'change', this.select );
    1406                 this.select();
    1407         },
    1408 
    1409         /**
    1410          * @abstract
    1411          */
    1412         createFilters: function() {
    1413                 this.filters = {};
    1414         },
    1415 
    1416         /**
    1417          * When the selected filter changes, update the Attachment Query properties to match.
    1418          */
    1419         change: function() {
    1420                 var filter = this.filters[ this.el.value ];
    1421                 if ( filter ) {
    1422                         this.model.set( filter.props );
    1423                 }
    1424         },
    1425 
    1426         select: function() {
    1427                 var model = this.model,
    1428                         value = 'all',
    1429                         props = model.toJSON();
    1430 
    1431                 _.find( this.filters, function( filter, id ) {
    1432                         var equal = _.all( filter.props, function( prop, key ) {
    1433                                 return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] );
    1434                         });
    1435 
    1436                         if ( equal ) {
    1437                                 return value = id;
    1438                         }
    1439                 });
    1440 
    1441                 this.$el.val( value );
    1442         }
    1443 });
    1444 
    1445 module.exports = AttachmentFilters;
    1446 
    1447 },{"./view.js":51}],13:[function(require,module,exports){
     334},{}],5:[function(require,module,exports){
    1448335/*globals wp */
    1449336
    1450337/**
    1451  * wp.media.view.AttachmentFilters.All
    1452  *
    1453  * @class
    1454  * @augments wp.media.view.AttachmentFilters
    1455  * @augments wp.media.View
    1456  * @augments wp.Backbone.View
    1457  * @augments Backbone.View
    1458  */
    1459 var AttachmentFilters = require( '../attachment-filters.js' ),
    1460         l10n = wp.media.view.l10n,
    1461         All;
    1462 
    1463 All = AttachmentFilters.extend({
    1464         createFilters: function() {
    1465                 var filters = {};
    1466 
    1467                 _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
    1468                         filters[ key ] = {
    1469                                 text: text,
    1470                                 props: {
    1471                                         status:  null,
    1472                                         type:    key,
    1473                                         uploadedTo: null,
    1474                                         orderby: 'date',
    1475                                         order:   'DESC'
    1476                                 }
    1477                         };
    1478                 });
    1479 
    1480                 filters.all = {
    1481                         text:  l10n.allMediaItems,
    1482                         props: {
    1483                                 status:  null,
    1484                                 type:    null,
    1485                                 uploadedTo: null,
    1486                                 orderby: 'date',
    1487                                 order:   'DESC'
    1488                         },
    1489                         priority: 10
    1490                 };
    1491 
    1492                 if ( wp.media.view.settings.post.id ) {
    1493                         filters.uploaded = {
    1494                                 text:  l10n.uploadedToThisPost,
    1495                                 props: {
    1496                                         status:  null,
    1497                                         type:    null,
    1498                                         uploadedTo: wp.media.view.settings.post.id,
    1499                                         orderby: 'menuOrder',
    1500                                         order:   'ASC'
    1501                                 },
    1502                                 priority: 20
    1503                         };
    1504                 }
    1505 
    1506                 filters.unattached = {
    1507                         text:  l10n.unattached,
    1508                         props: {
    1509                                 status:     null,
    1510                                 uploadedTo: 0,
    1511                                 type:       null,
    1512                                 orderby:    'menuOrder',
    1513                                 order:      'ASC'
    1514                         },
    1515                         priority: 50
    1516                 };
    1517 
    1518                 if ( wp.media.view.settings.mediaTrash &&
    1519                         this.controller.isModeActive( 'grid' ) ) {
    1520 
    1521                         filters.trash = {
    1522                                 text:  l10n.trash,
    1523                                 props: {
    1524                                         uploadedTo: null,
    1525                                         status:     'trash',
    1526                                         type:       null,
    1527                                         orderby:    'date',
    1528                                         order:      'DESC'
    1529                                 },
    1530                                 priority: 50
    1531                         };
    1532                 }
    1533 
    1534                 this.filters = filters;
    1535         }
    1536 });
    1537 
    1538 module.exports = All;
    1539 
    1540 },{"../attachment-filters.js":12}],14:[function(require,module,exports){
    1541 /*globals wp, _ */
    1542 
    1543 /**
    1544  * A filter dropdown for month/dates.
    1545  *
    1546  * @class
    1547  * @augments wp.media.view.AttachmentFilters
    1548  * @augments wp.media.View
    1549  * @augments wp.Backbone.View
    1550  * @augments Backbone.View
    1551  */
    1552 var AttachmentFilters = require( '../attachment-filters.js' ),
    1553         l10n = wp.media.view.l10n,
    1554         DateFilter;
    1555 
    1556 DateFilter = AttachmentFilters.extend({
    1557         id: 'media-attachment-date-filters',
    1558 
    1559         createFilters: function() {
    1560                 var filters = {};
    1561                 _.each( wp.media.view.settings.months || {}, function( value, index ) {
    1562                         filters[ index ] = {
    1563                                 text: value.text,
    1564                                 props: {
    1565                                         year: value.year,
    1566                                         monthnum: value.month
    1567                                 }
    1568                         };
    1569                 });
    1570                 filters.all = {
    1571                         text:  l10n.allDates,
    1572                         props: {
    1573                                 monthnum: false,
    1574                                 year:  false
    1575                         },
    1576                         priority: 10
    1577                 };
    1578                 this.filters = filters;
    1579         }
    1580 });
    1581 
    1582 module.exports = DateFilter;
    1583 
    1584 },{"../attachment-filters.js":12}],15:[function(require,module,exports){
    1585 /*globals wp */
    1586 
    1587 /**
    1588  * wp.media.view.AttachmentFilters.Uploaded
    1589  *
    1590  * @class
    1591  * @augments wp.media.view.AttachmentFilters
    1592  * @augments wp.media.View
    1593  * @augments wp.Backbone.View
    1594  * @augments Backbone.View
    1595  */
    1596 var AttachmentFilters = require( '../attachment-filters.js' ),
    1597         l10n = wp.media.view.l10n,
    1598         Uploaded;
    1599 
    1600 Uploaded = AttachmentFilters.extend({
    1601         createFilters: function() {
    1602                 var type = this.model.get('type'),
    1603                         types = wp.media.view.settings.mimeTypes,
    1604                         text;
    1605 
    1606                 if ( types && type ) {
    1607                         text = types[ type ];
    1608                 }
    1609 
    1610                 this.filters = {
    1611                         all: {
    1612                                 text:  text || l10n.allMediaItems,
    1613                                 props: {
    1614                                         uploadedTo: null,
    1615                                         orderby: 'date',
    1616                                         order:   'DESC'
    1617                                 },
    1618                                 priority: 10
    1619                         },
    1620 
    1621                         uploaded: {
    1622                                 text:  l10n.uploadedToThisPost,
    1623                                 props: {
    1624                                         uploadedTo: wp.media.view.settings.post.id,
    1625                                         orderby: 'menuOrder',
    1626                                         order:   'ASC'
    1627                                 },
    1628                                 priority: 20
    1629                         },
    1630 
    1631                         unattached: {
    1632                                 text:  l10n.unattached,
    1633                                 props: {
    1634                                         uploadedTo: 0,
    1635                                         orderby: 'menuOrder',
    1636                                         order:   'ASC'
    1637                                 },
    1638                                 priority: 50
    1639                         }
    1640                 };
    1641         }
    1642 });
    1643 
    1644 module.exports = Uploaded;
    1645 
    1646 },{"../attachment-filters.js":12}],16:[function(require,module,exports){
    1647 /*globals wp, _, jQuery */
    1648 
    1649 /**
    1650  * wp.media.view.Attachment
    1651  *
    1652  * @class
    1653  * @augments wp.media.View
    1654  * @augments wp.Backbone.View
    1655  * @augments Backbone.View
    1656  */
    1657 var View = require( './view.js' ),
    1658         $ = jQuery,
    1659         Attachment;
    1660 
    1661 Attachment = View.extend({
    1662         tagName:   'li',
    1663         className: 'attachment',
    1664         template:  wp.template('attachment'),
    1665 
    1666         attributes: function() {
    1667                 return {
    1668                         'tabIndex':     0,
    1669                         'role':         'checkbox',
    1670                         'aria-label':   this.model.get( 'title' ),
    1671                         'aria-checked': false,
    1672                         'data-id':      this.model.get( 'id' )
    1673                 };
    1674         },
    1675 
    1676         events: {
    1677                 'click .js--select-attachment':   'toggleSelectionHandler',
    1678                 'change [data-setting]':          'updateSetting',
    1679                 'change [data-setting] input':    'updateSetting',
    1680                 'change [data-setting] select':   'updateSetting',
    1681                 'change [data-setting] textarea': 'updateSetting',
    1682                 'click .close':                   'removeFromLibrary',
    1683                 'click .check':                   'checkClickHandler',
    1684                 'click a':                        'preventDefault',
    1685                 'keydown .close':                 'removeFromLibrary',
    1686                 'keydown':                        'toggleSelectionHandler'
    1687         },
    1688 
    1689         buttons: {},
    1690 
    1691         initialize: function() {
    1692                 var selection = this.options.selection,
    1693                         options = _.defaults( this.options, {
    1694                                 rerenderOnModelChange: true
    1695                         } );
    1696 
    1697                 if ( options.rerenderOnModelChange ) {
    1698                         this.listenTo( this.model, 'change', this.render );
    1699                 } else {
    1700                         this.listenTo( this.model, 'change:percent', this.progress );
    1701                 }
    1702                 this.listenTo( this.model, 'change:title', this._syncTitle );
    1703                 this.listenTo( this.model, 'change:caption', this._syncCaption );
    1704                 this.listenTo( this.model, 'change:artist', this._syncArtist );
    1705                 this.listenTo( this.model, 'change:album', this._syncAlbum );
    1706 
    1707                 // Update the selection.
    1708                 this.listenTo( this.model, 'add', this.select );
    1709                 this.listenTo( this.model, 'remove', this.deselect );
    1710                 if ( selection ) {
    1711                         selection.on( 'reset', this.updateSelect, this );
    1712                         // Update the model's details view.
    1713                         this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
    1714                         this.details( this.model, this.controller.state().get('selection') );
    1715                 }
    1716 
    1717                 this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
    1718         },
    1719         /**
    1720          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1721          */
    1722         dispose: function() {
    1723                 var selection = this.options.selection;
    1724 
    1725                 // Make sure all settings are saved before removing the view.
    1726                 this.updateAll();
    1727 
    1728                 if ( selection ) {
    1729                         selection.off( null, null, this );
    1730                 }
    1731                 /**
    1732                  * call 'dispose' directly on the parent class
    1733                  */
    1734                 View.prototype.dispose.apply( this, arguments );
    1735                 return this;
    1736         },
    1737         /**
    1738          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    1739          */
    1740         render: function() {
    1741                 var options = _.defaults( this.model.toJSON(), {
    1742                                 orientation:   'landscape',
    1743                                 uploading:     false,
    1744                                 type:          '',
    1745                                 subtype:       '',
    1746                                 icon:          '',
    1747                                 filename:      '',
    1748                                 caption:       '',
    1749                                 title:         '',
    1750                                 dateFormatted: '',
    1751                                 width:         '',
    1752                                 height:        '',
    1753                                 compat:        false,
    1754                                 alt:           '',
    1755                                 description:   ''
    1756                         }, this.options );
    1757 
    1758                 options.buttons  = this.buttons;
    1759                 options.describe = this.controller.state().get('describe');
    1760 
    1761                 if ( 'image' === options.type ) {
    1762                         options.size = this.imageSize();
    1763                 }
    1764 
    1765                 options.can = {};
    1766                 if ( options.nonces ) {
    1767                         options.can.remove = !! options.nonces['delete'];
    1768                         options.can.save = !! options.nonces.update;
    1769                 }
    1770 
    1771                 if ( this.controller.state().get('allowLocalEdits') ) {
    1772                         options.allowLocalEdits = true;
    1773                 }
    1774 
    1775                 if ( options.uploading && ! options.percent ) {
    1776                         options.percent = 0;
    1777                 }
    1778 
    1779                 this.views.detach();
    1780                 this.$el.html( this.template( options ) );
    1781 
    1782                 this.$el.toggleClass( 'uploading', options.uploading );
    1783 
    1784                 if ( options.uploading ) {
    1785                         this.$bar = this.$('.media-progress-bar div');
    1786                 } else {
    1787                         delete this.$bar;
    1788                 }
    1789 
    1790                 // Check if the model is selected.
    1791                 this.updateSelect();
    1792 
    1793                 // Update the save status.
    1794                 this.updateSave();
    1795 
    1796                 this.views.render();
    1797 
    1798                 return this;
    1799         },
    1800 
    1801         progress: function() {
    1802                 if ( this.$bar && this.$bar.length ) {
    1803                         this.$bar.width( this.model.get('percent') + '%' );
    1804                 }
    1805         },
    1806 
    1807         /**
    1808          * @param {Object} event
    1809          */
    1810         toggleSelectionHandler: function( event ) {
    1811                 var method;
    1812 
    1813                 // Don't do anything inside inputs.
    1814                 if ( 'INPUT' === event.target.nodeName ) {
    1815                         return;
    1816                 }
    1817 
    1818                 // Catch arrow events
    1819                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    1820                         this.controller.trigger( 'attachment:keydown:arrow', event );
    1821                         return;
    1822                 }
    1823 
    1824                 // Catch enter and space events
    1825                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    1826                         return;
    1827                 }
    1828 
    1829                 event.preventDefault();
    1830 
    1831                 // In the grid view, bubble up an edit:attachment event to the controller.
    1832                 if ( this.controller.isModeActive( 'grid' ) ) {
    1833                         if ( this.controller.isModeActive( 'edit' ) ) {
    1834                                 // Pass the current target to restore focus when closing
    1835                                 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
    1836                                 return;
    1837                         }
    1838 
    1839                         if ( this.controller.isModeActive( 'select' ) ) {
    1840                                 method = 'toggle';
    1841                         }
    1842                 }
    1843 
    1844                 if ( event.shiftKey ) {
    1845                         method = 'between';
    1846                 } else if ( event.ctrlKey || event.metaKey ) {
    1847                         method = 'toggle';
    1848                 }
    1849 
    1850                 this.toggleSelection({
    1851                         method: method
    1852                 });
    1853 
    1854                 this.controller.trigger( 'selection:toggle' );
    1855         },
    1856         /**
    1857          * @param {Object} options
    1858          */
    1859         toggleSelection: function( options ) {
    1860                 var collection = this.collection,
    1861                         selection = this.options.selection,
    1862                         model = this.model,
    1863                         method = options && options.method,
    1864                         single, models, singleIndex, modelIndex;
    1865 
    1866                 if ( ! selection ) {
    1867                         return;
    1868                 }
    1869 
    1870                 single = selection.single();
    1871                 method = _.isUndefined( method ) ? selection.multiple : method;
    1872 
    1873                 // If the `method` is set to `between`, select all models that
    1874                 // exist between the current and the selected model.
    1875                 if ( 'between' === method && single && selection.multiple ) {
    1876                         // If the models are the same, short-circuit.
    1877                         if ( single === model ) {
    1878                                 return;
    1879                         }
    1880 
    1881                         singleIndex = collection.indexOf( single );
    1882                         modelIndex  = collection.indexOf( this.model );
    1883 
    1884                         if ( singleIndex < modelIndex ) {
    1885                                 models = collection.models.slice( singleIndex, modelIndex + 1 );
    1886                         } else {
    1887                                 models = collection.models.slice( modelIndex, singleIndex + 1 );
    1888                         }
    1889 
    1890                         selection.add( models );
    1891                         selection.single( model );
    1892                         return;
    1893 
    1894                 // If the `method` is set to `toggle`, just flip the selection
    1895                 // status, regardless of whether the model is the single model.
    1896                 } else if ( 'toggle' === method ) {
    1897                         selection[ this.selected() ? 'remove' : 'add' ]( model );
    1898                         selection.single( model );
    1899                         return;
    1900                 } else if ( 'add' === method ) {
    1901                         selection.add( model );
    1902                         selection.single( model );
    1903                         return;
    1904                 }
    1905 
    1906                 // Fixes bug that loses focus when selecting a featured image
    1907                 if ( ! method ) {
    1908                         method = 'add';
    1909                 }
    1910 
    1911                 if ( method !== 'add' ) {
    1912                         method = 'reset';
    1913                 }
    1914 
    1915                 if ( this.selected() ) {
    1916                         // If the model is the single model, remove it.
    1917                         // If it is not the same as the single model,
    1918                         // it now becomes the single model.
    1919                         selection[ single === model ? 'remove' : 'single' ]( model );
    1920                 } else {
    1921                         // If the model is not selected, run the `method` on the
    1922                         // selection. By default, we `reset` the selection, but the
    1923                         // `method` can be set to `add` the model to the selection.
    1924                         selection[ method ]( model );
    1925                         selection.single( model );
    1926                 }
    1927         },
    1928 
    1929         updateSelect: function() {
    1930                 this[ this.selected() ? 'select' : 'deselect' ]();
    1931         },
    1932         /**
    1933          * @returns {unresolved|Boolean}
    1934          */
    1935         selected: function() {
    1936                 var selection = this.options.selection;
    1937                 if ( selection ) {
    1938                         return !! selection.get( this.model.cid );
    1939                 }
    1940         },
    1941         /**
    1942          * @param {Backbone.Model} model
    1943          * @param {Backbone.Collection} collection
    1944          */
    1945         select: function( model, collection ) {
    1946                 var selection = this.options.selection,
    1947                         controller = this.controller;
    1948 
    1949                 // Check if a selection exists and if it's the collection provided.
    1950                 // If they're not the same collection, bail; we're in another
    1951                 // selection's event loop.
    1952                 if ( ! selection || ( collection && collection !== selection ) ) {
    1953                         return;
    1954                 }
    1955 
    1956                 // Bail if the model is already selected.
    1957                 if ( this.$el.hasClass( 'selected' ) ) {
    1958                         return;
    1959                 }
    1960 
    1961                 // Add 'selected' class to model, set aria-checked to true.
    1962                 this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
    1963                 //  Make the checkbox tabable, except in media grid (bulk select mode).
    1964                 if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
    1965                         this.$( '.check' ).attr( 'tabindex', '0' );
    1966                 }
    1967         },
    1968         /**
    1969          * @param {Backbone.Model} model
    1970          * @param {Backbone.Collection} collection
    1971          */
    1972         deselect: function( model, collection ) {
    1973                 var selection = this.options.selection;
    1974 
    1975                 // Check if a selection exists and if it's the collection provided.
    1976                 // If they're not the same collection, bail; we're in another
    1977                 // selection's event loop.
    1978                 if ( ! selection || ( collection && collection !== selection ) ) {
    1979                         return;
    1980                 }
    1981                 this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
    1982                         .find( '.check' ).attr( 'tabindex', '-1' );
    1983         },
    1984         /**
    1985          * @param {Backbone.Model} model
    1986          * @param {Backbone.Collection} collection
    1987          */
    1988         details: function( model, collection ) {
    1989                 var selection = this.options.selection,
    1990                         details;
    1991 
    1992                 if ( selection !== collection ) {
    1993                         return;
    1994                 }
    1995 
    1996                 details = selection.single();
    1997                 this.$el.toggleClass( 'details', details === this.model );
    1998         },
    1999         /**
    2000          * @param {Object} event
    2001          */
    2002         preventDefault: function( event ) {
    2003                 event.preventDefault();
    2004         },
    2005         /**
    2006          * @param {string} size
    2007          * @returns {Object}
    2008          */
    2009         imageSize: function( size ) {
    2010                 var sizes = this.model.get('sizes'), matched = false;
    2011 
    2012                 size = size || 'medium';
    2013 
    2014                 // Use the provided image size if possible.
    2015                 if ( sizes ) {
    2016                         if ( sizes[ size ] ) {
    2017                                 matched = sizes[ size ];
    2018                         } else if ( sizes.large ) {
    2019                                 matched = sizes.large;
    2020                         } else if ( sizes.thumbnail ) {
    2021                                 matched = sizes.thumbnail;
    2022                         } else if ( sizes.full ) {
    2023                                 matched = sizes.full;
    2024                         }
    2025 
    2026                         if ( matched ) {
    2027                                 return _.clone( matched );
    2028                         }
    2029                 }
    2030 
    2031                 return {
    2032                         url:         this.model.get('url'),
    2033                         width:       this.model.get('width'),
    2034                         height:      this.model.get('height'),
    2035                         orientation: this.model.get('orientation')
    2036                 };
    2037         },
    2038         /**
    2039          * @param {Object} event
    2040          */
    2041         updateSetting: function( event ) {
    2042                 var $setting = $( event.target ).closest('[data-setting]'),
    2043                         setting, value;
    2044 
    2045                 if ( ! $setting.length ) {
    2046                         return;
    2047                 }
    2048 
    2049                 setting = $setting.data('setting');
    2050                 value   = event.target.value;
    2051 
    2052                 if ( this.model.get( setting ) !== value ) {
    2053                         this.save( setting, value );
    2054                 }
    2055         },
    2056 
    2057         /**
    2058          * Pass all the arguments to the model's save method.
    2059          *
    2060          * Records the aggregate status of all save requests and updates the
    2061          * view's classes accordingly.
    2062          */
    2063         save: function() {
    2064                 var view = this,
    2065                         save = this._save = this._save || { status: 'ready' },
    2066                         request = this.model.save.apply( this.model, arguments ),
    2067                         requests = save.requests ? $.when( request, save.requests ) : request;
    2068 
    2069                 // If we're waiting to remove 'Saved.', stop.
    2070                 if ( save.savedTimer ) {
    2071                         clearTimeout( save.savedTimer );
    2072                 }
    2073 
    2074                 this.updateSave('waiting');
    2075                 save.requests = requests;
    2076                 requests.always( function() {
    2077                         // If we've performed another request since this one, bail.
    2078                         if ( save.requests !== requests ) {
    2079                                 return;
    2080                         }
    2081 
    2082                         view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
    2083                         save.savedTimer = setTimeout( function() {
    2084                                 view.updateSave('ready');
    2085                                 delete save.savedTimer;
    2086                         }, 2000 );
    2087                 });
    2088         },
    2089         /**
    2090          * @param {string} status
    2091          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    2092          */
    2093         updateSave: function( status ) {
    2094                 var save = this._save = this._save || { status: 'ready' };
    2095 
    2096                 if ( status && status !== save.status ) {
    2097                         this.$el.removeClass( 'save-' + save.status );
    2098                         save.status = status;
    2099                 }
    2100 
    2101                 this.$el.addClass( 'save-' + save.status );
    2102                 return this;
    2103         },
    2104 
    2105         updateAll: function() {
    2106                 var $settings = this.$('[data-setting]'),
    2107                         model = this.model,
    2108                         changed;
    2109 
    2110                 changed = _.chain( $settings ).map( function( el ) {
    2111                         var $input = $('input, textarea, select, [value]', el ),
    2112                                 setting, value;
    2113 
    2114                         if ( ! $input.length ) {
    2115                                 return;
    2116                         }
    2117 
    2118                         setting = $(el).data('setting');
    2119                         value = $input.val();
    2120 
    2121                         // Record the value if it changed.
    2122                         if ( model.get( setting ) !== value ) {
    2123                                 return [ setting, value ];
    2124                         }
    2125                 }).compact().object().value();
    2126 
    2127                 if ( ! _.isEmpty( changed ) ) {
    2128                         model.save( changed );
    2129                 }
    2130         },
    2131         /**
    2132          * @param {Object} event
    2133          */
    2134         removeFromLibrary: function( event ) {
    2135                 // Catch enter and space events
    2136                 if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    2137                         return;
    2138                 }
    2139 
    2140                 // Stop propagation so the model isn't selected.
    2141                 event.stopPropagation();
    2142 
    2143                 this.collection.remove( this.model );
    2144         },
    2145 
    2146         /**
    2147          * Add the model if it isn't in the selection, if it is in the selection,
    2148          * remove it.
    2149          *
    2150          * @param  {[type]} event [description]
    2151          * @return {[type]}       [description]
    2152          */
    2153         checkClickHandler: function ( event ) {
    2154                 var selection = this.options.selection;
    2155                 if ( ! selection ) {
    2156                         return;
    2157                 }
    2158                 event.stopPropagation();
    2159                 if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
    2160                         selection.remove( this.model );
    2161                         // Move focus back to the attachment tile (from the check).
    2162                         this.$el.focus();
    2163                 } else {
    2164                         selection.add( this.model );
    2165                 }
    2166         }
    2167 });
    2168 
    2169 // Ensure settings remain in sync between attachment views.
    2170 _.each({
    2171         caption: '_syncCaption',
    2172         title:   '_syncTitle',
    2173         artist:  '_syncArtist',
    2174         album:   '_syncAlbum'
    2175 }, function( method, setting ) {
    2176         /**
    2177          * @param {Backbone.Model} model
    2178          * @param {string} value
    2179          * @returns {wp.media.view.Attachment} Returns itself to allow chaining
    2180          */
    2181         Attachment.prototype[ method ] = function( model, value ) {
    2182                 var $setting = this.$('[data-setting="' + setting + '"]');
    2183 
    2184                 if ( ! $setting.length ) {
    2185                         return this;
    2186                 }
    2187 
    2188                 // If the updated value is in sync with the value in the DOM, there
    2189                 // is no need to re-render. If we're currently editing the value,
    2190                 // it will automatically be in sync, suppressing the re-render for
    2191                 // the view we're editing, while updating any others.
    2192                 if ( value === $setting.find('input, textarea, select, [value]').val() ) {
    2193                         return this;
    2194                 }
    2195 
    2196                 return this.render();
    2197         };
    2198 });
    2199 
    2200 module.exports = Attachment;
    2201 
    2202 },{"./view.js":51}],17:[function(require,module,exports){
    2203 /*globals wp, _ */
    2204 
    2205 /**
    2206  * wp.media.view.Attachment.Details
    2207  *
    2208  * @class
    2209  * @augments wp.media.view.Attachment
    2210  * @augments wp.media.View
    2211  * @augments wp.Backbone.View
    2212  * @augments Backbone.View
    2213  */
    2214 var Attachment = require( '../attachment.js' ),
    2215         l10n = wp.media.view.l10n,
    2216         Details;
    2217 
    2218 Details = Attachment.extend({
    2219         tagName:   'div',
    2220         className: 'attachment-details',
    2221         template:  wp.template('attachment-details'),
    2222 
    2223         attributes: function() {
    2224                 return {
    2225                         'tabIndex':     0,
    2226                         'data-id':      this.model.get( 'id' )
    2227                 };
    2228         },
    2229 
    2230         events: {
    2231                 'change [data-setting]':          'updateSetting',
    2232                 'change [data-setting] input':    'updateSetting',
    2233                 'change [data-setting] select':   'updateSetting',
    2234                 'change [data-setting] textarea': 'updateSetting',
    2235                 'click .delete-attachment':       'deleteAttachment',
    2236                 'click .trash-attachment':        'trashAttachment',
    2237                 'click .untrash-attachment':      'untrashAttachment',
    2238                 'click .edit-attachment':         'editAttachment',
    2239                 'click .refresh-attachment':      'refreshAttachment',
    2240                 'keydown':                        'toggleSelectionHandler'
    2241         },
    2242 
    2243         initialize: function() {
    2244                 this.options = _.defaults( this.options, {
    2245                         rerenderOnModelChange: false
    2246                 });
    2247 
    2248                 this.on( 'ready', this.initialFocus );
    2249                 // Call 'initialize' directly on the parent class.
    2250                 Attachment.prototype.initialize.apply( this, arguments );
    2251         },
    2252 
    2253         initialFocus: function() {
    2254                 if ( ! wp.media.isTouchDevice ) {
    2255                         this.$( ':input' ).eq( 0 ).focus();
    2256                 }
    2257         },
    2258         /**
    2259          * @param {Object} event
    2260          */
    2261         deleteAttachment: function( event ) {
    2262                 event.preventDefault();
    2263 
    2264                 if ( window.confirm( l10n.warnDelete ) ) {
    2265                         this.model.destroy();
    2266                         // Keep focus inside media modal
    2267                         // after image is deleted
    2268                         this.controller.modal.focusManager.focus();
    2269                 }
    2270         },
    2271         /**
    2272          * @param {Object} event
    2273          */
    2274         trashAttachment: function( event ) {
    2275                 var library = this.controller.library;
    2276                 event.preventDefault();
    2277 
    2278                 if ( wp.media.view.settings.mediaTrash &&
    2279                         'edit-metadata' === this.controller.content.mode() ) {
    2280 
    2281                         this.model.set( 'status', 'trash' );
    2282                         this.model.save().done( function() {
    2283                                 library._requery( true );
    2284                         } );
    2285                 }  else {
    2286                         this.model.destroy();
    2287                 }
    2288         },
    2289         /**
    2290          * @param {Object} event
    2291          */
    2292         untrashAttachment: function( event ) {
    2293                 var library = this.controller.library;
    2294                 event.preventDefault();
    2295 
    2296                 this.model.set( 'status', 'inherit' );
    2297                 this.model.save().done( function() {
    2298                         library._requery( true );
    2299                 } );
    2300         },
    2301         /**
    2302          * @param {Object} event
    2303          */
    2304         editAttachment: function( event ) {
    2305                 var editState = this.controller.states.get( 'edit-image' );
    2306                 if ( window.imageEdit && editState ) {
    2307                         event.preventDefault();
    2308 
    2309                         editState.set( 'image', this.model );
    2310                         this.controller.setState( 'edit-image' );
    2311                 } else {
    2312                         this.$el.addClass('needs-refresh');
    2313                 }
    2314         },
    2315         /**
    2316          * @param {Object} event
    2317          */
    2318         refreshAttachment: function( event ) {
    2319                 this.$el.removeClass('needs-refresh');
    2320                 event.preventDefault();
    2321                 this.model.fetch();
    2322         },
    2323         /**
    2324          * When reverse tabbing(shift+tab) out of the right details panel, deliver
    2325          * the focus to the item in the list that was being edited.
    2326          *
    2327          * @param {Object} event
    2328          */
    2329         toggleSelectionHandler: function( event ) {
    2330                 if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
    2331                         this.controller.trigger( 'attachment:details:shift-tab', event );
    2332                         return false;
    2333                 }
    2334 
    2335                 if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
    2336                         this.controller.trigger( 'attachment:keydown:arrow', event );
    2337                         return;
    2338                 }
    2339         }
    2340 });
    2341 
    2342 module.exports = Details;
    2343 
    2344 },{"../attachment.js":16}],18:[function(require,module,exports){
    2345 /**
    2346  * wp.media.view.Attachment.Library
    2347  *
    2348  * @class
    2349  * @augments wp.media.view.Attachment
    2350  * @augments wp.media.View
    2351  * @augments wp.Backbone.View
    2352  * @augments Backbone.View
    2353  */
    2354 var Attachment = require( '../attachment.js' ),
    2355         Library;
    2356 
    2357 Library = Attachment.extend({
    2358         buttons: {
    2359                 check: true
    2360         }
    2361 });
    2362 
    2363 module.exports = Library;
    2364 
    2365 },{"../attachment.js":16}],19:[function(require,module,exports){
    2366 /*globals wp, _, jQuery */
    2367 
    2368 /**
    2369  * wp.media.view.Attachments
    2370  *
    2371  * @class
    2372  * @augments wp.media.View
    2373  * @augments wp.Backbone.View
    2374  * @augments Backbone.View
    2375  */
    2376 var View = require( './view.js' ),
    2377         Attachment = require( './attachment.js' ),
    2378         $ = jQuery,
    2379         Attachments;
    2380 
    2381 Attachments = View.extend({
    2382         tagName:   'ul',
    2383         className: 'attachments',
    2384 
    2385         attributes: {
    2386                 tabIndex: -1
    2387         },
    2388 
    2389         initialize: function() {
    2390                 this.el.id = _.uniqueId('__attachments-view-');
    2391 
    2392                 _.defaults( this.options, {
    2393                         refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
    2394                         refreshThreshold:   3,
    2395                         AttachmentView:     Attachment,
    2396                         sortable:           false,
    2397                         resize:             true,
    2398                         idealColumnWidth:   $( window ).width() < 640 ? 135 : 150
    2399                 });
    2400 
    2401                 this._viewsByCid = {};
    2402                 this.$window = $( window );
    2403                 this.resizeEvent = 'resize.media-modal-columns';
    2404 
    2405                 this.collection.on( 'add', function( attachment ) {
    2406                         this.views.add( this.createAttachmentView( attachment ), {
    2407                                 at: this.collection.indexOf( attachment )
    2408                         });
    2409                 }, this );
    2410 
    2411                 this.collection.on( 'remove', function( attachment ) {
    2412                         var view = this._viewsByCid[ attachment.cid ];
    2413                         delete this._viewsByCid[ attachment.cid ];
    2414 
    2415                         if ( view ) {
    2416                                 view.remove();
    2417                         }
    2418                 }, this );
    2419 
    2420                 this.collection.on( 'reset', this.render, this );
    2421 
    2422                 this.listenTo( this.controller, 'library:selection:add',    this.attachmentFocus );
    2423 
    2424                 // Throttle the scroll handler and bind this.
    2425                 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
    2426 
    2427                 this.options.scrollElement = this.options.scrollElement || this.el;
    2428                 $( this.options.scrollElement ).on( 'scroll', this.scroll );
    2429 
    2430                 this.initSortable();
    2431 
    2432                 _.bindAll( this, 'setColumns' );
    2433 
    2434                 if ( this.options.resize ) {
    2435                         this.on( 'ready', this.bindEvents );
    2436                         this.controller.on( 'open', this.setColumns );
    2437 
    2438                         // Call this.setColumns() after this view has been rendered in the DOM so
    2439                         // attachments get proper width applied.
    2440                         _.defer( this.setColumns, this );
    2441                 }
    2442         },
    2443 
    2444         bindEvents: function() {
    2445                 this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
    2446         },
    2447 
    2448         attachmentFocus: function() {
    2449                 this.$( 'li:first' ).focus();
    2450         },
    2451 
    2452         restoreFocus: function() {
    2453                 this.$( 'li.selected:first' ).focus();
    2454         },
    2455 
    2456         arrowEvent: function( event ) {
    2457                 var attachments = this.$el.children( 'li' ),
    2458                         perRow = this.columns,
    2459                         index = attachments.filter( ':focus' ).index(),
    2460                         row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
    2461 
    2462                 if ( index === -1 ) {
    2463                         return;
    2464                 }
    2465 
    2466                 // Left arrow
    2467                 if ( 37 === event.keyCode ) {
    2468                         if ( 0 === index ) {
    2469                                 return;
    2470                         }
    2471                         attachments.eq( index - 1 ).focus();
    2472                 }
    2473 
    2474                 // Up arrow
    2475                 if ( 38 === event.keyCode ) {
    2476                         if ( 1 === row ) {
    2477                                 return;
    2478                         }
    2479                         attachments.eq( index - perRow ).focus();
    2480                 }
    2481 
    2482                 // Right arrow
    2483                 if ( 39 === event.keyCode ) {
    2484                         if ( attachments.length === index ) {
    2485                                 return;
    2486                         }
    2487                         attachments.eq( index + 1 ).focus();
    2488                 }
    2489 
    2490                 // Down arrow
    2491                 if ( 40 === event.keyCode ) {
    2492                         if ( Math.ceil( attachments.length / perRow ) === row ) {
    2493                                 return;
    2494                         }
    2495                         attachments.eq( index + perRow ).focus();
    2496                 }
    2497         },
    2498 
    2499         dispose: function() {
    2500                 this.collection.props.off( null, null, this );
    2501                 if ( this.options.resize ) {
    2502                         this.$window.off( this.resizeEvent );
    2503                 }
    2504 
    2505                 /**
    2506                  * call 'dispose' directly on the parent class
    2507                  */
    2508                 View.prototype.dispose.apply( this, arguments );
    2509         },
    2510 
    2511         setColumns: function() {
    2512                 var prev = this.columns,
    2513                         width = this.$el.width();
    2514 
    2515                 if ( width ) {
    2516                         this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
    2517 
    2518                         if ( ! prev || prev !== this.columns ) {
    2519                                 this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
    2520                         }
    2521                 }
    2522         },
    2523 
    2524         initSortable: function() {
    2525                 var collection = this.collection;
    2526 
    2527                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    2528                         return;
    2529                 }
    2530 
    2531                 this.$el.sortable( _.extend({
    2532                         // If the `collection` has a `comparator`, disable sorting.
    2533                         disabled: !! collection.comparator,
    2534 
    2535                         // Change the position of the attachment as soon as the
    2536                         // mouse pointer overlaps a thumbnail.
    2537                         tolerance: 'pointer',
    2538 
    2539                         // Record the initial `index` of the dragged model.
    2540                         start: function( event, ui ) {
    2541                                 ui.item.data('sortableIndexStart', ui.item.index());
    2542                         },
    2543 
    2544                         // Update the model's index in the collection.
    2545                         // Do so silently, as the view is already accurate.
    2546                         update: function( event, ui ) {
    2547                                 var model = collection.at( ui.item.data('sortableIndexStart') ),
    2548                                         comparator = collection.comparator;
    2549 
    2550                                 // Temporarily disable the comparator to prevent `add`
    2551                                 // from re-sorting.
    2552                                 delete collection.comparator;
    2553 
    2554                                 // Silently shift the model to its new index.
    2555                                 collection.remove( model, {
    2556                                         silent: true
    2557                                 });
    2558                                 collection.add( model, {
    2559                                         silent: true,
    2560                                         at:     ui.item.index()
    2561                                 });
    2562 
    2563                                 // Restore the comparator.
    2564                                 collection.comparator = comparator;
    2565 
    2566                                 // Fire the `reset` event to ensure other collections sync.
    2567                                 collection.trigger( 'reset', collection );
    2568 
    2569                                 // If the collection is sorted by menu order,
    2570                                 // update the menu order.
    2571                                 collection.saveMenuOrder();
    2572                         }
    2573                 }, this.options.sortable ) );
    2574 
    2575                 // If the `orderby` property is changed on the `collection`,
    2576                 // check to see if we have a `comparator`. If so, disable sorting.
    2577                 collection.props.on( 'change:orderby', function() {
    2578                         this.$el.sortable( 'option', 'disabled', !! collection.comparator );
    2579                 }, this );
    2580 
    2581                 this.collection.props.on( 'change:orderby', this.refreshSortable, this );
    2582                 this.refreshSortable();
    2583         },
    2584 
    2585         refreshSortable: function() {
    2586                 if ( wp.media.isTouchDevice || ! this.options.sortable || ! $.fn.sortable ) {
    2587                         return;
    2588                 }
    2589 
    2590                 // If the `collection` has a `comparator`, disable sorting.
    2591                 var collection = this.collection,
    2592                         orderby = collection.props.get('orderby'),
    2593                         enabled = 'menuOrder' === orderby || ! collection.comparator;
    2594 
    2595                 this.$el.sortable( 'option', 'disabled', ! enabled );
    2596         },
    2597 
    2598         /**
    2599          * @param {wp.media.model.Attachment} attachment
    2600          * @returns {wp.media.View}
    2601          */
    2602         createAttachmentView: function( attachment ) {
    2603                 var view = new this.options.AttachmentView({
    2604                         controller:           this.controller,
    2605                         model:                attachment,
    2606                         collection:           this.collection,
    2607                         selection:            this.options.selection
    2608                 });
    2609 
    2610                 return this._viewsByCid[ attachment.cid ] = view;
    2611         },
    2612 
    2613         prepare: function() {
    2614                 // Create all of the Attachment views, and replace
    2615                 // the list in a single DOM operation.
    2616                 if ( this.collection.length ) {
    2617                         this.views.set( this.collection.map( this.createAttachmentView, this ) );
    2618 
    2619                 // If there are no elements, clear the views and load some.
    2620                 } else {
    2621                         this.views.unset();
    2622                         this.collection.more().done( this.scroll );
    2623                 }
    2624         },
    2625 
    2626         ready: function() {
    2627                 // Trigger the scroll event to check if we're within the
    2628                 // threshold to query for additional attachments.
    2629                 this.scroll();
    2630         },
    2631 
    2632         scroll: function() {
    2633                 var view = this,
    2634                         el = this.options.scrollElement,
    2635                         scrollTop = el.scrollTop,
    2636                         toolbar;
    2637 
    2638                 // The scroll event occurs on the document, but the element
    2639                 // that should be checked is the document body.
    2640                 if ( el === document ) {
    2641                         el = document.body;
    2642                         scrollTop = $(document).scrollTop();
    2643                 }
    2644 
    2645                 if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
    2646                         return;
    2647                 }
    2648 
    2649                 toolbar = this.views.parent.toolbar;
    2650 
    2651                 // Show the spinner only if we are close to the bottom.
    2652                 if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
    2653                         toolbar.get('spinner').show();
    2654                 }
    2655 
    2656                 if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
    2657                         this.collection.more().done(function() {
    2658                                 view.scroll();
    2659                                 toolbar.get('spinner').hide();
    2660                         });
    2661                 }
    2662         }
    2663 });
    2664 
    2665 module.exports = Attachments;
    2666 
    2667 },{"./attachment.js":16,"./view.js":51}],20:[function(require,module,exports){
    2668 /*globals wp, _, jQuery */
    2669 
    2670 /**
    2671  * wp.media.view.AttachmentsBrowser
    2672  *
    2673  * @class
    2674  * @augments wp.media.View
    2675  * @augments wp.Backbone.View
    2676  * @augments Backbone.View
    2677  *
    2678  * @param {object}      options
    2679  * @param {object}      [options.filters=false] Which filters to show in the browser's toolbar.
    2680  *                                              Accepts 'uploaded' and 'all'.
    2681  * @param {object}      [options.search=true]   Whether to show the search interface in the
    2682  *                                              browser's toolbar.
    2683  * @param {object}      [options.date=true]     Whether to show the date filter in the
    2684  *                                              browser's toolbar.
    2685  * @param {object}      [options.display=false] Whether to show the attachments display settings
    2686  *                                              view in the sidebar.
    2687  * @param {bool|string} [options.sidebar=true]  Whether to create a sidebar for the browser.
    2688  *                                              Accepts true, false, and 'errors'.
    2689  */
    2690 var View = require( '../view.js' ),
    2691         Library = require( '../attachment/library.js' ),
    2692         Toolbar = require( '../toolbar.js' ),
    2693         Spinner = require( '../spinner.js' ),
    2694         Search = require( '../search.js' ),
    2695         Label = require( '../label.js' ),
    2696         Uploaded = require( '../attachment-filters/uploaded.js' ),
    2697         All = require( '../attachment-filters/all.js' ),
    2698         DateFilter = require( '../attachment-filters/date.js' ),
    2699         UploaderInline = require( '../uploader/inline.js' ),
    2700         Attachments = require( '../attachments.js' ),
    2701         Sidebar = require( '../sidebar.js' ),
    2702         UploaderStatus = require( '../uploader/status.js' ),
    2703         Details = require( '../attachment/details.js' ),
    2704         AttachmentCompat = require( '../attachment-compat.js' ),
    2705         AttachmentDisplay = require( '../settings/attachment-display.js' ),
    2706         mediaTrash = wp.media.view.settings.mediaTrash,
    2707         l10n = wp.media.view.l10n,
    2708         $ = jQuery,
    2709         AttachmentsBrowser;
    2710 
    2711 AttachmentsBrowser = View.extend({
    2712         tagName:   'div',
    2713         className: 'attachments-browser',
    2714 
    2715         initialize: function() {
    2716                 _.defaults( this.options, {
    2717                         filters: false,
    2718                         search:  true,
    2719                         date:    true,
    2720                         display: false,
    2721                         sidebar: true,
    2722                         AttachmentView: Library
    2723                 });
    2724 
    2725                 this.listenTo( this.controller, 'toggle:upload:attachment', _.bind( this.toggleUploader, this ) );
    2726                 this.controller.on( 'edit:selection', this.editSelection );
    2727                 this.createToolbar();
    2728                 if ( this.options.sidebar ) {
    2729                         this.createSidebar();
    2730                 }
    2731                 this.createUploader();
    2732                 this.createAttachments();
    2733                 this.updateContent();
    2734 
    2735                 if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) {
    2736                         this.$el.addClass( 'hide-sidebar' );
    2737 
    2738                         if ( 'errors' === this.options.sidebar ) {
    2739                                 this.$el.addClass( 'sidebar-for-errors' );
    2740                         }
    2741                 }
    2742 
    2743                 this.collection.on( 'add remove reset', this.updateContent, this );
    2744         },
    2745 
    2746         editSelection: function( modal ) {
    2747                 modal.$( '.media-button-backToLibrary' ).focus();
    2748         },
    2749 
    2750         /**
    2751          * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining
    2752          */
    2753         dispose: function() {
    2754                 this.options.selection.off( null, null, this );
    2755                 View.prototype.dispose.apply( this, arguments );
    2756                 return this;
    2757         },
    2758 
    2759         createToolbar: function() {
    2760                 var LibraryViewSwitcher, Filters, toolbarOptions;
    2761 
    2762                 toolbarOptions = {
    2763                         controller: this.controller
    2764                 };
    2765 
    2766                 if ( this.controller.isModeActive( 'grid' ) ) {
    2767                         toolbarOptions.className = 'media-toolbar wp-filter';
    2768                 }
    2769 
    2770                 /**
    2771                 * @member {wp.media.view.Toolbar}
    2772                 */
    2773                 this.toolbar = new Toolbar( toolbarOptions );
    2774 
    2775                 this.views.add( this.toolbar );
    2776 
    2777                 this.toolbar.set( 'spinner', new Spinner({
    2778                         priority: -60
    2779                 }) );
    2780 
    2781                 if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) {
    2782                         // "Filters" will return a <select>, need to render
    2783                         // screen reader text before
    2784                         this.toolbar.set( 'filtersLabel', new Label({
    2785                                 value: l10n.filterByType,
    2786                                 attributes: {
    2787                                         'for':  'media-attachment-filters'
    2788                                 },
    2789                                 priority:   -80
    2790                         }).render() );
    2791 
    2792                         if ( 'uploaded' === this.options.filters ) {
    2793                                 this.toolbar.set( 'filters', new Uploaded({
    2794                                         controller: this.controller,
    2795                                         model:      this.collection.props,
    2796                                         priority:   -80
    2797                                 }).render() );
    2798                         } else {
    2799                                 Filters = new All({
    2800                                         controller: this.controller,
    2801                                         model:      this.collection.props,
    2802                                         priority:   -80
    2803                                 });
    2804 
    2805                                 this.toolbar.set( 'filters', Filters.render() );
    2806                         }
    2807                 }
    2808 
    2809                 // Feels odd to bring the global media library switcher into the Attachment
    2810                 // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
    2811                 // which the controller can tap into and add this view?
    2812                 if ( this.controller.isModeActive( 'grid' ) ) {
    2813                         LibraryViewSwitcher = View.extend({
    2814                                 className: 'view-switch media-grid-view-switch',
    2815                                 template: wp.template( 'media-library-view-switcher')
    2816                         });
    2817 
    2818                         this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({
    2819                                 controller: this.controller,
    2820                                 priority: -90
    2821                         }).render() );
    2822 
    2823                         // DateFilter is a <select>, screen reader text needs to be rendered before
    2824                         this.toolbar.set( 'dateFilterLabel', new Label({
    2825                                 value: l10n.filterByDate,
    2826                                 attributes: {
    2827                                         'for': 'media-attachment-date-filters'
    2828                                 },
    2829                                 priority: -75
    2830                         }).render() );
    2831                         this.toolbar.set( 'dateFilter', new DateFilter({
    2832                                 controller: this.controller,
    2833                                 model:      this.collection.props,
    2834                                 priority: -75
    2835                         }).render() );
    2836 
    2837                         // BulkSelection is a <div> with subviews, including screen reader text
    2838                         this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({
    2839                                 text: l10n.bulkSelect,
    2840                                 controller: this.controller,
    2841                                 priority: -70
    2842                         }).render() );
    2843 
    2844                         this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({
    2845                                 filters: Filters,
    2846                                 style: 'primary',
    2847                                 disabled: true,
    2848                                 text: mediaTrash ? l10n.trashSelected : l10n.deleteSelected,
    2849                                 controller: this.controller,
    2850                                 priority: -60,
    2851                                 click: function() {
    2852                                         var changed = [], removed = [],
    2853                                                 selection = this.controller.state().get( 'selection' ),
    2854                                                 library = this.controller.state().get( 'library' );
    2855 
    2856                                         if ( ! selection.length ) {
    2857                                                 return;
    2858                                         }
    2859 
    2860                                         if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) {
    2861                                                 return;
    2862                                         }
    2863 
    2864                                         if ( mediaTrash &&
    2865                                                 'trash' !== selection.at( 0 ).get( 'status' ) &&
    2866                                                 ! window.confirm( l10n.warnBulkTrash ) ) {
    2867 
    2868                                                 return;
    2869                                         }
    2870 
    2871                                         selection.each( function( model ) {
    2872                                                 if ( ! model.get( 'nonces' )['delete'] ) {
    2873                                                         removed.push( model );
    2874                                                         return;
    2875                                                 }
    2876 
    2877                                                 if ( mediaTrash && 'trash' === model.get( 'status' ) ) {
    2878                                                         model.set( 'status', 'inherit' );
    2879                                                         changed.push( model.save() );
    2880                                                         removed.push( model );
    2881                                                 } else if ( mediaTrash ) {
    2882                                                         model.set( 'status', 'trash' );
    2883                                                         changed.push( model.save() );
    2884                                                         removed.push( model );
    2885                                                 } else {
    2886                                                         model.destroy({wait: true});
    2887                                                 }
    2888                                         } );
    2889 
    2890                                         if ( changed.length ) {
    2891                                                 selection.remove( removed );
    2892 
    2893                                                 $.when.apply( null, changed ).then( _.bind( function() {
    2894                                                         library._requery( true );
    2895                                                         this.controller.trigger( 'selection:action:done' );
    2896                                                 }, this ) );
    2897                                         } else {
    2898                                                 this.controller.trigger( 'selection:action:done' );
    2899                                         }
    2900                                 }
    2901                         }).render() );
    2902 
    2903                         if ( mediaTrash ) {
    2904                                 this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({
    2905                                         filters: Filters,
    2906                                         style: 'primary',
    2907                                         disabled: true,
    2908                                         text: l10n.deleteSelected,
    2909                                         controller: this.controller,
    2910                                         priority: -55,
    2911                                         click: function() {
    2912                                                 var removed = [], selection = this.controller.state().get( 'selection' );
    2913 
    2914                                                 if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) {
    2915                                                         return;
    2916                                                 }
    2917 
    2918                                                 selection.each( function( model ) {
    2919                                                         if ( ! model.get( 'nonces' )['delete'] ) {
    2920                                                                 removed.push( model );
    2921                                                                 return;
    2922                                                         }
    2923 
    2924                                                         model.destroy();
    2925                                                 } );
    2926 
    2927                                                 selection.remove( removed );
    2928                                                 this.controller.trigger( 'selection:action:done' );
    2929                                         }
    2930                                 }).render() );
    2931                         }
    2932 
    2933                 } else if ( this.options.date ) {
    2934                         // DateFilter is a <select>, screen reader text needs to be rendered before
    2935                         this.toolbar.set( 'dateFilterLabel', new Label({
    2936                                 value: l10n.filterByDate,
    2937                                 attributes: {
    2938                                         'for': 'media-attachment-date-filters'
    2939                                 },
    2940                                 priority: -75
    2941                         }).render() );
    2942                         this.toolbar.set( 'dateFilter', new DateFilter({
    2943                                 controller: this.controller,
    2944                                 model:      this.collection.props,
    2945                                 priority: -75
    2946                         }).render() );
    2947                 }
    2948 
    2949                 if ( this.options.search ) {
    2950                         // Search is an input, screen reader text needs to be rendered before
    2951                         this.toolbar.set( 'searchLabel', new Label({
    2952                                 value: l10n.searchMediaLabel,
    2953                                 attributes: {
    2954                                         'for': 'media-search-input'
    2955                                 },
    2956                                 priority:   60
    2957                         }).render() );
    2958                         this.toolbar.set( 'search', new Search({
    2959                                 controller: this.controller,
    2960                                 model:      this.collection.props,
    2961                                 priority:   60
    2962                         }).render() );
    2963                 }
    2964 
    2965                 if ( this.options.dragInfo ) {
    2966                         this.toolbar.set( 'dragInfo', new View({
    2967                                 el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
    2968                                 priority: -40
    2969                         }) );
    2970                 }
    2971 
    2972                 if ( this.options.suggestedWidth && this.options.suggestedHeight ) {
    2973                         this.toolbar.set( 'suggestedDimensions', new View({
    2974                                 el: $( '<div class="instructions">' + l10n.suggestedDimensions + ' ' + this.options.suggestedWidth + ' &times; ' + this.options.suggestedHeight + '</div>' )[0],
    2975                                 priority: -40
    2976                         }) );
    2977                 }
    2978         },
    2979 
    2980         updateContent: function() {
    2981                 var view = this,
    2982                         noItemsView;
    2983 
    2984                 if ( this.controller.isModeActive( 'grid' ) ) {
    2985                         noItemsView = view.attachmentsNoResults;
    2986                 } else {
    2987                         noItemsView = view.uploader;
    2988                 }
    2989 
    2990                 if ( ! this.collection.length ) {
    2991                         this.toolbar.get( 'spinner' ).show();
    2992                         this.dfd = this.collection.more().done( function() {
    2993                                 if ( ! view.collection.length ) {
    2994                                         noItemsView.$el.removeClass( 'hidden' );
    2995                                 } else {
    2996                                         noItemsView.$el.addClass( 'hidden' );
    2997                                 }
    2998                                 view.toolbar.get( 'spinner' ).hide();
    2999                         } );
    3000                 } else {
    3001                         noItemsView.$el.addClass( 'hidden' );
    3002                         view.toolbar.get( 'spinner' ).hide();
    3003                 }
    3004         },
    3005 
    3006         createUploader: function() {
    3007                 this.uploader = new UploaderInline({
    3008                         controller: this.controller,
    3009                         status:     false,
    3010                         message:    this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound,
    3011                         canClose:   this.controller.isModeActive( 'grid' )
    3012                 });
    3013 
    3014                 this.uploader.hide();
    3015                 this.views.add( this.uploader );
    3016         },
    3017 
    3018         toggleUploader: function() {
    3019                 if ( this.uploader.$el.hasClass( 'hidden' ) ) {
    3020                         this.uploader.show();
    3021                 } else {
    3022                         this.uploader.hide();
    3023                 }
    3024         },
    3025 
    3026         createAttachments: function() {
    3027                 this.attachments = new Attachments({
    3028                         controller:           this.controller,
    3029                         collection:           this.collection,
    3030                         selection:            this.options.selection,
    3031                         model:                this.model,
    3032                         sortable:             this.options.sortable,
    3033                         scrollElement:        this.options.scrollElement,
    3034                         idealColumnWidth:     this.options.idealColumnWidth,
    3035 
    3036                         // The single `Attachment` view to be used in the `Attachments` view.
    3037                         AttachmentView: this.options.AttachmentView
    3038                 });
    3039 
    3040                 // Add keydown listener to the instance of the Attachments view
    3041                 this.attachments.listenTo( this.controller, 'attachment:keydown:arrow',     this.attachments.arrowEvent );
    3042                 this.attachments.listenTo( this.controller, 'attachment:details:shift-tab', this.attachments.restoreFocus );
    3043 
    3044                 this.views.add( this.attachments );
    3045 
    3046 
    3047                 if ( this.controller.isModeActive( 'grid' ) ) {
    3048                         this.attachmentsNoResults = new View({
    3049                                 controller: this.controller,
    3050                                 tagName: 'p'
    3051                         });
    3052 
    3053                         this.attachmentsNoResults.$el.addClass( 'hidden no-media' );
    3054                         this.attachmentsNoResults.$el.html( l10n.noMedia );
    3055 
    3056                         this.views.add( this.attachmentsNoResults );
    3057                 }
    3058         },
    3059 
    3060         createSidebar: function() {
    3061                 var options = this.options,
    3062                         selection = options.selection,
    3063                         sidebar = this.sidebar = new Sidebar({
    3064                                 controller: this.controller
    3065                         });
    3066 
    3067                 this.views.add( sidebar );
    3068 
    3069                 if ( this.controller.uploader ) {
    3070                         sidebar.set( 'uploads', new UploaderStatus({
    3071                                 controller: this.controller,
    3072                                 priority:   40
    3073                         }) );
    3074                 }
    3075 
    3076                 selection.on( 'selection:single', this.createSingle, this );
    3077                 selection.on( 'selection:unsingle', this.disposeSingle, this );
    3078 
    3079                 if ( selection.single() ) {
    3080                         this.createSingle();
    3081                 }
    3082         },
    3083 
    3084         createSingle: function() {
    3085                 var sidebar = this.sidebar,
    3086                         single = this.options.selection.single();
    3087 
    3088                 sidebar.set( 'details', new Details({
    3089                         controller: this.controller,
    3090                         model:      single,
    3091                         priority:   80
    3092                 }) );
    3093 
    3094                 sidebar.set( 'compat', new AttachmentCompat({
    3095                         controller: this.controller,
    3096                         model:      single,
    3097                         priority:   120
    3098                 }) );
    3099 
    3100                 if ( this.options.display ) {
    3101                         sidebar.set( 'display', new AttachmentDisplay({
    3102                                 controller:   this.controller,
    3103                                 model:        this.model.display( single ),
    3104                                 attachment:   single,
    3105                                 priority:     160,
    3106                                 userSettings: this.model.get('displayUserSettings')
    3107                         }) );
    3108                 }
    3109 
    3110                 // Show the sidebar on mobile
    3111                 if ( this.model.id === 'insert' ) {
    3112                         sidebar.$el.addClass( 'visible' );
    3113                 }
    3114         },
    3115 
    3116         disposeSingle: function() {
    3117                 var sidebar = this.sidebar;
    3118                 sidebar.unset('details');
    3119                 sidebar.unset('compat');
    3120                 sidebar.unset('display');
    3121                 // Hide the sidebar on mobile
    3122                 sidebar.$el.removeClass( 'visible' );
    3123         }
    3124 });
    3125 
    3126 module.exports = AttachmentsBrowser;
    3127 
    3128 },{"../attachment-compat.js":11,"../attachment-filters/all.js":13,"../attachment-filters/date.js":14,"../attachment-filters/uploaded.js":15,"../attachment/details.js":17,"../attachment/library.js":18,"../attachments.js":19,"../label.js":30,"../search.js":39,"../settings/attachment-display.js":41,"../sidebar.js":42,"../spinner.js":43,"../toolbar.js":44,"../uploader/inline.js":46,"../uploader/status.js":48,"../view.js":51}],21:[function(require,module,exports){
    3129 /*globals wp */
    3130 
    3131 /**
    3132338 * wp.media.view.AudioDetails
    3133339 *
    3134340 * @class
     
    3165371
    3166372module.exports = AudioDetails;
    3167373
    3168 },{"./media-details":31}],22:[function(require,module,exports){
    3169 /*globals _, Backbone */
    3170 
    3171 /**
    3172  * wp.media.view.Button
    3173  *
    3174  * @class
    3175  * @augments wp.media.View
    3176  * @augments wp.Backbone.View
    3177  * @augments Backbone.View
    3178  */
    3179 var View = require( './view.js' ),
    3180         Button;
    3181 
    3182 Button = View.extend({
    3183         tagName:    'a',
    3184         className:  'media-button',
    3185         attributes: { href: '#' },
    3186 
    3187         events: {
    3188                 'click': 'click'
    3189         },
    3190 
    3191         defaults: {
    3192                 text:     '',
    3193                 style:    '',
    3194                 size:     'large',
    3195                 disabled: false
    3196         },
    3197 
    3198         initialize: function() {
    3199                 /**
    3200                  * Create a model with the provided `defaults`.
    3201                  *
    3202                  * @member {Backbone.Model}
    3203                  */
    3204                 this.model = new Backbone.Model( this.defaults );
    3205 
    3206                 // If any of the `options` have a key from `defaults`, apply its
    3207                 // value to the `model` and remove it from the `options object.
    3208                 _.each( this.defaults, function( def, key ) {
    3209                         var value = this.options[ key ];
    3210                         if ( _.isUndefined( value ) ) {
    3211                                 return;
    3212                         }
    3213 
    3214                         this.model.set( key, value );
    3215                         delete this.options[ key ];
    3216                 }, this );
    3217 
    3218                 this.listenTo( this.model, 'change', this.render );
    3219         },
    3220         /**
    3221          * @returns {wp.media.view.Button} Returns itself to allow chaining
    3222          */
    3223         render: function() {
    3224                 var classes = [ 'button', this.className ],
    3225                         model = this.model.toJSON();
    3226 
    3227                 if ( model.style ) {
    3228                         classes.push( 'button-' + model.style );
    3229                 }
    3230 
    3231                 if ( model.size ) {
    3232                         classes.push( 'button-' + model.size );
    3233                 }
    3234 
    3235                 classes = _.uniq( classes.concat( this.options.classes ) );
    3236                 this.el.className = classes.join(' ');
    3237 
    3238                 this.$el.attr( 'disabled', model.disabled );
    3239                 this.$el.text( this.model.get('text') );
    3240 
    3241                 return this;
    3242         },
    3243         /**
    3244          * @param {Object} event
    3245          */
    3246         click: function( event ) {
    3247                 if ( '#' === this.attributes.href ) {
    3248                         event.preventDefault();
    3249                 }
    3250 
    3251                 if ( this.options.click && ! this.model.get('disabled') ) {
    3252                         this.options.click.apply( this, arguments );
    3253                 }
    3254         }
    3255 });
    3256 
    3257 module.exports = Button;
    3258 
    3259 },{"./view.js":51}],23:[function(require,module,exports){
    3260 /**
    3261  * wp.media.view.FocusManager
    3262  *
    3263  * @class
    3264  * @augments wp.media.View
    3265  * @augments wp.Backbone.View
    3266  * @augments Backbone.View
    3267  */
    3268 var View = require( './view.js' ),
    3269         FocusManager;
    3270 
    3271 FocusManager = View.extend({
    3272 
    3273         events: {
    3274                 'keydown': 'constrainTabbing'
    3275         },
    3276 
    3277         focus: function() { // Reset focus on first left menu item
    3278                 this.$('.media-menu-item').first().focus();
    3279         },
    3280         /**
    3281          * @param {Object} event
    3282          */
    3283         constrainTabbing: function( event ) {
    3284                 var tabbables;
    3285 
    3286                 // Look for the tab key.
    3287                 if ( 9 !== event.keyCode ) {
    3288                         return;
    3289                 }
    3290 
    3291                 // Skip the file input added by Plupload.
    3292                 tabbables = this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
    3293 
    3294                 // Keep tab focus within media modal while it's open
    3295                 if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
    3296                         tabbables.first().focus();
    3297                         return false;
    3298                 } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
    3299                         tabbables.last().focus();
    3300                         return false;
    3301                 }
    3302         }
    3303 
    3304 });
    3305 
    3306 module.exports = FocusManager;
    3307 
    3308 },{"./view.js":51}],24:[function(require,module,exports){
    3309 /*globals _, Backbone */
    3310 
    3311 /**
    3312  * wp.media.view.Frame
    3313  *
    3314  * A frame is a composite view consisting of one or more regions and one or more
    3315  * states.
    3316  *
    3317  * @see wp.media.controller.State
    3318  * @see wp.media.controller.Region
    3319  *
    3320  * @class
    3321  * @augments wp.media.View
    3322  * @augments wp.Backbone.View
    3323  * @augments Backbone.View
    3324  * @mixes wp.media.controller.StateMachine
    3325  */
    3326 var StateMachine = require( '../controllers/state-machine.js' ),
    3327         State = require( '../controllers/state.js' ),
    3328         Region = require( '../controllers/region.js' ),
    3329         View = require( './view.js' ),
    3330         Frame;
    3331 
    3332 Frame = View.extend({
    3333         initialize: function() {
    3334                 _.defaults( this.options, {
    3335                         mode: [ 'select' ]
    3336                 });
    3337                 this._createRegions();
    3338                 this._createStates();
    3339                 this._createModes();
    3340         },
    3341 
    3342         _createRegions: function() {
    3343                 // Clone the regions array.
    3344                 this.regions = this.regions ? this.regions.slice() : [];
    3345 
    3346                 // Initialize regions.
    3347                 _.each( this.regions, function( region ) {
    3348                         this[ region ] = new Region({
    3349                                 view:     this,
    3350                                 id:       region,
    3351                                 selector: '.media-frame-' + region
    3352                         });
    3353                 }, this );
    3354         },
    3355         /**
    3356          * Create the frame's states.
    3357          *
    3358          * @see wp.media.controller.State
    3359          * @see wp.media.controller.StateMachine
    3360          *
    3361          * @fires wp.media.controller.State#ready
    3362          */
    3363         _createStates: function() {
    3364                 // Create the default `states` collection.
    3365                 this.states = new Backbone.Collection( null, {
    3366                         model: State
    3367                 });
    3368 
    3369                 // Ensure states have a reference to the frame.
    3370                 this.states.on( 'add', function( model ) {
    3371                         model.frame = this;
    3372                         model.trigger('ready');
    3373                 }, this );
    3374 
    3375                 if ( this.options.states ) {
    3376                         this.states.add( this.options.states );
    3377                 }
    3378         },
    3379 
    3380         /**
    3381          * A frame can be in a mode or multiple modes at one time.
    3382          *
    3383          * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
    3384          */
    3385         _createModes: function() {
    3386                 // Store active "modes" that the frame is in. Unrelated to region modes.
    3387                 this.activeModes = new Backbone.Collection();
    3388                 this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
    3389 
    3390                 _.each( this.options.mode, function( mode ) {
    3391                         this.activateMode( mode );
    3392                 }, this );
    3393         },
    3394         /**
    3395          * Reset all states on the frame to their defaults.
    3396          *
    3397          * @returns {wp.media.view.Frame} Returns itself to allow chaining
    3398          */
    3399         reset: function() {
    3400                 this.states.invoke( 'trigger', 'reset' );
    3401                 return this;
    3402         },
    3403         /**
    3404          * Map activeMode collection events to the frame.
    3405          */
    3406         triggerModeEvents: function( model, collection, options ) {
    3407                 var collectionEvent,
    3408                         modeEventMap = {
    3409                                 add: 'activate',
    3410                                 remove: 'deactivate'
    3411                         },
    3412                         eventToTrigger;
    3413                 // Probably a better way to do this.
    3414                 _.each( options, function( value, key ) {
    3415                         if ( value ) {
    3416                                 collectionEvent = key;
    3417                         }
    3418                 } );
    3419 
    3420                 if ( ! _.has( modeEventMap, collectionEvent ) ) {
    3421                         return;
    3422                 }
    3423 
    3424                 eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
    3425                 this.trigger( eventToTrigger );
    3426         },
    3427         /**
    3428          * Activate a mode on the frame.
    3429          *
    3430          * @param string mode Mode ID.
    3431          * @returns {this} Returns itself to allow chaining.
    3432          */
    3433         activateMode: function( mode ) {
    3434                 // Bail if the mode is already active.
    3435                 if ( this.isModeActive( mode ) ) {
    3436                         return;
    3437                 }
    3438                 this.activeModes.add( [ { id: mode } ] );
    3439                 // Add a CSS class to the frame so elements can be styled for the mode.
    3440                 this.$el.addClass( 'mode-' + mode );
    3441 
    3442                 return this;
    3443         },
    3444         /**
    3445          * Deactivate a mode on the frame.
    3446          *
    3447          * @param string mode Mode ID.
    3448          * @returns {this} Returns itself to allow chaining.
    3449          */
    3450         deactivateMode: function( mode ) {
    3451                 // Bail if the mode isn't active.
    3452                 if ( ! this.isModeActive( mode ) ) {
    3453                         return this;
    3454                 }
    3455                 this.activeModes.remove( this.activeModes.where( { id: mode } ) );
    3456                 this.$el.removeClass( 'mode-' + mode );
    3457                 /**
    3458                  * Frame mode deactivation event.
    3459                  *
    3460                  * @event this#{mode}:deactivate
    3461                  */
    3462                 this.trigger( mode + ':deactivate' );
    3463 
    3464                 return this;
    3465         },
    3466         /**
    3467          * Check if a mode is enabled on the frame.
    3468          *
    3469          * @param  string mode Mode ID.
    3470          * @return bool
    3471          */
    3472         isModeActive: function( mode ) {
    3473                 return Boolean( this.activeModes.where( { id: mode } ).length );
    3474         }
    3475 });
    3476 
    3477 // Make the `Frame` a `StateMachine`.
    3478 _.extend( Frame.prototype, StateMachine.prototype );
    3479 
    3480 module.exports = Frame;
    3481 
    3482 },{"../controllers/region.js":5,"../controllers/state-machine.js":6,"../controllers/state.js":7,"./view.js":51}],25:[function(require,module,exports){
     374},{"./media-details":9}],6:[function(require,module,exports){
    3483375/*globals wp */
    3484376
    3485377/**
     
    3495387 * @augments Backbone.View
    3496388 * @mixes wp.media.controller.StateMachine
    3497389 */
    3498 var MediaDetails = require( './media-details' ),
    3499         MediaLibrary = require( '../../controllers/media-library.js' ),
     390var MediaDetails = wp.media.view.MediaFrame.MediaDetails,
     391        MediaLibrary = wp.media.controller.MediaLibrary,
    3500392        AudioDetailsView = require( '../audio-details.js' ),
    3501393        AudioDetailsController = require( '../../controllers/audio-details.js' ),
    3502394        l10n = wp.media.view.l10n,
     
    3558450
    3559451module.exports = AudioDetails;
    3560452
    3561 },{"../../controllers/audio-details.js":2,"../../controllers/media-library.js":4,"../audio-details.js":21,"./media-details":26}],26:[function(require,module,exports){
     453},{"../../controllers/audio-details.js":2,"../audio-details.js":5}],7:[function(require,module,exports){
    3562454/*globals wp */
    3563455
    3564456/**
     
    3573465 * @augments Backbone.View
    3574466 * @mixes wp.media.controller.StateMachine
    3575467 */
    3576 var View = require( '../view.js' ),
    3577         Toolbar = require( '../toolbar.js' ),
    3578         Select = require( './select.js' ),
     468var View = wp.media.View,
     469        Toolbar = wp.media.view.Toolbar,
     470        Select = wp.media.view.MediaFrame.Select,
    3579471        l10n = wp.media.view.l10n,
    3580472        MediaDetails;
    3581473
     
    3692584
    3693585module.exports = MediaDetails;
    3694586
    3695 },{"../toolbar.js":44,"../view.js":51,"./select.js":27}],27:[function(require,module,exports){
     587},{}],8:[function(require,module,exports){
    3696588/*globals wp, _ */
    3697589
    3698590/**
    3699  * wp.media.view.MediaFrame.Select
    3700  *
    3701  * A frame for selecting an item or items from the media library.
    3702  *
    3703  * @class
    3704  * @augments wp.media.view.MediaFrame
    3705  * @augments wp.media.view.Frame
    3706  * @augments wp.media.View
    3707  * @augments wp.Backbone.View
    3708  * @augments Backbone.View
    3709  * @mixes wp.media.controller.StateMachine
    3710  */
    3711 
    3712 var MediaFrame = require( '../media-frame.js' ),
    3713         Library = require( '../../controllers/library.js' ),
    3714         AttachmentsBrowser = require( '../attachments/browser.js' ),
    3715         UploaderInline = require( '../uploader/inline.js' ),
    3716         ToolbarSelect = require( '../toolbar/select.js' ),
    3717         l10n = wp.media.view.l10n,
    3718         Select;
    3719 
    3720 Select = MediaFrame.extend({
    3721         initialize: function() {
    3722                 // Call 'initialize' directly on the parent class.
    3723                 MediaFrame.prototype.initialize.apply( this, arguments );
    3724 
    3725                 _.defaults( this.options, {
    3726                         selection: [],
    3727                         library:   {},
    3728                         multiple:  false,
    3729                         state:    'library'
    3730                 });
    3731 
    3732                 this.createSelection();
    3733                 this.createStates();
    3734                 this.bindHandlers();
    3735         },
    3736 
    3737         /**
    3738          * Attach a selection collection to the frame.
    3739          *
    3740          * A selection is a collection of attachments used for a specific purpose
    3741          * by a media frame. e.g. Selecting an attachment (or many) to insert into
    3742          * post content.
    3743          *
    3744          * @see media.model.Selection
    3745          */
    3746         createSelection: function() {
    3747                 var selection = this.options.selection;
    3748 
    3749                 if ( ! (selection instanceof wp.media.model.Selection) ) {
    3750                         this.options.selection = new wp.media.model.Selection( selection, {
    3751                                 multiple: this.options.multiple
    3752                         });
    3753                 }
    3754 
    3755                 this._selection = {
    3756                         attachments: new wp.media.model.Attachments(),
    3757                         difference: []
    3758                 };
    3759         },
    3760 
    3761         /**
    3762          * Create the default states on the frame.
    3763          */
    3764         createStates: function() {
    3765                 var options = this.options;
    3766 
    3767                 if ( this.options.states ) {
    3768                         return;
    3769                 }
    3770 
    3771                 // Add the default states.
    3772                 this.states.add([
    3773                         // Main states.
    3774                         new Library({
    3775                                 library:   wp.media.query( options.library ),
    3776                                 multiple:  options.multiple,
    3777                                 title:     options.title,
    3778                                 priority:  20
    3779                         })
    3780                 ]);
    3781         },
    3782 
    3783         /**
    3784          * Bind region mode event callbacks.
    3785          *
    3786          * @see media.controller.Region.render
    3787          */
    3788         bindHandlers: function() {
    3789                 this.on( 'router:create:browse', this.createRouter, this );
    3790                 this.on( 'router:render:browse', this.browseRouter, this );
    3791                 this.on( 'content:create:browse', this.browseContent, this );
    3792                 this.on( 'content:render:upload', this.uploadContent, this );
    3793                 this.on( 'toolbar:create:select', this.createSelectToolbar, this );
    3794         },
    3795 
    3796         /**
    3797          * Render callback for the router region in the `browse` mode.
    3798          *
    3799          * @param {wp.media.view.Router} routerView
    3800          */
    3801         browseRouter: function( routerView ) {
    3802                 routerView.set({
    3803                         upload: {
    3804                                 text:     l10n.uploadFilesTitle,
    3805                                 priority: 20
    3806                         },
    3807                         browse: {
    3808                                 text:     l10n.mediaLibraryTitle,
    3809                                 priority: 40
    3810                         }
    3811                 });
    3812         },
    3813 
    3814         /**
    3815          * Render callback for the content region in the `browse` mode.
    3816          *
    3817          * @param {wp.media.controller.Region} contentRegion
    3818          */
    3819         browseContent: function( contentRegion ) {
    3820                 var state = this.state();
    3821 
    3822                 this.$el.removeClass('hide-toolbar');
    3823 
    3824                 // Browse our library of attachments.
    3825                 contentRegion.view = new AttachmentsBrowser({
    3826                         controller: this,
    3827                         collection: state.get('library'),
    3828                         selection:  state.get('selection'),
    3829                         model:      state,
    3830                         sortable:   state.get('sortable'),
    3831                         search:     state.get('searchable'),
    3832                         filters:    state.get('filterable'),
    3833                         display:    state.has('display') ? state.get('display') : state.get('displaySettings'),
    3834                         dragInfo:   state.get('dragInfo'),
    3835 
    3836                         idealColumnWidth: state.get('idealColumnWidth'),
    3837                         suggestedWidth:   state.get('suggestedWidth'),
    3838                         suggestedHeight:  state.get('suggestedHeight'),
    3839 
    3840                         AttachmentView: state.get('AttachmentView')
    3841                 });
    3842         },
    3843 
    3844         /**
    3845          * Render callback for the content region in the `upload` mode.
    3846          */
    3847         uploadContent: function() {
    3848                 this.$el.removeClass( 'hide-toolbar' );
    3849                 this.content.set( new UploaderInline({
    3850                         controller: this
    3851                 }) );
    3852         },
    3853 
    3854         /**
    3855          * Toolbars
    3856          *
    3857          * @param {Object} toolbar
    3858          * @param {Object} [options={}]
    3859          * @this wp.media.controller.Region
    3860          */
    3861         createSelectToolbar: function( toolbar, options ) {
    3862                 options = options || this.options.button || {};
    3863                 options.controller = this;
    3864 
    3865                 toolbar.view = new ToolbarSelect( options );
    3866         }
    3867 });
    3868 
    3869 module.exports = Select;
    3870 
    3871 },{"../../controllers/library.js":3,"../attachments/browser.js":20,"../media-frame.js":32,"../toolbar/select.js":45,"../uploader/inline.js":46}],28:[function(require,module,exports){
    3872 /*globals wp, _ */
    3873 
    3874 /**
    3875591 * wp.media.view.MediaFrame.VideoDetails
    3876592 *
    3877593 * @class
     
    3884600 * @augments Backbone.View
    3885601 * @mixes wp.media.controller.StateMachine
    3886602 */
    3887 var MediaDetails = require( './media-details' ),
    3888         MediaLibrary = require( '../../controllers/media-library.js' ),
     603var MediaDetails = wp.media.view.MediaFrame.MediaDetails,
     604        MediaLibrary = wp.media.controller.MediaLibrary,
    3889605        VideoDetailsView = require( '../video-details.js' ),
    3890606        VideoDetailsController = require( '../../controllers/video-details.js' ),
    3891607        l10n = wp.media.view.l10n,
     
    4007723
    4008724module.exports = VideoDetails;
    4009725
    4010 },{"../../controllers/media-library.js":4,"../../controllers/video-details.js":8,"../video-details.js":50,"./media-details":26}],29:[function(require,module,exports){
    4011 /**
    4012  * wp.media.view.Iframe
    4013  *
    4014  * @class
    4015  * @augments wp.media.View
    4016  * @augments wp.Backbone.View
    4017  * @augments Backbone.View
    4018  */
    4019 var View = require( './view.js' ),
    4020         Iframe;
    4021 
    4022 Iframe = View.extend({
    4023         className: 'media-iframe',
    4024         /**
    4025          * @returns {wp.media.view.Iframe} Returns itself to allow chaining
    4026          */
    4027         render: function() {
    4028                 this.views.detach();
    4029                 this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
    4030                 this.views.render();
    4031                 return this;
    4032         }
    4033 });
    4034 
    4035 module.exports = Iframe;
    4036 
    4037 },{"./view.js":51}],30:[function(require,module,exports){
    4038 /**
    4039  * wp.media.view.Label
    4040  *
    4041  * @class
    4042  * @augments wp.media.View
    4043  * @augments wp.Backbone.View
    4044  * @augments Backbone.View
    4045  */
    4046 var View = require( './view.js' ),
    4047         Label;
    4048 
    4049 Label = View.extend({
    4050         tagName: 'label',
    4051         className: 'screen-reader-text',
    4052 
    4053         initialize: function() {
    4054                 this.value = this.options.value;
    4055         },
    4056 
    4057         render: function() {
    4058                 this.$el.html( this.value );
    4059 
    4060                 return this;
    4061         }
    4062 });
    4063 
    4064 module.exports = Label;
    4065 
    4066 },{"./view.js":51}],31:[function(require,module,exports){
     726},{"../../controllers/video-details.js":3,"../video-details.js":12}],9:[function(require,module,exports){
    4067727/*global wp, jQuery, _, MediaElementPlayer */
    4068728
    4069729/**
     
    4231891
    4232892module.exports = MediaDetails;
    4233893
    4234 },{"./settings/attachment-display.js":41}],32:[function(require,module,exports){
    4235 /*globals wp, _, jQuery */
    4236 
    4237 /**
    4238  * wp.media.view.MediaFrame
    4239  *
    4240  * The frame used to create the media modal.
    4241  *
    4242  * @class
    4243  * @augments wp.media.view.Frame
    4244  * @augments wp.media.View
    4245  * @augments wp.Backbone.View
    4246  * @augments Backbone.View
    4247  * @mixes wp.media.controller.StateMachine
    4248  */
    4249 var View = require( './view.js' ),
    4250         Frame = require( './frame.js' ),
    4251         Modal = require( './modal.js' ),
    4252         UploaderWindow = require( './uploader/window.js' ),
    4253         Menu = require( './menu.js' ),
    4254         Toolbar = require( './toolbar.js' ),
    4255         Router = require( './router.js' ),
    4256         Iframe = require( './iframe.js' ),
    4257         $ = jQuery,
    4258         MediaFrame;
    4259 
    4260 MediaFrame = Frame.extend({
    4261         className: 'media-frame',
    4262         template:  wp.template('media-frame'),
    4263         regions:   ['menu','title','content','toolbar','router'],
    4264 
    4265         events: {
    4266                 'click div.media-frame-title h1': 'toggleMenu'
    4267         },
    4268 
    4269         /**
    4270          * @global wp.Uploader
    4271          */
    4272         initialize: function() {
    4273                 Frame.prototype.initialize.apply( this, arguments );
    4274 
    4275                 _.defaults( this.options, {
    4276                         title:    '',
    4277                         modal:    true,
    4278                         uploader: true
    4279                 });
    4280 
    4281                 // Ensure core UI is enabled.
    4282                 this.$el.addClass('wp-core-ui');
    4283 
    4284                 // Initialize modal container view.
    4285                 if ( this.options.modal ) {
    4286                         this.modal = new Modal({
    4287                                 controller: this,
    4288                                 title:      this.options.title
    4289                         });
    4290 
    4291                         this.modal.content( this );
    4292                 }
    4293 
    4294                 // Force the uploader off if the upload limit has been exceeded or
    4295                 // if the browser isn't supported.
    4296                 if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
    4297                         this.options.uploader = false;
    4298                 }
    4299 
    4300                 // Initialize window-wide uploader.
    4301                 if ( this.options.uploader ) {
    4302                         this.uploader = new UploaderWindow({
    4303                                 controller: this,
    4304                                 uploader: {
    4305                                         dropzone:  this.modal ? this.modal.$el : this.$el,
    4306                                         container: this.$el
    4307                                 }
    4308                         });
    4309                         this.views.set( '.media-frame-uploader', this.uploader );
    4310                 }
    4311 
    4312                 this.on( 'attach', _.bind( this.views.ready, this.views ), this );
    4313 
    4314                 // Bind default title creation.
    4315                 this.on( 'title:create:default', this.createTitle, this );
    4316                 this.title.mode('default');
    4317 
    4318                 this.on( 'title:render', function( view ) {
    4319                         view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' );
    4320                 });
    4321 
    4322                 // Bind default menu.
    4323                 this.on( 'menu:create:default', this.createMenu, this );
    4324         },
    4325         /**
    4326          * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    4327          */
    4328         render: function() {
    4329                 // Activate the default state if no active state exists.
    4330                 if ( ! this.state() && this.options.state ) {
    4331                         this.setState( this.options.state );
    4332                 }
    4333                 /**
    4334                  * call 'render' directly on the parent class
    4335                  */
    4336                 return Frame.prototype.render.apply( this, arguments );
    4337         },
    4338         /**
    4339          * @param {Object} title
    4340          * @this wp.media.controller.Region
    4341          */
    4342         createTitle: function( title ) {
    4343                 title.view = new View({
    4344                         controller: this,
    4345                         tagName: 'h1'
    4346                 });
    4347         },
    4348         /**
    4349          * @param {Object} menu
    4350          * @this wp.media.controller.Region
    4351          */
    4352         createMenu: function( menu ) {
    4353                 menu.view = new Menu({
    4354                         controller: this
    4355                 });
    4356         },
    4357 
    4358         toggleMenu: function() {
    4359                 this.$el.find( '.media-menu' ).toggleClass( 'visible' );
    4360         },
    4361 
    4362         /**
    4363          * @param {Object} toolbar
    4364          * @this wp.media.controller.Region
    4365          */
    4366         createToolbar: function( toolbar ) {
    4367                 toolbar.view = new Toolbar({
    4368                         controller: this
    4369                 });
    4370         },
    4371         /**
    4372          * @param {Object} router
    4373          * @this wp.media.controller.Region
    4374          */
    4375         createRouter: function( router ) {
    4376                 router.view = new Router({
    4377                         controller: this
    4378                 });
    4379         },
    4380         /**
    4381          * @param {Object} options
    4382          */
    4383         createIframeStates: function( options ) {
    4384                 var settings = wp.media.view.settings,
    4385                         tabs = settings.tabs,
    4386                         tabUrl = settings.tabUrl,
    4387                         $postId;
    4388 
    4389                 if ( ! tabs || ! tabUrl ) {
    4390                         return;
    4391                 }
    4392 
    4393                 // Add the post ID to the tab URL if it exists.
    4394                 $postId = $('#post_ID');
    4395                 if ( $postId.length ) {
    4396                         tabUrl += '&post_id=' + $postId.val();
    4397                 }
    4398 
    4399                 // Generate the tab states.
    4400                 _.each( tabs, function( title, id ) {
    4401                         this.state( 'iframe:' + id ).set( _.defaults({
    4402                                 tab:     id,
    4403                                 src:     tabUrl + '&tab=' + id,
    4404                                 title:   title,
    4405                                 content: 'iframe',
    4406                                 menu:    'default'
    4407                         }, options ) );
    4408                 }, this );
    4409 
    4410                 this.on( 'content:create:iframe', this.iframeContent, this );
    4411                 this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
    4412                 this.on( 'menu:render:default', this.iframeMenu, this );
    4413                 this.on( 'open', this.hijackThickbox, this );
    4414                 this.on( 'close', this.restoreThickbox, this );
    4415         },
    4416 
    4417         /**
    4418          * @param {Object} content
    4419          * @this wp.media.controller.Region
    4420          */
    4421         iframeContent: function( content ) {
    4422                 this.$el.addClass('hide-toolbar');
    4423                 content.view = new Iframe({
    4424                         controller: this
    4425                 });
    4426         },
    4427 
    4428         iframeContentCleanup: function() {
    4429                 this.$el.removeClass('hide-toolbar');
    4430         },
    4431 
    4432         iframeMenu: function( view ) {
    4433                 var views = {};
    4434 
    4435                 if ( ! view ) {
    4436                         return;
    4437                 }
    4438 
    4439                 _.each( wp.media.view.settings.tabs, function( title, id ) {
    4440                         views[ 'iframe:' + id ] = {
    4441                                 text: this.state( 'iframe:' + id ).get('title'),
    4442                                 priority: 200
    4443                         };
    4444                 }, this );
    4445 
    4446                 view.set( views );
    4447         },
    4448 
    4449         hijackThickbox: function() {
    4450                 var frame = this;
    4451 
    4452                 if ( ! window.tb_remove || this._tb_remove ) {
    4453                         return;
    4454                 }
    4455 
    4456                 this._tb_remove = window.tb_remove;
    4457                 window.tb_remove = function() {
    4458                         frame.close();
    4459                         frame.reset();
    4460                         frame.setState( frame.options.state );
    4461                         frame._tb_remove.call( window );
    4462                 };
    4463         },
    4464 
    4465         restoreThickbox: function() {
    4466                 if ( ! this._tb_remove ) {
    4467                         return;
    4468                 }
    4469 
    4470                 window.tb_remove = this._tb_remove;
    4471                 delete this._tb_remove;
    4472         }
    4473 });
    4474 
    4475 // Map some of the modal's methods to the frame.
    4476 _.each(['open','close','attach','detach','escape'], function( method ) {
    4477         /**
    4478          * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
    4479          */
    4480         MediaFrame.prototype[ method ] = function() {
    4481                 if ( this.modal ) {
    4482                         this.modal[ method ].apply( this.modal, arguments );
    4483                 }
    4484                 return this;
    4485         };
    4486 });
    4487 
    4488 module.exports = MediaFrame;
    4489 
    4490 },{"./frame.js":24,"./iframe.js":29,"./menu.js":34,"./modal.js":35,"./router.js":38,"./toolbar.js":44,"./uploader/window.js":49,"./view.js":51}],33:[function(require,module,exports){
    4491 /*globals jQuery */
    4492 
    4493 /**
    4494  * wp.media.view.MenuItem
    4495  *
    4496  * @class
    4497  * @augments wp.media.View
    4498  * @augments wp.Backbone.View
    4499  * @augments Backbone.View
    4500  */
    4501 var View = require( './view.js' ),
    4502         $ = jQuery,
    4503         MenuItem;
    4504 
    4505 MenuItem = View.extend({
    4506         tagName:   'a',
    4507         className: 'media-menu-item',
    4508 
    4509         attributes: {
    4510                 href: '#'
    4511         },
    4512 
    4513         events: {
    4514                 'click': '_click'
    4515         },
    4516         /**
    4517          * @param {Object} event
    4518          */
    4519         _click: function( event ) {
    4520                 var clickOverride = this.options.click;
    4521 
    4522                 if ( event ) {
    4523                         event.preventDefault();
    4524                 }
    4525 
    4526                 if ( clickOverride ) {
    4527                         clickOverride.call( this );
    4528                 } else {
    4529                         this.click();
    4530                 }
    4531 
    4532                 // When selecting a tab along the left side,
    4533                 // focus should be transferred into the main panel
    4534                 if ( ! wp.media.isTouchDevice ) {
    4535                         $('.media-frame-content input').first().focus();
    4536                 }
    4537         },
    4538 
    4539         click: function() {
    4540                 var state = this.options.state;
    4541 
    4542                 if ( state ) {
    4543                         this.controller.setState( state );
    4544                         this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
    4545                 }
    4546         },
    4547         /**
    4548          * @returns {wp.media.view.MenuItem} returns itself to allow chaining
    4549          */
    4550         render: function() {
    4551                 var options = this.options;
    4552 
    4553                 if ( options.text ) {
    4554                         this.$el.text( options.text );
    4555                 } else if ( options.html ) {
    4556                         this.$el.html( options.html );
    4557                 }
    4558 
    4559                 return this;
    4560         }
    4561 });
    4562 
    4563 module.exports = MenuItem;
    4564 
    4565 },{"./view.js":51}],34:[function(require,module,exports){
    4566 /**
    4567  * wp.media.view.Menu
    4568  *
    4569  * @class
    4570  * @augments wp.media.view.PriorityList
    4571  * @augments wp.media.View
    4572  * @augments wp.Backbone.View
    4573  * @augments Backbone.View
    4574  */
    4575 var MenuItem = require( './menu-item.js' ),
    4576         PriorityList = require( './priority-list.js' ),
    4577         Menu;
    4578 
    4579 Menu = PriorityList.extend({
    4580         tagName:   'div',
    4581         className: 'media-menu',
    4582         property:  'state',
    4583         ItemView:  MenuItem,
    4584         region:    'menu',
    4585 
    4586         /* TODO: alternatively hide on any click anywhere
    4587         events: {
    4588                 'click': 'click'
    4589         },
    4590 
    4591         click: function() {
    4592                 this.$el.removeClass( 'visible' );
    4593         },
    4594         */
    4595 
    4596         /**
    4597          * @param {Object} options
    4598          * @param {string} id
    4599          * @returns {wp.media.View}
    4600          */
    4601         toView: function( options, id ) {
    4602                 options = options || {};
    4603                 options[ this.property ] = options[ this.property ] || id;
    4604                 return new this.ItemView( options ).render();
    4605         },
    4606 
    4607         ready: function() {
    4608                 /**
    4609                  * call 'ready' directly on the parent class
    4610                  */
    4611                 PriorityList.prototype.ready.apply( this, arguments );
    4612                 this.visibility();
    4613         },
    4614 
    4615         set: function() {
    4616                 /**
    4617                  * call 'set' directly on the parent class
    4618                  */
    4619                 PriorityList.prototype.set.apply( this, arguments );
    4620                 this.visibility();
    4621         },
    4622 
    4623         unset: function() {
    4624                 /**
    4625                  * call 'unset' directly on the parent class
    4626                  */
    4627                 PriorityList.prototype.unset.apply( this, arguments );
    4628                 this.visibility();
    4629         },
    4630 
    4631         visibility: function() {
    4632                 var region = this.region,
    4633                         view = this.controller[ region ].get(),
    4634                         views = this.views.get(),
    4635                         hide = ! views || views.length < 2;
    4636 
    4637                 if ( this === view ) {
    4638                         this.controller.$el.toggleClass( 'hide-' + region, hide );
    4639                 }
    4640         },
    4641         /**
    4642          * @param {string} id
    4643          */
    4644         select: function( id ) {
    4645                 var view = this.get( id );
    4646 
    4647                 if ( ! view ) {
    4648                         return;
    4649                 }
    4650 
    4651                 this.deselect();
    4652                 view.$el.addClass('active');
    4653         },
    4654 
    4655         deselect: function() {
    4656                 this.$el.children().removeClass('active');
    4657         },
    4658 
    4659         hide: function( id ) {
    4660                 var view = this.get( id );
    4661 
    4662                 if ( ! view ) {
    4663                         return;
    4664                 }
    4665 
    4666                 view.$el.addClass('hidden');
    4667         },
    4668 
    4669         show: function( id ) {
    4670                 var view = this.get( id );
    4671 
    4672                 if ( ! view ) {
    4673                         return;
    4674                 }
    4675 
    4676                 view.$el.removeClass('hidden');
    4677         }
    4678 });
    4679 
    4680 module.exports = Menu;
    4681 
    4682 },{"./menu-item.js":33,"./priority-list.js":36}],35:[function(require,module,exports){
    4683 /*globals wp, _, jQuery */
    4684 
    4685 /**
    4686  * wp.media.view.Modal
    4687  *
    4688  * A modal view, which the media modal uses as its default container.
    4689  *
    4690  * @class
    4691  * @augments wp.media.View
    4692  * @augments wp.Backbone.View
    4693  * @augments Backbone.View
    4694  */
    4695 var View = require( './view.js' ),
    4696         FocusManager = require( './focus-manager.js' ),
    4697         $ = jQuery,
    4698         Modal;
    4699 
    4700 Modal = View.extend({
    4701         tagName:  'div',
    4702         template: wp.template('media-modal'),
    4703 
    4704         attributes: {
    4705                 tabindex: 0
    4706         },
    4707 
    4708         events: {
    4709                 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
    4710                 'keydown': 'keydown'
    4711         },
    4712 
    4713         initialize: function() {
    4714                 _.defaults( this.options, {
    4715                         container: document.body,
    4716                         title:     '',
    4717                         propagate: true,
    4718                         freeze:    true
    4719                 });
    4720 
    4721                 this.focusManager = new FocusManager({
    4722                         el: this.el
    4723                 });
    4724         },
    4725         /**
    4726          * @returns {Object}
    4727          */
    4728         prepare: function() {
    4729                 return {
    4730                         title: this.options.title
    4731                 };
    4732         },
    4733 
    4734         /**
    4735          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4736          */
    4737         attach: function() {
    4738                 if ( this.views.attached ) {
    4739                         return this;
    4740                 }
    4741 
    4742                 if ( ! this.views.rendered ) {
    4743                         this.render();
    4744                 }
    4745 
    4746                 this.$el.appendTo( this.options.container );
    4747 
    4748                 // Manually mark the view as attached and trigger ready.
    4749                 this.views.attached = true;
    4750                 this.views.ready();
    4751 
    4752                 return this.propagate('attach');
    4753         },
    4754 
    4755         /**
    4756          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4757          */
    4758         detach: function() {
    4759                 if ( this.$el.is(':visible') ) {
    4760                         this.close();
    4761                 }
    4762 
    4763                 this.$el.detach();
    4764                 this.views.attached = false;
    4765                 return this.propagate('detach');
    4766         },
    4767 
    4768         /**
    4769          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4770          */
    4771         open: function() {
    4772                 var $el = this.$el,
    4773                         options = this.options,
    4774                         mceEditor;
    4775 
    4776                 if ( $el.is(':visible') ) {
    4777                         return this;
    4778                 }
    4779 
    4780                 if ( ! this.views.attached ) {
    4781                         this.attach();
    4782                 }
    4783 
    4784                 // If the `freeze` option is set, record the window's scroll position.
    4785                 if ( options.freeze ) {
    4786                         this._freeze = {
    4787                                 scrollTop: $( window ).scrollTop()
    4788                         };
    4789                 }
    4790 
    4791                 // Disable page scrolling.
    4792                 $( 'body' ).addClass( 'modal-open' );
    4793 
    4794                 $el.show();
    4795 
    4796                 // Try to close the onscreen keyboard
    4797                 if ( 'ontouchend' in document ) {
    4798                         if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor )  && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
    4799                                 mceEditor.iframeElement.focus();
    4800                                 mceEditor.iframeElement.blur();
    4801 
    4802                                 setTimeout( function() {
    4803                                         mceEditor.iframeElement.blur();
    4804                                 }, 100 );
    4805                         }
    4806                 }
    4807 
    4808                 this.$el.focus();
    4809 
    4810                 return this.propagate('open');
    4811         },
    4812 
    4813         /**
    4814          * @param {Object} options
    4815          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4816          */
    4817         close: function( options ) {
    4818                 var freeze = this._freeze;
    4819 
    4820                 if ( ! this.views.attached || ! this.$el.is(':visible') ) {
    4821                         return this;
    4822                 }
    4823 
    4824                 // Enable page scrolling.
    4825                 $( 'body' ).removeClass( 'modal-open' );
    4826 
    4827                 // Hide modal and remove restricted media modal tab focus once it's closed
    4828                 this.$el.hide().undelegate( 'keydown' );
    4829 
    4830                 // Put focus back in useful location once modal is closed
    4831                 $('#wpbody-content').focus();
    4832 
    4833                 this.propagate('close');
    4834 
    4835                 // If the `freeze` option is set, restore the container's scroll position.
    4836                 if ( freeze ) {
    4837                         $( window ).scrollTop( freeze.scrollTop );
    4838                 }
    4839 
    4840                 if ( options && options.escape ) {
    4841                         this.propagate('escape');
    4842                 }
    4843 
    4844                 return this;
    4845         },
    4846         /**
    4847          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4848          */
    4849         escape: function() {
    4850                 return this.close({ escape: true });
    4851         },
    4852         /**
    4853          * @param {Object} event
    4854          */
    4855         escapeHandler: function( event ) {
    4856                 event.preventDefault();
    4857                 this.escape();
    4858         },
    4859 
    4860         /**
    4861          * @param {Array|Object} content Views to register to '.media-modal-content'
    4862          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4863          */
    4864         content: function( content ) {
    4865                 this.views.set( '.media-modal-content', content );
    4866                 return this;
    4867         },
    4868 
    4869         /**
    4870          * Triggers a modal event and if the `propagate` option is set,
    4871          * forwards events to the modal's controller.
    4872          *
    4873          * @param {string} id
    4874          * @returns {wp.media.view.Modal} Returns itself to allow chaining
    4875          */
    4876         propagate: function( id ) {
    4877                 this.trigger( id );
    4878 
    4879                 if ( this.options.propagate ) {
    4880                         this.controller.trigger( id );
    4881                 }
    4882 
    4883                 return this;
    4884         },
    4885         /**
    4886          * @param {Object} event
    4887          */
    4888         keydown: function( event ) {
    4889                 // Close the modal when escape is pressed.
    4890                 if ( 27 === event.which && this.$el.is(':visible') ) {
    4891                         this.escape();
    4892                         event.stopImmediatePropagation();
    4893                 }
    4894         }
    4895 });
    4896 
    4897 module.exports = Modal;
    4898 
    4899 },{"./focus-manager.js":23,"./view.js":51}],36:[function(require,module,exports){
    4900 /*globals _, Backbone */
    4901 
    4902 /**
    4903  * wp.media.view.PriorityList
    4904  *
    4905  * @class
    4906  * @augments wp.media.View
    4907  * @augments wp.Backbone.View
    4908  * @augments Backbone.View
    4909  */
    4910 var View = require( './view.js' ),
    4911         PriorityList;
    4912 
    4913 PriorityList = View.extend({
    4914         tagName:   'div',
    4915 
    4916         initialize: function() {
    4917                 this._views = {};
    4918 
    4919                 this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
    4920                 delete this.options.views;
    4921 
    4922                 if ( ! this.options.silent ) {
    4923                         this.render();
    4924                 }
    4925         },
    4926         /**
    4927          * @param {string} id
    4928          * @param {wp.media.View|Object} view
    4929          * @param {Object} options
    4930          * @returns {wp.media.view.PriorityList} Returns itself to allow chaining
    4931          */
    4932         set: function( id, view, options ) {
    4933                 var priority, views, index;
    4934 
    4935                 options = options || {};
    4936 
    4937                 // Accept an object with an `id` : `view` mapping.
    4938                 if ( _.isObject( id ) ) {
    4939                         _.each( id, function( view, id ) {
    4940                                 this.set( id, view );
    4941                         }, this );
    4942                         return this;
    4943                 }
    4944 
    4945                 if ( ! (view instanceof Backbone.View) ) {
    4946                         view = this.toView( view, id, options );
    4947                 }
    4948                 view.controller = view.controller || this.controller;
    4949 
    4950                 this.unset( id );
    4951 
    4952                 priority = view.options.priority || 10;
    4953                 views = this.views.get() || [];
    4954 
    4955                 _.find( views, function( existing, i ) {
    4956                         if ( existing.options.priority > priority ) {
    4957                                 index = i;
    4958                                 return true;
    4959                         }
    4960                 });
    4961 
    4962                 this._views[ id ] = view;
    4963                 this.views.add( view, {
    4964                         at: _.isNumber( index ) ? index : views.length || 0
    4965                 });
    4966 
    4967                 return this;
    4968         },
    4969         /**
    4970          * @param {string} id
    4971          * @returns {wp.media.View}
    4972          */
    4973         get: function( id ) {
    4974                 return this._views[ id ];
    4975         },
    4976         /**
    4977          * @param {string} id
    4978          * @returns {wp.media.view.PriorityList}
    4979          */
    4980         unset: function( id ) {
    4981                 var view = this.get( id );
    4982 
    4983                 if ( view ) {
    4984                         view.remove();
    4985                 }
    4986 
    4987                 delete this._views[ id ];
    4988                 return this;
    4989         },
    4990         /**
    4991          * @param {Object} options
    4992          * @returns {wp.media.View}
    4993          */
    4994         toView: function( options ) {
    4995                 return new View( options );
    4996         }
    4997 });
    4998 
    4999 module.exports = PriorityList;
    5000 
    5001 },{"./view.js":51}],37:[function(require,module,exports){
    5002 /**
    5003  * wp.media.view.RouterItem
    5004  *
    5005  * @class
    5006  * @augments wp.media.view.MenuItem
    5007  * @augments wp.media.View
    5008  * @augments wp.Backbone.View
    5009  * @augments Backbone.View
    5010  */
    5011 var MenuItem = require( './menu-item.js' ),
    5012         RouterItem;
    5013 
    5014 RouterItem = MenuItem.extend({
    5015         /**
    5016          * On click handler to activate the content region's corresponding mode.
    5017          */
    5018         click: function() {
    5019                 var contentMode = this.options.contentMode;
    5020                 if ( contentMode ) {
    5021                         this.controller.content.mode( contentMode );
    5022                 }
    5023         }
    5024 });
    5025 
    5026 module.exports = RouterItem;
    5027 
    5028 },{"./menu-item.js":33}],38:[function(require,module,exports){
    5029 /**
    5030  * wp.media.view.Router
    5031  *
    5032  * @class
    5033  * @augments wp.media.view.Menu
    5034  * @augments wp.media.view.PriorityList
    5035  * @augments wp.media.View
    5036  * @augments wp.Backbone.View
    5037  * @augments Backbone.View
    5038  */
    5039 var Menu = require( './menu.js' ),
    5040         RouterItem = require( './router-item.js' ),
    5041         Router;
    5042 
    5043 Router = Menu.extend({
    5044         tagName:   'div',
    5045         className: 'media-router',
    5046         property:  'contentMode',
    5047         ItemView:  RouterItem,
    5048         region:    'router',
    5049 
    5050         initialize: function() {
    5051                 this.controller.on( 'content:render', this.update, this );
    5052                 // Call 'initialize' directly on the parent class.
    5053                 Menu.prototype.initialize.apply( this, arguments );
    5054         },
    5055 
    5056         update: function() {
    5057                 var mode = this.controller.content.mode();
    5058                 if ( mode ) {
    5059                         this.select( mode );
    5060                 }
    5061         }
    5062 });
    5063 
    5064 module.exports = Router;
    5065 
    5066 },{"./menu.js":34,"./router-item.js":37}],39:[function(require,module,exports){
    5067 /*globals wp */
    5068 
    5069 /**
    5070  * wp.media.view.Search
    5071  *
    5072  * @class
    5073  * @augments wp.media.View
    5074  * @augments wp.Backbone.View
    5075  * @augments Backbone.View
    5076  */
    5077 var View = require( './view.js' ),
    5078         l10n = wp.media.view.l10n,
    5079         Search;
    5080 
    5081 Search = View.extend({
    5082         tagName:   'input',
    5083         className: 'search',
    5084         id:        'media-search-input',
    5085 
    5086         attributes: {
    5087                 type:        'search',
    5088                 placeholder: l10n.search
    5089         },
    5090 
    5091         events: {
    5092                 'input':  'search',
    5093                 'keyup':  'search',
    5094                 'change': 'search',
    5095                 'search': 'search'
    5096         },
    5097 
    5098         /**
    5099          * @returns {wp.media.view.Search} Returns itself to allow chaining
    5100          */
    5101         render: function() {
    5102                 this.el.value = this.model.escape('search');
    5103                 return this;
    5104         },
    5105 
    5106         search: function( event ) {
    5107                 if ( event.target.value ) {
    5108                         this.model.set( 'search', event.target.value );
    5109                 } else {
    5110                         this.model.unset('search');
    5111                 }
    5112         }
    5113 });
    5114 
    5115 module.exports = Search;
    5116 
    5117 },{"./view.js":51}],40:[function(require,module,exports){
     894},{"./settings/attachment-display.js":11}],10:[function(require,module,exports){
    5118895/*globals _, jQuery, Backbone */
    5119896
    5120897/**
     
    52371014
    52381015module.exports = Settings;
    52391016
    5240 },{"./view.js":51}],41:[function(require,module,exports){
     1017},{"./view.js":13}],11:[function(require,module,exports){
    52411018/*globals wp, _ */
    52421019
    52431020/**
     
    53331110
    53341111module.exports = AttachmentDisplay;
    53351112
    5336 },{"../settings.js":40}],42:[function(require,module,exports){
    5337 /**
    5338  * wp.media.view.Sidebar
    5339  *
    5340  * @class
    5341  * @augments wp.media.view.PriorityList
    5342  * @augments wp.media.View
    5343  * @augments wp.Backbone.View
    5344  * @augments Backbone.View
    5345  */
    5346 var PriorityList = require( './priority-list.js' ),
    5347         Sidebar;
    5348 
    5349 Sidebar = PriorityList.extend({
    5350         className: 'media-sidebar'
    5351 });
    5352 
    5353 module.exports = Sidebar;
    5354 
    5355 },{"./priority-list.js":36}],43:[function(require,module,exports){
    5356 /*globals _ */
    5357 
    5358 /**
    5359  * wp.media.view.Spinner
    5360  *
    5361  * @class
    5362  * @augments wp.media.View
    5363  * @augments wp.Backbone.View
    5364  * @augments Backbone.View
    5365  */
    5366 var View = require( './view.js' ),
    5367         Spinner;
    5368 
    5369 Spinner = View.extend({
    5370         tagName:   'span',
    5371         className: 'spinner',
    5372         spinnerTimeout: false,
    5373         delay: 400,
    5374 
    5375         show: function() {
    5376                 if ( ! this.spinnerTimeout ) {
    5377                         this.spinnerTimeout = _.delay(function( $el ) {
    5378                                 $el.show();
    5379                         }, this.delay, this.$el );
    5380                 }
    5381 
    5382                 return this;
    5383         },
    5384 
    5385         hide: function() {
    5386                 this.$el.hide();
    5387                 this.spinnerTimeout = clearTimeout( this.spinnerTimeout );
    5388 
    5389                 return this;
    5390         }
    5391 });
    5392 
    5393 module.exports = Spinner;
    5394 
    5395 },{"./view.js":51}],44:[function(require,module,exports){
    5396 /*globals _, Backbone */
    5397 
    5398 /**
    5399  * wp.media.view.Toolbar
    5400  *
    5401  * A toolbar which consists of a primary and a secondary section. Each sections
    5402  * can be filled with views.
    5403  *
    5404  * @class
    5405  * @augments wp.media.View
    5406  * @augments wp.Backbone.View
    5407  * @augments Backbone.View
    5408  */
    5409 var View = require( './view.js' ),
    5410         Button = require( './button.js' ),
    5411         PriorityList = require( './priority-list.js' ),
    5412         Toolbar;
    5413 
    5414 Toolbar = View.extend({
    5415         tagName:   'div',
    5416         className: 'media-toolbar',
    5417 
    5418         initialize: function() {
    5419                 var state = this.controller.state(),
    5420                         selection = this.selection = state.get('selection'),
    5421                         library = this.library = state.get('library');
    5422 
    5423                 this._views = {};
    5424 
    5425                 // The toolbar is composed of two `PriorityList` views.
    5426                 this.primary   = new PriorityList();
    5427                 this.secondary = new PriorityList();
    5428                 this.primary.$el.addClass('media-toolbar-primary search-form');
    5429                 this.secondary.$el.addClass('media-toolbar-secondary');
    5430 
    5431                 this.views.set([ this.secondary, this.primary ]);
    5432 
    5433                 if ( this.options.items ) {
    5434                         this.set( this.options.items, { silent: true });
    5435                 }
    5436 
    5437                 if ( ! this.options.silent ) {
    5438                         this.render();
    5439                 }
    5440 
    5441                 if ( selection ) {
    5442                         selection.on( 'add remove reset', this.refresh, this );
    5443                 }
    5444 
    5445                 if ( library ) {
    5446                         library.on( 'add remove reset', this.refresh, this );
    5447                 }
    5448         },
    5449         /**
    5450          * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining
    5451          */
    5452         dispose: function() {
    5453                 if ( this.selection ) {
    5454                         this.selection.off( null, null, this );
    5455                 }
    5456 
    5457                 if ( this.library ) {
    5458                         this.library.off( null, null, this );
    5459                 }
    5460                 /**
    5461                  * call 'dispose' directly on the parent class
    5462                  */
    5463                 return View.prototype.dispose.apply( this, arguments );
    5464         },
    5465 
    5466         ready: function() {
    5467                 this.refresh();
    5468         },
    5469 
    5470         /**
    5471          * @param {string} id
    5472          * @param {Backbone.View|Object} view
    5473          * @param {Object} [options={}]
    5474          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    5475          */
    5476         set: function( id, view, options ) {
    5477                 var list;
    5478                 options = options || {};
    5479 
    5480                 // Accept an object with an `id` : `view` mapping.
    5481                 if ( _.isObject( id ) ) {
    5482                         _.each( id, function( view, id ) {
    5483                                 this.set( id, view, { silent: true });
    5484                         }, this );
    5485 
    5486                 } else {
    5487                         if ( ! ( view instanceof Backbone.View ) ) {
    5488                                 view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
    5489                                 view = new Button( view ).render();
    5490                         }
    5491 
    5492                         view.controller = view.controller || this.controller;
    5493 
    5494                         this._views[ id ] = view;
    5495 
    5496                         list = view.options.priority < 0 ? 'secondary' : 'primary';
    5497                         this[ list ].set( id, view, options );
    5498                 }
    5499 
    5500                 if ( ! options.silent ) {
    5501                         this.refresh();
    5502                 }
    5503 
    5504                 return this;
    5505         },
    5506         /**
    5507          * @param {string} id
    5508          * @returns {wp.media.view.Button}
    5509          */
    5510         get: function( id ) {
    5511                 return this._views[ id ];
    5512         },
    5513         /**
    5514          * @param {string} id
    5515          * @param {Object} options
    5516          * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
    5517          */
    5518         unset: function( id, options ) {
    5519                 delete this._views[ id ];
    5520                 this.primary.unset( id, options );
    5521                 this.secondary.unset( id, options );
    5522 
    5523                 if ( ! options || ! options.silent ) {
    5524                         this.refresh();
    5525                 }
    5526                 return this;
    5527         },
    5528 
    5529         refresh: function() {
    5530                 var state = this.controller.state(),
    5531                         library = state.get('library'),
    5532                         selection = state.get('selection');
    5533 
    5534                 _.each( this._views, function( button ) {
    5535                         if ( ! button.model || ! button.options || ! button.options.requires ) {
    5536                                 return;
    5537                         }
    5538 
    5539                         var requires = button.options.requires,
    5540                                 disabled = false;
    5541 
    5542                         // Prevent insertion of attachments if any of them are still uploading
    5543                         disabled = _.some( selection.models, function( attachment ) {
    5544                                 return attachment.get('uploading') === true;
    5545                         });
    5546 
    5547                         if ( requires.selection && selection && ! selection.length ) {
    5548                                 disabled = true;
    5549                         } else if ( requires.library && library && ! library.length ) {
    5550                                 disabled = true;
    5551                         }
    5552                         button.model.set( 'disabled', disabled );
    5553                 });
    5554         }
    5555 });
    5556 
    5557 module.exports = Toolbar;
    5558 
    5559 },{"./button.js":22,"./priority-list.js":36,"./view.js":51}],45:[function(require,module,exports){
    5560 /*globals wp, _ */
    5561 
    5562 /**
    5563  * wp.media.view.Toolbar.Select
    5564  *
    5565  * @class
    5566  * @augments wp.media.view.Toolbar
    5567  * @augments wp.media.View
    5568  * @augments wp.Backbone.View
    5569  * @augments Backbone.View
    5570  */
    5571 var Toolbar = require( '../toolbar.js' ),
    5572         l10n = wp.media.view.l10n,
    5573         Select;
    5574 
    5575 Select = Toolbar.extend({
    5576         initialize: function() {
    5577                 var options = this.options;
    5578 
    5579                 _.bindAll( this, 'clickSelect' );
    5580 
    5581                 _.defaults( options, {
    5582                         event: 'select',
    5583                         state: false,
    5584                         reset: true,
    5585                         close: true,
    5586                         text:  l10n.select,
    5587 
    5588                         // Does the button rely on the selection?
    5589                         requires: {
    5590                                 selection: true
    5591                         }
    5592                 });
    5593 
    5594                 options.items = _.defaults( options.items || {}, {
    5595                         select: {
    5596                                 style:    'primary',
    5597                                 text:     options.text,
    5598                                 priority: 80,
    5599                                 click:    this.clickSelect,
    5600                                 requires: options.requires
    5601                         }
    5602                 });
    5603                 // Call 'initialize' directly on the parent class.
    5604                 Toolbar.prototype.initialize.apply( this, arguments );
    5605         },
    5606 
    5607         clickSelect: function() {
    5608                 var options = this.options,
    5609                         controller = this.controller;
    5610 
    5611                 if ( options.close ) {
    5612                         controller.close();
    5613                 }
    5614 
    5615                 if ( options.event ) {
    5616                         controller.state().trigger( options.event );
    5617                 }
    5618 
    5619                 if ( options.state ) {
    5620                         controller.setState( options.state );
    5621                 }
    5622 
    5623                 if ( options.reset ) {
    5624                         controller.reset();
    5625                 }
    5626         }
    5627 });
    5628 
    5629 module.exports = Select;
    5630 
    5631 },{"../toolbar.js":44}],46:[function(require,module,exports){
    5632 /*globals wp, _ */
    5633 
    5634 /**
    5635  * wp.media.view.UploaderInline
    5636  *
    5637  * The inline uploader that shows up in the 'Upload Files' tab.
    5638  *
    5639  * @class
    5640  * @augments wp.media.View
    5641  * @augments wp.Backbone.View
    5642  * @augments Backbone.View
    5643  */
    5644 var View = require( '../view.js' ),
    5645         UploaderStatus = require( './status.js' ),
    5646         UploaderInline;
    5647 
    5648 UploaderInline = View.extend({
    5649         tagName:   'div',
    5650         className: 'uploader-inline',
    5651         template:  wp.template('uploader-inline'),
    5652 
    5653         events: {
    5654                 'click .close': 'hide'
    5655         },
    5656 
    5657         initialize: function() {
    5658                 _.defaults( this.options, {
    5659                         message: '',
    5660                         status:  true,
    5661                         canClose: false
    5662                 });
    5663 
    5664                 if ( ! this.options.$browser && this.controller.uploader ) {
    5665                         this.options.$browser = this.controller.uploader.$browser;
    5666                 }
    5667 
    5668                 if ( _.isUndefined( this.options.postId ) ) {
    5669                         this.options.postId = wp.media.view.settings.post.id;
    5670                 }
    5671 
    5672                 if ( this.options.status ) {
    5673                         this.views.set( '.upload-inline-status', new UploaderStatus({
    5674                                 controller: this.controller
    5675                         }) );
    5676                 }
    5677         },
    5678 
    5679         prepare: function() {
    5680                 var suggestedWidth = this.controller.state().get('suggestedWidth'),
    5681                         suggestedHeight = this.controller.state().get('suggestedHeight'),
    5682                         data = {};
    5683 
    5684                 data.message = this.options.message;
    5685                 data.canClose = this.options.canClose;
    5686 
    5687                 if ( suggestedWidth && suggestedHeight ) {
    5688                         data.suggestedWidth = suggestedWidth;
    5689                         data.suggestedHeight = suggestedHeight;
    5690                 }
    5691 
    5692                 return data;
    5693         },
    5694         /**
    5695          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
    5696          */
    5697         dispose: function() {
    5698                 if ( this.disposing ) {
    5699                         /**
    5700                          * call 'dispose' directly on the parent class
    5701                          */
    5702                         return View.prototype.dispose.apply( this, arguments );
    5703                 }
    5704 
    5705                 // Run remove on `dispose`, so we can be sure to refresh the
    5706                 // uploader with a view-less DOM. Track whether we're disposing
    5707                 // so we don't trigger an infinite loop.
    5708                 this.disposing = true;
    5709                 return this.remove();
    5710         },
    5711         /**
    5712          * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
    5713          */
    5714         remove: function() {
    5715                 /**
    5716                  * call 'remove' directly on the parent class
    5717                  */
    5718                 var result = View.prototype.remove.apply( this, arguments );
    5719 
    5720                 _.defer( _.bind( this.refresh, this ) );
    5721                 return result;
    5722         },
    5723 
    5724         refresh: function() {
    5725                 var uploader = this.controller.uploader;
    5726 
    5727                 if ( uploader ) {
    5728                         uploader.refresh();
    5729                 }
    5730         },
    5731         /**
    5732          * @returns {wp.media.view.UploaderInline}
    5733          */
    5734         ready: function() {
    5735                 var $browser = this.options.$browser,
    5736                         $placeholder;
    5737 
    5738                 if ( this.controller.uploader ) {
    5739                         $placeholder = this.$('.browser');
    5740 
    5741                         // Check if we've already replaced the placeholder.
    5742                         if ( $placeholder[0] === $browser[0] ) {
    5743                                 return;
    5744                         }
    5745 
    5746                         $browser.detach().text( $placeholder.text() );
    5747                         $browser[0].className = $placeholder[0].className;
    5748                         $placeholder.replaceWith( $browser.show() );
    5749                 }
    5750 
    5751                 this.refresh();
    5752                 return this;
    5753         },
    5754         show: function() {
    5755                 this.$el.removeClass( 'hidden' );
    5756         },
    5757         hide: function() {
    5758                 this.$el.addClass( 'hidden' );
    5759         }
    5760 
    5761 });
    5762 
    5763 module.exports = UploaderInline;
    5764 
    5765 },{"../view.js":51,"./status.js":48}],47:[function(require,module,exports){
     1113},{"../settings.js":10}],12:[function(require,module,exports){
    57661114/*globals wp */
    57671115
    57681116/**
    5769  * wp.media.view.UploaderStatusError
    5770  *
    5771  * @class
    5772  * @augments wp.media.View
    5773  * @augments wp.Backbone.View
    5774  * @augments Backbone.View
    5775  */
    5776 var View = require( '../view.js' ),
    5777         UploaderStatusError;
    5778 
    5779 UploaderStatusError = View.extend({
    5780         className: 'upload-error',
    5781         template:  wp.template('uploader-status-error')
    5782 });
    5783 
    5784 module.exports = UploaderStatusError;
    5785 
    5786 },{"../view.js":51}],48:[function(require,module,exports){
    5787 /*globals wp, _ */
    5788 
    5789 /**
    5790  * wp.media.view.UploaderStatus
    5791  *
    5792  * An uploader status for on-going uploads.
    5793  *
    5794  * @class
    5795  * @augments wp.media.View
    5796  * @augments wp.Backbone.View
    5797  * @augments Backbone.View
    5798  */
    5799 var View = require( '../view.js' ),
    5800         UploaderStatusError = require( './status-error.js' ),
    5801         UploaderStatus;
    5802 
    5803 UploaderStatus = View.extend({
    5804         className: 'media-uploader-status',
    5805         template:  wp.template('uploader-status'),
    5806 
    5807         events: {
    5808                 'click .upload-dismiss-errors': 'dismiss'
    5809         },
    5810 
    5811         initialize: function() {
    5812                 this.queue = wp.Uploader.queue;
    5813                 this.queue.on( 'add remove reset', this.visibility, this );
    5814                 this.queue.on( 'add remove reset change:percent', this.progress, this );
    5815                 this.queue.on( 'add remove reset change:uploading', this.info, this );
    5816 
    5817                 this.errors = wp.Uploader.errors;
    5818                 this.errors.reset();
    5819                 this.errors.on( 'add remove reset', this.visibility, this );
    5820                 this.errors.on( 'add', this.error, this );
    5821         },
    5822         /**
    5823          * @global wp.Uploader
    5824          * @returns {wp.media.view.UploaderStatus}
    5825          */
    5826         dispose: function() {
    5827                 wp.Uploader.queue.off( null, null, this );
    5828                 /**
    5829                  * call 'dispose' directly on the parent class
    5830                  */
    5831                 View.prototype.dispose.apply( this, arguments );
    5832                 return this;
    5833         },
    5834 
    5835         visibility: function() {
    5836                 this.$el.toggleClass( 'uploading', !! this.queue.length );
    5837                 this.$el.toggleClass( 'errors', !! this.errors.length );
    5838                 this.$el.toggle( !! this.queue.length || !! this.errors.length );
    5839         },
    5840 
    5841         ready: function() {
    5842                 _.each({
    5843                         '$bar':      '.media-progress-bar div',
    5844                         '$index':    '.upload-index',
    5845                         '$total':    '.upload-total',
    5846                         '$filename': '.upload-filename'
    5847                 }, function( selector, key ) {
    5848                         this[ key ] = this.$( selector );
    5849                 }, this );
    5850 
    5851                 this.visibility();
    5852                 this.progress();
    5853                 this.info();
    5854         },
    5855 
    5856         progress: function() {
    5857                 var queue = this.queue,
    5858                         $bar = this.$bar;
    5859 
    5860                 if ( ! $bar || ! queue.length ) {
    5861                         return;
    5862                 }
    5863 
    5864                 $bar.width( ( queue.reduce( function( memo, attachment ) {
    5865                         if ( ! attachment.get('uploading') ) {
    5866                                 return memo + 100;
    5867                         }
    5868 
    5869                         var percent = attachment.get('percent');
    5870                         return memo + ( _.isNumber( percent ) ? percent : 100 );
    5871                 }, 0 ) / queue.length ) + '%' );
    5872         },
    5873 
    5874         info: function() {
    5875                 var queue = this.queue,
    5876                         index = 0, active;
    5877 
    5878                 if ( ! queue.length ) {
    5879                         return;
    5880                 }
    5881 
    5882                 active = this.queue.find( function( attachment, i ) {
    5883                         index = i;
    5884                         return attachment.get('uploading');
    5885                 });
    5886 
    5887                 this.$index.text( index + 1 );
    5888                 this.$total.text( queue.length );
    5889                 this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
    5890         },
    5891         /**
    5892          * @param {string} filename
    5893          * @returns {string}
    5894          */
    5895         filename: function( filename ) {
    5896                 return wp.media.truncate( _.escape( filename ), 24 );
    5897         },
    5898         /**
    5899          * @param {Backbone.Model} error
    5900          */
    5901         error: function( error ) {
    5902                 this.views.add( '.upload-errors', new UploaderStatusError({
    5903                         filename: this.filename( error.get('file').name ),
    5904                         message:  error.get('message')
    5905                 }), { at: 0 });
    5906         },
    5907 
    5908         /**
    5909          * @global wp.Uploader
    5910          *
    5911          * @param {Object} event
    5912          */
    5913         dismiss: function( event ) {
    5914                 var errors = this.views.get('.upload-errors');
    5915 
    5916                 event.preventDefault();
    5917 
    5918                 if ( errors ) {
    5919                         _.invoke( errors, 'remove' );
    5920                 }
    5921                 wp.Uploader.errors.reset();
    5922         }
    5923 });
    5924 
    5925 module.exports = UploaderStatus;
    5926 
    5927 },{"../view.js":51,"./status-error.js":47}],49:[function(require,module,exports){
    5928 /*globals wp, _, jQuery */
    5929 
    5930 /**
    5931  * wp.media.view.UploaderWindow
    5932  *
    5933  * An uploader window that allows for dragging and dropping media.
    5934  *
    5935  * @class
    5936  * @augments wp.media.View
    5937  * @augments wp.Backbone.View
    5938  * @augments Backbone.View
    5939  *
    5940  * @param {object} [options]                   Options hash passed to the view.
    5941  * @param {object} [options.uploader]          Uploader properties.
    5942  * @param {jQuery} [options.uploader.browser]
    5943  * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
    5944  * @param {object} [options.uploader.params]
    5945  */
    5946 var View = require( '../view.js' ),
    5947         $ = jQuery,
    5948         UploaderWindow;
    5949 
    5950 UploaderWindow = View.extend({
    5951         tagName:   'div',
    5952         className: 'uploader-window',
    5953         template:  wp.template('uploader-window'),
    5954 
    5955         initialize: function() {
    5956                 var uploader;
    5957 
    5958                 this.$browser = $('<a href="#" class="browser" />').hide().appendTo('body');
    5959 
    5960                 uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
    5961                         dropzone:  this.$el,
    5962                         browser:   this.$browser,
    5963                         params:    {}
    5964                 });
    5965 
    5966                 // Ensure the dropzone is a jQuery collection.
    5967                 if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
    5968                         uploader.dropzone = $( uploader.dropzone );
    5969                 }
    5970 
    5971                 this.controller.on( 'activate', this.refresh, this );
    5972 
    5973                 this.controller.on( 'detach', function() {
    5974                         this.$browser.remove();
    5975                 }, this );
    5976         },
    5977 
    5978         refresh: function() {
    5979                 if ( this.uploader ) {
    5980                         this.uploader.refresh();
    5981                 }
    5982         },
    5983 
    5984         ready: function() {
    5985                 var postId = wp.media.view.settings.post.id,
    5986                         dropzone;
    5987 
    5988                 // If the uploader already exists, bail.
    5989                 if ( this.uploader ) {
    5990                         return;
    5991                 }
    5992 
    5993                 if ( postId ) {
    5994                         this.options.uploader.params.post_id = postId;
    5995                 }
    5996                 this.uploader = new wp.Uploader( this.options.uploader );
    5997 
    5998                 dropzone = this.uploader.dropzone;
    5999                 dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
    6000                 dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
    6001 
    6002                 $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
    6003         },
    6004 
    6005         _ready: function() {
    6006                 this.controller.trigger( 'uploader:ready' );
    6007         },
    6008 
    6009         show: function() {
    6010                 var $el = this.$el.show();
    6011 
    6012                 // Ensure that the animation is triggered by waiting until
    6013                 // the transparent element is painted into the DOM.
    6014                 _.defer( function() {
    6015                         $el.css({ opacity: 1 });
    6016                 });
    6017         },
    6018 
    6019         hide: function() {
    6020                 var $el = this.$el.css({ opacity: 0 });
    6021 
    6022                 wp.media.transition( $el ).done( function() {
    6023                         // Transition end events are subject to race conditions.
    6024                         // Make sure that the value is set as intended.
    6025                         if ( '0' === $el.css('opacity') ) {
    6026                                 $el.hide();
    6027                         }
    6028                 });
    6029 
    6030                 // https://core.trac.wordpress.org/ticket/27341
    6031                 _.delay( function() {
    6032                         if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
    6033                                 $el.hide();
    6034                         }
    6035                 }, 500 );
    6036         }
    6037 });
    6038 
    6039 module.exports = UploaderWindow;
    6040 
    6041 },{"../view.js":51}],50:[function(require,module,exports){
    6042 /*globals wp */
    6043 
    6044 /**
    60451117 * wp.media.view.VideoDetails
    60461118 *
    60471119 * @class
     
    60831155
    60841156module.exports = VideoDetails;
    60851157
    6086 },{"./media-details":31}],51:[function(require,module,exports){
     1158},{"./media-details":9}],13:[function(require,module,exports){
    60871159/*globals wp */
    60881160
    60891161/**
  • src/wp-includes/js/media/controllers/audio-details.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        AudioDetails;
    1515
  • 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/controllers/video-details.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        VideoDetails;
    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,
    3754620        Router = require( '../../routers/manage.js' ),
    3755         Library = require( '../../controllers/library.js' ),
     621        Library = wp.media.controller.Library,
    3756622        $ = jQuery,
    3757623        Manage;
    3758624
     
    3979845
    3980846module.exports = Manage;
    3981847
    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]);
     848},{"../../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/audio-details.js

     
    1313 * @augments Backbone.View
    1414 * @mixes wp.media.controller.StateMachine
    1515 */
    16 var MediaDetails = require( './media-details' ),
    17         MediaLibrary = require( '../../controllers/media-library.js' ),
     16var MediaDetails = wp.media.view.MediaFrame.MediaDetails,
     17        MediaLibrary = wp.media.controller.MediaLibrary,
    1818        AudioDetailsView = require( '../audio-details.js' ),
    1919        AudioDetailsController = require( '../../controllers/audio-details.js' ),
    2020        l10n = wp.media.view.l10n,
  • 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,
    2121        Router = require( '../../routers/manage.js' ),
    22         Library = require( '../../controllers/library.js' ),
     22        Library = wp.media.controller.Library,
    2323        $ = jQuery,
    2424        Manage;
    2525
  • src/wp-includes/js/media/views/frame/media-details.js

     
    1212 * @augments Backbone.View
    1313 * @mixes wp.media.controller.StateMachine
    1414 */
    15 var View = require( '../view.js' ),
    16         Toolbar = require( '../toolbar.js' ),
    17         Select = require( './select.js' ),
     15var View = wp.media.View,
     16        Toolbar = wp.media.view.Toolbar,
     17        Select = wp.media.view.MediaFrame.Select,
    1818        l10n = wp.media.view.l10n,
    1919        MediaDetails;
    2020
  • src/wp-includes/js/media/views/frame/video-details.js

     
    1313 * @augments Backbone.View
    1414 * @mixes wp.media.controller.StateMachine
    1515 */
    16 var MediaDetails = require( './media-details' ),
    17         MediaLibrary = require( '../../controllers/media-library.js' ),
     16var MediaDetails = wp.media.view.MediaFrame.MediaDetails,
     17        MediaLibrary = wp.media.controller.MediaLibrary,
    1818        VideoDetailsView = require( '../video-details.js' ),
    1919        VideoDetailsController = require( '../../controllers/video-details.js' ),
    2020        l10n = wp.media.view.l10n,