WordPress.org

Make WordPress Core

Ticket #28510: 28510.diff

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

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

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

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

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

     
    1010 * @augments wp.media.controller.State
    1111 * @augments Backbone.Model
    1212 */
    13 var State = require( './state.js' ),
     13var State = wp.media.controller.State,
    1414        l10n = wp.media.view.l10n,
    1515        EditAttachmentMetadata;
    1616
     
    2929
    3030module.exports = EditAttachmentMetadata;
    3131
    32 },{"./state.js":6}],2:[function(require,module,exports){
     32},{}],2:[function(require,module,exports){
    3333/*globals wp */
    3434
    35 /**
    36  * wp.media.controller.EditImage
    37  *
    38  * A state for editing (cropping, etc.) an image.
    39  *
    40  * @class
    41  * @augments wp.media.controller.State
    42  * @augments Backbone.Model
    43  *
    44  * @param {object}                    attributes                      The attributes hash passed to the state.
    45  * @param {wp.media.model.Attachment} attributes.model                The attachment.
    46  * @param {string}                    [attributes.id=edit-image]      Unique identifier.
    47  * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
    48  * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
    49  * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
    50  * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
    51  * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
    52  */
    53 var State = require( './state.js' ),
    54         ToolbarView = require( '../views/toolbar.js' ),
    55         l10n = wp.media.view.l10n,
    56         EditImage;
    57 
    58 EditImage = State.extend({
    59         defaults: {
    60                 id:      'edit-image',
    61                 title:   l10n.editImage,
    62                 menu:    false,
    63                 toolbar: 'edit-image',
    64                 content: 'edit-image',
    65                 url:     ''
    66         },
    67 
    68         /**
    69          * @since 3.9.0
    70          */
    71         activate: function() {
    72                 this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
    73         },
    74 
    75         /**
    76          * @since 3.9.0
    77          */
    78         deactivate: function() {
    79                 this.stopListening( this.frame );
    80         },
    81 
    82         /**
    83          * @since 3.9.0
    84          */
    85         toolbar: function() {
    86                 var frame = this.frame,
    87                         lastState = frame.lastState(),
    88                         previous = lastState && lastState.id;
    89 
    90                 frame.toolbar.set( new ToolbarView({
    91                         controller: frame,
    92                         items: {
    93                                 back: {
    94                                         style: 'primary',
    95                                         text:     l10n.back,
    96                                         priority: 20,
    97                                         click:    function() {
    98                                                 if ( previous ) {
    99                                                         frame.setState( previous );
    100                                                 } else {
    101                                                         frame.close();
    102                                                 }
    103                                         }
    104                                 }
    105                         }
    106                 }) );
    107         }
    108 });
    109 
    110 module.exports = EditImage;
    111 
    112 },{"../views/toolbar.js":44,"./state.js":6}],3:[function(require,module,exports){
    113 /*globals wp, _, Backbone */
    114 
    115 /**
    116  * wp.media.controller.Library
    117  *
    118  * A state for choosing an attachment or group of attachments from the media library.
    119  *
    120  * @class
    121  * @augments wp.media.controller.State
    122  * @augments Backbone.Model
    123  * @mixes media.selectionSync
    124  *
    125  * @param {object}                          [attributes]                         The attributes hash passed to the state.
    126  * @param {string}                          [attributes.id=library]              Unique identifier.
    127  * @param {string}                          [attributes.title=Media library]     Title for the state. Displays in the media menu and the frame's title region.
    128  * @param {wp.media.model.Attachments}      [attributes.library]                 The attachments collection to browse.
    129  *                                                                               If one is not supplied, a collection of all attachments will be created.
    130  * @param {wp.media.model.Selection|object} [attributes.selection]               A collection to contain attachment selections within the state.
    131  *                                                                               If the 'selection' attribute is a plain JS object,
    132  *                                                                               a Selection will be created using its values as the selection instance's `props` model.
    133  *                                                                               Otherwise, it will copy the library's `props` model.
    134  * @param {boolean}                         [attributes.multiple=false]          Whether multi-select is enabled.
    135  * @param {string}                          [attributes.content=upload]          Initial mode for the content region.
    136  *                                                                               Overridden by persistent user setting if 'contentUserSetting' is true.
    137  * @param {string}                          [attributes.menu=default]            Initial mode for the menu region.
    138  * @param {string}                          [attributes.router=browse]           Initial mode for the router region.
    139  * @param {string}                          [attributes.toolbar=select]          Initial mode for the toolbar region.
    140  * @param {boolean}                         [attributes.searchable=true]         Whether the library is searchable.
    141  * @param {boolean|string}                  [attributes.filterable=false]        Whether the library is filterable, and if so what filters should be shown.
    142  *                                                                               Accepts 'all', 'uploaded', or 'unattached'.
    143  * @param {boolean}                         [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
    144  * @param {boolean}                         [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
    145  * @param {boolean}                         [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
    146  * @param {boolean}                         [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
    147  * @param {boolean}                         [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
    148  */
    149 var selectionSync = require( '../utils/selection-sync.js' ),
    150         State = require( './state.js' ),
    151         l10n = wp.media.view.l10n,
    152         getUserSetting = window.getUserSetting,
    153         setUserSetting = window.setUserSetting,
    154         Library;
    155 
    156 Library = State.extend({
    157         defaults: {
    158                 id:                 'library',
    159                 title:              l10n.mediaLibraryTitle,
    160                 multiple:           false,
    161                 content:            'upload',
    162                 menu:               'default',
    163                 router:             'browse',
    164                 toolbar:            'select',
    165                 searchable:         true,
    166                 filterable:         false,
    167                 sortable:           true,
    168                 autoSelect:         true,
    169                 describe:           false,
    170                 contentUserSetting: true,
    171                 syncSelection:      true
    172         },
    173 
    174         /**
    175          * If a library isn't provided, query all media items.
    176          * If a selection instance isn't provided, create one.
    177          *
    178          * @since 3.5.0
    179          */
    180         initialize: function() {
    181                 var selection = this.get('selection'),
    182                         props;
    183 
    184                 if ( ! this.get('library') ) {
    185                         this.set( 'library', wp.media.query() );
    186                 }
    187 
    188                 if ( ! ( selection instanceof wp.media.model.Selection ) ) {
    189                         props = selection;
    190 
    191                         if ( ! props ) {
    192                                 props = this.get('library').props.toJSON();
    193                                 props = _.omit( props, 'orderby', 'query' );
    194                         }
    195 
    196                         this.set( 'selection', new wp.media.model.Selection( null, {
    197                                 multiple: this.get('multiple'),
    198                                 props: props
    199                         }) );
    200                 }
    201 
    202                 this.resetDisplays();
    203         },
    204 
    205         /**
    206          * @since 3.5.0
    207          */
    208         activate: function() {
    209                 this.syncSelection();
    210 
    211                 wp.Uploader.queue.on( 'add', this.uploading, this );
    212 
    213                 this.get('selection').on( 'add remove reset', this.refreshContent, this );
    214 
    215                 if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
    216                         this.frame.on( 'content:activate', this.saveContentMode, this );
    217                         this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
    218                 }
    219         },
    220 
    221         /**
    222          * @since 3.5.0
    223          */
    224         deactivate: function() {
    225                 this.recordSelection();
    226 
    227                 this.frame.off( 'content:activate', this.saveContentMode, this );
    228 
    229                 // Unbind all event handlers that use this state as the context
    230                 // from the selection.
    231                 this.get('selection').off( null, null, this );
    232 
    233                 wp.Uploader.queue.off( null, null, this );
    234         },
    235 
    236         /**
    237          * Reset the library to its initial state.
    238          *
    239          * @since 3.5.0
    240          */
    241         reset: function() {
    242                 this.get('selection').reset();
    243                 this.resetDisplays();
    244                 this.refreshContent();
    245         },
    246 
    247         /**
    248          * Reset the attachment display settings defaults to the site options.
    249          *
    250          * If site options don't define them, fall back to a persistent user setting.
    251          *
    252          * @since 3.5.0
    253          */
    254         resetDisplays: function() {
    255                 var defaultProps = wp.media.view.settings.defaultProps;
    256                 this._displays = [];
    257                 this._defaultDisplaySettings = {
    258                         align: defaultProps.align || getUserSetting( 'align', 'none' ),
    259                         size:  defaultProps.size  || getUserSetting( 'imgsize', 'medium' ),
    260                         link:  defaultProps.link  || getUserSetting( 'urlbutton', 'file' )
    261                 };
    262         },
    263 
    264         /**
    265          * Create a model to represent display settings (alignment, etc.) for an attachment.
    266          *
    267          * @since 3.5.0
    268          *
    269          * @param {wp.media.model.Attachment} attachment
    270          * @returns {Backbone.Model}
    271          */
    272         display: function( attachment ) {
    273                 var displays = this._displays;
    274 
    275                 if ( ! displays[ attachment.cid ] ) {
    276                         displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
    277                 }
    278                 return displays[ attachment.cid ];
    279         },
    280 
    281         /**
    282          * Given an attachment, create attachment display settings properties.
    283          *
    284          * @since 3.6.0
    285          *
    286          * @param {wp.media.model.Attachment} attachment
    287          * @returns {Object}
    288          */
    289         defaultDisplaySettings: function( attachment ) {
    290                 var settings = this._defaultDisplaySettings;
    291                 if ( settings.canEmbed = this.canEmbed( attachment ) ) {
    292                         settings.link = 'embed';
    293                 }
    294                 return settings;
    295         },
    296 
    297         /**
    298          * Whether an attachment can be embedded (audio or video).
    299          *
    300          * @since 3.6.0
    301          *
    302          * @param {wp.media.model.Attachment} attachment
    303          * @returns {Boolean}
    304          */
    305         canEmbed: function( attachment ) {
    306                 // If uploading, we know the filename but not the mime type.
    307                 if ( ! attachment.get('uploading') ) {
    308                         var type = attachment.get('type');
    309                         if ( type !== 'audio' && type !== 'video' ) {
    310                                 return false;
    311                         }
    312                 }
    313 
    314                 return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
    315         },
    316 
    317 
    318         /**
    319          * If the state is active, no items are selected, and the current
    320          * content mode is not an option in the state's router (provided
    321          * the state has a router), reset the content mode to the default.
    322          *
    323          * @since 3.5.0
    324          */
    325         refreshContent: function() {
    326                 var selection = this.get('selection'),
    327                         frame = this.frame,
    328                         router = frame.router.get(),
    329                         mode = frame.content.mode();
    330 
    331                 if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
    332                         this.frame.content.render( this.get('content') );
    333                 }
    334         },
    335 
    336         /**
    337          * Callback handler when an attachment is uploaded.
    338          *
    339          * Switch to the Media Library if uploaded from the 'Upload Files' tab.
    340          *
    341          * Adds any uploading attachments to the selection.
    342          *
    343          * If the state only supports one attachment to be selected and multiple
    344          * attachments are uploaded, the last attachment in the upload queue will
    345          * be selected.
    346          *
    347          * @since 3.5.0
    348          *
    349          * @param {wp.media.model.Attachment} attachment
    350          */
    351         uploading: function( attachment ) {
    352                 var content = this.frame.content;
    353 
    354                 if ( 'upload' === content.mode() ) {
    355                         this.frame.content.mode('browse');
    356                 }
    357 
    358                 if ( this.get( 'autoSelect' ) ) {
    359                         this.get('selection').add( attachment );
    360                         this.frame.trigger( 'library:selection:add' );
    361                 }
    362         },
    363 
    364         /**
    365          * Persist the mode of the content region as a user setting.
    366          *
    367          * @since 3.5.0
    368          */
    369         saveContentMode: function() {
    370                 if ( 'browse' !== this.get('router') ) {
    371                         return;
    372                 }
    373 
    374                 var mode = this.frame.content.mode(),
    375                         view = this.frame.router.get();
    376 
    377                 if ( view && view.get( mode ) ) {
    378                         setUserSetting( 'libraryContent', mode );
    379                 }
    380         }
    381 });
    382 
    383 // Make selectionSync available on any Media Library state.
    384 _.extend( Library.prototype, selectionSync );
    385 
    386 module.exports = Library;
    387 
    388 },{"../utils/selection-sync.js":9,"./state.js":6}],4:[function(require,module,exports){
    389 /*globals Backbone, _ */
    390 
    391 /**
    392  * wp.media.controller.Region
    393  *
    394  * A region is a persistent application layout area.
    395  *
    396  * A region assumes one mode at any time, and can be switched to another.
    397  *
    398  * When mode changes, events are triggered on the region's parent view.
    399  * The parent view will listen to specific events and fill the region with an
    400  * appropriate view depending on mode. For example, a frame listens for the
    401  * 'browse' mode t be activated on the 'content' view and then fills the region
    402  * with an AttachmentsBrowser view.
    403  *
    404  * @class
    405  *
    406  * @param {object}        options          Options hash for the region.
    407  * @param {string}        options.id       Unique identifier for the region.
    408  * @param {Backbone.View} options.view     A parent view the region exists within.
    409  * @param {string}        options.selector jQuery selector for the region within the parent view.
    410  */
    411 var Region = function( options ) {
    412         _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
    413 };
    414 
    415 // Use Backbone's self-propagating `extend` inheritance method.
    416 Region.extend = Backbone.Model.extend;
    417 
    418 _.extend( Region.prototype, {
    419         /**
    420          * Activate a mode.
    421          *
    422          * @since 3.5.0
    423          *
    424          * @param {string} mode
    425          *
    426          * @fires this.view#{this.id}:activate:{this._mode}
    427          * @fires this.view#{this.id}:activate
    428          * @fires this.view#{this.id}:deactivate:{this._mode}
    429          * @fires this.view#{this.id}:deactivate
    430          *
    431          * @returns {wp.media.controller.Region} Returns itself to allow chaining.
    432          */
    433         mode: function( mode ) {
    434                 if ( ! mode ) {
    435                         return this._mode;
    436                 }
    437                 // Bail if we're trying to change to the current mode.
    438                 if ( mode === this._mode ) {
    439                         return this;
    440                 }
    441 
    442                 /**
    443                  * Region mode deactivation event.
    444                  *
    445                  * @event this.view#{this.id}:deactivate:{this._mode}
    446                  * @event this.view#{this.id}:deactivate
    447                  */
    448                 this.trigger('deactivate');
    449 
    450                 this._mode = mode;
    451                 this.render( mode );
    452 
    453                 /**
    454                  * Region mode activation event.
    455                  *
    456                  * @event this.view#{this.id}:activate:{this._mode}
    457                  * @event this.view#{this.id}:activate
    458                  */
    459                 this.trigger('activate');
    460                 return this;
    461         },
    462         /**
    463          * Render a mode.
    464          *
    465          * @since 3.5.0
    466          *
    467          * @param {string} mode
    468          *
    469          * @fires this.view#{this.id}:create:{this._mode}
    470          * @fires this.view#{this.id}:create
    471          * @fires this.view#{this.id}:render:{this._mode}
    472          * @fires this.view#{this.id}:render
    473          *
    474          * @returns {wp.media.controller.Region} Returns itself to allow chaining
    475          */
    476         render: function( mode ) {
    477                 // If the mode isn't active, activate it.
    478                 if ( mode && mode !== this._mode ) {
    479                         return this.mode( mode );
    480                 }
    481 
    482                 var set = { view: null },
    483                         view;
    484 
    485                 /**
    486                  * Create region view event.
    487                  *
    488                  * Region view creation takes place in an event callback on the frame.
    489                  *
    490                  * @event this.view#{this.id}:create:{this._mode}
    491                  * @event this.view#{this.id}:create
    492                  */
    493                 this.trigger( 'create', set );
    494                 view = set.view;
    495 
    496                 /**
    497                  * Render region view event.
    498                  *
    499                  * Region view creation takes place in an event callback on the frame.
    500                  *
    501                  * @event this.view#{this.id}:create:{this._mode}
    502                  * @event this.view#{this.id}:create
    503                  */
    504                 this.trigger( 'render', view );
    505                 if ( view ) {
    506                         this.set( view );
    507                 }
    508                 return this;
    509         },
    510 
    511         /**
    512          * Get the region's view.
    513          *
    514          * @since 3.5.0
    515          *
    516          * @returns {wp.media.View}
    517          */
    518         get: function() {
    519                 return this.view.views.first( this.selector );
    520         },
    521 
    522         /**
    523          * Set the region's view as a subview of the frame.
    524          *
    525          * @since 3.5.0
    526          *
    527          * @param {Array|Object} views
    528          * @param {Object} [options={}]
    529          * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
    530          */
    531         set: function( views, options ) {
    532                 if ( options ) {
    533                         options.add = false;
    534                 }
    535                 return this.view.views.set( this.selector, views, options );
    536         },
    537 
    538         /**
    539          * Trigger regional view events on the frame.
    540          *
    541          * @since 3.5.0
    542          *
    543          * @param {string} event
    544          * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
    545          */
    546         trigger: function( event ) {
    547                 var base, args;
    548 
    549                 if ( ! this._mode ) {
    550                         return;
    551                 }
    552 
    553                 args = _.toArray( arguments );
    554                 base = this.id + ':' + event;
    555 
    556                 // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
    557                 args[0] = base + ':' + this._mode;
    558                 this.view.trigger.apply( this.view, args );
    559 
    560                 // Trigger `{this.id}:{event}` event on the frame.
    561                 args[0] = base;
    562                 this.view.trigger.apply( this.view, args );
    563                 return this;
    564         }
    565 });
    566 
    567 module.exports = Region;
    568 
    569 },{}],5:[function(require,module,exports){
    570 /*globals _, Backbone */
    571 
    572 /**
    573  * wp.media.controller.StateMachine
    574  *
    575  * A state machine keeps track of state. It is in one state at a time,
    576  * and can change from one state to another.
    577  *
    578  * States are stored as models in a Backbone collection.
    579  *
    580  * @since 3.5.0
    581  *
    582  * @class
    583  * @augments Backbone.Model
    584  * @mixin
    585  * @mixes Backbone.Events
    586  *
    587  * @param {Array} states
    588  */
    589 var StateMachine = function( states ) {
    590         // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
    591         this.states = new Backbone.Collection( states );
    592 };
    593 
    594 // Use Backbone's self-propagating `extend` inheritance method.
    595 StateMachine.extend = Backbone.Model.extend;
    596 
    597 _.extend( StateMachine.prototype, Backbone.Events, {
    598         /**
    599          * Fetch a state.
    600          *
    601          * If no `id` is provided, returns the active state.
    602          *
    603          * Implicitly creates states.
    604          *
    605          * Ensure that the `states` collection exists so the `StateMachine`
    606          *   can be used as a mixin.
    607          *
    608          * @since 3.5.0
    609          *
    610          * @param {string} id
    611          * @returns {wp.media.controller.State} Returns a State model
    612          *   from the StateMachine collection
    613          */
    614         state: function( id ) {
    615                 this.states = this.states || new Backbone.Collection();
    616 
    617                 // Default to the active state.
    618                 id = id || this._state;
    619 
    620                 if ( id && ! this.states.get( id ) ) {
    621                         this.states.add({ id: id });
    622                 }
    623                 return this.states.get( id );
    624         },
    625 
    626         /**
    627          * Sets the active state.
    628          *
    629          * Bail if we're trying to select the current state, if we haven't
    630          * created the `states` collection, or are trying to select a state
    631          * that does not exist.
    632          *
    633          * @since 3.5.0
    634          *
    635          * @param {string} id
    636          *
    637          * @fires wp.media.controller.State#deactivate
    638          * @fires wp.media.controller.State#activate
    639          *
    640          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
    641          */
    642         setState: function( id ) {
    643                 var previous = this.state();
    644 
    645                 if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
    646                         return this;
    647                 }
    648 
    649                 if ( previous ) {
    650                         previous.trigger('deactivate');
    651                         this._lastState = previous.id;
    652                 }
    653 
    654                 this._state = id;
    655                 this.state().trigger('activate');
    656 
    657                 return this;
    658         },
    659 
    660         /**
    661          * Returns the previous active state.
    662          *
    663          * Call the `state()` method with no parameters to retrieve the current
    664          * active state.
    665          *
    666          * @since 3.5.0
    667          *
    668          * @returns {wp.media.controller.State} Returns a State model
    669          *    from the StateMachine collection
    670          */
    671         lastState: function() {
    672                 if ( this._lastState ) {
    673                         return this.state( this._lastState );
    674                 }
    675         }
    676 });
    677 
    678 // Map all event binding and triggering on a StateMachine to its `states` collection.
    679 _.each([ 'on', 'off', 'trigger' ], function( method ) {
    680         /**
    681          * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
    682          */
    683         StateMachine.prototype[ method ] = function() {
    684                 // Ensure that the `states` collection exists so the `StateMachine`
    685                 // can be used as a mixin.
    686                 this.states = this.states || new Backbone.Collection();
    687                 // Forward the method to the `states` collection.
    688                 this.states[ method ].apply( this.states, arguments );
    689                 return this;
    690         };
    691 });
    692 
    693 module.exports = StateMachine;
    694 
    695 },{}],6:[function(require,module,exports){
    696 /*globals _, Backbone */
    697 
    698 /**
    699  * wp.media.controller.State
    700  *
    701  * A state is a step in a workflow that when set will trigger the controllers
    702  * for the regions to be updated as specified in the frame.
    703  *
    704  * A state has an event-driven lifecycle:
    705  *
    706  *     'ready'      triggers when a state is added to a state machine's collection.
    707  *     'activate'   triggers when a state is activated by a state machine.
    708  *     'deactivate' triggers when a state is deactivated by a state machine.
    709  *     'reset'      is not triggered automatically. It should be invoked by the
    710  *                  proper controller to reset the state to its default.
    711  *
    712  * @class
    713  * @augments Backbone.Model
    714  */
    715 var State = Backbone.Model.extend({
    716         /**
    717          * Constructor.
    718          *
    719          * @since 3.5.0
    720          */
    721         constructor: function() {
    722                 this.on( 'activate', this._preActivate, this );
    723