From 640edbdfbc4cae9a180436422400c82072449735 Mon Sep 17 00:00:00 2001 From: Daryl Koopersmith Date: Tue, 30 Oct 2012 23:15:16 +0000 Subject: [PATCH] Media JS: Improve handling of single attachments in selections. * Adds `wp.media.model.Selection.single()` to specify a single item used in a multi-item selection. * Fixes a bug where the `details` class would not be removed when "Clear Selection" was clicked. see #21390. git-svn-id: http://core.svn.wordpress.org/trunk@22335 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/css/media-views.css | 1 - wp-includes/js/media-models.js | 36 ++++++++++++++++++++++-- wp-includes/js/media-views.js | 50 +++++++++++++-------------------- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css index 4b191ce1a5..74efa1431c 100644 --- a/wp-includes/css/media-views.css +++ b/wp-includes/css/media-views.css @@ -244,7 +244,6 @@ text-shadow: 0 1px 0 rgba( 0, 0, 0, 0.5 ); background: #777; border: 1px solid #fff; - /*border-width: 0 1px 1px 0;*/ border-width: 0 0 1px 1px; box-shadow: -1px 1px 0 rgba( 0, 0, 0, 0.1 ); } diff --git a/wp-includes/js/media-models.js b/wp-includes/js/media-models.js index aebedbd395..e9776b4840 100644 --- a/wp-includes/js/media-models.js +++ b/wp-includes/js/media-models.js @@ -622,6 +622,11 @@ window.wp = window.wp || {}; initialize: function( models, options ) { Attachments.prototype.initialize.apply( this, arguments ); this.multiple = options && options.multiple; + + // Refresh the `single` model whenever the selection changes. + // Binds `single` instead of using the context argument to ensure + // it receives no parameters. + this.on( 'add remove reset', _.bind( this.single, this ) ); }, // Override the selection's add method. @@ -638,14 +643,16 @@ window.wp = window.wp || {}; // Removes all models from the selection. clear: function( options ) { - return this.remove( this.models, options ); + this.remove( this.models, options ).single(); + return this; }, // Override the selection's reset method. // Always direct items through add and remove, // as we need them to fire. reset: function( models, options ) { - return this.clear( options ).add( models, options ); + this.clear( options ).add( models, options ).single(); + return this; }, // Create selection.has, which determines if a model @@ -653,6 +660,31 @@ window.wp = window.wp || {}; // instead of direct comparison. has: function( attachment ) { return !! ( this.getByCid( attachment.cid ) || this.get( attachment.id ) ); + }, + + single: function( model ) { + var previous = this._single; + + // If a `model` is provided, use it as the single model. + if ( model ) + this._single = model; + + // If the single model isn't in the selection, remove it. + if ( this._single && ! this.has( this._single ) ) + delete this._single; + + this._single = this._single || this.last(); + + // If single has changed, fire an event. + if ( this._single !== previous ) { + if ( this._single ) + this._single.trigger( 'selection:single', this._single, this ); + if ( previous ) + previous.trigger( 'selection:unsingle', previous, this ); + } + + // Return the single model, or the last model as a fallback. + return this._single; } }); diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index e62593e9cd..ea7b9a7bac 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -212,7 +212,7 @@ }, details: function( options ) { - var model = this.get('details'), + var model = this.get('selection').single(), view; if ( model ) { @@ -232,34 +232,18 @@ }, toggleSelection: function( model ) { - var details = this.get('details'), - selection = this.get('selection'), - selected = selection.has( model ); + var selection = this.get('selection'); - if ( ! selection ) - return; - - if ( ! selected ) - selection.add( model ); - - // If the model is not the same as the details model, - // it now becomes the details model. If the model is - // in the selection, it is not removed. - if ( details !== model ) { - this.set( 'details', model ); - return; + if ( selection.has( model ) ) { + // If the model is the single model, remove it. + // If it is not the same as the single model, + // it now becomes the single model. + selection[ selection.single() === model ? 'remove' : 'single' ]( model ); + } else { + selection.add( model ).single(); } - // The model is the details model. - // Removed it from the selection. - selection.remove( model ); - - // Show the last selected item, or clear the details view. - if ( selection.length ) - this.set( 'details', selection.last() ); - else - this.unset('details'); - + return this; } }); @@ -1087,14 +1071,14 @@ this.select(); // Update the model's details view. - this.controller.state().on( 'change:details', this.details, this ); + this.model.on( 'selection:single selection:unsingle', this.details, this ); this.details(); return this; }, destroy: function() { - this.controller.state().off( 'change:details', this.details, this ); + this.model.off( 'single', this.details, this ); }, progress: function() { @@ -1136,8 +1120,14 @@ this.$el.removeClass('selected'); }, - details: function() { - var details = this.controller.state().get('details'); + details: function( model, collection ) { + var selection = this.controller.state().get('selection'), + details; + + if ( selection !== collection ) + return; + + details = selection.single(); this.$el.toggleClass( 'details', details === this.model ); },