WordPress.org

Make WordPress Core

Changeset 31493


Ignore:
Timestamp:
02/22/2015 08:28:30 AM (5 years ago)
Author:
wonderboymusic
Message:

Make sure the audio-video build does not load files from the views build.

See #28510.

Location:
trunk/src/wp-includes/js/media
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/js/media/audio-video.js

    r31492 r31493  
    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":10}],2:[function(require,module,exports){
    221221/*globals wp */
    222222
     
    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;
     
    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.
     255},{}],3:[function(require,module,exports){
     256/*globals wp */
     257
     258/**
     259 * wp.media.controller.VideoDetails
     260 *
     261 * The controller for the Video Details state
    262262 *
    263263 * @class
    264264 * @augments wp.media.controller.State
    265265 * @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){
    1134 /*globals wp */
    1135 
    1136 /**
    1137  * wp.media.controller.VideoDetails
    1138  *
    1139  * The controller for the Video Details state
    1140  *
    1141  * @class
    1142  * @augments wp.media.controller.State
    1143  * @augments Backbone.Model
    1144  */
    1145 var State = require( './state.js' ),
     266 */
     267var State = wp.media.controller.State,
    1146268    l10n = wp.media.view.l10n,
    1147269    VideoDetails;
     
    1166288module.exports = VideoDetails;
    1167289
    1168 },{"./state.js":7}],9:[function(require,module,exports){
     290},{}],4:[function(require,module,exports){
    1169291/*globals wp, Backbone, _ */
    1170292
     
    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){
    1448 /*globals wp */
    1449 
    1450 /**
    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){
     334},{}],5:[function(require,module,exports){
    3129335/*globals wp */
    3130336
     
    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
     
    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,
     392   
    3500393    AudioDetailsView = require( '../audio-details.js' ),
    3501394    AudioDetailsController = require( '../../controllers/audio-details.js' ),
     
    3559452module.exports = AudioDetails;
    3560453
    3561 },{"../../controllers/audio-details.js":2,"../../controllers/media-library.js":4,"../audio-details.js":21,"./media-details":26}],26:[function(require,module,exports){
     454},{"../../controllers/audio-details.js":2,"../audio-details.js":5}],7:[function(require,module,exports){
    3562455/*globals wp */
    3563456
     
    3574467 * @mixes wp.media.controller.StateMachine
    3575468 */
    3576 var View = require( '../view.js' ),
    3577     Toolbar = require( '../toolbar.js' ),
    3578     Select = require( './select.js' ),
     469var View = wp.media.View,
     470    Toolbar = wp.media.view.Toolbar,
     471    Select = wp.media.view.MediaFrame.Select,
    3579472    l10n = wp.media.view.l10n,
    3580473    MediaDetails;
     
    3693586module.exports = MediaDetails;
    3694587
    3695 },{"../toolbar.js":44,"../view.js":51,"./select.js":27}],27:[function(require,module,exports){
    3696 /*globals wp, _ */
    3697 
    3698 /**
    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){
     588},{}],8:[function(require,module,exports){
    3872589/*globals wp, _ */
    3873590
     
    3885602 * @mixes wp.media.controller.StateMachine
    3886603 */
    3887 var MediaDetails = require( './media-details' ),
    3888     MediaLibrary = require( '../../controllers/media-library.js' ),
     604var MediaDetails = wp.media.view.MediaFrame.MediaDetails,
     605    MediaLibrary = wp.media.controller.MediaLibrary,
    3889606    VideoDetailsView = require( '../video-details.js' ),
    3890607    VideoDetailsController = require( '../../controllers/video-details.js' ),
     
    4008725module.exports = VideoDetails;
    4009726
    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){
     727},{"../../controllers/video-details.js":3,"../video-details.js":10}],9:[function(require,module,exports){
    4067728/*global wp, jQuery, _, MediaElementPlayer */
    4068729
     
    4077738 * @augments Backbone.View
    4078739 */
    4079 var AttachmentDisplay = require( './settings/attachment-display.js' ),
     740var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
    4080741    $ = jQuery,
    4081742    MediaDetails;
     
    4160821        }
    4161822
    4162         if ( this.media.src.indexOf( 'vimeo' ) && ! ( 'Froogaloop' in window ) ) {
     823        if ( this.media.src.indexOf( 'vimeo' ) > -1 && ! ( 'Froogaloop' in window ) ) {
    4163824            baseSettings = wp.media.mixin.mejsSettings;
    4164825            this.scriptXhr = $.getScript( baseSettings.pluginPath + 'froogaloop.min.js', _.bind( this.loadPlayer, this ) );
     
    4232893module.exports = MediaDetails;
    4233894
    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){
    5118 /*globals _, jQuery, Backbone */
    5119 
    5120 /**
    5121  * wp.media.view.Settings
    5122  *
    5123  * @class
    5124  * @augments wp.media.View
    5125  * @augments wp.Backbone.View
    5126  * @augments Backbone.View
    5127  */
    5128 var View = require( './view.js' ),
    5129     $ = jQuery,
    5130     Settings;
    5131 
    5132 Settings = View.extend({
    5133     events: {
    5134         'click button':    'updateHandler',
    5135         'change input':    'updateHandler',
    5136         'change select':   'updateHandler',
    5137         'change textarea': 'updateHandler'
    5138     },
    5139 
    5140     initialize: function() {
    5141         this.model = this.model || new Backbone.Model();
    5142         this.listenTo( this.model, 'change', this.updateChanges );
    5143     },
    5144 
    5145     prepare: function() {
    5146         return _.defaults({
    5147             model: this.model.toJSON()
    5148         }, this.options );
    5149     },
    5150     /**
    5151      * @returns {wp.media.view.Settings} Returns itself to allow chaining
    5152      */
    5153     render: function() {
    5154         View.prototype.render.apply( this, arguments );
    5155         // Select the correct values.
    5156         _( this.model.attributes ).chain().keys().each( this.update, this );
    5157         return this;
    5158     },
    5159     /**
    5160      * @param {string} key
    5161      */
    5162     update: function( key ) {
    5163         var value = this.model.get( key ),
    5164             $setting = this.$('[data-setting="' + key + '"]'),
    5165             $buttons, $value;
    5166 
    5167         // Bail if we didn't find a matching setting.
    5168         if ( ! $setting.length ) {
    5169             return;
    5170         }
    5171 
    5172         // Attempt to determine how the setting is rendered and update
    5173         // the selected value.
    5174 
    5175         // Handle dropdowns.
    5176         if ( $setting.is('select') ) {
    5177             $value = $setting.find('[value="' + value + '"]');
    5178 
    5179             if ( $value.length ) {
    5180                 $setting.find('option').prop( 'selected', false );
    5181                 $value.prop( 'selected', true );
    5182             } else {
    5183                 // If we can't find the desired value, record what *is* selected.
    5184                 this.model.set( key, $setting.find(':selected').val() );
    5185             }
    5186 
    5187         // Handle button groups.
    5188         } else if ( $setting.hasClass('button-group') ) {
    5189             $buttons = $setting.find('button').removeClass('active');
    5190             $buttons.filter( '[value="' + value + '"]' ).addClass('active');
    5191 
    5192         // Handle text inputs and textareas.
    5193         } else if ( $setting.is('input[type="text"], textarea') ) {
    5194             if ( ! $setting.is(':focus') ) {
    5195                 $setting.val( value );
    5196             }
    5197         // Handle checkboxes.
    5198         } else if ( $setting.is('input[type="checkbox"]') ) {
    5199             $setting.prop( 'checked', !! value && 'false' !== value );
    5200         }
    5201     },
    5202     /**
    5203      * @param {Object} event
    5204      */
    5205     updateHandler: function( event ) {
    5206         var $setting = $( event.target ).closest('[data-setting]'),
    5207             value = event.target.value,
    5208             userSetting;
    5209 
    5210         event.preventDefault();
    5211 
    5212         if ( ! $setting.length ) {
    5213             return;
    5214         }
    5215 
    5216         // Use the correct value for checkboxes.
    5217         if ( $setting.is('input[type="checkbox"]') ) {
    5218             value = $setting[0].checked;
    5219         }
    5220 
    5221         // Update the corresponding setting.
    5222         this.model.set( $setting.data('setting'), value );
    5223 
    5224         // If the setting has a corresponding user setting,
    5225         // update that as well.
    5226         if ( userSetting = $setting.data('userSetting') ) {
    5227             window.setUserSetting( userSetting, value );
    5228         }
    5229     },
    5230 
    5231     updateChanges: function( model ) {
    5232         if ( model.hasChanged() ) {
    5233             _( model.changed ).chain().keys().each( this.update, this );
    5234         }
    5235     }
    5236 });
    5237 
    5238 module.exports = Settings;
    5239 
    5240 },{"./view.js":51}],41:[function(require,module,exports){
    5241 /*globals wp, _ */
    5242 
    5243 /**
    5244  * wp.media.view.Settings.AttachmentDisplay
    5245  *
    5246  * @class
    5247  * @augments wp.media.view.Settings
    5248  * @augments wp.media.View
    5249  * @augments wp.Backbone.View
    5250  * @augments Backbone.View
    5251  */
    5252 var Settings = require( '../settings.js' ),
    5253     AttachmentDisplay;
    5254 
    5255 AttachmentDisplay = Settings.extend({
    5256     className: 'attachment-display-settings',
    5257     template:  wp.template('attachment-display-settings'),
    5258 
    5259     initialize: function() {
    5260         var attachment = this.options.attachment;
    5261 
    5262         _.defaults( this.options, {
    5263             userSettings: false
    5264         });
    5265         // Call 'initialize' directly on the parent class.
    5266         Settings.prototype.initialize.apply( this, arguments );
    5267         this.listenTo( this.model, 'change:link', this.updateLinkTo );
    5268 
    5269         if ( attachment ) {
    5270             attachment.on( 'change:uploading', this.render, this );
    5271         }
    5272     },
    5273 
    5274     dispose: function() {
    5275         var attachment = this.options.attachment;
    5276         if ( attachment ) {
    5277             attachment.off( null, null, this );
    5278         }
    5279         /**
    5280          * call 'dispose' directly on the parent class
    5281          */
    5282         Settings.prototype.dispose.apply( this, arguments );
    5283     },
    5284     /**
    5285      * @returns {wp.media.view.AttachmentDisplay} Returns itself to allow chaining
    5286      */
    5287     render: function() {
    5288         var attachment = this.options.attachment;
    5289         if ( attachment ) {
    5290             _.extend( this.options, {
    5291                 sizes: attachment.get('sizes'),
    5292                 type:  attachment.get('type')
    5293             });
    5294         }
    5295         /**
    5296          * call 'render' directly on the parent class
    5297          */
    5298         Settings.prototype.render.call( this );
    5299         this.updateLinkTo();
    5300         return this;
    5301     },
    5302 
    5303     updateLinkTo: function() {
    5304         var linkTo = this.model.get('link'),
    5305             $input = this.$('.link-to-custom'),
    5306             attachment = this.options.attachment;
    5307 
    5308         if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
    5309             $input.addClass( 'hidden' );
    5310             return;
    5311         }
    5312 
    5313         if ( attachment ) {
    5314             if ( 'post' === linkTo ) {
    5315                 $input.val( attachment.get('link') );
    5316             } else if ( 'file' === linkTo ) {
    5317                 $input.val( attachment.get('url') );
    5318             } else if ( ! this.model.get('linkUrl') ) {
    5319                 $input.val('http://');
    5320             }
    5321 
    5322             $input.prop( 'readonly', 'custom' !== linkTo );
    5323         }
    5324 
    5325         $input.removeClass( 'hidden' );
    5326 
    5327         // If the input is visible, focus and select its contents.
    5328         if ( ! wp.media.isTouchDevice && $input.is(':visible') ) {
    5329             $input.focus()[0].select();
    5330         }
    5331     }
    5332 });
    5333 
    5334 module.exports = AttachmentDisplay;
    5335 
    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){
    5766 /*globals wp */
    5767 
    5768 /**
    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){
     895},{}],10:[function(require,module,exports){
    6042896/*globals wp */
    6043897
     
    6084938module.exports = VideoDetails;
    6085939
    6086 },{"./media-details":31}],51:[function(require,module,exports){
    6087 /*globals wp */
    6088 
    6089 /**
    6090  * wp.media.View
    6091  *
    6092  * The base view class for media.
    6093  *
    6094  * Undelegating events, removing events from the model, and
    6095  * removing events from the controller mirror the code for
    6096  * `Backbone.View.dispose` in Backbone 0.9.8 development.
    6097  *
    6098  * This behavior has since been removed, and should not be used
    6099  * outside of the media manager.
    6100  *
    6101  * @class
    6102  * @augments wp.Backbone.View
    6103  * @augments Backbone.View
    6104  */
    6105 var View = wp.Backbone.View.extend({
    6106     constructor: function( options ) {
    6107         if ( options && options.controller ) {
    6108             this.controller = options.controller;
    6109         }
    6110         wp.Backbone.View.apply( this, arguments );
    6111     },
    6112     /**
    6113      * @todo The internal comment mentions this might have been a stop-gap
    6114      *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
    6115      *       care of this in Backbone.View now.
    6116      *
    6117      * @returns {wp.media.View} Returns itself to allow chaining
    6118      */
    6119     dispose: function() {
    6120         // Undelegating events, removing events from the model, and
    6121         // removing events from the controller mirror the code for
    6122         // `Backbone.View.dispose` in Backbone 0.9.8 development.
    6123         this.undelegateEvents();
    6124 
    6125         if ( this.model && this.model.off ) {
    6126             this.model.off( null, null, this );
    6127         }
    6128 
    6129         if ( this.collection && this.collection.off ) {
    6130             this.collection.off( null, null, this );
    6131         }
    6132 
    6133         // Unbind controller events.
    6134         if ( this.controller && this.controller.off ) {
    6135             this.controller.off( null, null, this );
    6136         }
    6137 
    6138         return this;
    6139     },
    6140     /**
    6141      * @returns {wp.media.View} Returns itself to allow chaining
    6142      */
    6143     remove: function() {
    6144         this.dispose();
    6145         /**
    6146          * call 'remove' directly on the parent class
    6147          */
    6148         return wp.Backbone.View.prototype.remove.apply( this, arguments );
    6149     }
    6150 });
    6151 
    6152 module.exports = View;
    6153 
    6154 },{}]},{},[1]);
     940},{"./media-details":9}]},{},[1]);
  • trunk/src/wp-includes/js/media/controllers/audio-details.js

    r31491 r31493  
    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;
  • trunk/src/wp-includes/js/media/controllers/video-details.js

    r31491 r31493  
    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;
  • trunk/src/wp-includes/js/media/views/frame/audio-details.js

    r31491 r31493  
    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,
     18   
    1819    AudioDetailsView = require( '../audio-details.js' ),
    1920    AudioDetailsController = require( '../../controllers/audio-details.js' ),
  • trunk/src/wp-includes/js/media/views/frame/media-details.js

    r31491 r31493  
    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;
  • trunk/src/wp-includes/js/media/views/frame/video-details.js

    r31491 r31493  
    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' ),
  • trunk/src/wp-includes/js/media/views/media-details.js

    r31491 r31493  
    1111 * @augments Backbone.View
    1212 */
    13 var AttachmentDisplay = require( './settings/attachment-display.js' ),
     13var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
    1414    $ = jQuery,
    1515    MediaDetails;
     
    9494        }
    9595
    96         if ( this.media.src.indexOf( 'vimeo' ) && ! ( 'Froogaloop' in window ) ) {
     96        if ( this.media.src.indexOf( 'vimeo' ) > -1 && ! ( 'Froogaloop' in window ) ) {
    9797            baseSettings = wp.media.mixin.mejsSettings;
    9898            this.scriptXhr = $.getScript( baseSettings.pluginPath + 'froogaloop.min.js', _.bind( this.loadPlayer, this ) );
Note: See TracChangeset for help on using the changeset viewer.