wp/wp-includes/js/media-models.js
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    79 /******/ 	// __webpack_public_path__
    79 /******/ 	// __webpack_public_path__
    80 /******/ 	__webpack_require__.p = "";
    80 /******/ 	__webpack_require__.p = "";
    81 /******/
    81 /******/
    82 /******/
    82 /******/
    83 /******/ 	// Load entry module and return exports
    83 /******/ 	// Load entry module and return exports
    84 /******/ 	return __webpack_require__(__webpack_require__.s = 22);
    84 /******/ 	return __webpack_require__(__webpack_require__.s = 2);
    85 /******/ })
    85 /******/ })
    86 /************************************************************************/
    86 /************************************************************************/
    87 /******/ ({
    87 /******/ ({
    88 
    88 
    89 /***/ 22:
    89 /***/ "0Ym0":
       
    90 /***/ (function(module, exports) {
       
    91 
       
    92 var $ = Backbone.$,
       
    93 	Attachment;
       
    94 
       
    95 /**
       
    96  * wp.media.model.Attachment
       
    97  *
       
    98  * @memberOf wp.media.model
       
    99  *
       
   100  * @class
       
   101  * @augments Backbone.Model
       
   102  */
       
   103 Attachment = Backbone.Model.extend(/** @lends wp.media.model.Attachment.prototype */{
       
   104 	/**
       
   105 	 * Triggered when attachment details change
       
   106 	 * Overrides Backbone.Model.sync
       
   107 	 *
       
   108 	 * @param {string} method
       
   109 	 * @param {wp.media.model.Attachment} model
       
   110 	 * @param {Object} [options={}]
       
   111 	 *
       
   112 	 * @return {Promise}
       
   113 	 */
       
   114 	sync: function( method, model, options ) {
       
   115 		// If the attachment does not yet have an `id`, return an instantly
       
   116 		// rejected promise. Otherwise, all of our requests will fail.
       
   117 		if ( _.isUndefined( this.id ) ) {
       
   118 			return $.Deferred().rejectWith( this ).promise();
       
   119 		}
       
   120 
       
   121 		// Overload the `read` request so Attachment.fetch() functions correctly.
       
   122 		if ( 'read' === method ) {
       
   123 			options = options || {};
       
   124 			options.context = this;
       
   125 			options.data = _.extend( options.data || {}, {
       
   126 				action: 'get-attachment',
       
   127 				id: this.id
       
   128 			});
       
   129 			return wp.media.ajax( options );
       
   130 
       
   131 		// Overload the `update` request so properties can be saved.
       
   132 		} else if ( 'update' === method ) {
       
   133 			// If we do not have the necessary nonce, fail immediately.
       
   134 			if ( ! this.get('nonces') || ! this.get('nonces').update ) {
       
   135 				return $.Deferred().rejectWith( this ).promise();
       
   136 			}
       
   137 
       
   138 			options = options || {};
       
   139 			options.context = this;
       
   140 
       
   141 			// Set the action and ID.
       
   142 			options.data = _.extend( options.data || {}, {
       
   143 				action:  'save-attachment',
       
   144 				id:      this.id,
       
   145 				nonce:   this.get('nonces').update,
       
   146 				post_id: wp.media.model.settings.post.id
       
   147 			});
       
   148 
       
   149 			// Record the values of the changed attributes.
       
   150 			if ( model.hasChanged() ) {
       
   151 				options.data.changes = {};
       
   152 
       
   153 				_.each( model.changed, function( value, key ) {
       
   154 					options.data.changes[ key ] = this.get( key );
       
   155 				}, this );
       
   156 			}
       
   157 
       
   158 			return wp.media.ajax( options );
       
   159 
       
   160 		// Overload the `delete` request so attachments can be removed.
       
   161 		// This will permanently delete an attachment.
       
   162 		} else if ( 'delete' === method ) {
       
   163 			options = options || {};
       
   164 
       
   165 			if ( ! options.wait ) {
       
   166 				this.destroyed = true;
       
   167 			}
       
   168 
       
   169 			options.context = this;
       
   170 			options.data = _.extend( options.data || {}, {
       
   171 				action:   'delete-post',
       
   172 				id:       this.id,
       
   173 				_wpnonce: this.get('nonces')['delete']
       
   174 			});
       
   175 
       
   176 			return wp.media.ajax( options ).done( function() {
       
   177 				this.destroyed = true;
       
   178 			}).fail( function() {
       
   179 				this.destroyed = false;
       
   180 			});
       
   181 
       
   182 		// Otherwise, fall back to `Backbone.sync()`.
       
   183 		} else {
       
   184 			/**
       
   185 			 * Call `sync` directly on Backbone.Model
       
   186 			 */
       
   187 			return Backbone.Model.prototype.sync.apply( this, arguments );
       
   188 		}
       
   189 	},
       
   190 	/**
       
   191 	 * Convert date strings into Date objects.
       
   192 	 *
       
   193 	 * @param {Object} resp The raw response object, typically returned by fetch()
       
   194 	 * @return {Object} The modified response object, which is the attributes hash
       
   195 	 *                  to be set on the model.
       
   196 	 */
       
   197 	parse: function( resp ) {
       
   198 		if ( ! resp ) {
       
   199 			return resp;
       
   200 		}
       
   201 
       
   202 		resp.date = new Date( resp.date );
       
   203 		resp.modified = new Date( resp.modified );
       
   204 		return resp;
       
   205 	},
       
   206 	/**
       
   207 	 * @param {Object} data The properties to be saved.
       
   208 	 * @param {Object} options Sync options. e.g. patch, wait, success, error.
       
   209 	 *
       
   210 	 * @this Backbone.Model
       
   211 	 *
       
   212 	 * @return {Promise}
       
   213 	 */
       
   214 	saveCompat: function( data, options ) {
       
   215 		var model = this;
       
   216 
       
   217 		// If we do not have the necessary nonce, fail immediately.
       
   218 		if ( ! this.get('nonces') || ! this.get('nonces').update ) {
       
   219 			return $.Deferred().rejectWith( this ).promise();
       
   220 		}
       
   221 
       
   222 		return wp.media.post( 'save-attachment-compat', _.defaults({
       
   223 			id:      this.id,
       
   224 			nonce:   this.get('nonces').update,
       
   225 			post_id: wp.media.model.settings.post.id
       
   226 		}, data ) ).done( function( resp, status, xhr ) {
       
   227 			model.set( model.parse( resp, xhr ), options );
       
   228 		});
       
   229 	}
       
   230 },/** @lends wp.media.model.Attachment */{
       
   231 	/**
       
   232 	 * Create a new model on the static 'all' attachments collection and return it.
       
   233 	 *
       
   234 	 * @static
       
   235 	 *
       
   236 	 * @param {Object} attrs
       
   237 	 * @return {wp.media.model.Attachment}
       
   238 	 */
       
   239 	create: function( attrs ) {
       
   240 		var Attachments = wp.media.model.Attachments;
       
   241 		return Attachments.all.push( attrs );
       
   242 	},
       
   243 	/**
       
   244 	 * Create a new model on the static 'all' attachments collection and return it.
       
   245 	 *
       
   246 	 * If this function has already been called for the id,
       
   247 	 * it returns the specified attachment.
       
   248 	 *
       
   249 	 * @static
       
   250 	 * @param {string} id A string used to identify a model.
       
   251 	 * @param {Backbone.Model|undefined} attachment
       
   252 	 * @return {wp.media.model.Attachment}
       
   253 	 */
       
   254 	get: _.memoize( function( id, attachment ) {
       
   255 		var Attachments = wp.media.model.Attachments;
       
   256 		return Attachments.all.push( attachment || { id: id } );
       
   257 	})
       
   258 });
       
   259 
       
   260 module.exports = Attachment;
       
   261 
       
   262 
       
   263 /***/ }),
       
   264 
       
   265 /***/ 2:
    90 /***/ (function(module, exports, __webpack_require__) {
   266 /***/ (function(module, exports, __webpack_require__) {
    91 
   267 
    92 module.exports = __webpack_require__(23);
   268 module.exports = __webpack_require__("dx5j");
    93 
   269 
    94 
   270 
    95 /***/ }),
   271 /***/ }),
    96 
   272 
    97 /***/ 23:
   273 /***/ "Io+g":
       
   274 /***/ (function(module, exports) {
       
   275 
       
   276 var Attachments = wp.media.model.Attachments,
       
   277 	Selection;
       
   278 
       
   279 /**
       
   280  * wp.media.model.Selection
       
   281  *
       
   282  * A selection of attachments.
       
   283  *
       
   284  * @memberOf wp.media.model
       
   285  *
       
   286  * @class
       
   287  * @augments wp.media.model.Attachments
       
   288  * @augments Backbone.Collection
       
   289  */
       
   290 Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{
       
   291 	/**
       
   292 	 * Refresh the `single` model whenever the selection changes.
       
   293 	 * Binds `single` instead of using the context argument to ensure
       
   294 	 * it receives no parameters.
       
   295 	 *
       
   296 	 * @param {Array} [models=[]] Array of models used to populate the collection.
       
   297 	 * @param {Object} [options={}]
       
   298 	 */
       
   299 	initialize: function( models, options ) {
       
   300 		/**
       
   301 		 * call 'initialize' directly on the parent class
       
   302 		 */
       
   303 		Attachments.prototype.initialize.apply( this, arguments );
       
   304 		this.multiple = options && options.multiple;
       
   305 
       
   306 		this.on( 'add remove reset', _.bind( this.single, this, false ) );
       
   307 	},
       
   308 
       
   309 	/**
       
   310 	 * If the workflow does not support multi-select, clear out the selection
       
   311 	 * before adding a new attachment to it.
       
   312 	 *
       
   313 	 * @param {Array} models
       
   314 	 * @param {Object} options
       
   315 	 * @return {wp.media.model.Attachment[]}
       
   316 	 */
       
   317 	add: function( models, options ) {
       
   318 		if ( ! this.multiple ) {
       
   319 			this.remove( this.models );
       
   320 		}
       
   321 		/**
       
   322 		 * call 'add' directly on the parent class
       
   323 		 */
       
   324 		return Attachments.prototype.add.call( this, models, options );
       
   325 	},
       
   326 
       
   327 	/**
       
   328 	 * Fired when toggling (clicking on) an attachment in the modal.
       
   329 	 *
       
   330 	 * @param {undefined|boolean|wp.media.model.Attachment} model
       
   331 	 *
       
   332 	 * @fires wp.media.model.Selection#selection:single
       
   333 	 * @fires wp.media.model.Selection#selection:unsingle
       
   334 	 *
       
   335 	 * @return {Backbone.Model}
       
   336 	 */
       
   337 	single: function( model ) {
       
   338 		var previous = this._single;
       
   339 
       
   340 		// If a `model` is provided, use it as the single model.
       
   341 		if ( model ) {
       
   342 			this._single = model;
       
   343 		}
       
   344 		// If the single model isn't in the selection, remove it.
       
   345 		if ( this._single && ! this.get( this._single.cid ) ) {
       
   346 			delete this._single;
       
   347 		}
       
   348 
       
   349 		this._single = this._single || this.last();
       
   350 
       
   351 		// If single has changed, fire an event.
       
   352 		if ( this._single !== previous ) {
       
   353 			if ( previous ) {
       
   354 				previous.trigger( 'selection:unsingle', previous, this );
       
   355 
       
   356 				// If the model was already removed, trigger the collection
       
   357 				// event manually.
       
   358 				if ( ! this.get( previous.cid ) ) {
       
   359 					this.trigger( 'selection:unsingle', previous, this );
       
   360 				}
       
   361 			}
       
   362 			if ( this._single ) {
       
   363 				this._single.trigger( 'selection:single', this._single, this );
       
   364 			}
       
   365 		}
       
   366 
       
   367 		// Return the single model, or the last model as a fallback.
       
   368 		return this._single;
       
   369 	}
       
   370 });
       
   371 
       
   372 module.exports = Selection;
       
   373 
       
   374 
       
   375 /***/ }),
       
   376 
       
   377 /***/ "K0z/":
       
   378 /***/ (function(module, exports) {
       
   379 
       
   380 /**
       
   381  * wp.media.model.Attachments
       
   382  *
       
   383  * A collection of attachments.
       
   384  *
       
   385  * This collection has no persistence with the server without supplying
       
   386  * 'options.props.query = true', which will mirror the collection
       
   387  * to an Attachments Query collection - @see wp.media.model.Attachments.mirror().
       
   388  *
       
   389  * @memberOf wp.media.model
       
   390  *
       
   391  * @class
       
   392  * @augments Backbone.Collection
       
   393  *
       
   394  * @param {array}  [models]                Models to initialize with the collection.
       
   395  * @param {object} [options]               Options hash for the collection.
       
   396  * @param {string} [options.props]         Options hash for the initial query properties.
       
   397  * @param {string} [options.props.order]   Initial order (ASC or DESC) for the collection.
       
   398  * @param {string} [options.props.orderby] Initial attribute key to order the collection by.
       
   399  * @param {string} [options.props.query]   Whether the collection is linked to an attachments query.
       
   400  * @param {string} [options.observe]
       
   401  * @param {string} [options.filters]
       
   402  *
       
   403  */
       
   404 var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachments.prototype */{
       
   405 	/**
       
   406 	 * @type {wp.media.model.Attachment}
       
   407 	 */
       
   408 	model: wp.media.model.Attachment,
       
   409 	/**
       
   410 	 * @param {Array} [models=[]] Array of models used to populate the collection.
       
   411 	 * @param {Object} [options={}]
       
   412 	 */
       
   413 	initialize: function( models, options ) {
       
   414 		options = options || {};
       
   415 
       
   416 		this.props   = new Backbone.Model();
       
   417 		this.filters = options.filters || {};
       
   418 
       
   419 		// Bind default `change` events to the `props` model.
       
   420 		this.props.on( 'change', this._changeFilteredProps, this );
       
   421 
       
   422 		this.props.on( 'change:order',   this._changeOrder,   this );
       
   423 		this.props.on( 'change:orderby', this._changeOrderby, this );
       
   424 		this.props.on( 'change:query',   this._changeQuery,   this );
       
   425 
       
   426 		this.props.set( _.defaults( options.props || {} ) );
       
   427 
       
   428 		if ( options.observe ) {
       
   429 			this.observe( options.observe );
       
   430 		}
       
   431 	},
       
   432 	/**
       
   433 	 * Sort the collection when the order attribute changes.
       
   434 	 *
       
   435 	 * @access private
       
   436 	 */
       
   437 	_changeOrder: function() {
       
   438 		if ( this.comparator ) {
       
   439 			this.sort();
       
   440 		}
       
   441 	},
       
   442 	/**
       
   443 	 * Set the default comparator only when the `orderby` property is set.
       
   444 	 *
       
   445 	 * @access private
       
   446 	 *
       
   447 	 * @param {Backbone.Model} model
       
   448 	 * @param {string} orderby
       
   449 	 */
       
   450 	_changeOrderby: function( model, orderby ) {
       
   451 		// If a different comparator is defined, bail.
       
   452 		if ( this.comparator && this.comparator !== Attachments.comparator ) {
       
   453 			return;
       
   454 		}
       
   455 
       
   456 		if ( orderby && 'post__in' !== orderby ) {
       
   457 			this.comparator = Attachments.comparator;
       
   458 		} else {
       
   459 			delete this.comparator;
       
   460 		}
       
   461 	},
       
   462 	/**
       
   463 	 * If the `query` property is set to true, query the server using
       
   464 	 * the `props` values, and sync the results to this collection.
       
   465 	 *
       
   466 	 * @access private
       
   467 	 *
       
   468 	 * @param {Backbone.Model} model
       
   469 	 * @param {boolean} query
       
   470 	 */
       
   471 	_changeQuery: function( model, query ) {
       
   472 		if ( query ) {
       
   473 			this.props.on( 'change', this._requery, this );
       
   474 			this._requery();
       
   475 		} else {
       
   476 			this.props.off( 'change', this._requery, this );
       
   477 		}
       
   478 	},
       
   479 	/**
       
   480 	 * @access private
       
   481 	 *
       
   482 	 * @param {Backbone.Model} model
       
   483 	 */
       
   484 	_changeFilteredProps: function( model ) {
       
   485 		// If this is a query, updating the collection will be handled by
       
   486 		// `this._requery()`.
       
   487 		if ( this.props.get('query') ) {
       
   488 			return;
       
   489 		}
       
   490 
       
   491 		var changed = _.chain( model.changed ).map( function( t, prop ) {
       
   492 			var filter = Attachments.filters[ prop ],
       
   493 				term = model.get( prop );
       
   494 
       
   495 			if ( ! filter ) {
       
   496 				return;
       
   497 			}
       
   498 
       
   499 			if ( term && ! this.filters[ prop ] ) {
       
   500 				this.filters[ prop ] = filter;
       
   501 			} else if ( ! term && this.filters[ prop ] === filter ) {
       
   502 				delete this.filters[ prop ];
       
   503 			} else {
       
   504 				return;
       
   505 			}
       
   506 
       
   507 			// Record the change.
       
   508 			return true;
       
   509 		}, this ).any().value();
       
   510 
       
   511 		if ( ! changed ) {
       
   512 			return;
       
   513 		}
       
   514 
       
   515 		// If no `Attachments` model is provided to source the searches from,
       
   516 		// then automatically generate a source from the existing models.
       
   517 		if ( ! this._source ) {
       
   518 			this._source = new Attachments( this.models );
       
   519 		}
       
   520 
       
   521 		this.reset( this._source.filter( this.validator, this ) );
       
   522 	},
       
   523 
       
   524 	validateDestroyed: false,
       
   525 	/**
       
   526 	 * Checks whether an attachment is valid.
       
   527 	 *
       
   528 	 * @param {wp.media.model.Attachment} attachment
       
   529 	 * @return {boolean}
       
   530 	 */
       
   531 	validator: function( attachment ) {
       
   532 
       
   533 		if ( ! this.validateDestroyed && attachment.destroyed ) {
       
   534 			return false;
       
   535 		}
       
   536 		return _.all( this.filters, function( filter ) {
       
   537 			return !! filter.call( this, attachment );
       
   538 		}, this );
       
   539 	},
       
   540 	/**
       
   541 	 * Add or remove an attachment to the collection depending on its validity.
       
   542 	 *
       
   543 	 * @param {wp.media.model.Attachment} attachment
       
   544 	 * @param {Object} options
       
   545 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   546 	 */
       
   547 	validate: function( attachment, options ) {
       
   548 		var valid = this.validator( attachment ),
       
   549 			hasAttachment = !! this.get( attachment.cid );
       
   550 
       
   551 		if ( ! valid && hasAttachment ) {
       
   552 			this.remove( attachment, options );
       
   553 		} else if ( valid && ! hasAttachment ) {
       
   554 			this.add( attachment, options );
       
   555 		}
       
   556 
       
   557 		return this;
       
   558 	},
       
   559 
       
   560 	/**
       
   561 	 * Add or remove all attachments from another collection depending on each one's validity.
       
   562 	 *
       
   563 	 * @param {wp.media.model.Attachments} attachments
       
   564 	 * @param {Object} [options={}]
       
   565 	 *
       
   566 	 * @fires wp.media.model.Attachments#reset
       
   567 	 *
       
   568 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   569 	 */
       
   570 	validateAll: function( attachments, options ) {
       
   571 		options = options || {};
       
   572 
       
   573 		_.each( attachments.models, function( attachment ) {
       
   574 			this.validate( attachment, { silent: true });
       
   575 		}, this );
       
   576 
       
   577 		if ( ! options.silent ) {
       
   578 			this.trigger( 'reset', this, options );
       
   579 		}
       
   580 		return this;
       
   581 	},
       
   582 	/**
       
   583 	 * Start observing another attachments collection change events
       
   584 	 * and replicate them on this collection.
       
   585 	 *
       
   586 	 * @param {wp.media.model.Attachments} The attachments collection to observe.
       
   587 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   588 	 */
       
   589 	observe: function( attachments ) {
       
   590 		this.observers = this.observers || [];
       
   591 		this.observers.push( attachments );
       
   592 
       
   593 		attachments.on( 'add change remove', this._validateHandler, this );
       
   594 		attachments.on( 'add', this._addToTotalAttachments, this );
       
   595 		attachments.on( 'remove', this._removeFromTotalAttachments, this );
       
   596 		attachments.on( 'reset', this._validateAllHandler, this );
       
   597 		this.validateAll( attachments );
       
   598 		return this;
       
   599 	},
       
   600 	/**
       
   601 	 * Stop replicating collection change events from another attachments collection.
       
   602 	 *
       
   603 	 * @param {wp.media.model.Attachments} The attachments collection to stop observing.
       
   604 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   605 	 */
       
   606 	unobserve: function( attachments ) {
       
   607 		if ( attachments ) {
       
   608 			attachments.off( null, null, this );
       
   609 			this.observers = _.without( this.observers, attachments );
       
   610 
       
   611 		} else {
       
   612 			_.each( this.observers, function( attachments ) {
       
   613 				attachments.off( null, null, this );
       
   614 			}, this );
       
   615 			delete this.observers;
       
   616 		}
       
   617 
       
   618 		return this;
       
   619 	},
       
   620 	/**
       
   621 	 * Update total attachment count when items are added to a collection.
       
   622 	 *
       
   623 	 * @access private
       
   624 	 *
       
   625 	 * @since 5.8.0
       
   626 	 */
       
   627 	_removeFromTotalAttachments: function() {
       
   628 		if ( this.mirroring ) {
       
   629 			this.mirroring.totalAttachments = this.mirroring.totalAttachments - 1;
       
   630 		}
       
   631 	},
       
   632 	/**
       
   633 	 * Update total attachment count when items are added to a collection.
       
   634 	 *
       
   635 	 * @access private
       
   636 	 *
       
   637 	 * @since 5.8.0
       
   638 	 */
       
   639 	_addToTotalAttachments: function() {
       
   640 		if ( this.mirroring ) {
       
   641 			this.mirroring.totalAttachments = this.mirroring.totalAttachments + 1;
       
   642 		}
       
   643 	},
       
   644 	/**
       
   645 	 * @access private
       
   646 	 *
       
   647 	 * @param {wp.media.model.Attachments} attachment
       
   648 	 * @param {wp.media.model.Attachments} attachments
       
   649 	 * @param {Object} options
       
   650 	 *
       
   651 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   652 	 */
       
   653 	_validateHandler: function( attachment, attachments, options ) {
       
   654 		// If we're not mirroring this `attachments` collection,
       
   655 		// only retain the `silent` option.
       
   656 		options = attachments === this.mirroring ? options : {
       
   657 			silent: options && options.silent
       
   658 		};
       
   659 
       
   660 		return this.validate( attachment, options );
       
   661 	},
       
   662 	/**
       
   663 	 * @access private
       
   664 	 *
       
   665 	 * @param {wp.media.model.Attachments} attachments
       
   666 	 * @param {Object} options
       
   667 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   668 	 */
       
   669 	_validateAllHandler: function( attachments, options ) {
       
   670 		return this.validateAll( attachments, options );
       
   671 	},
       
   672 	/**
       
   673 	 * Start mirroring another attachments collection, clearing out any models already
       
   674 	 * in the collection.
       
   675 	 *
       
   676 	 * @param {wp.media.model.Attachments} The attachments collection to mirror.
       
   677 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   678 	 */
       
   679 	mirror: function( attachments ) {
       
   680 		if ( this.mirroring && this.mirroring === attachments ) {
       
   681 			return this;
       
   682 		}
       
   683 
       
   684 		this.unmirror();
       
   685 		this.mirroring = attachments;
       
   686 
       
   687 		// Clear the collection silently. A `reset` event will be fired
       
   688 		// when `observe()` calls `validateAll()`.
       
   689 		this.reset( [], { silent: true } );
       
   690 		this.observe( attachments );
       
   691 
       
   692 		// Used for the search results.
       
   693 		this.trigger( 'attachments:received', this );
       
   694 		return this;
       
   695 	},
       
   696 	/**
       
   697 	 * Stop mirroring another attachments collection.
       
   698 	 */
       
   699 	unmirror: function() {
       
   700 		if ( ! this.mirroring ) {
       
   701 			return;
       
   702 		}
       
   703 
       
   704 		this.unobserve( this.mirroring );
       
   705 		delete this.mirroring;
       
   706 	},
       
   707 	/**
       
   708 	 * Retrieve more attachments from the server for the collection.
       
   709 	 *
       
   710 	 * Only works if the collection is mirroring a Query Attachments collection,
       
   711 	 * and forwards to its `more` method. This collection class doesn't have
       
   712 	 * server persistence by itself.
       
   713 	 *
       
   714 	 * @param {Object} options
       
   715 	 * @return {Promise}
       
   716 	 */
       
   717 	more: function( options ) {
       
   718 		var deferred = jQuery.Deferred(),
       
   719 			mirroring = this.mirroring,
       
   720 			attachments = this;
       
   721 
       
   722 		if ( ! mirroring || ! mirroring.more ) {
       
   723 			return deferred.resolveWith( this ).promise();
       
   724 		}
       
   725 		/*
       
   726 		 * If we're mirroring another collection, forward `more` to
       
   727 		 * the mirrored collection. Account for a race condition by
       
   728 		 * checking if we're still mirroring that collection when
       
   729 		 * the request resolves.
       
   730 		 */
       
   731 		mirroring.more( options ).done( function() {
       
   732 			if ( this === attachments.mirroring ) {
       
   733 				deferred.resolveWith( this );
       
   734 			}
       
   735 
       
   736 			// Used for the search results.
       
   737 			attachments.trigger( 'attachments:received', this );
       
   738 		});
       
   739 
       
   740 		return deferred.promise();
       
   741 	},
       
   742 	/**
       
   743 	 * Whether there are more attachments that haven't been sync'd from the server
       
   744 	 * that match the collection's query.
       
   745 	 *
       
   746 	 * Only works if the collection is mirroring a Query Attachments collection,
       
   747 	 * and forwards to its `hasMore` method. This collection class doesn't have
       
   748 	 * server persistence by itself.
       
   749 	 *
       
   750 	 * @return {boolean}
       
   751 	 */
       
   752 	hasMore: function() {
       
   753 		return this.mirroring ? this.mirroring.hasMore() : false;
       
   754 	},
       
   755 	/**
       
   756 	 * Holds the total number of attachments.
       
   757 	 *
       
   758 	 * @since 5.8.0
       
   759 	 */
       
   760 	totalAttachments: 0,
       
   761 
       
   762 	/**
       
   763 	 * Gets the total number of attachments.
       
   764 	 *
       
   765 	 * @since 5.8.0
       
   766 	 *
       
   767 	 * @return {number} The total number of attachments.
       
   768 	 */
       
   769 	getTotalAttachments: function() {
       
   770 		return this.mirroring ? this.mirroring.totalAttachments : 0;
       
   771 	},
       
   772 
       
   773 	/**
       
   774 	 * A custom Ajax-response parser.
       
   775 	 *
       
   776 	 * See trac ticket #24753.
       
   777 	 *
       
   778 	 * Called automatically by Backbone whenever a collection's models are returned
       
   779 	 * by the server, in fetch. The default implementation is a no-op, simply
       
   780 	 * passing through the JSON response. We override this to add attributes to
       
   781 	 * the collection items.
       
   782 	 *
       
   783 	 * @param {Object|Array} response The raw response Object/Array.
       
   784 	 * @param {Object} xhr
       
   785 	 * @return {Array} The array of model attributes to be added to the collection
       
   786 	 */
       
   787 	parse: function( response, xhr ) {
       
   788 		if ( ! _.isArray( response ) ) {
       
   789 			  response = [response];
       
   790 		}
       
   791 		return _.map( response, function( attrs ) {
       
   792 			var id, attachment, newAttributes;
       
   793 
       
   794 			if ( attrs instanceof Backbone.Model ) {
       
   795 				id = attrs.get( 'id' );
       
   796 				attrs = attrs.attributes;
       
   797 			} else {
       
   798 				id = attrs.id;
       
   799 			}
       
   800 
       
   801 			attachment = wp.media.model.Attachment.get( id );
       
   802 			newAttributes = attachment.parse( attrs, xhr );
       
   803 
       
   804 			if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
       
   805 				attachment.set( newAttributes );
       
   806 			}
       
   807 
       
   808 			return attachment;
       
   809 		});
       
   810 	},
       
   811 
       
   812 	/**
       
   813 	 * If the collection is a query, create and mirror an Attachments Query collection.
       
   814 	 *
       
   815 	 * @access private
       
   816 	 * @param {Boolean} refresh Deprecated, refresh parameter no longer used.
       
   817 	 */
       
   818 	_requery: function() {
       
   819 		var props;
       
   820 		if ( this.props.get('query') ) {
       
   821 			props = this.props.toJSON();
       
   822 			this.mirror( wp.media.model.Query.get( props ) );
       
   823 		}
       
   824 	},
       
   825 	/**
       
   826 	 * If this collection is sorted by `menuOrder`, recalculates and saves
       
   827 	 * the menu order to the database.
       
   828 	 *
       
   829 	 * @return {undefined|Promise}
       
   830 	 */
       
   831 	saveMenuOrder: function() {
       
   832 		if ( 'menuOrder' !== this.props.get('orderby') ) {
       
   833 			return;
       
   834 		}
       
   835 
       
   836 		/*
       
   837 		 * Removes any uploading attachments, updates each attachment's
       
   838 		 * menu order, and returns an object with an { id: menuOrder }
       
   839 		 * mapping to pass to the request.
       
   840 		 */
       
   841 		var attachments = this.chain().filter( function( attachment ) {
       
   842 			return ! _.isUndefined( attachment.id );
       
   843 		}).map( function( attachment, index ) {
       
   844 			// Indices start at 1.
       
   845 			index = index + 1;
       
   846 			attachment.set( 'menuOrder', index );
       
   847 			return [ attachment.id, index ];
       
   848 		}).object().value();
       
   849 
       
   850 		if ( _.isEmpty( attachments ) ) {
       
   851 			return;
       
   852 		}
       
   853 
       
   854 		return wp.media.post( 'save-attachment-order', {
       
   855 			nonce:       wp.media.model.settings.post.nonce,
       
   856 			post_id:     wp.media.model.settings.post.id,
       
   857 			attachments: attachments
       
   858 		});
       
   859 	}
       
   860 },/** @lends wp.media.model.Attachments */{
       
   861 	/**
       
   862 	 * A function to compare two attachment models in an attachments collection.
       
   863 	 *
       
   864 	 * Used as the default comparator for instances of wp.media.model.Attachments
       
   865 	 * and its subclasses. @see wp.media.model.Attachments._changeOrderby().
       
   866 	 *
       
   867 	 * @param {Backbone.Model} a
       
   868 	 * @param {Backbone.Model} b
       
   869 	 * @param {Object} options
       
   870 	 * @return {number} -1 if the first model should come before the second,
       
   871 	 *                   0 if they are of the same rank and
       
   872 	 *                   1 if the first model should come after.
       
   873 	 */
       
   874 	comparator: function( a, b, options ) {
       
   875 		var key   = this.props.get('orderby'),
       
   876 			order = this.props.get('order') || 'DESC',
       
   877 			ac    = a.cid,
       
   878 			bc    = b.cid;
       
   879 
       
   880 		a = a.get( key );
       
   881 		b = b.get( key );
       
   882 
       
   883 		if ( 'date' === key || 'modified' === key ) {
       
   884 			a = a || new Date();
       
   885 			b = b || new Date();
       
   886 		}
       
   887 
       
   888 		// If `options.ties` is set, don't enforce the `cid` tiebreaker.
       
   889 		if ( options && options.ties ) {
       
   890 			ac = bc = null;
       
   891 		}
       
   892 
       
   893 		return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
       
   894 	},
       
   895 	/** @namespace wp.media.model.Attachments.filters */
       
   896 	filters: {
       
   897 		/**
       
   898 		 * @static
       
   899 		 * Note that this client-side searching is *not* equivalent
       
   900 		 * to our server-side searching.
       
   901 		 *
       
   902 		 * @param {wp.media.model.Attachment} attachment
       
   903 		 *
       
   904 		 * @this wp.media.model.Attachments
       
   905 		 *
       
   906 		 * @return {Boolean}
       
   907 		 */
       
   908 		search: function( attachment ) {
       
   909 			if ( ! this.props.get('search') ) {
       
   910 				return true;
       
   911 			}
       
   912 
       
   913 			return _.any(['title','filename','description','caption','name'], function( key ) {
       
   914 				var value = attachment.get( key );
       
   915 				return value && -1 !== value.search( this.props.get('search') );
       
   916 			}, this );
       
   917 		},
       
   918 		/**
       
   919 		 * @static
       
   920 		 * @param {wp.media.model.Attachment} attachment
       
   921 		 *
       
   922 		 * @this wp.media.model.Attachments
       
   923 		 *
       
   924 		 * @return {boolean}
       
   925 		 */
       
   926 		type: function( attachment ) {
       
   927 			var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
       
   928 
       
   929 			if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
       
   930 				return true;
       
   931 			}
       
   932 
       
   933 			mime = atts.mime || ( atts.file && atts.file.type ) || '';
       
   934 
       
   935 			if ( _.isArray( type ) ) {
       
   936 				found = _.find( type, function (t) {
       
   937 					return -1 !== mime.indexOf( t );
       
   938 				} );
       
   939 			} else {
       
   940 				found = -1 !== mime.indexOf( type );
       
   941 			}
       
   942 
       
   943 			return found;
       
   944 		},
       
   945 		/**
       
   946 		 * @static
       
   947 		 * @param {wp.media.model.Attachment} attachment
       
   948 		 *
       
   949 		 * @this wp.media.model.Attachments
       
   950 		 *
       
   951 		 * @return {boolean}
       
   952 		 */
       
   953 		uploadedTo: function( attachment ) {
       
   954 			var uploadedTo = this.props.get('uploadedTo');
       
   955 			if ( _.isUndefined( uploadedTo ) ) {
       
   956 				return true;
       
   957 			}
       
   958 
       
   959 			return uploadedTo === attachment.get('uploadedTo');
       
   960 		},
       
   961 		/**
       
   962 		 * @static
       
   963 		 * @param {wp.media.model.Attachment} attachment
       
   964 		 *
       
   965 		 * @this wp.media.model.Attachments
       
   966 		 *
       
   967 		 * @return {boolean}
       
   968 		 */
       
   969 		status: function( attachment ) {
       
   970 			var status = this.props.get('status');
       
   971 			if ( _.isUndefined( status ) ) {
       
   972 				return true;
       
   973 			}
       
   974 
       
   975 			return status === attachment.get('status');
       
   976 		}
       
   977 	}
       
   978 });
       
   979 
       
   980 module.exports = Attachments;
       
   981 
       
   982 
       
   983 /***/ }),
       
   984 
       
   985 /***/ "dx5j":
    98 /***/ (function(module, exports, __webpack_require__) {
   986 /***/ (function(module, exports, __webpack_require__) {
    99 
   987 
   100 /**
   988 /**
   101  * @output wp-includes/js/media-models.js
   989  * @output wp-includes/js/media-models.js
   102  */
   990  */
   165 
  1053 
   166 // Link any settings.
  1054 // Link any settings.
   167 media.model.settings = l10n.settings || {};
  1055 media.model.settings = l10n.settings || {};
   168 delete l10n.settings;
  1056 delete l10n.settings;
   169 
  1057 
   170 Attachment = media.model.Attachment = __webpack_require__( 24 );
  1058 Attachment = media.model.Attachment = __webpack_require__( "0Ym0" );
   171 Attachments = media.model.Attachments = __webpack_require__( 25 );
  1059 Attachments = media.model.Attachments = __webpack_require__( "K0z/" );
   172 
  1060 
   173 media.model.Query = __webpack_require__( 26 );
  1061 media.model.Query = __webpack_require__( "efdO" );
   174 media.model.PostImage = __webpack_require__( 27 );
  1062 media.model.PostImage = __webpack_require__( "r1z7" );
   175 media.model.Selection = __webpack_require__( 28 );
  1063 media.model.Selection = __webpack_require__( "Io+g" );
   176 
  1064 
   177 /**
  1065 /**
   178  * ========================================================================
  1066  * ========================================================================
   179  * UTILITIES
  1067  * UTILITIES
   180  * ========================================================================
  1068  * ========================================================================
   343 });
  1231 });
   344 
  1232 
   345 
  1233 
   346 /***/ }),
  1234 /***/ }),
   347 
  1235 
   348 /***/ 24:
  1236 /***/ "efdO":
   349 /***/ (function(module, exports) {
       
   350 
       
   351 var $ = Backbone.$,
       
   352 	Attachment;
       
   353 
       
   354 /**
       
   355  * wp.media.model.Attachment
       
   356  *
       
   357  * @memberOf wp.media.model
       
   358  *
       
   359  * @class
       
   360  * @augments Backbone.Model
       
   361  */
       
   362 Attachment = Backbone.Model.extend(/** @lends wp.media.model.Attachment.prototype */{
       
   363 	/**
       
   364 	 * Triggered when attachment details change
       
   365 	 * Overrides Backbone.Model.sync
       
   366 	 *
       
   367 	 * @param {string} method
       
   368 	 * @param {wp.media.model.Attachment} model
       
   369 	 * @param {Object} [options={}]
       
   370 	 *
       
   371 	 * @return {Promise}
       
   372 	 */
       
   373 	sync: function( method, model, options ) {
       
   374 		// If the attachment does not yet have an `id`, return an instantly
       
   375 		// rejected promise. Otherwise, all of our requests will fail.
       
   376 		if ( _.isUndefined( this.id ) ) {
       
   377 			return $.Deferred().rejectWith( this ).promise();
       
   378 		}
       
   379 
       
   380 		// Overload the `read` request so Attachment.fetch() functions correctly.
       
   381 		if ( 'read' === method ) {
       
   382 			options = options || {};
       
   383 			options.context = this;
       
   384 			options.data = _.extend( options.data || {}, {
       
   385 				action: 'get-attachment',
       
   386 				id: this.id
       
   387 			});
       
   388 			return wp.media.ajax( options );
       
   389 
       
   390 		// Overload the `update` request so properties can be saved.
       
   391 		} else if ( 'update' === method ) {
       
   392 			// If we do not have the necessary nonce, fail immediately.
       
   393 			if ( ! this.get('nonces') || ! this.get('nonces').update ) {
       
   394 				return $.Deferred().rejectWith( this ).promise();
       
   395 			}
       
   396 
       
   397 			options = options || {};
       
   398 			options.context = this;
       
   399 
       
   400 			// Set the action and ID.
       
   401 			options.data = _.extend( options.data || {}, {
       
   402 				action:  'save-attachment',
       
   403 				id:      this.id,
       
   404 				nonce:   this.get('nonces').update,
       
   405 				post_id: wp.media.model.settings.post.id
       
   406 			});
       
   407 
       
   408 			// Record the values of the changed attributes.
       
   409 			if ( model.hasChanged() ) {
       
   410 				options.data.changes = {};
       
   411 
       
   412 				_.each( model.changed, function( value, key ) {
       
   413 					options.data.changes[ key ] = this.get( key );
       
   414 				}, this );
       
   415 			}
       
   416 
       
   417 			return wp.media.ajax( options );
       
   418 
       
   419 		// Overload the `delete` request so attachments can be removed.
       
   420 		// This will permanently delete an attachment.
       
   421 		} else if ( 'delete' === method ) {
       
   422 			options = options || {};
       
   423 
       
   424 			if ( ! options.wait ) {
       
   425 				this.destroyed = true;
       
   426 			}
       
   427 
       
   428 			options.context = this;
       
   429 			options.data = _.extend( options.data || {}, {
       
   430 				action:   'delete-post',
       
   431 				id:       this.id,
       
   432 				_wpnonce: this.get('nonces')['delete']
       
   433 			});
       
   434 
       
   435 			return wp.media.ajax( options ).done( function() {
       
   436 				this.destroyed = true;
       
   437 			}).fail( function() {
       
   438 				this.destroyed = false;
       
   439 			});
       
   440 
       
   441 		// Otherwise, fall back to `Backbone.sync()`.
       
   442 		} else {
       
   443 			/**
       
   444 			 * Call `sync` directly on Backbone.Model
       
   445 			 */
       
   446 			return Backbone.Model.prototype.sync.apply( this, arguments );
       
   447 		}
       
   448 	},
       
   449 	/**
       
   450 	 * Convert date strings into Date objects.
       
   451 	 *
       
   452 	 * @param {Object} resp The raw response object, typically returned by fetch()
       
   453 	 * @return {Object} The modified response object, which is the attributes hash
       
   454 	 *                  to be set on the model.
       
   455 	 */
       
   456 	parse: function( resp ) {
       
   457 		if ( ! resp ) {
       
   458 			return resp;
       
   459 		}
       
   460 
       
   461 		resp.date = new Date( resp.date );
       
   462 		resp.modified = new Date( resp.modified );
       
   463 		return resp;
       
   464 	},
       
   465 	/**
       
   466 	 * @param {Object} data The properties to be saved.
       
   467 	 * @param {Object} options Sync options. e.g. patch, wait, success, error.
       
   468 	 *
       
   469 	 * @this Backbone.Model
       
   470 	 *
       
   471 	 * @return {Promise}
       
   472 	 */
       
   473 	saveCompat: function( data, options ) {
       
   474 		var model = this;
       
   475 
       
   476 		// If we do not have the necessary nonce, fail immediately.
       
   477 		if ( ! this.get('nonces') || ! this.get('nonces').update ) {
       
   478 			return $.Deferred().rejectWith( this ).promise();
       
   479 		}
       
   480 
       
   481 		return wp.media.post( 'save-attachment-compat', _.defaults({
       
   482 			id:      this.id,
       
   483 			nonce:   this.get('nonces').update,
       
   484 			post_id: wp.media.model.settings.post.id
       
   485 		}, data ) ).done( function( resp, status, xhr ) {
       
   486 			model.set( model.parse( resp, xhr ), options );
       
   487 		});
       
   488 	}
       
   489 },/** @lends wp.media.model.Attachment */{
       
   490 	/**
       
   491 	 * Create a new model on the static 'all' attachments collection and return it.
       
   492 	 *
       
   493 	 * @static
       
   494 	 *
       
   495 	 * @param {Object} attrs
       
   496 	 * @return {wp.media.model.Attachment}
       
   497 	 */
       
   498 	create: function( attrs ) {
       
   499 		var Attachments = wp.media.model.Attachments;
       
   500 		return Attachments.all.push( attrs );
       
   501 	},
       
   502 	/**
       
   503 	 * Create a new model on the static 'all' attachments collection and return it.
       
   504 	 *
       
   505 	 * If this function has already been called for the id,
       
   506 	 * it returns the specified attachment.
       
   507 	 *
       
   508 	 * @static
       
   509 	 * @param {string} id A string used to identify a model.
       
   510 	 * @param {Backbone.Model|undefined} attachment
       
   511 	 * @return {wp.media.model.Attachment}
       
   512 	 */
       
   513 	get: _.memoize( function( id, attachment ) {
       
   514 		var Attachments = wp.media.model.Attachments;
       
   515 		return Attachments.all.push( attachment || { id: id } );
       
   516 	})
       
   517 });
       
   518 
       
   519 module.exports = Attachment;
       
   520 
       
   521 
       
   522 /***/ }),
       
   523 
       
   524 /***/ 25:
       
   525 /***/ (function(module, exports) {
       
   526 
       
   527 /**
       
   528  * wp.media.model.Attachments
       
   529  *
       
   530  * A collection of attachments.
       
   531  *
       
   532  * This collection has no persistence with the server without supplying
       
   533  * 'options.props.query = true', which will mirror the collection
       
   534  * to an Attachments Query collection - @see wp.media.model.Attachments.mirror().
       
   535  *
       
   536  * @memberOf wp.media.model
       
   537  *
       
   538  * @class
       
   539  * @augments Backbone.Collection
       
   540  *
       
   541  * @param {array}  [models]                Models to initialize with the collection.
       
   542  * @param {object} [options]               Options hash for the collection.
       
   543  * @param {string} [options.props]         Options hash for the initial query properties.
       
   544  * @param {string} [options.props.order]   Initial order (ASC or DESC) for the collection.
       
   545  * @param {string} [options.props.orderby] Initial attribute key to order the collection by.
       
   546  * @param {string} [options.props.query]   Whether the collection is linked to an attachments query.
       
   547  * @param {string} [options.observe]
       
   548  * @param {string} [options.filters]
       
   549  *
       
   550  */
       
   551 var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachments.prototype */{
       
   552 	/**
       
   553 	 * @type {wp.media.model.Attachment}
       
   554 	 */
       
   555 	model: wp.media.model.Attachment,
       
   556 	/**
       
   557 	 * @param {Array} [models=[]] Array of models used to populate the collection.
       
   558 	 * @param {Object} [options={}]
       
   559 	 */
       
   560 	initialize: function( models, options ) {
       
   561 		options = options || {};
       
   562 
       
   563 		this.props   = new Backbone.Model();
       
   564 		this.filters = options.filters || {};
       
   565 
       
   566 		// Bind default `change` events to the `props` model.
       
   567 		this.props.on( 'change', this._changeFilteredProps, this );
       
   568 
       
   569 		this.props.on( 'change:order',   this._changeOrder,   this );
       
   570 		this.props.on( 'change:orderby', this._changeOrderby, this );
       
   571 		this.props.on( 'change:query',   this._changeQuery,   this );
       
   572 
       
   573 		this.props.set( _.defaults( options.props || {} ) );
       
   574 
       
   575 		if ( options.observe ) {
       
   576 			this.observe( options.observe );
       
   577 		}
       
   578 	},
       
   579 	/**
       
   580 	 * Sort the collection when the order attribute changes.
       
   581 	 *
       
   582 	 * @access private
       
   583 	 */
       
   584 	_changeOrder: function() {
       
   585 		if ( this.comparator ) {
       
   586 			this.sort();
       
   587 		}
       
   588 	},
       
   589 	/**
       
   590 	 * Set the default comparator only when the `orderby` property is set.
       
   591 	 *
       
   592 	 * @access private
       
   593 	 *
       
   594 	 * @param {Backbone.Model} model
       
   595 	 * @param {string} orderby
       
   596 	 */
       
   597 	_changeOrderby: function( model, orderby ) {
       
   598 		// If a different comparator is defined, bail.
       
   599 		if ( this.comparator && this.comparator !== Attachments.comparator ) {
       
   600 			return;
       
   601 		}
       
   602 
       
   603 		if ( orderby && 'post__in' !== orderby ) {
       
   604 			this.comparator = Attachments.comparator;
       
   605 		} else {
       
   606 			delete this.comparator;
       
   607 		}
       
   608 	},
       
   609 	/**
       
   610 	 * If the `query` property is set to true, query the server using
       
   611 	 * the `props` values, and sync the results to this collection.
       
   612 	 *
       
   613 	 * @access private
       
   614 	 *
       
   615 	 * @param {Backbone.Model} model
       
   616 	 * @param {boolean} query
       
   617 	 */
       
   618 	_changeQuery: function( model, query ) {
       
   619 		if ( query ) {
       
   620 			this.props.on( 'change', this._requery, this );
       
   621 			this._requery();
       
   622 		} else {
       
   623 			this.props.off( 'change', this._requery, this );
       
   624 		}
       
   625 	},
       
   626 	/**
       
   627 	 * @access private
       
   628 	 *
       
   629 	 * @param {Backbone.Model} model
       
   630 	 */
       
   631 	_changeFilteredProps: function( model ) {
       
   632 		// If this is a query, updating the collection will be handled by
       
   633 		// `this._requery()`.
       
   634 		if ( this.props.get('query') ) {
       
   635 			return;
       
   636 		}
       
   637 
       
   638 		var changed = _.chain( model.changed ).map( function( t, prop ) {
       
   639 			var filter = Attachments.filters[ prop ],
       
   640 				term = model.get( prop );
       
   641 
       
   642 			if ( ! filter ) {
       
   643 				return;
       
   644 			}
       
   645 
       
   646 			if ( term && ! this.filters[ prop ] ) {
       
   647 				this.filters[ prop ] = filter;
       
   648 			} else if ( ! term && this.filters[ prop ] === filter ) {
       
   649 				delete this.filters[ prop ];
       
   650 			} else {
       
   651 				return;
       
   652 			}
       
   653 
       
   654 			// Record the change.
       
   655 			return true;
       
   656 		}, this ).any().value();
       
   657 
       
   658 		if ( ! changed ) {
       
   659 			return;
       
   660 		}
       
   661 
       
   662 		// If no `Attachments` model is provided to source the searches from,
       
   663 		// then automatically generate a source from the existing models.
       
   664 		if ( ! this._source ) {
       
   665 			this._source = new Attachments( this.models );
       
   666 		}
       
   667 
       
   668 		this.reset( this._source.filter( this.validator, this ) );
       
   669 	},
       
   670 
       
   671 	validateDestroyed: false,
       
   672 	/**
       
   673 	 * Checks whether an attachment is valid.
       
   674 	 *
       
   675 	 * @param {wp.media.model.Attachment} attachment
       
   676 	 * @return {boolean}
       
   677 	 */
       
   678 	validator: function( attachment ) {
       
   679 
       
   680 		// Filter out contextually created attachments (e.g. headers, logos, etc.).
       
   681 		if (
       
   682 			! _.isUndefined( attachment.attributes.context ) &&
       
   683 			'' !== attachment.attributes.context
       
   684 		) {
       
   685 			return false;
       
   686 		}
       
   687 
       
   688 		if ( ! this.validateDestroyed && attachment.destroyed ) {
       
   689 			return false;
       
   690 		}
       
   691 		return _.all( this.filters, function( filter ) {
       
   692 			return !! filter.call( this, attachment );
       
   693 		}, this );
       
   694 	},
       
   695 	/**
       
   696 	 * Add or remove an attachment to the collection depending on its validity.
       
   697 	 *
       
   698 	 * @param {wp.media.model.Attachment} attachment
       
   699 	 * @param {Object} options
       
   700 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   701 	 */
       
   702 	validate: function( attachment, options ) {
       
   703 		var valid = this.validator( attachment ),
       
   704 			hasAttachment = !! this.get( attachment.cid );
       
   705 
       
   706 		if ( ! valid && hasAttachment ) {
       
   707 			this.remove( attachment, options );
       
   708 		} else if ( valid && ! hasAttachment ) {
       
   709 			this.add( attachment, options );
       
   710 		}
       
   711 
       
   712 		return this;
       
   713 	},
       
   714 
       
   715 	/**
       
   716 	 * Add or remove all attachments from another collection depending on each one's validity.
       
   717 	 *
       
   718 	 * @param {wp.media.model.Attachments} attachments
       
   719 	 * @param {Object} [options={}]
       
   720 	 *
       
   721 	 * @fires wp.media.model.Attachments#reset
       
   722 	 *
       
   723 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   724 	 */
       
   725 	validateAll: function( attachments, options ) {
       
   726 		options = options || {};
       
   727 
       
   728 		_.each( attachments.models, function( attachment ) {
       
   729 			this.validate( attachment, { silent: true });
       
   730 		}, this );
       
   731 
       
   732 		if ( ! options.silent ) {
       
   733 			this.trigger( 'reset', this, options );
       
   734 		}
       
   735 		return this;
       
   736 	},
       
   737 	/**
       
   738 	 * Start observing another attachments collection change events
       
   739 	 * and replicate them on this collection.
       
   740 	 *
       
   741 	 * @param {wp.media.model.Attachments} The attachments collection to observe.
       
   742 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   743 	 */
       
   744 	observe: function( attachments ) {
       
   745 		this.observers = this.observers || [];
       
   746 		this.observers.push( attachments );
       
   747 
       
   748 		attachments.on( 'add change remove', this._validateHandler, this );
       
   749 		attachments.on( 'reset', this._validateAllHandler, this );
       
   750 		this.validateAll( attachments );
       
   751 		return this;
       
   752 	},
       
   753 	/**
       
   754 	 * Stop replicating collection change events from another attachments collection.
       
   755 	 *
       
   756 	 * @param {wp.media.model.Attachments} The attachments collection to stop observing.
       
   757 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   758 	 */
       
   759 	unobserve: function( attachments ) {
       
   760 		if ( attachments ) {
       
   761 			attachments.off( null, null, this );
       
   762 			this.observers = _.without( this.observers, attachments );
       
   763 
       
   764 		} else {
       
   765 			_.each( this.observers, function( attachments ) {
       
   766 				attachments.off( null, null, this );
       
   767 			}, this );
       
   768 			delete this.observers;
       
   769 		}
       
   770 
       
   771 		return this;
       
   772 	},
       
   773 	/**
       
   774 	 * @access private
       
   775 	 *
       
   776 	 * @param {wp.media.model.Attachments} attachment
       
   777 	 * @param {wp.media.model.Attachments} attachments
       
   778 	 * @param {Object} options
       
   779 	 *
       
   780 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   781 	 */
       
   782 	_validateHandler: function( attachment, attachments, options ) {
       
   783 		// If we're not mirroring this `attachments` collection,
       
   784 		// only retain the `silent` option.
       
   785 		options = attachments === this.mirroring ? options : {
       
   786 			silent: options && options.silent
       
   787 		};
       
   788 
       
   789 		return this.validate( attachment, options );
       
   790 	},
       
   791 	/**
       
   792 	 * @access private
       
   793 	 *
       
   794 	 * @param {wp.media.model.Attachments} attachments
       
   795 	 * @param {Object} options
       
   796 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   797 	 */
       
   798 	_validateAllHandler: function( attachments, options ) {
       
   799 		return this.validateAll( attachments, options );
       
   800 	},
       
   801 	/**
       
   802 	 * Start mirroring another attachments collection, clearing out any models already
       
   803 	 * in the collection.
       
   804 	 *
       
   805 	 * @param {wp.media.model.Attachments} The attachments collection to mirror.
       
   806 	 * @return {wp.media.model.Attachments} Returns itself to allow chaining.
       
   807 	 */
       
   808 	mirror: function( attachments ) {
       
   809 		if ( this.mirroring && this.mirroring === attachments ) {
       
   810 			return this;
       
   811 		}
       
   812 
       
   813 		this.unmirror();
       
   814 		this.mirroring = attachments;
       
   815 
       
   816 		// Clear the collection silently. A `reset` event will be fired
       
   817 		// when `observe()` calls `validateAll()`.
       
   818 		this.reset( [], { silent: true } );
       
   819 		this.observe( attachments );
       
   820 
       
   821 		// Used for the search results.
       
   822 		this.trigger( 'attachments:received', this );
       
   823 		return this;
       
   824 	},
       
   825 	/**
       
   826 	 * Stop mirroring another attachments collection.
       
   827 	 */
       
   828 	unmirror: function() {
       
   829 		if ( ! this.mirroring ) {
       
   830 			return;
       
   831 		}
       
   832 
       
   833 		this.unobserve( this.mirroring );
       
   834 		delete this.mirroring;
       
   835 	},
       
   836 	/**
       
   837 	 * Retrieve more attachments from the server for the collection.
       
   838 	 *
       
   839 	 * Only works if the collection is mirroring a Query Attachments collection,
       
   840 	 * and forwards to its `more` method. This collection class doesn't have
       
   841 	 * server persistence by itself.
       
   842 	 *
       
   843 	 * @param {Object} options
       
   844 	 * @return {Promise}
       
   845 	 */
       
   846 	more: function( options ) {
       
   847 		var deferred = jQuery.Deferred(),
       
   848 			mirroring = this.mirroring,
       
   849 			attachments = this;
       
   850 
       
   851 		if ( ! mirroring || ! mirroring.more ) {
       
   852 			return deferred.resolveWith( this ).promise();
       
   853 		}
       
   854 		/*
       
   855 		 * If we're mirroring another collection, forward `more` to
       
   856 		 * the mirrored collection. Account for a race condition by
       
   857 		 * checking if we're still mirroring that collection when
       
   858 		 * the request resolves.
       
   859 		 */
       
   860 		mirroring.more( options ).done( function() {
       
   861 			if ( this === attachments.mirroring ) {
       
   862 				deferred.resolveWith( this );
       
   863 			}
       
   864 
       
   865 			// Used for the search results.
       
   866 			attachments.trigger( 'attachments:received', this );
       
   867 		});
       
   868 
       
   869 		return deferred.promise();
       
   870 	},
       
   871 	/**
       
   872 	 * Whether there are more attachments that haven't been sync'd from the server
       
   873 	 * that match the collection's query.
       
   874 	 *
       
   875 	 * Only works if the collection is mirroring a Query Attachments collection,
       
   876 	 * and forwards to its `hasMore` method. This collection class doesn't have
       
   877 	 * server persistence by itself.
       
   878 	 *
       
   879 	 * @return {boolean}
       
   880 	 */
       
   881 	hasMore: function() {
       
   882 		return this.mirroring ? this.mirroring.hasMore() : false;
       
   883 	},
       
   884 	/**
       
   885 	 * A custom Ajax-response parser.
       
   886 	 *
       
   887 	 * See trac ticket #24753
       
   888 	 *
       
   889 	 * @param {Object|Array} resp The raw response Object/Array.
       
   890 	 * @param {Object} xhr
       
   891 	 * @return {Array} The array of model attributes to be added to the collection
       
   892 	 */
       
   893 	parse: function( resp, xhr ) {
       
   894 		if ( ! _.isArray( resp ) ) {
       
   895 			resp = [resp];
       
   896 		}
       
   897 
       
   898 		return _.map( resp, function( attrs ) {
       
   899 			var id, attachment, newAttributes;
       
   900 
       
   901 			if ( attrs instanceof Backbone.Model ) {
       
   902 				id = attrs.get( 'id' );
       
   903 				attrs = attrs.attributes;
       
   904 			} else {
       
   905 				id = attrs.id;
       
   906 			}
       
   907 
       
   908 			attachment = wp.media.model.Attachment.get( id );
       
   909 			newAttributes = attachment.parse( attrs, xhr );
       
   910 
       
   911 			if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
       
   912 				attachment.set( newAttributes );
       
   913 			}
       
   914 
       
   915 			return attachment;
       
   916 		});
       
   917 	},
       
   918 	/**
       
   919 	 * If the collection is a query, create and mirror an Attachments Query collection.
       
   920 	 *
       
   921 	 * @access private
       
   922 	 */
       
   923 	_requery: function( refresh ) {
       
   924 		var props;
       
   925 		if ( this.props.get('query') ) {
       
   926 			props = this.props.toJSON();
       
   927 			props.cache = ( true !== refresh );
       
   928 			this.mirror( wp.media.model.Query.get( props ) );
       
   929 		}
       
   930 	},
       
   931 	/**
       
   932 	 * If this collection is sorted by `menuOrder`, recalculates and saves
       
   933 	 * the menu order to the database.
       
   934 	 *
       
   935 	 * @return {undefined|Promise}
       
   936 	 */
       
   937 	saveMenuOrder: function() {
       
   938 		if ( 'menuOrder' !== this.props.get('orderby') ) {
       
   939 			return;
       
   940 		}
       
   941 
       
   942 		/*
       
   943 		 * Removes any uploading attachments, updates each attachment's
       
   944 		 * menu order, and returns an object with an { id: menuOrder }
       
   945 		 * mapping to pass to the request.
       
   946 		 */
       
   947 		var attachments = this.chain().filter( function( attachment ) {
       
   948 			return ! _.isUndefined( attachment.id );
       
   949 		}).map( function( attachment, index ) {
       
   950 			// Indices start at 1.
       
   951 			index = index + 1;
       
   952 			attachment.set( 'menuOrder', index );
       
   953 			return [ attachment.id, index ];
       
   954 		}).object().value();
       
   955 
       
   956 		if ( _.isEmpty( attachments ) ) {
       
   957 			return;
       
   958 		}
       
   959 
       
   960 		return wp.media.post( 'save-attachment-order', {
       
   961 			nonce:       wp.media.model.settings.post.nonce,
       
   962 			post_id:     wp.media.model.settings.post.id,
       
   963 			attachments: attachments
       
   964 		});
       
   965 	}
       
   966 },/** @lends wp.media.model.Attachments */{
       
   967 	/**
       
   968 	 * A function to compare two attachment models in an attachments collection.
       
   969 	 *
       
   970 	 * Used as the default comparator for instances of wp.media.model.Attachments
       
   971 	 * and its subclasses. @see wp.media.model.Attachments._changeOrderby().
       
   972 	 *
       
   973 	 * @param {Backbone.Model} a
       
   974 	 * @param {Backbone.Model} b
       
   975 	 * @param {Object} options
       
   976 	 * @return {number} -1 if the first model should come before the second,
       
   977 	 *                   0 if they are of the same rank and
       
   978 	 *                   1 if the first model should come after.
       
   979 	 */
       
   980 	comparator: function( a, b, options ) {
       
   981 		var key   = this.props.get('orderby'),
       
   982 			order = this.props.get('order') || 'DESC',
       
   983 			ac    = a.cid,
       
   984 			bc    = b.cid;
       
   985 
       
   986 		a = a.get( key );
       
   987 		b = b.get( key );
       
   988 
       
   989 		if ( 'date' === key || 'modified' === key ) {
       
   990 			a = a || new Date();
       
   991 			b = b || new Date();
       
   992 		}
       
   993 
       
   994 		// If `options.ties` is set, don't enforce the `cid` tiebreaker.
       
   995 		if ( options && options.ties ) {
       
   996 			ac = bc = null;
       
   997 		}
       
   998 
       
   999 		return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
       
  1000 	},
       
  1001 	/** @namespace wp.media.model.Attachments.filters */
       
  1002 	filters: {
       
  1003 		/**
       
  1004 		 * @static
       
  1005 		 * Note that this client-side searching is *not* equivalent
       
  1006 		 * to our server-side searching.
       
  1007 		 *
       
  1008 		 * @param {wp.media.model.Attachment} attachment
       
  1009 		 *
       
  1010 		 * @this wp.media.model.Attachments
       
  1011 		 *
       
  1012 		 * @return {Boolean}
       
  1013 		 */
       
  1014 		search: function( attachment ) {
       
  1015 			if ( ! this.props.get('search') ) {
       
  1016 				return true;
       
  1017 			}
       
  1018 
       
  1019 			return _.any(['title','filename','description','caption','name'], function( key ) {
       
  1020 				var value = attachment.get( key );
       
  1021 				return value && -1 !== value.search( this.props.get('search') );
       
  1022 			}, this );
       
  1023 		},
       
  1024 		/**
       
  1025 		 * @static
       
  1026 		 * @param {wp.media.model.Attachment} attachment
       
  1027 		 *
       
  1028 		 * @this wp.media.model.Attachments
       
  1029 		 *
       
  1030 		 * @return {boolean}
       
  1031 		 */
       
  1032 		type: function( attachment ) {
       
  1033 			var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
       
  1034 
       
  1035 			if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
       
  1036 				return true;
       
  1037 			}
       
  1038 
       
  1039 			mime = atts.mime || ( atts.file && atts.file.type ) || '';
       
  1040 
       
  1041 			if ( _.isArray( type ) ) {
       
  1042 				found = _.find( type, function (t) {
       
  1043 					return -1 !== mime.indexOf( t );
       
  1044 				} );
       
  1045 			} else {
       
  1046 				found = -1 !== mime.indexOf( type );
       
  1047 			}
       
  1048 
       
  1049 			return found;
       
  1050 		},
       
  1051 		/**
       
  1052 		 * @static
       
  1053 		 * @param {wp.media.model.Attachment} attachment
       
  1054 		 *
       
  1055 		 * @this wp.media.model.Attachments
       
  1056 		 *
       
  1057 		 * @return {boolean}
       
  1058 		 */
       
  1059 		uploadedTo: function( attachment ) {
       
  1060 			var uploadedTo = this.props.get('uploadedTo');
       
  1061 			if ( _.isUndefined( uploadedTo ) ) {
       
  1062 				return true;
       
  1063 			}
       
  1064 
       
  1065 			return uploadedTo === attachment.get('uploadedTo');
       
  1066 		},
       
  1067 		/**
       
  1068 		 * @static
       
  1069 		 * @param {wp.media.model.Attachment} attachment
       
  1070 		 *
       
  1071 		 * @this wp.media.model.Attachments
       
  1072 		 *
       
  1073 		 * @return {boolean}
       
  1074 		 */
       
  1075 		status: function( attachment ) {
       
  1076 			var status = this.props.get('status');
       
  1077 			if ( _.isUndefined( status ) ) {
       
  1078 				return true;
       
  1079 			}
       
  1080 
       
  1081 			return status === attachment.get('status');
       
  1082 		}
       
  1083 	}
       
  1084 });
       
  1085 
       
  1086 module.exports = Attachments;
       
  1087 
       
  1088 
       
  1089 /***/ }),
       
  1090 
       
  1091 /***/ 26:
       
  1092 /***/ (function(module, exports) {
  1237 /***/ (function(module, exports) {
  1093 
  1238 
  1094 var Attachments = wp.media.model.Attachments,
  1239 var Attachments = wp.media.model.Attachments,
  1095 	Query;
  1240 	Query;
  1096 
  1241 
  1203 		}
  1348 		}
  1204 
  1349 
  1205 		options = options || {};
  1350 		options = options || {};
  1206 		options.remove = false;
  1351 		options.remove = false;
  1207 
  1352 
  1208 		return this._more = this.fetch( options ).done( function( resp ) {
  1353 		return this._more = this.fetch( options ).done( function( response ) {
  1209 			if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
  1354 			if ( _.isEmpty( response ) || -1 === query.args.posts_per_page || response.length < query.args.posts_per_page ) {
  1210 				query._hasMore = false;
  1355 				query._hasMore = false;
  1211 			}
  1356 			}
  1212 		});
  1357 		});
  1213 	},
  1358 	},
  1214 	/**
  1359 	/**
  1262 	},
  1407 	},
  1263 	/**
  1408 	/**
  1264 	 * @readonly
  1409 	 * @readonly
  1265 	 */
  1410 	 */
  1266 	defaultArgs: {
  1411 	defaultArgs: {
  1267 		posts_per_page: 40
  1412 		posts_per_page: 80
  1268 	},
  1413 	},
  1269 	/**
  1414 	/**
  1270 	 * @readonly
  1415 	 * @readonly
  1271 	 */
  1416 	 */
  1272 	orderby: {
  1417 	orderby: {
  1304 	 *
  1449 	 *
  1305 	 * @static
  1450 	 * @static
  1306 	 * @method
  1451 	 * @method
  1307 	 *
  1452 	 *
  1308 	 * @param {object} [props]
  1453 	 * @param {object} [props]
  1309 	 * @param {Object} [props.cache=true]   Whether to use the query cache or not.
       
  1310 	 * @param {Object} [props.order]
  1454 	 * @param {Object} [props.order]
  1311 	 * @param {Object} [props.orderby]
  1455 	 * @param {Object} [props.orderby]
  1312 	 * @param {Object} [props.include]
  1456 	 * @param {Object} [props.include]
  1313 	 * @param {Object} [props.exclude]
  1457 	 * @param {Object} [props.exclude]
  1314 	 * @param {Object} [props.s]
  1458 	 * @param {Object} [props.s]
  1334 		 */
  1478 		 */
  1335 		return function( props, options ) {
  1479 		return function( props, options ) {
  1336 			var args     = {},
  1480 			var args     = {},
  1337 				orderby  = Query.orderby,
  1481 				orderby  = Query.orderby,
  1338 				defaults = Query.defaultProps,
  1482 				defaults = Query.defaultProps,
  1339 				query,
  1483 				query;
  1340 				cache    = !! props.cache || _.isUndefined( props.cache );
       
  1341 
  1484 
  1342 			// Remove the `query` property. This isn't linked to a query,
  1485 			// Remove the `query` property. This isn't linked to a query,
  1343 			// this *is* the query.
  1486 			// this *is* the query.
  1344 			delete props.query;
  1487 			delete props.query;
  1345 			delete props.cache;
       
  1346 
  1488 
  1347 			// Fill default args.
  1489 			// Fill default args.
  1348 			_.defaults( props, defaults );
  1490 			_.defaults( props, defaults );
  1349 
  1491 
  1350 			// Normalize the order.
  1492 			// Normalize the order.
  1379 
  1521 
  1380 			// `props.orderby` does not always map directly to `args.orderby`.
  1522 			// `props.orderby` does not always map directly to `args.orderby`.
  1381 			// Substitute exceptions specified in orderby.keymap.
  1523 			// Substitute exceptions specified in orderby.keymap.
  1382 			args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
  1524 			args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
  1383 
  1525 
  1384 			// Search the query cache for a matching query.
  1526 			queries = [];
  1385 			if ( cache ) {
       
  1386 				query = _.find( queries, function( query ) {
       
  1387 					return _.isEqual( query.args, args );
       
  1388 				});
       
  1389 			} else {
       
  1390 				queries = [];
       
  1391 			}
       
  1392 
  1527 
  1393 			// Otherwise, create a new query and add it to the cache.
  1528 			// Otherwise, create a new query and add it to the cache.
  1394 			if ( ! query ) {
  1529 			if ( ! query ) {
  1395 				query = new Query( [], _.extend( options || {}, {
  1530 				query = new Query( [], _.extend( options || {}, {
  1396 					props: props,
  1531 					props: props,
  1407 module.exports = Query;
  1542 module.exports = Query;
  1408 
  1543 
  1409 
  1544 
  1410 /***/ }),
  1545 /***/ }),
  1411 
  1546 
  1412 /***/ 27:
  1547 /***/ "r1z7":
  1413 /***/ (function(module, exports) {
  1548 /***/ (function(module, exports) {
  1414 
  1549 
  1415 /**
  1550 /**
  1416  * wp.media.model.PostImage
  1551  * wp.media.model.PostImage
  1417  *
  1552  *
  1566 });
  1701 });
  1567 
  1702 
  1568 module.exports = PostImage;
  1703 module.exports = PostImage;
  1569 
  1704 
  1570 
  1705 
  1571 /***/ }),
       
  1572 
       
  1573 /***/ 28:
       
  1574 /***/ (function(module, exports) {
       
  1575 
       
  1576 var Attachments = wp.media.model.Attachments,
       
  1577 	Selection;
       
  1578 
       
  1579 /**
       
  1580  * wp.media.model.Selection
       
  1581  *
       
  1582  * A selection of attachments.
       
  1583  *
       
  1584  * @memberOf wp.media.model
       
  1585  *
       
  1586  * @class
       
  1587  * @augments wp.media.model.Attachments
       
  1588  * @augments Backbone.Collection
       
  1589  */
       
  1590 Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{
       
  1591 	/**
       
  1592 	 * Refresh the `single` model whenever the selection changes.
       
  1593 	 * Binds `single` instead of using the context argument to ensure
       
  1594 	 * it receives no parameters.
       
  1595 	 *
       
  1596 	 * @param {Array} [models=[]] Array of models used to populate the collection.
       
  1597 	 * @param {Object} [options={}]
       
  1598 	 */
       
  1599 	initialize: function( models, options ) {
       
  1600 		/**
       
  1601 		 * call 'initialize' directly on the parent class
       
  1602 		 */
       
  1603 		Attachments.prototype.initialize.apply( this, arguments );
       
  1604 		this.multiple = options && options.multiple;
       
  1605 
       
  1606 		this.on( 'add remove reset', _.bind( this.single, this, false ) );
       
  1607 	},
       
  1608 
       
  1609 	/**
       
  1610 	 * If the workflow does not support multi-select, clear out the selection
       
  1611 	 * before adding a new attachment to it.
       
  1612 	 *
       
  1613 	 * @param {Array} models
       
  1614 	 * @param {Object} options
       
  1615 	 * @return {wp.media.model.Attachment[]}
       
  1616 	 */
       
  1617 	add: function( models, options ) {
       
  1618 		if ( ! this.multiple ) {
       
  1619 			this.remove( this.models );
       
  1620 		}
       
  1621 		/**
       
  1622 		 * call 'add' directly on the parent class
       
  1623 		 */
       
  1624 		return Attachments.prototype.add.call( this, models, options );
       
  1625 	},
       
  1626 
       
  1627 	/**
       
  1628 	 * Fired when toggling (clicking on) an attachment in the modal.
       
  1629 	 *
       
  1630 	 * @param {undefined|boolean|wp.media.model.Attachment} model
       
  1631 	 *
       
  1632 	 * @fires wp.media.model.Selection#selection:single
       
  1633 	 * @fires wp.media.model.Selection#selection:unsingle
       
  1634 	 *
       
  1635 	 * @return {Backbone.Model}
       
  1636 	 */
       
  1637 	single: function( model ) {
       
  1638 		var previous = this._single;
       
  1639 
       
  1640 		// If a `model` is provided, use it as the single model.
       
  1641 		if ( model ) {
       
  1642 			this._single = model;
       
  1643 		}
       
  1644 		// If the single model isn't in the selection, remove it.
       
  1645 		if ( this._single && ! this.get( this._single.cid ) ) {
       
  1646 			delete this._single;
       
  1647 		}
       
  1648 
       
  1649 		this._single = this._single || this.last();
       
  1650 
       
  1651 		// If single has changed, fire an event.
       
  1652 		if ( this._single !== previous ) {
       
  1653 			if ( previous ) {
       
  1654 				previous.trigger( 'selection:unsingle', previous, this );
       
  1655 
       
  1656 				// If the model was already removed, trigger the collection
       
  1657 				// event manually.
       
  1658 				if ( ! this.get( previous.cid ) ) {
       
  1659 					this.trigger( 'selection:unsingle', previous, this );
       
  1660 				}
       
  1661 			}
       
  1662 			if ( this._single ) {
       
  1663 				this._single.trigger( 'selection:single', this._single, this );
       
  1664 			}
       
  1665 		}
       
  1666 
       
  1667 		// Return the single model, or the last model as a fallback.
       
  1668 		return this._single;
       
  1669 	}
       
  1670 });
       
  1671 
       
  1672 module.exports = Selection;
       
  1673 
       
  1674 
       
  1675 /***/ })
  1706 /***/ })
  1676 
  1707 
  1677 /******/ });
  1708 /******/ });