X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/874d2a2f468a0d1e69aab49b1fe2d9d79d3e1142..refs/tags/wordpress-4.1:/wp-includes/js/media-views.js diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index e334b207..ffe65f5c 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -71,19 +71,25 @@ 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' ) ); @@ -96,6 +102,8 @@ /** * Activate a mode. * + * @since 3.5.0 + * * @param {string} mode * * @fires this.view#{this.id}:activate:{this._mode} @@ -137,6 +145,8 @@ /** * Render a mode. * + * @since 3.5.0 + * * @param {string} mode * * @fires this.view#{this.id}:create:{this._mode} @@ -184,6 +194,8 @@ /** * Get the region's view. * + * @since 3.5.0 + * * @returns {wp.media.View} */ get: function() { @@ -193,6 +205,8 @@ /** * 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 @@ -207,6 +221,8 @@ /** * 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. */ @@ -234,7 +250,14 @@ /** * 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 @@ -242,6 +265,7 @@ * @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 ); }; @@ -259,6 +283,8 @@ * 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 @@ -282,6 +308,8 @@ * 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 @@ -313,6 +341,8 @@ * 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 */ @@ -342,13 +372,25 @@ * 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 ); @@ -365,35 +407,56 @@ 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 ); @@ -409,8 +472,10 @@ this._content(); this._router(); }, + /** * @access private + * @since 3.5.0 */ _deactivate: function() { this.active = false; @@ -422,20 +487,26 @@ 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, @@ -454,8 +525,10 @@ view.select( this.frame.content.mode() ); } }, + /** * @access private + * @since 3.5.0 */ _menu: function() { var menu = this.frame.menu, @@ -474,8 +547,10 @@ view.select( this.id ); } }, + /** * @access private + * @since 3.5.0 */ _updateMenu: function() { var previous = this.previous('menu'), @@ -489,8 +564,14 @@ 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'), @@ -525,7 +606,20 @@ }; }); + /** + * 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; @@ -553,6 +647,8 @@ * 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'), @@ -574,39 +670,62 @@ }; /** - * 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'), @@ -624,10 +743,6 @@ 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 @@ -637,6 +752,9 @@ this.resetDisplays(); }, + /** + * @since 3.5.0 + */ activate: function() { this.syncSelection(); @@ -650,6 +768,9 @@ } }, + /** + * @since 3.5.0 + */ deactivate: function() { this.recordSelection(); @@ -662,12 +783,24 @@ 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 = []; @@ -679,6 +812,10 @@ }, /** + * 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} */ @@ -692,6 +829,10 @@ }, /** + * Given an attachment, create attachment display settings properties. + * + * @since 3.6.0 + * * @param {wp.media.model.Attachment} attachment * @returns {Object} */ @@ -704,6 +845,10 @@ }, /** + * Whether an attachment can be embedded (audio or video). + * + * @since 3.6.0 + * * @param {wp.media.model.Attachment} attachment * @returns {Boolean} */ @@ -724,6 +869,8 @@ * 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'), @@ -737,13 +884,17 @@ }, /** - * 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 */ @@ -761,7 +912,9 @@ }, /** - * 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') ) { @@ -777,73 +930,116 @@ } }); + // 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') ) @@ -855,6 +1051,9 @@ media.controller.Library.prototype.initialize.apply( this, arguments ); }, + /** + * @since 3.5.0 + */ activate: function() { var library = this.get('library'); @@ -869,6 +1068,9 @@ 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 ); @@ -878,6 +1080,11 @@ media.controller.Library.prototype.deactivate.apply( this, arguments ); }, + /** + * @since 3.5.0 + * + * @param browser + */ gallerySettings: function( browser ) { if ( ! this.get('displaySettings') ) { return; @@ -911,39 +1118,60 @@ }); /** - * 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'); @@ -971,33 +1199,55 @@ /** * 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'); @@ -1019,6 +1269,9 @@ media.controller.Library.prototype.initialize.apply( this, arguments ); }, + /** + * @since 3.9.0 + */ activate: function() { var library = this.get('library'); @@ -1033,6 +1286,9 @@ 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 ); @@ -1042,14 +1298,24 @@ 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; } @@ -1061,16 +1327,17 @@ 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: $( '
' + dragInfoText + '
' )[0], priority: -40 }) ); } - browser.toolbar.set( 'reverse', { + // Add the 'Reverse order' button to the toolbar. + attachmentsBrowserView.toolbar.set( 'reverse', { text: l10n.reverseOrder, priority: 80, @@ -1084,10 +1351,35 @@ /** * 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( { @@ -1100,6 +1392,9 @@ syncSelection: false }, media.controller.Library.prototype.defaults ), + /** + * @since 3.9.0 + */ initialize: function() { var collectionType = this.get('collectionType'); @@ -1118,6 +1413,9 @@ media.controller.Library.prototype.initialize.apply( this, arguments ); }, + /** + * @since 3.9.0 + */ activate: function() { var library = this.get('library'), editLibrary = this.get('editLibrary'), @@ -1145,28 +1443,50 @@ }); /** + * 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; @@ -1200,6 +1520,9 @@ library.observe( this.get('selection') ); }, + /** + * @since 3.5.0 + */ activate: function() { this.updateSelection(); this.frame.on( 'open', this.updateSelection, this ); @@ -1207,12 +1530,18 @@ 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, @@ -1228,29 +1557,53 @@ }); /** + * 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; @@ -1285,11 +1638,17 @@ 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; @@ -1299,32 +1658,50 @@ }); /** + * 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(), @@ -1353,7 +1730,7 @@ /** * wp.media.controller.MediaLibrary * - * @constructor + * @class * @augments wp.media.controller.Library * @augments wp.media.controller.State * @augments Backbone.Model @@ -1368,6 +1745,11 @@ syncSelection: false }, media.controller.Library.prototype.defaults ), + /** + * @since 3.9.0 + * + * @param options + */ initialize: function( options ) { this.media = options.media; this.type = options.type; @@ -1376,7 +1758,11 @@ 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; @@ -1388,19 +1774,31 @@ /** * 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: '', @@ -1420,6 +1818,8 @@ }, /** + * Trigger a scan of the embedded URL's content for metadata required to embed. + * * @fires wp.media.controller.Embed#scan */ scan: function() { @@ -1452,6 +1852,8 @@ this.set( attributes ); }, /** + * Try scanning the embed as an image to discover its dimensions. + * * @param {Object} attributes */ scanImage: function( attributes ) { @@ -1501,9 +1903,9 @@ /** * 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 */ @@ -1604,17 +2006,10 @@ } }); - /** - * ======================================================================== - * 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 @@ -1623,7 +2018,7 @@ * 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 */ @@ -1635,6 +2030,10 @@ 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() { @@ -1674,9 +2073,12 @@ * 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 @@ -1706,6 +2108,11 @@ }, this ); }, /** + * Create the frame's states. + * + * @see wp.media.controller.State + * @see wp.media.controller.StateMachine + * * @fires wp.media.controller.State#ready */ _createStates: function() { @@ -1724,6 +2131,12 @@ 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(); @@ -1734,6 +2147,8 @@ }, this ); }, /** + * Reset all states on the frame to their defaults. + * * @returns {wp.media.view.Frame} Returns itself to allow chaining */ reset: function() { @@ -1820,9 +2235,9 @@ /** * 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 @@ -1980,6 +2395,7 @@ }, 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 ); @@ -1996,6 +2412,10 @@ }); }, + iframeContentCleanup: function() { + this.$el.removeClass('hide-toolbar'); + }, + iframeMenu: function( view ) { var views = {}; @@ -2055,9 +2475,9 @@ /** * 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 @@ -2067,9 +2487,7 @@ */ 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, { @@ -2180,6 +2598,7 @@ 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'), @@ -2219,7 +2638,9 @@ /** * 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 @@ -2247,18 +2668,19 @@ 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({ @@ -2600,6 +3022,9 @@ // Browse our library of attachments. this.content.set( view ); + + // Trigger the controller to set focus + this.trigger( 'edit:selection', this ); }, editImageContent: function() { @@ -2939,7 +3364,10 @@ /** * 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 @@ -3106,7 +3534,9 @@ /** * 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 @@ -3311,7 +3741,7 @@ /** * wp.media.view.FocusManager * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -3353,10 +3783,18 @@ /** * 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', @@ -3448,9 +3886,12 @@ }); /** + * 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 @@ -3465,6 +3906,9 @@ overDropzone: false, draggingFile: null, + /** + * Bind drag'n'drop events to callbacks. + */ initialize: function() { var self = this; @@ -3495,6 +3939,11 @@ return this; }, + /** + * Check browser support for drag'n'drop. + * + * @return Boolean + */ browserSupport: function() { var supports = false, div = document.createElement('div'); @@ -3554,8 +4003,14 @@ 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 ); @@ -3578,7 +4033,12 @@ 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 ); @@ -3588,6 +4048,9 @@ return false; }, + /** + * Add the files to the uploader. + */ addFiles: function() { if ( this.files.length ) { this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) ); @@ -3638,7 +4101,9 @@ /** * 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 @@ -3761,7 +4226,9 @@ /** * wp.media.view.UploaderStatus * - * @constructor + * An uploader status for on-going uploads. + * + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -3891,7 +4358,7 @@ /** * wp.media.view.UploaderStatusError * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -3904,7 +4371,10 @@ /** * 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 @@ -4055,7 +4525,7 @@ /** * wp.media.view.Toolbar.Select * - * @constructor + * @class * @augments wp.media.view.Toolbar * @augments wp.media.View * @augments wp.Backbone.View @@ -4089,9 +4559,7 @@ 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 ); }, @@ -4120,7 +4588,7 @@ /** * wp.media.view.Toolbar.Embed * - * @constructor + * @class * @augments wp.media.view.Toolbar.Select * @augments wp.media.view.Toolbar * @augments wp.media.View @@ -4133,9 +4601,7 @@ 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 ); }, @@ -4152,7 +4618,7 @@ /** * wp.media.view.Button * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -4235,7 +4701,7 @@ /** * wp.media.view.ButtonGroup * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -4275,7 +4741,7 @@ /** * wp.media.view.PriorityList * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -4369,7 +4835,7 @@ /** * wp.media.view.MenuItem * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -4435,7 +4901,7 @@ /** * wp.media.view.Menu * - * @constructor + * @class * @augments wp.media.view.PriorityList * @augments wp.media.View * @augments wp.Backbone.View @@ -4545,7 +5011,7 @@ /** * wp.media.view.RouterItem * - * @constructor + * @class * @augments wp.media.view.MenuItem * @augments wp.media.View * @augments wp.Backbone.View @@ -4566,7 +5032,7 @@ /** * wp.media.view.Router * - * @constructor + * @class * @augments wp.media.view.Menu * @augments wp.media.view.PriorityList * @augments wp.media.View @@ -4582,9 +5048,7 @@ 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 ); }, @@ -4599,7 +5063,7 @@ /** * wp.media.view.Sidebar * - * @constructor + * @class * @augments wp.media.view.PriorityList * @augments wp.media.View * @augments wp.Backbone.View @@ -4612,7 +5076,7 @@ /** * wp.media.view.Attachment * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -4641,6 +5105,7 @@ 'click .close': 'removeFromLibrary', 'click .check': 'checkClickHandler', 'click a': 'preventDefault', + 'keydown .close': 'removeFromLibrary', 'keydown': 'toggleSelectionHandler' }, @@ -4671,6 +5136,8 @@ 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 @@ -4782,14 +5249,13 @@ 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; } @@ -4809,9 +5275,6 @@ }); this.controller.trigger( 'selection:toggle' ); - - // Don't scroll the view and don't attempt to submit anything. - event.stopPropagation(); }, /** * @param {Object} options @@ -5080,6 +5543,11 @@ * @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(); @@ -5143,7 +5611,7 @@ /** * wp.media.view.Attachment.Library * - * @constructor + * @class * @augments wp.media.view.Attachment * @augments wp.media.View * @augments wp.Backbone.View @@ -5158,7 +5626,7 @@ /** * wp.media.view.Attachment.EditLibrary * - * @constructor + * @class * @augments wp.media.view.Attachment * @augments wp.media.View * @augments wp.Backbone.View @@ -5173,7 +5641,7 @@ /** * wp.media.view.Attachments * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -5332,10 +5800,6 @@ // 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', @@ -5469,7 +5933,7 @@ /** * wp.media.view.Search * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -5511,7 +5975,7 @@ /** * wp.media.view.AttachmentFilters * - * @constructor + * @class * @augments wp.media.View * @augments wp.Backbone.View * @augments Backbone.View @@ -5551,8 +6015,7 @@ }, /** - * 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 ]; @@ -5580,10 +6043,45 @@ } }); + /** + * 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 @@ -5618,6 +6116,16 @@ order: 'ASC' }, priority: 20 + }, + + unattached: { + text: l10n.unattached, + props: { + uploadedTo: 0, + orderby: 'menuOrder', + order: 'ASC' + }, + priority: 50 } }; } @@ -5626,7 +6134,7 @@ /** * wp.media.view.AttachmentFilters.All * - * @constructor + * @class * @augments wp.media.view.AttachmentFilters * @augments wp.media.View * @augments wp.Backbone.View @@ -5710,10 +6218,22 @@ /** * 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', @@ -5723,13 +6243,14 @@ _.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(); @@ -5748,6 +6269,11 @@ 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 */ @@ -5884,7 +6410,7 @@ changed.push( model.save() ); removed.push( model ); } else { - model.destroy(); + model.destroy({wait: true}); } } ); @@ -5900,6 +6426,51 @@ } } }).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