: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( ! this.mirroring ) {
this.unobserve( this.mirroring );
* Retrieve more attachments from the server for the collection.
* Only works if the collection is mirroring a Query Attachments collection,
* and forwards to its `more` method. This collection class doesn't have
* server persistence by itself.
* @param {Object} options
more: function( options ) {
var deferred = jQuery.Deferred(),
mirroring = this.mirroring,
if ( ! mirroring || ! mirroring.more ) {
return deferred.resolveWith( this ).promise();
* If we're mirroring another collection, forward `more` to
* the mirrored collection. Account for a race condition by
* checking if we're still mirroring that collection when
mirroring.more( options ).done( function() {
if ( this === attachments.mirroring ) {
deferred.resolveWith( this );
// Used for the search results.
attachments.trigger( 'attachments:received', this );
return deferred.promise();
* Whether there are more attachments that haven't been sync'd from the server
* that match the collection's query.
* Only works if the collection is mirroring a Query Attachments collection,
* and forwards to its `hasMore` method. This collection class doesn't have
* server persistence by itself.
return this.mirroring ? this.mirroring.hasMore() : false;
* Holds the total number of attachments.
* Gets the total number of attachments.
* @return {number} The total number of attachments.
getTotalAttachments: function() {
return this.mirroring ? this.mirroring.totalAttachments : 0;
* A custom Ajax-response parser.
* See trac ticket #24753.
* Called automatically by Backbone whenever a collection's models are returned
* by the server, in fetch. The default implementation is a no-op, simply
* passing through the JSON response. We override this to add attributes to
* @param {Object|Array} response The raw response Object/Array.
* @return {Array} The array of model attributes to be added to the collection
parse: function( response, xhr ) {
if ( ! _.isArray( response ) ) {
return _.map( response, function( attrs ) {
var id, attachment, newAttributes;
if ( attrs instanceof Backbone.Model ) {
attrs = attrs.attributes;
attachment = wp.media.model.Attachment.get( id );
newAttributes = attachment.parse( attrs, xhr );
if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
attachment.set( newAttributes );
* If the collection is a query, create and mirror an Attachments Query collection.
* @param {Boolean} refresh Deprecated, refresh parameter no longer used.
if ( this.props.get('query') ) {
props = this.props.toJSON();
this.mirror( wp.media.model.Query.get( props ) );
* If this collection is sorted by `menuOrder`, recalculates and saves
* the menu order to the database.
* @return {undefined|Promise}
saveMenuOrder: function() {
if ( 'menuOrder' !== this.props.get('orderby') ) {
* Removes any uploading attachments, updates each attachment's
* menu order, and returns an object with an { id: menuOrder }
* mapping to pass to the request.
var attachments = this.chain().filter( function( attachment ) {
return ! _.isUndefined( attachment.id );
}).map( function( attachment, index ) {
attachment.set( 'menuOrder', index );
return [ attachment.id, index ];
if ( _.isEmpty( attachments ) ) {
return wp.media.post( 'save-attachment-order', {
nonce: wp.media.model.settings.post.nonce,
post_id: wp.media.model.settings.post.id,
},/** @lends wp.media.model.Attachments */{
* A function to compare two attachment models in an attachments collection.
* Used as the default comparator for instances of wp.media.model.Attachments
* and its subclasses. @see wp.media.model.Attachments._changeOrderby().
* @param {Backbone.Model} a
* @param {Backbone.Model} b
* @param {Object} options
* @return {number} -1 if the first model should come before the second,
* 0 if they are of the same rank and
* 1 if the first model should come after.
comparator: function( a, b, options ) {
var key = this.props.get('orderby'),
order = this.props.get('order') || 'DESC',
if ( 'date' === key || 'modified' === key ) {
// If `options.ties` is set, don't enforce the `cid` tiebreaker.
if ( options && options.ties ) {
return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
/** @namespace wp.media.model.Attachments.filters */
* Note that this client-side searching is *not* equivalent
* to our server-side searching.
* @param {wp.media.model.Attachment} attachment
* @this wp.media.model.Attachments
search: function( attachment ) {
if ( ! this.props.get('search') ) {
return _.any(['title','filename','description','caption','name'], function( key ) {
var value = attachment.get( key );
return value && -1 !== value.search( this.props.get('search') );
* @param {wp.media.model.Attachment} attachment
* @this wp.media.model.Attachments
type: function( attachment ) {
var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
mime = atts.mime || ( atts.file && atts.file.type ) || '';
if ( _.isArray( type ) ) {
found = _.find( type, function (t) {
return -1 !== mime.indexOf( t );
found = -1 !== mime.indexOf( type );
* @param {wp.media.model.Attachment} attachment
* @this wp.media.model.Attachments
uploadedTo: function( attachment ) {
var uploadedTo = this.props.get('uploadedTo');
if ( _.isUndefined( uploadedTo ) ) {
return uploadedTo === attachment.get('uploadedTo');
* @param {wp.media.model.Attachment} attachment
* @this wp.media.model.Attachments
status: function( attachment ) {
var status = this.props.get('status');
if ( _.isUndefined( status ) ) {
return status === attachment.get('status');
module.exports = Attachments;
* wp.media.model.PostImage
* An instance of an image that's been embedded into a post.
* Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails.
* @memberOf wp.media.model
* @augments Backbone.Model
* @param {int} [attributes] Initial model attributes.
* @param {int} [attributes.attachment_id] ID of the attachment.
var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{
initialize: function( attributes ) {
var Attachment = wp.media.model.Attachment;
if ( attributes.attachment_id ) {
this.attachment = Attachment.get( attributes.attachment_id );
if ( this.attachment.get( 'url' ) ) {
this.dfd = jQuery.Deferred();
this.dfd = this.attachment.fetch();
this.bindAttachmentListeners();
// Keep URL in sync with changes to the type of link.
this.on( 'change:link', this.updateLinkUrl, this );
this.on( 'change:size', this.updateSize, this );
this.setLinkTypeFromUrl();
this.set( 'originalUrl', attributes.url );
bindAttachmentListeners: function() {
this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
this.listenTo( this.attachment, 'sync', this.setAspectRatio );
this.listenTo( this.attachment, 'change', this.updateSize );
changeAttachment: function( attachment, props ) {
this.stopListening( this.attachment );
this.attachment = attachment;
this.bindAttachmentListeners();
this.set( 'attachment_id', this.attachment.get( 'id' ) );
this.set( 'caption', this.attachment.get( 'caption' ) );
this.set( 'alt', this.attachment.get( 'alt' ) );
this.set( 'size', props.get( 'size' ) );
this.set( 'align', props.get( 'align' ) );
this.set( 'link', props.get( 'link' ) );
setLinkTypeFromUrl: function() {
var linkUrl = this.get( 'linkUrl' ),
this.set( 'link', 'none' );
// Default to custom if there is a linkUrl.
if ( this.attachment.get( 'url' ) === linkUrl ) {
} else if ( this.attachment.get( 'link' ) === linkUrl ) {
if ( this.get( 'url' ) === linkUrl ) {
this.set( 'link', type );
updateLinkUrl: function() {
var link = this.get( 'link' ),
url = this.attachment.get( 'url' );
this.set( 'linkUrl', url );
this.set( 'linkUrl', this.attachment.get( 'link' ) );
this.set( 'linkUrl', '' );
if ( ! this.attachment ) {
if ( this.get( 'size' ) === 'custom' ) {
this.set( 'width', this.get( 'customWidth' ) );
this.set( 'height', this.get( 'customHeight' ) );
this.set( 'url', this.get( 'originalUrl' ) );
size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
this.set( 'url', size.url );
this.set( 'width', size.width );
this.set( 'height', size.height );
setAspectRatio: function() {
if ( this.attachment && this.attachment.get( 'sizes' ) ) {
full = this.attachment.get( 'sizes' ).full;
this.set( 'aspectRatio', full.width / full.height );
this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );
module.exports = PostImage;
var Attachments = wp.media.model.Attachments,
* A collection of attachments that match the supplied query arguments.
* Note: Do NOT change this.args after the query has been initialized.
* @memberOf wp.media.model
* @augments wp.media.model.Attachments
* @augments Backbone.Collection
* @param {array} [models] Models to initialize with the collection.
* @param {object} [options] Options hash.
* @param {object} [options.args] Attachments query arguments.
* @param {object} [options.args.posts_per_page]
Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{
* @param {Array} [models=[]] Array of initial models to populate the collection.
* @param {Object} [options={}]
initialize: function( models, options ) {
Attachments.prototype.initialize.apply( this, arguments );
this.args = options.args;
this.created = new Date();
this.filters.order = function( attachment ) {
var orderby = this.props.get('orderby'),
order = this.props.get('order');
if ( ! this.comparator ) {
* We want any items that can be placed before the last
* item in the set. If we add any items after the last
* item, then we can't guarantee the set is complete.