]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/media-views.js
WordPress 4.1
[autoinstalls/wordpress.git] / wp-includes / js / media-views.js
index e334b20735fd024fa3c15bd07cf298b2300bdfcb..ffe65f5c79bbd7a8b355f144e0923c3c2404220d 100644 (file)
                return deferred.promise();
        };
 
-       /**
-        * ========================================================================
-        * CONTROLLERS
-        * ========================================================================
-        */
-
        /**
         * wp.media.controller.Region
         *
-        * @constructor
-        * @augments Backbone.Model
+        * A region is a persistent application layout area.
+        *
+        * A region assumes one mode at any time, and can be switched to another.
+        *
+        * When mode changes, events are triggered on the region's parent view.
+        * The parent view will listen to specific events and fill the region with an
+        * appropriate view depending on mode. For example, a frame listens for the
+        * 'browse' mode t be activated on the 'content' view and then fills the region
+        * with an AttachmentsBrowser view.
         *
-        * @param {Object} [options={}]
+        * @class
+        *
+        * @param {object}        options          Options hash for the region.
+        * @param {string}        options.id       Unique identifier for the region.
+        * @param {Backbone.View} options.view     A parent view the region exists within.
+        * @param {string}        options.selector jQuery selector for the region within the parent view.
         */
        media.controller.Region = function( options ) {
                _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
                /**
                 * Activate a mode.
                 *
+                * @since 3.5.0
+                *
                 * @param {string} mode
                 *
                 * @fires this.view#{this.id}:activate:{this._mode}
                /**
                 * Render a mode.
                 *
+                * @since 3.5.0
+                *
                 * @param {string} mode
                 *
                 * @fires this.view#{this.id}:create:{this._mode}
                /**
                 * Get the region's view.
                 *
+                * @since 3.5.0
+                *
                 * @returns {wp.media.View}
                 */
                get: function() {
                /**
                 * Set the region's view as a subview of the frame.
                 *
+                * @since 3.5.0
+                *
                 * @param {Array|Object} views
                 * @param {Object} [options={}]
                 * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
                /**
                 * Trigger regional view events on the frame.
                 *
+                * @since 3.5.0
+                *
                 * @param {string} event
                 * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
                 */
        /**
         * wp.media.controller.StateMachine
         *
-        * @constructor
+        * A state machine keeps track of state. It is in one state at a time,
+        * and can change from one state to another.
+        *
+        * States are stored as models in a Backbone collection.
+        *
+        * @since 3.5.0
+        *
+        * @class
         * @augments Backbone.Model
         * @mixin
         * @mixes Backbone.Events
         * @param {Array} states
         */
        media.controller.StateMachine = function( states ) {
+               // @todo This is dead code. The states collection gets created in media.view.Frame._createStates.
                this.states = new Backbone.Collection( states );
        };
 
                 * Ensure that the `states` collection exists so the `StateMachine`
                 *   can be used as a mixin.
                 *
+                * @since 3.5.0
+                *
                 * @param {string} id
                 * @returns {wp.media.controller.State} Returns a State model
                 *   from the StateMachine collection
                 * created the `states` collection, or are trying to select a state
                 * that does not exist.
                 *
+                * @since 3.5.0
+                *
                 * @param {string} id
                 *
                 * @fires wp.media.controller.State#deactivate
                 * Call the `state()` method with no parameters to retrieve the current
                 * active state.
                 *
+                * @since 3.5.0
+                *
                 * @returns {wp.media.controller.State} Returns a State model
                 *    from the StateMachine collection
                 */
         * wp.media.controller.State
         *
         * A state is a step in a workflow that when set will trigger the controllers
-        * for the regions to be updated as specified in the frame. This is the base
-        * class that the various states used in wp.media extend.
+        * for the regions to be updated as specified in the frame.
+        *
+        * A state has an event-driven lifecycle:
         *
-        * @constructor
+        *     'ready'      triggers when a state is added to a state machine's collection.
+        *     'activate'   triggers when a state is activated by a state machine.
+        *     'deactivate' triggers when a state is deactivated by a state machine.
+        *     'reset'      is not triggered automatically. It should be invoked by the
+        *                  proper controller to reset the state to its default.
+        *
+        * @class
         * @augments Backbone.Model
         */
        media.controller.State = Backbone.Model.extend({
+               /**
+                * Constructor.
+                *
+                * @since 3.5.0
+                */
                constructor: function() {
                        this.on( 'activate', this._preActivate, this );
                        this.on( 'activate', this.activate, this );
                        this.on( 'change:menu', this._updateMenu, this );
                },
                /**
+                * Ready event callback.
+                *
                 * @abstract
+                * @since 3.5.0
                 */
                ready: function() {},
+
                /**
+                * Activate event callback.
+                *
                 * @abstract
+                * @since 3.5.0
                 */
                activate: function() {},
+
                /**
+                * Deactivate event callback.
+                *
                 * @abstract
+                * @since 3.5.0
                 */
                deactivate: function() {},
+
                /**
+                * Reset event callback.
+                *
                 * @abstract
+                * @since 3.5.0
                 */
                reset: function() {},
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _ready: function() {
                        this._updateMenu();
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                */
                _preActivate: function() {
                        this.active = true;
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _postActivate: function() {
                        this.on( 'change:menu', this._menu, this );
                        this._content();
                        this._router();
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _deactivate: function() {
                        this.active = false;
                        this.off( 'change:content', this._content, this );
                        this.off( 'change:toolbar', this._toolbar, this );
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _title: function() {
                        this.frame.title.render( this.get('titleMode') || 'default' );
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _renderTitle: function( view ) {
                        view.$el.text( this.get('title') || '' );
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _router: function() {
                        var router = this.frame.router,
                                view.select( this.frame.content.mode() );
                        }
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _menu: function() {
                        var menu = this.frame.menu,
                                view.select( this.id );
                        }
                },
+
                /**
                 * @access private
+                * @since 3.5.0
                 */
                _updateMenu: function() {
                        var previous = this.previous('menu'),
                                this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
                        }
                },
+
                /**
+                * Create a view in the media menu for the state.
+                *
                 * @access private
+                * @since 3.5.0
+                *
+                * @param {media.view.Menu} view The menu view.
                 */
                _renderMenu: function( view ) {
                        var menuItem = this.get('menuItem'),
                };
        });
 
+       /**
+        * wp.media.selectionSync
+        *
+        * Sync an attachments selection in a state with another state.
+        *
+        * Allows for selecting multiple images in the Insert Media workflow, and then
+        * switching to the Insert Gallery workflow while preserving the attachments selection.
+        *
+        * @mixin
+        */
        media.selectionSync = {
+               /**
+                * @since 3.5.0
+                */
                syncSelection: function() {
                        var selection = this.get('selection'),
                                manager = this.frame._selection;
                 * of the selection's attachments and the set of selected
                 * attachments that this specific selection considered invalid.
                 * Reset the difference and record the single attachment.
+                *
+                * @since 3.5.0
                 */
                recordSelection: function() {
                        var selection = this.get('selection'),
        };
 
        /**
-        * A state for choosing an attachment from the media library.
+        * wp.media.controller.Library
         *
-        * @constructor
+        * A state for choosing an attachment or group of attachments from the media library.
+        *
+        * @class
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        * @mixes media.selectionSync
+        *
+        * @param {object}                          [attributes]                         The attributes hash passed to the state.
+        * @param {string}                          [attributes.id=library]              Unique identifier.
+        * @param {string}                          [attributes.title=Media library]     Title for the state. Displays in the media menu and the frame's title region.
+        * @param {wp.media.model.Attachments}      [attributes.library]                 The attachments collection to browse.
+        *                                                                               If one is not supplied, a collection of all attachments will be created.
+        * @param {wp.media.model.Selection|object} [attributes.selection]               A collection to contain attachment selections within the state.
+        *                                                                               If the 'selection' attribute is a plain JS object,
+        *                                                                               a Selection will be created using its values as the selection instance's `props` model.
+        *                                                                               Otherwise, it will copy the library's `props` model.
+        * @param {boolean}                         [attributes.multiple=false]          Whether multi-select is enabled.
+        * @param {string}                          [attributes.content=upload]          Initial mode for the content region.
+        *                                                                               Overridden by persistent user setting if 'contentUserSetting' is true.
+        * @param {string}                          [attributes.menu=default]            Initial mode for the menu region.
+        * @param {string}                          [attributes.router=browse]           Initial mode for the router region.
+        * @param {string}                          [attributes.toolbar=select]          Initial mode for the toolbar region.
+        * @param {boolean}                         [attributes.searchable=true]         Whether the library is searchable.
+        * @param {boolean|string}                  [attributes.filterable=false]        Whether the library is filterable, and if so what filters should be shown.
+        *                                                                               Accepts 'all', 'uploaded', or 'unattached'.
+        * @param {boolean}                         [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                         [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
+        * @param {boolean}                         [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
+        * @param {boolean}                         [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
+        * @param {boolean}                         [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
         */
        media.controller.Library = media.controller.State.extend({
                defaults: {
                        id:                 'library',
                        title:              l10n.mediaLibraryTitle,
-                       // Selection defaults. @see media.model.Selection
                        multiple:           false,
-                       // Initial region modes.
                        content:            'upload',
                        menu:               'default',
                        router:             'browse',
                        toolbar:            'select',
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
                        searchable:         true,
                        filterable:         false,
                        sortable:           true,
-
                        autoSelect:         true,
                        describe:           false,
-                       // Uses a user setting to override the content mode.
                        contentUserSetting: true,
-                       // Sync the selection from the last state when 'multiple' matches.
                        syncSelection:      true
                },
 
                /**
                 * If a library isn't provided, query all media items.
                 * If a selection instance isn't provided, create one.
+                *
+                * @since 3.5.0
                 */
                initialize: function() {
                        var selection = this.get('selection'),
                                        props = _.omit( props, 'orderby', 'query' );
                                }
 
-                               // If the `selection` attribute is set to an object,
-                               // it will use those values as the selection instance's
-                               // `props` model. Otherwise, it will copy the library's
-                               // `props` model.
                                this.set( 'selection', new media.model.Selection( null, {
                                        multiple: this.get('multiple'),
                                        props: props
                        this.resetDisplays();
                },
 
+               /**
+                * @since 3.5.0
+                */
                activate: function() {
                        this.syncSelection();
 
                        }
                },
 
+               /**
+                * @since 3.5.0
+                */
                deactivate: function() {
                        this.recordSelection();
 
                        wp.Uploader.queue.off( null, null, this );
                },
 
+               /**
+                * Reset the library to its initial state.
+                *
+                * @since 3.5.0
+                */
                reset: function() {
                        this.get('selection').reset();
                        this.resetDisplays();
                        this.refreshContent();
                },
 
+               /**
+                * Reset the attachment display settings defaults to the site options.
+                *
+                * If site options don't define them, fall back to a persistent user setting.
+                *
+                * @since 3.5.0
+                */
                resetDisplays: function() {
                        var defaultProps = media.view.settings.defaultProps;
                        this._displays = [];
                },
 
                /**
+                * Create a model to represent display settings (alignment, etc.) for an attachment.
+                *
+                * @since 3.5.0
+                *
                 * @param {wp.media.model.Attachment} attachment
                 * @returns {Backbone.Model}
                 */
                },
 
                /**
+                * Given an attachment, create attachment display settings properties.
+                *
+                * @since 3.6.0
+                *
                 * @param {wp.media.model.Attachment} attachment
                 * @returns {Object}
                 */
                },
 
                /**
+                * Whether an attachment can be embedded (audio or video).
+                *
+                * @since 3.6.0
+                *
                 * @param {wp.media.model.Attachment} attachment
                 * @returns {Boolean}
                 */
                 * If the state is active, no items are selected, and the current
                 * content mode is not an option in the state's router (provided
                 * the state has a router), reset the content mode to the default.
+                *
+                * @since 3.5.0
                 */
                refreshContent: function() {
                        var selection = this.get('selection'),
                },
 
                /**
-                * If the uploader was selected, navigate to the browser.
+                * Callback handler when an attachment is uploaded.
+                *
+                * Switch to the Media Library if uploaded from the 'Upload Files' tab.
+                *
+                * Adds any uploading attachments to the selection.
                 *
-                * Automatically select any uploading attachments.
+                * If the state only supports one attachment to be selected and multiple
+                * attachments are uploaded, the last attachment in the upload queue will
+                * be selected.
                 *
-                * Selections that don't support multiple attachments automatically
-                * limit themselves to one attachment (in this case, the last
-                * attachment in the upload queue).
+                * @since 3.5.0
                 *
                 * @param {wp.media.model.Attachment} attachment
                 */
                },
 
                /**
-                * Only track the browse router on library states.
+                * Persist the mode of the content region as a user setting.
+                *
+                * @since 3.5.0
                 */
                saveContentMode: function() {
                        if ( 'browse' !== this.get('router') ) {
                }
        });
 
+       // Make selectionSync available on any Media Library state.
        _.extend( media.controller.Library.prototype, media.selectionSync );
 
        /**
-        * A state for editing the settings of an image within a content editor.
+        * wp.media.controller.ImageDetails
+        *
+        * A state for editing the attachment display settings of an image that's been
+        * inserted into the editor.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                    [attributes]                       The attributes hash passed to the state.
+        * @param {string}                    [attributes.id=image-details]      Unique identifier.
+        * @param {string}                    [attributes.title=Image Details]   Title for the state. Displays in the frame's title region.
+        * @param {wp.media.model.Attachment} attributes.image                   The image's model.
+        * @param {string|false}              [attributes.content=image-details] Initial mode for the content region.
+        * @param {string|false}              [attributes.menu=false]            Initial mode for the menu region.
+        * @param {string|false}              [attributes.router=false]          Initial mode for the router region.
+        * @param {string|false}              [attributes.toolbar=image-details] Initial mode for the toolbar region.
+        * @param {boolean}                   [attributes.editing=false]         Unused.
+        * @param {int}                       [attributes.priority=60]           Unused.
+        *
+        * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
+        *       however this may not do anything.
         */
        media.controller.ImageDetails = media.controller.State.extend({
                defaults: _.defaults({
                        id:       'image-details',
                        title:    l10n.imageDetailsTitle,
-                       // Initial region modes.
                        content:  'image-details',
                        menu:     false,
                        router:   false,
                        toolbar:  'image-details',
-
                        editing:  false,
                        priority: 60
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.9.0
+                *
+                * @param options Attributes
+                */
                initialize: function( options ) {
                        this.image = options.image;
                        media.controller.State.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
                        this.frame.modal.$el.addClass('image-details');
                }
        });
 
        /**
+        * wp.media.controller.GalleryEdit
+        *
         * A state for editing a gallery's images and settings.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                       The attributes hash passed to the state.
+        * @param {string}                     [attributes.id=gallery-edit]       Unique identifier.
+        * @param {string}                     [attributes.title=Edit Gallery]    Title for the state. Displays in the frame's title region.
+        * @param {wp.media.model.Attachments} [attributes.library]               The collection of attachments in the gallery.
+        *                                                                        If one is not supplied, an empty media.model.Selection collection is created.
+        * @param {boolean}                    [attributes.multiple=false]        Whether multi-select is enabled.
+        * @param {boolean}                    [attributes.searchable=false]      Whether the library is searchable.
+        * @param {boolean}                    [attributes.sortable=true]         Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {string|false}               [attributes.content=browse]        Initial mode for the content region.
+        * @param {string|false}               [attributes.toolbar=image-details] Initial mode for the toolbar region.
+        * @param {boolean}                    [attributes.describe=true]         Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
+        * @param {boolean}                    [attributes.displaySettings=true]  Whether to show the attachment display settings interface.
+        * @param {boolean}                    [attributes.dragInfo=true]         Whether to show instructional text about the attachments being sortable.
+        * @param {int}                        [attributes.idealColumnWidth=170]  The ideal column width in pixels for attachments.
+        * @param {boolean}                    [attributes.editing=false]         Whether the gallery is being created, or editing an existing instance.
+        * @param {int}                        [attributes.priority=60]           The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.syncSelection=false]   Whether the Attachments selection should be persisted from the last state.
+        *                                                                        Defaults to false for this state, because the library passed in  *is* the selection.
+        * @param {view}                       [attributes.AttachmentView]        The single `Attachment` view to be used in the `Attachments`.
+        *                                                                        If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
         */
        media.controller.GalleryEdit = media.controller.Library.extend({
                defaults: {
-                       id:              'gallery-edit',
-                       title:           l10n.editGalleryTitle,
-                       // Selection defaults. @see media.model.Selection
-                       multiple:        false,
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
-                       searchable:      false,
-                       sortable:        true,
-                       display:         false,
-                       // Initial region modes.
-                       content:         'browse',
-                       toolbar:         'gallery-edit',
-
+                       id:               'gallery-edit',
+                       title:            l10n.editGalleryTitle,
+                       multiple:         false,
+                       searchable:       false,
+                       sortable:         true,
+                       display:          false,
+                       content:          'browse',
+                       toolbar:          'gallery-edit',
                        describe:         true,
                        displaySettings:  true,
                        dragInfo:         true,
                        idealColumnWidth: 170,
                        editing:          false,
                        priority:         60,
-
-                       // Don't sync the selection, as the Edit Gallery library
-                       // *is* the selection.
-                       syncSelection: false
+                       syncSelection:    false
                },
 
+               /**
+                * @since 3.5.0
+                */
                initialize: function() {
                        // If we haven't been provided a `library`, create a `Selection`.
                        if ( ! this.get('library') )
                        media.controller.Library.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                */
                activate: function() {
                        var library = this.get('library');
 
                        media.controller.Library.prototype.activate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                */
                deactivate: function() {
                        // Stop watching for uploaded attachments.
                        this.get('library').unobserve( wp.Uploader.queue );
                        media.controller.Library.prototype.deactivate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                *
+                * @param browser
+                */
                gallerySettings: function( browser ) {
                        if ( ! this.get('displaySettings') ) {
                                return;
        });
 
        /**
-        * A state for adding an image to a gallery.
+        * A state for selecting more images to add to a gallery.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                         The attributes hash passed to the state.
+        * @param {string}                     [attributes.id=gallery-library]      Unique identifier.
+        * @param {string}                     [attributes.title=Add to Gallery]    Title for the state. Displays in the frame's title region.
+        * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
+        * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
+        *                                                                          If one is not supplied, a collection of all images will be created.
+        * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
+        *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
+        * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
+        * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
+        *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
+        * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
+        * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
+        * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
+        * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
+        * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
+        * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
+        *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
         */
        media.controller.GalleryAdd = media.controller.Library.extend({
                defaults: _.defaults({
                        id:            'gallery-library',
                        title:         l10n.addToGalleryTitle,
-                       // Selection defaults. @see media.model.Selection
                        multiple:      'add',
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
                        filterable:    'uploaded',
-                       // Initial region modes.
                        menu:          'gallery',
                        toolbar:       'gallery-add',
-
                        priority:      100,
-                       // Don't sync the selection, as the Edit Gallery library
-                       // *is* the selection.
                        syncSelection: false
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.5.0
+                */
                initialize: function() {
-                       // If we haven't been provided a `library`, create a `Selection`.
+                       // If a library wasn't supplied, create a library of images.
                        if ( ! this.get('library') )
                                this.set( 'library', media.query({ type: 'image' }) );
 
                        media.controller.Library.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                */
                activate: function() {
                        var library = this.get('library'),
                                edit    = this.frame.state('gallery-edit').get('library');
        /**
         * wp.media.controller.CollectionEdit
         *
-        * @constructor
+        * A state for editing a collection, which is used by audio and video playlists,
+        * and can be used for other collections.
+        *
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                      The attributes hash passed to the state.
+        * @param {string}                     attributes.title                  Title for the state. Displays in the media menu and the frame's title region.
+        * @param {wp.media.model.Attachments} [attributes.library]              The attachments collection to edit.
+        *                                                                       If one is not supplied, an empty media.model.Selection collection is created.
+        * @param {boolean}                    [attributes.multiple=false]       Whether multi-select is enabled.
+        * @param {string}                     [attributes.content=browse]       Initial mode for the content region.
+        * @param {string}                     attributes.menu                   Initial mode for the menu region. @todo this needs a better explanation.
+        * @param {boolean}                    [attributes.searchable=false]     Whether the library is searchable.
+        * @param {boolean}                    [attributes.sortable=true]        Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                    [attributes.describe=true]        Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
+        * @param {boolean}                    [attributes.dragInfo=true]        Whether to show instructional text about the attachments being sortable.
+        * @param {boolean}                    [attributes.dragInfoText]         Instructional text about the attachments being sortable.
+        * @param {int}                        [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
+        * @param {boolean}                    [attributes.editing=false]        Whether the gallery is being created, or editing an existing instance.
+        * @param {int}                        [attributes.priority=60]          The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.syncSelection=false]  Whether the Attachments selection should be persisted from the last state.
+        *                                                                       Defaults to false for this state, because the library passed in  *is* the selection.
+        * @param {view}                       [attributes.SettingsView]         The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
+        * @param {view}                       [attributes.AttachmentView]       The single `Attachment` view to be used in the `Attachments`.
+        *                                                                       If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
+        * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
+        * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
         */
        media.controller.CollectionEdit = media.controller.Library.extend({
                defaults: {
-                       // Selection defaults. @see media.model.Selection
-                       multiple:     false,
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
-                       sortable:     true,
-                       searchable:   false,
-                       // Region mode defaults.
-                       content:      'browse',
-
+                       multiple:         false,
+                       sortable:         true,
+                       searchable:       false,
+                       content:          'browse',
                        describe:         true,
                        dragInfo:         true,
                        idealColumnWidth: 170,
                        editing:          false,
                        priority:         60,
                        SettingsView:     false,
-
-                       // Don't sync the selection, as the Edit {Collection} library
-                       // *is* the selection.
-                       syncSelection: false
+                       syncSelection:    false
                },
 
+               /**
+                * @since 3.9.0
+                */
                initialize: function() {
                        var collectionType = this.get('collectionType');
 
                        media.controller.Library.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
                        var library = this.get('library');
 
                        media.controller.Library.prototype.activate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                deactivate: function() {
                        // Stop watching for uploaded attachments.
                        this.get('library').unobserve( wp.Uploader.queue );
                        media.controller.Library.prototype.deactivate.apply( this, arguments );
                },
 
-               renderSettings: function( browser ) {
+               /**
+                * Render the collection embed settings view in the browser sidebar.
+                *
+                * @todo This is against the pattern elsewhere in media. Typically the frame
+                *       is responsible for adding region mode callbacks. Explain.
+                *
+                * @since 3.9.0
+                *
+                * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
+                */
+               renderSettings: function( attachmentsBrowserView ) {
                        var library = this.get('library'),
                                collectionType = this.get('collectionType'),
                                dragInfoText = this.get('dragInfoText'),
                                SettingsView = this.get('SettingsView'),
                                obj = {};
 
-                       if ( ! library || ! browser ) {
+                       if ( ! library || ! attachmentsBrowserView ) {
                                return;
                        }
 
                                priority:   40
                        });
 
-                       browser.sidebar.set( obj );
+                       attachmentsBrowserView.sidebar.set( obj );
 
                        if ( dragInfoText ) {
-                               browser.toolbar.set( 'dragInfo', new media.View({
+                               attachmentsBrowserView.toolbar.set( 'dragInfo', new media.View({
                                        el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
                                        priority: -40
                                }) );
                        }
 
-                       browser.toolbar.set( 'reverse', {
+                       // Add the 'Reverse order' button to the toolbar.
+                       attachmentsBrowserView.toolbar.set( 'reverse', {
                                text:     l10n.reverseOrder,
                                priority: 80,
 
        /**
         * wp.media.controller.CollectionAdd
         *
-        * @constructor
+        * A state for adding attachments to a collection (e.g. video playlist).
+        *
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                         The attributes hash passed to the state.
+        * @param {string}                     [attributes.id=library]      Unique identifier.
+        * @param {string}                     attributes.title                    Title for the state. Displays in the frame's title region.
+        * @param {boolean}                    [attributes.multiple=add]            Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
+        * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
+        *                                                                          If one is not supplied, a collection of attachments of the specified type will be created.
+        * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
+        *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
+        * @param {string}                     [attributes.menu=gallery]            Initial mode for the menu region.
+        * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
+        *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
+        * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
+        * @param {string}                     [attributes.toolbar=gallery-add]     Initial mode for the toolbar region.
+        * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
+        * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
+        * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
+        * @param {int}                        [attributes.priority=100]            The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.syncSelection=false]     Whether the Attachments selection should be persisted from the last state.
+        *                                                                          Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
+        * @param {string}                     attributes.type                   The collection's media type. (e.g. 'video').
+        * @param {string}                     attributes.collectionType         The collection type. (e.g. 'playlist').
         */
        media.controller.CollectionAdd = media.controller.Library.extend({
                defaults: _.defaults( {
                        syncSelection: false
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.9.0
+                */
                initialize: function() {
                        var collectionType = this.get('collectionType');
 
                        media.controller.Library.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
                        var library = this.get('library'),
                                editLibrary = this.get('editLibrary'),
        });
 
        /**
+        * wp.media.controller.FeaturedImage
+        *
         * A state for selecting a featured image for a post.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                          The attributes hash passed to the state.
+        * @param {string}                     [attributes.id=featured-image]        Unique identifier.
+        * @param {string}                     [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
+        * @param {wp.media.model.Attachments} [attributes.library]                  The attachments collection to browse.
+        *                                                                           If one is not supplied, a collection of all images will be created.
+        * @param {boolean}                    [attributes.multiple=false]           Whether multi-select is enabled.
+        * @param {string}                     [attributes.content=upload]           Initial mode for the content region.
+        *                                                                           Overridden by persistent user setting if 'contentUserSetting' is true.
+        * @param {string}                     [attributes.menu=default]             Initial mode for the menu region.
+        * @param {string}                     [attributes.router=browse]            Initial mode for the router region.
+        * @param {string}                     [attributes.toolbar=featured-image]   Initial mode for the toolbar region.
+        * @param {int}                        [attributes.priority=60]              The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.searchable=true]          Whether the library is searchable.
+        * @param {boolean|string}             [attributes.filterable=false]         Whether the library is filterable, and if so what filters should be shown.
+        *                                                                           Accepts 'all', 'uploaded', or 'unattached'.
+        * @param {boolean}                    [attributes.sortable=true]            Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                    [attributes.autoSelect=true]          Whether an uploaded attachment should be automatically added to the selection.
+        * @param {boolean}                    [attributes.describe=false]           Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
+        * @param {boolean}                    [attributes.contentUserSetting=true]  Whether the content region's mode should be set and persisted per user.
+        * @param {boolean}                    [attributes.syncSelection=true]       Whether the Attachments selection should be persisted from the last state.
         */
        media.controller.FeaturedImage = media.controller.Library.extend({
                defaults: _.defaults({
                        id:            'featured-image',
                        title:         l10n.setFeaturedImageTitle,
-                       // Selection defaults. @see media.model.Selection
                        multiple:      false,
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
                        filterable:    'uploaded',
-                       // Region mode defaults.
                        toolbar:       'featured-image',
-
                        priority:      60,
                        syncSelection: true
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.5.0
+                */
                initialize: function() {
                        var library, comparator;
 
                        library.observe( this.get('selection') );
                },
 
+               /**
+                * @since 3.5.0
+                */
                activate: function() {
                        this.updateSelection();
                        this.frame.on( 'open', this.updateSelection, this );
                        media.controller.Library.prototype.activate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                */
                deactivate: function() {
                        this.frame.off( 'open', this.updateSelection, this );
 
                        media.controller.Library.prototype.deactivate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.5.0
+                */
                updateSelection: function() {
                        var selection = this.get('selection'),
                                id = media.view.settings.post.featuredImageId,
        });
 
        /**
+        * wp.media.controller.ReplaceImage
+        *
         * A state for replacing an image.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                     [attributes]                         The attributes hash passed to the state.
+        * @param {string}                     [attributes.id=replace-image]        Unique identifier.
+        * @param {string}                     [attributes.title=Replace Image]     Title for the state. Displays in the media menu and the frame's title region.
+        * @param {wp.media.model.Attachments} [attributes.library]                 The attachments collection to browse.
+        *                                                                          If one is not supplied, a collection of all images will be created.
+        * @param {boolean}                    [attributes.multiple=false]          Whether multi-select is enabled.
+        * @param {string}                     [attributes.content=upload]          Initial mode for the content region.
+        *                                                                          Overridden by persistent user setting if 'contentUserSetting' is true.
+        * @param {string}                     [attributes.menu=default]            Initial mode for the menu region.
+        * @param {string}                     [attributes.router=browse]           Initial mode for the router region.
+        * @param {string}                     [attributes.toolbar=replace]         Initial mode for the toolbar region.
+        * @param {int}                        [attributes.priority=60]             The priority for the state link in the media menu.
+        * @param {boolean}                    [attributes.searchable=true]         Whether the library is searchable.
+        * @param {boolean|string}             [attributes.filterable=uploaded]     Whether the library is filterable, and if so what filters should be shown.
+        *                                                                          Accepts 'all', 'uploaded', or 'unattached'.
+        * @param {boolean}                    [attributes.sortable=true]           Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
+        * @param {boolean}                    [attributes.autoSelect=true]         Whether an uploaded attachment should be automatically added to the selection.
+        * @param {boolean}                    [attributes.describe=false]          Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
+        * @param {boolean}                    [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
+        * @param {boolean}                    [attributes.syncSelection=true]      Whether the Attachments selection should be persisted from the last state.
         */
        media.controller.ReplaceImage = media.controller.Library.extend({
                defaults: _.defaults({
                        id:            'replace-image',
                        title:         l10n.replaceImageTitle,
-                       // Selection defaults. @see media.model.Selection
                        multiple:      false,
-                       // Attachments browser defaults. @see media.view.AttachmentsBrowser
                        filterable:    'uploaded',
-                       // Region mode defaults.
                        toolbar:       'replace',
                        menu:          false,
-
                        priority:      60,
                        syncSelection: true
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.9.0
+                *
+                * @param options
+                */
                initialize: function( options ) {
                        var library, comparator;
 
                        library.observe( this.get('selection') );
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
                        this.updateSelection();
                        media.controller.Library.prototype.activate.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                updateSelection: function() {
                        var selection = this.get('selection'),
                                attachment = this.image.attachment;
        });
 
        /**
+        * wp.media.controller.EditImage
+        *
         * A state for editing (cropping, etc.) an image.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object}                    attributes                      The attributes hash passed to the state.
+        * @param {wp.media.model.Attachment} attributes.model                The attachment.
+        * @param {string}                    [attributes.id=edit-image]      Unique identifier.
+        * @param {string}                    [attributes.title=Edit Image]   Title for the state. Displays in the media menu and the frame's title region.
+        * @param {string}                    [attributes.content=edit-image] Initial mode for the content region.
+        * @param {string}                    [attributes.toolbar=edit-image] Initial mode for the toolbar region.
+        * @param {string}                    [attributes.menu=false]         Initial mode for the menu region.
+        * @param {string}                    [attributes.url]                Unused. @todo Consider removal.
         */
        media.controller.EditImage = media.controller.State.extend({
                defaults: {
                        id:      'edit-image',
                        title:   l10n.editImage,
-                       // Region mode defaults.
                        menu:    false,
                        toolbar: 'edit-image',
                        content: 'edit-image',
-
                        url:     ''
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
                        this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
                },
 
+               /**
+                * @since 3.9.0
+                */
                deactivate: function() {
                        this.stopListening( this.frame );
                },
 
+               /**
+                * @since 3.9.0
+                */
                toolbar: function() {
                        var frame = this.frame,
                                lastState = frame.lastState(),
        /**
         * wp.media.controller.MediaLibrary
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.Library
         * @augments wp.media.controller.State
         * @augments Backbone.Model
                        syncSelection:   false
                }, media.controller.Library.prototype.defaults ),
 
+               /**
+                * @since 3.9.0
+                *
+                * @param options
+                */
                initialize: function( options ) {
                        this.media = options.media;
                        this.type = options.type;
                        media.controller.Library.prototype.initialize.apply( this, arguments );
                },
 
+               /**
+                * @since 3.9.0
+                */
                activate: function() {
+                       // @todo this should use this.frame.
                        if ( media.frame.lastMime ) {
                                this.set( 'library', media.query({ type: media.frame.lastMime }) );
                                delete media.frame.lastMime;
        /**
         * wp.media.controller.Embed
         *
-        * @constructor
+        * A state for embedding media from a URL.
+        *
+        * @class
         * @augments wp.media.controller.State
         * @augments Backbone.Model
+        *
+        * @param {object} attributes                         The attributes hash passed to the state.
+        * @param {string} [attributes.id=embed]              Unique identifier.
+        * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
+        * @param {string} [attributes.content=embed]         Initial mode for the content region.
+        * @param {string} [attributes.menu=default]          Initial mode for the menu region.
+        * @param {string} [attributes.toolbar=main-embed]    Initial mode for the toolbar region.
+        * @param {string} [attributes.menu=false]            Initial mode for the menu region.
+        * @param {int}    [attributes.priority=120]          The priority for the state link in the media menu.
+        * @param {string} [attributes.type=link]             The type of embed. Currently only link is supported.
+        * @param {string} [attributes.url]                   The embed URL.
+        * @param {object} [attributes.metadata={}]           Properties of the embed, which will override attributes.url if set.
         */
        media.controller.Embed = media.controller.State.extend({
                defaults: {
                        id:       'embed',
                        title:    l10n.insertFromUrlTitle,
-                       // Region mode defaults.
                        content:  'embed',
                        menu:     'default',
                        toolbar:  'main-embed',
-
                        priority: 120,
                        type:     'link',
                        url:      '',
                },
 
                /**
+                * Trigger a scan of the embedded URL's content for metadata required to embed.
+                *
                 * @fires wp.media.controller.Embed#scan
                 */
                scan: function() {
                        this.set( attributes );
                },
                /**
+                * Try scanning the embed as an image to discover its dimensions.
+                *
                 * @param {Object} attributes
                 */
                scanImage: function( attributes ) {
        /**
         * wp.media.controller.Cropper
         *
-        * Allows for a cropping step.
+        * A state for cropping an image.
         *
-        * @constructor
+        * @class
         * @augments wp.media.controller.State
         * @augments Backbone.Model
         */
                }
        });
 
-       /**
-        * ========================================================================
-        * VIEWS
-        * ========================================================================
-        */
-
        /**
         * wp.media.View
-        * -------------
         *
-        * The base view class.
+        * The base view class for media.
         *
         * Undelegating events, removing events from the model, and
         * removing events from the controller mirror the code for
         * This behavior has since been removed, and should not be used
         * outside of the media manager.
         *
-        * @constructor
+        * @class
         * @augments wp.Backbone.View
         * @augments Backbone.View
         */
                        wp.Backbone.View.apply( this, arguments );
                },
                /**
+                * @todo The internal comment mentions this might have been a stop-gap
+                *       before Backbone 0.9.8 came out. Figure out if Backbone core takes
+                *       care of this in Backbone.View now.
+                *
                 * @returns {wp.media.View} Returns itself to allow chaining
                 */
                dispose: function() {
         * wp.media.view.Frame
         *
         * A frame is a composite view consisting of one or more regions and one or more
-        * states. Only one state can be active at any given moment.
+        * states.
         *
-        * @constructor
+        * @see wp.media.controller.State
+        * @see wp.media.controller.Region
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                        }, this );
                },
                /**
+                * Create the frame's states.
+                *
+                * @see wp.media.controller.State
+                * @see wp.media.controller.StateMachine
+                *
                 * @fires wp.media.controller.State#ready
                 */
                _createStates: function() {
                                this.states.add( this.options.states );
                        }
                },
+
+               /**
+                * A frame can be in a mode or multiple modes at one time.
+                *
+                * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
+                */
                _createModes: function() {
                        // Store active "modes" that the frame is in. Unrelated to region modes.
                        this.activeModes = new Backbone.Collection();
                        }, this );
                },
                /**
+                * Reset all states on the frame to their defaults.
+                *
                 * @returns {wp.media.view.Frame} Returns itself to allow chaining
                 */
                reset: function() {
        /**
         * wp.media.view.MediaFrame
         *
-        * Type of frame used to create the media modal.
+        * The frame used to create the media modal.
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Frame
         * @augments wp.media.View
         * @augments wp.Backbone.View
                        }, this );
 
                        this.on( 'content:create:iframe', this.iframeContent, this );
+                       this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
                        this.on( 'menu:render:default', this.iframeMenu, this );
                        this.on( 'open', this.hijackThickbox, this );
                        this.on( 'close', this.restoreThickbox, this );
                        });
                },
 
+               iframeContentCleanup: function() {
+                       this.$el.removeClass('hide-toolbar');
+               },
+
                iframeMenu: function( view ) {
                        var views = {};
 
        /**
         * wp.media.view.MediaFrame.Select
         *
-        * Type of media frame that is used to select an item or items from the media library
+        * A frame for selecting an item or items from the media library.
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.MediaFrame
         * @augments wp.media.view.Frame
         * @augments wp.media.View
         */
        media.view.MediaFrame.Select = media.view.MediaFrame.extend({
                initialize: function() {
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.MediaFrame.prototype.initialize.apply( this, arguments );
 
                        _.defaults( this.options, {
                                sortable:   state.get('sortable'),
                                search:     state.get('searchable'),
                                filters:    state.get('filterable'),
+                               date:       state.get('date'),
                                display:    state.has('display') ? state.get('display') : state.get('displaySettings'),
                                dragInfo:   state.get('dragInfo'),
 
        /**
         * wp.media.view.MediaFrame.Post
         *
-        * @constructor
+        * The frame for manipulating media on the Edit Post page.
+        *
+        * @class
         * @augments wp.media.view.MediaFrame.Select
         * @augments wp.media.view.MediaFrame
         * @augments wp.media.view.Frame
                                state:    'insert',
                                metadata:  {}
                        });
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+
+                       // Call 'initialize' directly on the parent class.
                        media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments );
                        this.createIframeStates();
 
                },
 
+               /**
+                * Create the default states.
+                */
                createStates: function() {
                        var options = this.options;
 
-                       // Add the default states.
                        this.states.add([
                                // Main states.
                                new media.controller.Library({
 
                        // Browse our library of attachments.
                        this.content.set( view );
+
+                       // Trigger the controller to set focus
+                       this.trigger( 'edit:selection', this );
                },
 
                editImageContent: function() {
        /**
         * wp.media.view.MediaFrame.ImageDetails
         *
-        * @constructor
+        * A media frame for manipulating an image that's already been inserted
+        * into a post.
+        *
+        * @class
         * @augments wp.media.view.MediaFrame.Select
         * @augments wp.media.view.MediaFrame
         * @augments wp.media.view.Frame
        /**
         * wp.media.view.Modal
         *
-        * @constructor
+        * A modal view, which the media modal uses as its default container.
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.FocusManager
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.UploaderWindow
         *
-        * @constructor
+        * An uploader window that allows for dragging and dropping media.
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
+        *
+        * @param {object} [options]                   Options hash passed to the view.
+        * @param {object} [options.uploader]          Uploader properties.
+        * @param {jQuery} [options.uploader.browser]
+        * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
+        * @param {object} [options.uploader.params]
         */
        media.view.UploaderWindow = media.View.extend({
                tagName:   'div',
        });
 
        /**
+        * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap
+        * or #wp-fullscreen-body) and relays drag'n'dropped files to a media workflow.
+        *
         * wp.media.view.EditorUploader
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                overDropzone: false,
                draggingFile: null,
 
+               /**
+                * Bind drag'n'drop events to callbacks.
+                */
                initialize: function() {
                        var self = this;
 
                        return this;
                },
 
+               /**
+                * Check browser support for drag'n'drop.
+                *
+                * @return Boolean
+                */
                browserSupport: function() {
                        var supports = false, div = document.createElement('div');
 
                        return this;
                },
 
+               /**
+                * When a file is dropped on the editor uploader, open up an editor media workflow
+                * and upload the file immediately.
+                *
+                * @param  {jQuery.Event} event The 'drop' event.
+                */
                drop: function( event ) {
-                       var $wrap = null;
+                       var $wrap = null, uploadView;
 
                        this.containerDragleave( event );
                        this.dropzoneDragleave( event );
                                        title:    wp.media.view.l10n.addMedia,
                                        multiple: true
                                });
-                               this.workflow.on( 'uploader:ready', this.addFiles, this );
+                               uploadView = this.workflow.uploader;
+                               if ( uploadView.uploader && uploadView.uploader.ready ) {
+                                       this.addFiles.apply( this );
+                               } else {
+                                       this.workflow.on( 'uploader:ready', this.addFiles, this );
+                               }
                        } else {
                                this.workflow.state().reset();
                                this.addFiles.apply( this );
                        return false;
                },
 
+               /**
+                * Add the files to the uploader.
+                */
                addFiles: function() {
                        if ( this.files.length ) {
                                this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
        /**
         * wp.media.view.UploaderInline
         *
-        * @constructor
+        * The inline uploader that shows up in the 'Upload Files' tab.
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.UploaderStatus
         *
-        * @constructor
+        * An uploader status for on-going uploads.
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.UploaderStatusError
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Toolbar
         *
-        * @constructor
+        * A toolbar which consists of a primary and a secondary section. Each sections
+        * can be filled with views.
+        *
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Toolbar.Select
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Toolbar
         * @augments wp.media.View
         * @augments wp.Backbone.View
                                        requires: options.requires
                                }
                        });
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.Toolbar.prototype.initialize.apply( this, arguments );
                },
 
        /**
         * wp.media.view.Toolbar.Embed
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Toolbar.Select
         * @augments wp.media.view.Toolbar
         * @augments wp.media.View
                                text: l10n.insertIntoPost,
                                requires: false
                        });
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.Toolbar.Select.prototype.initialize.apply( this, arguments );
                },
 
        /**
         * wp.media.view.Button
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.ButtonGroup
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.PriorityList
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.MenuItem
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Menu
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.PriorityList
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.RouterItem
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.MenuItem
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Router
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Menu
         * @augments wp.media.view.PriorityList
         * @augments wp.media.View
 
                initialize: function() {
                        this.controller.on( 'content:render', this.update, this );
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.Menu.prototype.initialize.apply( this, arguments );
                },
 
        /**
         * wp.media.view.Sidebar
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.PriorityList
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Attachment
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                        'click .close':                   'removeFromLibrary',
                        'click .check':                   'checkClickHandler',
                        'click a':                        'preventDefault',
+                       'keydown .close':                 'removeFromLibrary',
                        'keydown':                        'toggleSelectionHandler'
                },
 
                                this.model.on( 'selection:single selection:unsingle', this.details, this );
                                this.details( this.model, this.controller.state().get('selection') );
                        }
+
+                       this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
                },
                /**
                 * @returns {wp.media.view.Attachment} Returns itself to allow chaining
                                return;
                        }
 
+                       event.preventDefault();
+
                        // In the grid view, bubble up an edit:attachment event to the controller.
                        if ( this.controller.isModeActive( 'grid' ) ) {
                                if ( this.controller.isModeActive( 'edit' ) ) {
                                        // Pass the current target to restore focus when closing
                                        this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
-
-                                       // Don't scroll the view and don't attempt to submit anything.
-                                       event.stopPropagation();
                                        return;
                                }
 
                        });
 
                        this.controller.trigger( 'selection:toggle' );
-
-                       // Don't scroll the view and don't attempt to submit anything.
-                       event.stopPropagation();
                },
                /**
                 * @param {Object} options
                 * @param {Object} event
                 */
                removeFromLibrary: function( event ) {
+                       // Catch enter and space events
+                       if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
+                               return;
+                       }
+
                        // Stop propagation so the model isn't selected.
                        event.stopPropagation();
 
        /**
         * wp.media.view.Attachment.Library
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachment
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Attachment.EditLibrary
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachment
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Attachments
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                                // If the `collection` has a `comparator`, disable sorting.
                                disabled: !! collection.comparator,
 
-                               // Prevent attachments from being dragged outside the bounding
-                               // box of the list.
-                               containment: this.$el,
-
                                // Change the position of the attachment as soon as the
                                // mouse pointer overlaps a thumbnail.
                                tolerance: 'pointer',
        /**
         * wp.media.view.Search
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.AttachmentFilters
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                },
 
                /**
-                * When the selection changes, set the Query properties
-                * accordingly for the selected filter.
+                * When the selected filter changes, update the Attachment Query properties to match.
                 */
                change: function() {
                        var filter = this.filters[ this.el.value ];
                }
        });
 
+       /**
+        * A filter dropdown for month/dates.
+        *
+        * @class
+        * @augments wp.media.view.AttachmentFilters
+        * @augments wp.media.View
+        * @augments wp.Backbone.View
+        * @augments Backbone.View
+        */
+       media.view.DateFilter = media.view.AttachmentFilters.extend({
+               id: 'media-attachment-date-filters',
+
+               createFilters: function() {
+                       var filters = {};
+                       _.each( media.view.settings.months || {}, function( value, index ) {
+                               filters[ index ] = {
+                                       text: value.text,
+                                       props: {
+                                               year: value.year,
+                                               monthnum: value.month
+                                       }
+                               };
+                       });
+                       filters.all = {
+                               text:  l10n.allDates,
+                               props: {
+                                       monthnum: false,
+                                       year:  false
+                               },
+                               priority: 10
+                       };
+                       this.filters = filters;
+               }
+       });
+
        /**
         * wp.media.view.AttachmentFilters.Uploaded
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.AttachmentFilters
         * @augments wp.media.View
         * @augments wp.Backbone.View
                                                order:   'ASC'
                                        },
                                        priority: 20
+                               },
+
+                               unattached: {
+                                       text:  l10n.unattached,
+                                       props: {
+                                               uploadedTo: 0,
+                                               orderby: 'menuOrder',
+                                               order:   'ASC'
+                                       },
+                                       priority: 50
                                }
                        };
                }
        /**
         * wp.media.view.AttachmentFilters.All
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.AttachmentFilters
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.AttachmentsBrowser
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
+        *
+        * @param {object}      options
+        * @param {object}      [options.filters=false] Which filters to show in the browser's toolbar.
+        *                                              Accepts 'uploaded' and 'all'.
+        * @param {object}      [options.search=true]   Whether to show the search interface in the
+        *                                              browser's toolbar.
+        * @param {object}      [options.date=true]     Whether to show the date filter in the
+        *                                              browser's toolbar.
+        * @param {object}      [options.display=false] Whether to show the attachments display settings
+        *                                              view in the sidebar.
+        * @param {bool|string} [options.sidebar=true]  Whether to create a sidebar for the browser.
+        *                                              Accepts true, false, and 'errors'.
         */
        media.view.AttachmentsBrowser = media.View.extend({
                tagName:   'div',
                        _.defaults( this.options, {
                                filters: false,
                                search:  true,
+                               date:    true,
                                display: false,
                                sidebar: true,
                                AttachmentView: media.view.Attachment.Library
                        });
 
                        this.listenTo( this.controller, 'toggle:upload:attachment', _.bind( this.toggleUploader, this ) );
-
+                       this.controller.on( 'edit:selection', this.editSelection );
                        this.createToolbar();
                        if ( this.options.sidebar ) {
                                this.createSidebar();
 
                        this.collection.on( 'add remove reset', this.updateContent, this );
                },
+
+               editSelection: function( modal ) {
+                       modal.$( '.media-button-backToLibrary' ).focus();
+               },
+
                /**
                 * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining
                 */
                                                                changed.push( model.save() );
                                                                removed.push( model );
                                                        } else {
-                                                               model.destroy();
+                                                               model.destroy({wait: true});
                                                        }
                                                } );
 
                                                }
                                        }
                                }).render() );
+
+                               if ( media.view.settings.mediaTrash ) {
+                                       this.toolbar.set( 'deleteSelectedPermanentlyButton', new media.view.DeleteSelectedPermanentlyButton({
+                                               filters: Filters,
+                                               style: 'primary',
+                                               disabled: true,
+                                               text: l10n.deleteSelected,
+                                               controller: this.controller,
+                                               priority: -55,
+                                               click: function() {
+                                                       var removed = [], selection = this.controller.state().get( 'selection' );
+
+                                                       if ( ! selection.length || ! confirm( l10n.warnBulkDelete ) ) {
+                                                               return;
+                                                       }
+
+                                                       selection.each( function( model ) {
+                                                               if ( ! model.get( 'nonces' )['delete'] ) {
+                                                                       removed.push( model );
+                                                                       return;
+                                                               }
+
+                                                               model.destroy();
+                                                       } );
+
+                                                       selection.remove( removed );
+                                                       this.controller.trigger( 'selection:action:done' );
+                                               }
+                                       }).render() );
+                               }
+
+                       } else if ( this.options.date ) {
+                               // DateFilter is a <select>, screen reader text needs to be rendered before
+                               this.toolbar.set( 'dateFilterLabel', new media.view.Label({
+                                       value: l10n.filterByDate,
+                                       attributes: {
+                                               'for': 'media-attachment-date-filters'
+                                       },
+                                       priority: -75
+                               }).render() );
+                               this.toolbar.set( 'dateFilter', new media.view.DateFilter({
+                                       controller: this.controller,
+                                       model:      this.collection.props,
+                                       priority: -75
+                               }).render() );
                        }
 
                        if ( this.options.search ) {
        /**
         * wp.media.view.Selection
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Attachment.Selection
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachment
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Attachments.Selection
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachments
         * @augments wp.media.View
         * @augments wp.Backbone.View
                events: {},
                initialize: function() {
                        _.defaults( this.options, {
-                               sortable:   true,
+                               sortable:   false,
                                resize:     false,
 
                                // The single `Attachment` view to be used in the `Attachments` view.
                                AttachmentView: media.view.Attachment.Selection
                        });
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        return media.view.Attachments.prototype.initialize.apply( this, arguments );
                }
        });
        /**
         * wp.media.view.Attachments.EditSelection
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachment.Selection
         * @augments wp.media.view.Attachment
         * @augments wp.media.View
        /**
         * wp.media.view.Settings
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Settings.AttachmentDisplay
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings
         * @augments wp.media.View
         * @augments wp.Backbone.View
                        _.defaults( this.options, {
                                userSettings: false
                        });
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.Settings.prototype.initialize.apply( this, arguments );
                        this.model.on( 'change:link', this.updateLinkTo, this );
 
        /**
         * wp.media.view.Settings.Gallery
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Settings.Playlist
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.Attachment.Details
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Attachment
         * @augments wp.media.View
         * @augments wp.Backbone.View
                className: 'attachment-details',
                template:  media.template('attachment-details'),
 
+               attributes: function() {
+                       return {
+                               'tabIndex':     0,
+                               'data-id':      this.model.get( 'id' )
+                       };
+               },
+
                events: {
                        'change [data-setting]':          'updateSetting',
                        'change [data-setting] input':    'updateSetting',
                        });
 
                        this.on( 'ready', this.initialFocus );
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
+                       // Call 'initialize' directly on the parent class.
                        media.view.Attachment.prototype.initialize.apply( this, arguments );
                },
 
         *
         * A view to display fields added via the `attachment_fields_to_edit` filter.
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
                                data[ pair.name ] = pair.value;
                        });
 
-                       this.model.saveCompat( data );
+                       this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
+                       this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
+               },
+
+               postSave: function() {
+                       this.controller.trigger( 'attachment:compat:ready', ['ready'] );
                }
        });
 
        /**
         * wp.media.view.Iframe
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Embed
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        });
 
        /**
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.EmbedUrl
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.EmbedLink
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings
         * @augments wp.media.View
         * @augments wp.Backbone.View
        /**
         * wp.media.view.EmbedImage
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings.AttachmentDisplay
         * @augments wp.media.view.Settings
         * @augments wp.media.View
        /**
         * wp.media.view.ImageDetails
         *
-        * @constructor
+        * @class
         * @augments wp.media.view.Settings.AttachmentDisplay
         * @augments wp.media.view.Settings
         * @augments wp.media.View
         * wp.customize.HeaderControl.calculateImageSelectOptions via
         * wp.customize.HeaderControl.openMM.
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View
        /**
         * wp.media.view.Spinner
         *
-        * @constructor
+        * @class
         * @augments wp.media.View
         * @augments wp.Backbone.View
         * @augments Backbone.View