]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/media-models.js
WordPress 4.7
[autoinstalls/wordpress.git] / wp-includes / js / media-models.js
index 84bb02fa05c57cccc75d6a9353daa5ecef65ddc7..f5683a939e305de7aad74f3c08d5d10c4f20dab5 100644 (file)
-/* global _wpMediaModelsL10n:false */
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+var $ = jQuery,
+       Attachment, Attachments, l10n, media;
+
 window.wp = window.wp || {};
 
-(function($){
-       var Attachment, Attachments, Query, PostImage, compare, l10n, media;
+/**
+ * Create and return a media frame.
+ *
+ * Handles the default media experience.
+ *
+ * @param  {object} attributes The properties passed to the main media controller.
+ * @return {wp.media.view.MediaFrame} A media workflow.
+ */
+media = wp.media = function( attributes ) {
+       var MediaFrame = media.view.MediaFrame,
+               frame;
+
+       if ( ! MediaFrame ) {
+               return;
+       }
+
+       attributes = _.defaults( attributes || {}, {
+               frame: 'select'
+       });
 
+       if ( 'select' === attributes.frame && MediaFrame.Select ) {
+               frame = new MediaFrame.Select( attributes );
+       } else if ( 'post' === attributes.frame && MediaFrame.Post ) {
+               frame = new MediaFrame.Post( attributes );
+       } else if ( 'manage' === attributes.frame && MediaFrame.Manage ) {
+               frame = new MediaFrame.Manage( attributes );
+       } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) {
+               frame = new MediaFrame.ImageDetails( attributes );
+       } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) {
+               frame = new MediaFrame.AudioDetails( attributes );
+       } else if ( 'video' === attributes.frame && MediaFrame.VideoDetails ) {
+               frame = new MediaFrame.VideoDetails( attributes );
+       } else if ( 'edit-attachments' === attributes.frame && MediaFrame.EditAttachments ) {
+               frame = new MediaFrame.EditAttachments( attributes );
+       }
+
+       delete attributes.frame;
+
+       media.frame = frame;
+
+       return frame;
+};
+
+_.extend( media, { model: {}, view: {}, controller: {}, frames: {} });
+
+// Link any localized strings.
+l10n = media.model.l10n = window._wpMediaModelsL10n || {};
+
+// Link any settings.
+media.model.settings = l10n.settings || {};
+delete l10n.settings;
+
+Attachment = media.model.Attachment = require( './models/attachment.js' );
+Attachments = media.model.Attachments = require( './models/attachments.js' );
+
+media.model.Query = require( './models/query.js' );
+media.model.PostImage = require( './models/post-image.js' );
+media.model.Selection = require( './models/selection.js' );
+
+/**
+ * ========================================================================
+ * UTILITIES
+ * ========================================================================
+ */
+
+/**
+ * A basic equality comparator for Backbone models.
+ *
+ * Used to order models within a collection - @see wp.media.model.Attachments.comparator().
+ *
+ * @param  {mixed}  a  The primary parameter to compare.
+ * @param  {mixed}  b  The primary parameter to compare.
+ * @param  {string} ac The fallback parameter to compare, a's cid.
+ * @param  {string} bc The fallback parameter to compare, b's cid.
+ * @return {number}    -1: a should come before b.
+ *                      0: a and b are of the same rank.
+ *                      1: b should come before a.
+ */
+media.compare = function( a, b, ac, bc ) {
+       if ( _.isEqual( a, b ) ) {
+               return ac === bc ? 0 : (ac > bc ? -1 : 1);
+       } else {
+               return a > b ? -1 : 1;
+       }
+};
+
+_.extend( media, {
        /**
-        * Create and return a media frame.
+        * media.template( id )
+        *
+        * Fetch a JavaScript template for an id, and return a templating function for it.
         *
-        * Handles the default media experience.
+        * See wp.template() in `wp-includes/js/wp-util.js`.
         *
-        * @param  {object} attributes The properties passed to the main media controller.
-        * @return {wp.media.view.MediaFrame} A media workflow.
+        * @borrows wp.template as template
         */
-       media = wp.media = function( attributes ) {
-               var MediaFrame = media.view.MediaFrame,
-                       frame;
-
-               if ( ! MediaFrame ) {
-                       return;
-               }
-
-               attributes = _.defaults( attributes || {}, {
-                       frame: 'select'
-               });
-
-               if ( 'select' === attributes.frame && MediaFrame.Select ) {
-                       frame = new MediaFrame.Select( attributes );
-               } else if ( 'post' === attributes.frame && MediaFrame.Post ) {
-                       frame = new MediaFrame.Post( attributes );
-               } else if ( 'manage' === attributes.frame && MediaFrame.Manage ) {
-                       frame = new MediaFrame.Manage( attributes );
-               } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) {
-                       frame = new MediaFrame.ImageDetails( attributes );
-               } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) {
-                       frame = new MediaFrame.AudioDetails( attributes );
-               } else if ( 'video' === attributes.frame && MediaFrame.VideoDetails ) {
-                       frame = new MediaFrame.VideoDetails( attributes );
-               } else if ( 'edit-attachments' === attributes.frame && MediaFrame.EditAttachments ) {
-                       frame = new MediaFrame.EditAttachments( attributes );
-               }
-
-               delete attributes.frame;
-
-               media.frame = frame;
-
-               return frame;
-       };
-
-       _.extend( media, { model: {}, view: {}, controller: {}, frames: {} });
-
-       // Link any localized strings.
-       l10n = media.model.l10n = typeof _wpMediaModelsL10n === 'undefined' ? {} : _wpMediaModelsL10n;
-
-       // Link any settings.
-       media.model.settings = l10n.settings || {};
-       delete l10n.settings;
+       template: wp.template,
 
        /**
-        * ========================================================================
-        * UTILITIES
-        * ========================================================================
+        * media.post( [action], [data] )
+        *
+        * Sends a POST request to WordPress.
+        * See wp.ajax.post() in `wp-includes/js/wp-util.js`.
+        *
+        * @borrows wp.ajax.post as post
         */
+       post: wp.ajax.post,
 
        /**
-        * A basic equality comparator for Backbone models.
+        * media.ajax( [action], [options] )
         *
-        * Used to order models within a collection - @see wp.media.model.Attachments.comparator().
+        * Sends an XHR request to WordPress.
+        * See wp.ajax.send() in `wp-includes/js/wp-util.js`.
         *
-        * @param  {mixed}  a  The primary parameter to compare.
-        * @param  {mixed}  b  The primary parameter to compare.
-        * @param  {string} ac The fallback parameter to compare, a's cid.
-        * @param  {string} bc The fallback parameter to compare, b's cid.
-        * @return {number}    -1: a should come before b.
-        *                      0: a and b are of the same rank.
-        *                      1: b should come before a.
+        * @borrows wp.ajax.send as ajax
         */
-       compare = function( a, b, ac, bc ) {
-               if ( _.isEqual( a, b ) ) {
-                       return ac === bc ? 0 : (ac > bc ? -1 : 1);
-               } else {
-                       return a > b ? -1 : 1;
-               }
-       };
-
-       _.extend( media, {
-               /**
-                * media.template( id )
-                *
-                * Fetch a JavaScript template for an id, and return a templating function for it.
-                *
-                * See wp.template() in `wp-includes/js/wp-util.js`.
-                *
-                * @borrows wp.template as template
-                */
-               template: wp.template,
-
-               /**
-                * media.post( [action], [data] )
-                *
-                * Sends a POST request to WordPress.
-                * See wp.ajax.post() in `wp-includes/js/wp-util.js`.
-                *
-                * @borrows wp.ajax.post as post
-                */
-               post: wp.ajax.post,
-
-               /**
-                * media.ajax( [action], [options] )
-                *
-                * Sends an XHR request to WordPress.
-                * See wp.ajax.send() in `wp-includes/js/wp-util.js`.
-                *
-                * @borrows wp.ajax.send as ajax
-                */
-               ajax: wp.ajax.send,
-
-               /**
-                * Scales a set of dimensions to fit within bounding dimensions.
-                *
-                * @param {Object} dimensions
-                * @returns {Object}
-                */
-               fit: function( dimensions ) {
-                       var width     = dimensions.width,
-                               height    = dimensions.height,
-                               maxWidth  = dimensions.maxWidth,
-                               maxHeight = dimensions.maxHeight,
-                               constraint;
-
-                       // Compare ratios between the two values to determine which
-                       // max to constrain by. If a max value doesn't exist, then the
-                       // opposite side is the constraint.
-                       if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) {
-                               constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height';
-                       } else if ( _.isUndefined( maxHeight ) ) {
-                               constraint = 'width';
-                       } else if (  _.isUndefined( maxWidth ) && height > maxHeight ) {
-                               constraint = 'height';
-                       }
-
-                       // If the value of the constrained side is larger than the max,
-                       // then scale the values. Otherwise return the originals; they fit.
-                       if ( 'width' === constraint && width > maxWidth ) {
-                               return {
-                                       width : maxWidth,
-                                       height: Math.round( maxWidth * height / width )
-                               };
-                       } else if ( 'height' === constraint && height > maxHeight ) {
-                               return {
-                                       width : Math.round( maxHeight * width / height ),
-                                       height: maxHeight
-                               };
-                       } else {
-                               return {
-                                       width : width,
-                                       height: height
-                               };
-                       }
-               },
-               /**
-                * Truncates a string by injecting an ellipsis into the middle.
-                * Useful for filenames.
-                *
-                * @param {String} string
-                * @param {Number} [length=30]
-                * @param {String} [replacement=&hellip;]
-                * @returns {String} The string, unless length is greater than string.length.
-                */
-               truncate: function( string, length, replacement ) {
-                       length = length || 30;
-                       replacement = replacement || '&hellip;';
-
-                       if ( string.length <= length ) {
-                               return string;
-                       }
-
-                       return string.substr( 0, length / 2 ) + replacement + string.substr( -1 * length / 2 );
-               }
-       });
+       ajax: wp.ajax.send,
 
        /**
-        * ========================================================================
-        * MODELS
-        * ========================================================================
+        * Scales a set of dimensions to fit within bounding dimensions.
+        *
+        * @param {Object} dimensions
+        * @returns {Object}
         */
+       fit: function( dimensions ) {
+               var width     = dimensions.width,
+                       height    = dimensions.height,
+                       maxWidth  = dimensions.maxWidth,
+                       maxHeight = dimensions.maxHeight,
+                       constraint;
+
+               // Compare ratios between the two values to determine which
+               // max to constrain by. If a max value doesn't exist, then the
+               // opposite side is the constraint.
+               if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) {
+                       constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height';
+               } else if ( _.isUndefined( maxHeight ) ) {
+                       constraint = 'width';
+               } else if (  _.isUndefined( maxWidth ) && height > maxHeight ) {
+                       constraint = 'height';
+               }
+
+               // If the value of the constrained side is larger than the max,
+               // then scale the values. Otherwise return the originals; they fit.
+               if ( 'width' === constraint && width > maxWidth ) {
+                       return {
+                               width : maxWidth,
+                               height: Math.round( maxWidth * height / width )
+                       };
+               } else if ( 'height' === constraint && height > maxHeight ) {
+                       return {
+                               width : Math.round( maxHeight * width / height ),
+                               height: maxHeight
+                       };
+               } else {
+                       return {
+                               width : width,
+                               height: height
+                       };
+               }
+       },
        /**
-        * wp.media.attachment
+        * Truncates a string by injecting an ellipsis into the middle.
+        * Useful for filenames.
         *
-        * @static
-        * @param {String} id A string used to identify a model.
-        * @returns {wp.media.model.Attachment}
+        * @param {String} string
+        * @param {Number} [length=30]
+        * @param {String} [replacement=&hellip;]
+        * @returns {String} The string, unless length is greater than string.length.
         */
-       media.attachment = function( id ) {
-               return Attachment.get( id );
-       };
+       truncate: function( string, length, replacement ) {
+               length = length || 30;
+               replacement = replacement || '&hellip;';
 
+               if ( string.length <= length ) {
+                       return string;
+               }
+
+               return string.substr( 0, length / 2 ) + replacement + string.substr( -1 * length / 2 );
+       }
+});
+
+/**
+ * ========================================================================
+ * MODELS
+ * ========================================================================
+ */
+/**
+ * wp.media.attachment
+ *
+ * @static
+ * @param {String} id A string used to identify a model.
+ * @returns {wp.media.model.Attachment}
+ */
+media.attachment = function( id ) {
+       return Attachment.get( id );
+};
+
+/**
+ * A collection of all attachments that have been fetched from the server.
+ *
+ * @static
+ * @member {wp.media.model.Attachments}
+ */
+Attachments.all = new Attachments();
+
+/**
+ * wp.media.query
+ *
+ * Shorthand for creating a new Attachments Query.
+ *
+ * @param {object} [props]
+ * @returns {wp.media.model.Attachments}
+ */
+media.query = function( props ) {
+       return new Attachments( null, {
+               props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
+       });
+};
+
+// Clean up. Prevents mobile browsers caching
+$(window).on('unload', function(){
+       window.wp = null;
+});
+
+},{"./models/attachment.js":2,"./models/attachments.js":3,"./models/post-image.js":4,"./models/query.js":5,"./models/selection.js":6}],2:[function(require,module,exports){
+/**
+ * wp.media.model.Attachment
+ *
+ * @class
+ * @augments Backbone.Model
+ */
+var $ = Backbone.$,
+       Attachment;
+
+Attachment = Backbone.Model.extend({
        /**
-        * wp.media.model.Attachment
+        * Triggered when attachment details change
+        * Overrides Backbone.Model.sync
+        *
+        * @param {string} method
+        * @param {wp.media.model.Attachment} model
+        * @param {Object} [options={}]
         *
-        * @class
-        * @augments Backbone.Model
+        * @returns {Promise}
         */
-       Attachment = media.model.Attachment = Backbone.Model.extend({
-               /**
-                * Triggered when attachment details change
-                * Overrides Backbone.Model.sync
-                *
-                * @param {string} method
-                * @param {wp.media.model.Attachment} model
-                * @param {Object} [options={}]
-                *
-                * @returns {Promise}
-                */
-               sync: function( method, model, options ) {
-                       // If the attachment does not yet have an `id`, return an instantly
-                       // rejected promise. Otherwise, all of our requests will fail.
-                       if ( _.isUndefined( this.id ) ) {
-                               return $.Deferred().rejectWith( this ).promise();
-                       }
-
-                       // Overload the `read` request so Attachment.fetch() functions correctly.
-                       if ( 'read' === method ) {
-                               options = options || {};
-                               options.context = this;
-                               options.data = _.extend( options.data || {}, {
-                                       action: 'get-attachment',
-                                       id: this.id
-                               });
-                               return media.ajax( options );
+       sync: function( method, model, options ) {
+               // If the attachment does not yet have an `id`, return an instantly
+               // rejected promise. Otherwise, all of our requests will fail.
+               if ( _.isUndefined( this.id ) ) {
+                       return $.Deferred().rejectWith( this ).promise();
+               }
 
-                       // Overload the `update` request so properties can be saved.
-                       } else if ( 'update' === method ) {
-                               // If we do not have the necessary nonce, fail immeditately.
-                               if ( ! this.get('nonces') || ! this.get('nonces').update ) {
-                                       return $.Deferred().rejectWith( this ).promise();
-                               }
+               // Overload the `read` request so Attachment.fetch() functions correctly.
+               if ( 'read' === method ) {
+                       options = options || {};
+                       options.context = this;
+                       options.data = _.extend( options.data || {}, {
+                               action: 'get-attachment',
+                               id: this.id
+                       });
+                       return wp.media.ajax( options );
 
-                               options = options || {};
-                               options.context = this;
+               // Overload the `update` request so properties can be saved.
+               } else if ( 'update' === method ) {
+                       // If we do not have the necessary nonce, fail immeditately.
+                       if ( ! this.get('nonces') || ! this.get('nonces').update ) {
+                               return $.Deferred().rejectWith( this ).promise();
+                       }
 
-                               // Set the action and ID.
-                               options.data = _.extend( options.data || {}, {
-                                       action:  'save-attachment',
-                                       id:      this.id,
-                                       nonce:   this.get('nonces').update,
-                                       post_id: media.model.settings.post.id
-                               });
+                       options = options || {};
+                       options.context = this;
 
-                               // Record the values of the changed attributes.
-                               if ( model.hasChanged() ) {
-                                       options.data.changes = {};
+                       // Set the action and ID.
+                       options.data = _.extend( options.data || {}, {
+                               action:  'save-attachment',
+                               id:      this.id,
+                               nonce:   this.get('nonces').update,
+                               post_id: wp.media.model.settings.post.id
+                       });
 
-                                       _.each( model.changed, function( value, key ) {
-                                               options.data.changes[ key ] = this.get( key );
-                                       }, this );
-                               }
+                       // Record the values of the changed attributes.
+                       if ( model.hasChanged() ) {
+                               options.data.changes = {};
 
-                               return media.ajax( options );
+                               _.each( model.changed, function( value, key ) {
+                                       options.data.changes[ key ] = this.get( key );
+                               }, this );
+                       }
 
-                       // Overload the `delete` request so attachments can be removed.
-                       // This will permanently delete an attachment.
-                       } else if ( 'delete' === method ) {
-                               options = options || {};
+                       return wp.media.ajax( options );
 
-                               if ( ! options.wait ) {
-                                       this.destroyed = true;
-                               }
+               // Overload the `delete` request so attachments can be removed.
+               // This will permanently delete an attachment.
+               } else if ( 'delete' === method ) {
+                       options = options || {};
 
-                               options.context = this;
-                               options.data = _.extend( options.data || {}, {
-                                       action:   'delete-post',
-                                       id:       this.id,
-                                       _wpnonce: this.get('nonces')['delete']
-                               });
+                       if ( ! options.wait ) {
+                               this.destroyed = true;
+                       }
 
-                               return media.ajax( options ).done( function() {
-                                       this.destroyed = true;
-                               }).fail( function() {
-                                       this.destroyed = false;
-                               });
+                       options.context = this;
+                       options.data = _.extend( options.data || {}, {
+                               action:   'delete-post',
+                               id:       this.id,
+                               _wpnonce: this.get('nonces')['delete']
+                       });
 
-                       // Otherwise, fall back to `Backbone.sync()`.
-                       } else {
-                               /**
-                                * Call `sync` directly on Backbone.Model
-                                */
-                               return Backbone.Model.prototype.sync.apply( this, arguments );
-                       }
-               },
-               /**
-                * Convert date strings into Date objects.
-                *
-                * @param {Object} resp The raw response object, typically returned by fetch()
-                * @returns {Object} The modified response object, which is the attributes hash
-                *    to be set on the model.
-                */
-               parse: function( resp ) {
-                       if ( ! resp ) {
-                               return resp;
-                       }
+                       return wp.media.ajax( options ).done( function() {
+                               this.destroyed = true;
+                       }).fail( function() {
+                               this.destroyed = false;
+                       });
 
-                       resp.date = new Date( resp.date );
-                       resp.modified = new Date( resp.modified );
+               // Otherwise, fall back to `Backbone.sync()`.
+               } else {
+                       /**
+                        * Call `sync` directly on Backbone.Model
+                        */
+                       return Backbone.Model.prototype.sync.apply( this, arguments );
+               }
+       },
+       /**
+        * Convert date strings into Date objects.
+        *
+        * @param {Object} resp The raw response object, typically returned by fetch()
+        * @returns {Object} The modified response object, which is the attributes hash
+        *    to be set on the model.
+        */
+       parse: function( resp ) {
+               if ( ! resp ) {
                        return resp;
-               },
-               /**
-                * @param {Object} data The properties to be saved.
-                * @param {Object} options Sync options. e.g. patch, wait, success, error.
-                *
-                * @this Backbone.Model
-                *
-                * @returns {Promise}
-                */
-               saveCompat: function( data, options ) {
-                       var model = this;
+               }
 
-                       // If we do not have the necessary nonce, fail immeditately.
-                       if ( ! this.get('nonces') || ! this.get('nonces').update ) {
-                               return $.Deferred().rejectWith( this ).promise();
-                       }
+               resp.date = new Date( resp.date );
+               resp.modified = new Date( resp.modified );
+               return resp;
+       },
+       /**
+        * @param {Object} data The properties to be saved.
+        * @param {Object} options Sync options. e.g. patch, wait, success, error.
+        *
+        * @this Backbone.Model
+        *
+        * @returns {Promise}
+        */
+       saveCompat: function( data, options ) {
+               var model = this;
 
-                       return media.post( 'save-attachment-compat', _.defaults({
-                               id:      this.id,
-                               nonce:   this.get('nonces').update,
-                               post_id: media.model.settings.post.id
-                       }, data ) ).done( function( resp, status, xhr ) {
-                               model.set( model.parse( resp, xhr ), options );
-                       });
+               // If we do not have the necessary nonce, fail immeditately.
+               if ( ! this.get('nonces') || ! this.get('nonces').update ) {
+                       return $.Deferred().rejectWith( this ).promise();
                }
-       }, {
-               /**
-                * Create a new model on the static 'all' attachments collection and return it.
-                *
-                * @static
-                * @param {Object} attrs
-                * @returns {wp.media.model.Attachment}
-                */
-               create: function( attrs ) {
-                       return Attachments.all.push( attrs );
-               },
-               /**
-                * Create a new model on the static 'all' attachments collection and return it.
-                *
-                * If this function has already been called for the id,
-                * it returns the specified attachment.
-                *
-                * @static
-                * @param {string} id A string used to identify a model.
-                * @param {Backbone.Model|undefined} attachment
-                * @returns {wp.media.model.Attachment}
-                */
-               get: _.memoize( function( id, attachment ) {
-                       return Attachments.all.push( attachment || { id: id } );
-               })
-       });
 
+               return wp.media.post( 'save-attachment-compat', _.defaults({
+                       id:      this.id,
+                       nonce:   this.get('nonces').update,
+                       post_id: wp.media.model.settings.post.id
+               }, data ) ).done( function( resp, status, xhr ) {
+                       model.set( model.parse( resp, xhr ), options );
+               });
+       }
+}, {
        /**
-        * wp.media.model.PostImage
-        *
-        * An instance of an image that's been embedded into a post.
+        * Create a new model on the static 'all' attachments collection and return it.
         *
-        * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails.
+        * @static
+        * @param {Object} attrs
+        * @returns {wp.media.model.Attachment}
+        */
+       create: function( attrs ) {
+               var Attachments = wp.media.model.Attachments;
+               return Attachments.all.push( attrs );
+       },
+       /**
+        * Create a new model on the static 'all' attachments collection and return it.
         *
-        * @class
-        * @augments Backbone.Model
+        * If this function has already been called for the id,
+        * it returns the specified attachment.
         *
-        * @param {int} [attributes]               Initial model attributes.
-        * @param {int} [attributes.attachment_id] ID of the attachment.
-        **/
-       PostImage = media.model.PostImage = Backbone.Model.extend({
-
-               initialize: function( attributes ) {
-                       this.attachment = false;
-
-                       if ( attributes.attachment_id ) {
-                               this.attachment = Attachment.get( attributes.attachment_id );
-                               if ( this.attachment.get( 'url' ) ) {
-                                       this.dfd = $.Deferred();
-                                       this.dfd.resolve();
-                               } else {
-                                       this.dfd = this.attachment.fetch();
-                               }
-                               this.bindAttachmentListeners();
-                       }
+        * @static
+        * @param {string} id A string used to identify a model.
+        * @param {Backbone.Model|undefined} attachment
+        * @returns {wp.media.model.Attachment}
+        */
+       get: _.memoize( function( id, attachment ) {
+               var Attachments = wp.media.model.Attachments;
+               return Attachments.all.push( attachment || { id: id } );
+       })
+});
+
+module.exports = Attachment;
+
+},{}],3:[function(require,module,exports){
+/**
+ * wp.media.model.Attachments
+ *
+ * A collection of attachments.
+ *
+ * This collection has no persistence with the server without supplying
+ * 'options.props.query = true', which will mirror the collection
+ * to an Attachments Query collection - @see wp.media.model.Attachments.mirror().
+ *
+ * @class
+ * @augments Backbone.Collection
+ *
+ * @param {array}  [models]                Models to initialize with the collection.
+ * @param {object} [options]               Options hash for the collection.
+ * @param {string} [options.props]         Options hash for the initial query properties.
+ * @param {string} [options.props.order]   Initial order (ASC or DESC) for the collection.
+ * @param {string} [options.props.orderby] Initial attribute key to order the collection by.
+ * @param {string} [options.props.query]   Whether the collection is linked to an attachments query.
+ * @param {string} [options.observe]
+ * @param {string} [options.filters]
+ *
+ */
+var Attachments = Backbone.Collection.extend({
+       /**
+        * @type {wp.media.model.Attachment}
+        */
+       model: wp.media.model.Attachment,
+       /**
+        * @param {Array} [models=[]] Array of models used to populate the collection.
+        * @param {Object} [options={}]
+        */
+       initialize: function( models, options ) {
+               options = options || {};
 
-                       // 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.props   = new Backbone.Model();
+               this.filters = options.filters || {};
 
-                       this.setLinkTypeFromUrl();
-                       this.setAspectRatio();
+               // Bind default `change` events to the `props` model.
+               this.props.on( 'change', this._changeFilteredProps, this );
 
-                       this.set( 'originalUrl', attributes.url );
-               },
+               this.props.on( 'change:order',   this._changeOrder,   this );
+               this.props.on( 'change:orderby', this._changeOrderby, this );
+               this.props.on( 'change:query',   this._changeQuery,   this );
 
-               bindAttachmentListeners: function() {
-                       this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
-                       this.listenTo( this.attachment, 'sync', this.setAspectRatio );
-                       this.listenTo( this.attachment, 'change', this.updateSize );
-               },
+               this.props.set( _.defaults( options.props || {} ) );
 
-               changeAttachment: function( attachment, props ) {
-                       this.stopListening( this.attachment );
-                       this.attachment = attachment;
-                       this.bindAttachmentListeners();
+               if ( options.observe ) {
+                       this.observe( options.observe );
+               }
+       },
+       /**
+        * Sort the collection when the order attribute changes.
+        *
+        * @access private
+        */
+       _changeOrder: function() {
+               if ( this.comparator ) {
+                       this.sort();
+               }
+       },
+       /**
+        * Set the default comparator only when the `orderby` property is set.
+        *
+        * @access private
+        *
+        * @param {Backbone.Model} model
+        * @param {string} orderby
+        */
+       _changeOrderby: function( model, orderby ) {
+               // If a different comparator is defined, bail.
+               if ( this.comparator && this.comparator !== Attachments.comparator ) {
+                       return;
+               }
 
-                       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' ) );
-                       this.updateLinkUrl();
-                       this.updateSize();
-               },
+               if ( orderby && 'post__in' !== orderby ) {
+                       this.comparator = Attachments.comparator;
+               } else {
+                       delete this.comparator;
+               }
+       },
+       /**
+        * If the `query` property is set to true, query the server using
+        * the `props` values, and sync the results to this collection.
+        *
+        * @access private
+        *
+        * @param {Backbone.Model} model
+        * @param {Boolean} query
+        */
+       _changeQuery: function( model, query ) {
+               if ( query ) {
+                       this.props.on( 'change', this._requery, this );
+                       this._requery();
+               } else {
+                       this.props.off( 'change', this._requery, this );
+               }
+       },
+       /**
+        * @access private
+        *
+        * @param {Backbone.Model} model
+        */
+       _changeFilteredProps: function( model ) {
+               // If this is a query, updating the collection will be handled by
+               // `this._requery()`.
+               if ( this.props.get('query') ) {
+                       return;
+               }
 
-               setLinkTypeFromUrl: function() {
-                       var linkUrl = this.get( 'linkUrl' ),
-                               type;
+               var changed = _.chain( model.changed ).map( function( t, prop ) {
+                       var filter = Attachments.filters[ prop ],
+                               term = model.get( prop );
 
-                       if ( ! linkUrl ) {
-                               this.set( 'link', 'none' );
+                       if ( ! filter ) {
                                return;
                        }
 
-                       // default to custom if there is a linkUrl
-                       type = 'custom';
-
-                       if ( this.attachment ) {
-                               if ( this.attachment.get( 'url' ) === linkUrl ) {
-                                       type = 'file';
-                               } else if ( this.attachment.get( 'link' ) === linkUrl ) {
-                                       type = 'post';
-                               }
+                       if ( term && ! this.filters[ prop ] ) {
+                               this.filters[ prop ] = filter;
+                       } else if ( ! term && this.filters[ prop ] === filter ) {
+                               delete this.filters[ prop ];
                        } else {
-                               if ( this.get( 'url' ) === linkUrl ) {
-                                       type = 'file';
-                               }
-                       }
-
-                       this.set( 'link', type );
-               },
-
-               updateLinkUrl: function() {
-                       var link = this.get( 'link' ),
-                               url;
-
-                       switch( link ) {
-                               case 'file':
-                                       if ( this.attachment ) {
-                                               url = this.attachment.get( 'url' );
-                                       } else {
-                                               url = this.get( 'url' );
-                                       }
-                                       this.set( 'linkUrl', url );
-                                       break;
-                               case 'post':
-                                       this.set( 'linkUrl', this.attachment.get( 'link' ) );
-                                       break;
-                               case 'none':
-                                       this.set( 'linkUrl', '' );
-                                       break;
+                               return;
                        }
-               },
 
-               updateSize: function() {
-                       var size;
+                       // Record the change.
+                       return true;
+               }, this ).any().value();
 
-                       if ( ! this.attachment ) {
-                               return;
-                       }
+               if ( ! changed ) {
+                       return;
+               }
 
-                       if ( this.get( 'size' ) === 'custom' ) {
-                               this.set( 'width', this.get( 'customWidth' ) );
-                               this.set( 'height', this.get( 'customHeight' ) );
-                               this.set( 'url', this.get( 'originalUrl' ) );
-                               return;
-                       }
+               // If no `Attachments` model is provided to source the searches
+               // from, then automatically generate a source from the existing
+               // models.
+               if ( ! this._source ) {
+                       this._source = new Attachments( this.models );
+               }
 
-                       size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
+               this.reset( this._source.filter( this.validator, this ) );
+       },
 
-                       if ( ! size ) {
-                               return;
-                       }
+       validateDestroyed: false,
+       /**
+        * Checks whether an attachment is valid.
+        *
+        * @param {wp.media.model.Attachment} attachment
+        * @returns {Boolean}
+        */
+       validator: function( attachment ) {
+               if ( ! this.validateDestroyed && attachment.destroyed ) {
+                       return false;
+               }
+               return _.all( this.filters, function( filter ) {
+                       return !! filter.call( this, attachment );
+               }, this );
+       },
+       /**
+        * Add or remove an attachment to the collection depending on its validity.
+        *
+        * @param {wp.media.model.Attachment} attachment
+        * @param {Object} options
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+        */
+       validate: function( attachment, options ) {
+               var valid = this.validator( attachment ),
+                       hasAttachment = !! this.get( attachment.cid );
+
+               if ( ! valid && hasAttachment ) {
+                       this.remove( attachment, options );
+               } else if ( valid && ! hasAttachment ) {
+                       this.add( attachment, options );
+               }
 
-                       this.set( 'url', size.url );
-                       this.set( 'width', size.width );
-                       this.set( 'height', size.height );
-               },
+               return this;
+       },
 
-               setAspectRatio: function() {
-                       var full;
+       /**
+        * Add or remove all attachments from another collection depending on each one's validity.
+        *
+        * @param {wp.media.model.Attachments} attachments
+        * @param {object} [options={}]
+        *
+        * @fires wp.media.model.Attachments#reset
+        *
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+        */
+       validateAll: function( attachments, options ) {
+               options = options || {};
 
-                       if ( this.attachment && this.attachment.get( 'sizes' ) ) {
-                               full = this.attachment.get( 'sizes' ).full;
+               _.each( attachments.models, function( attachment ) {
+                       this.validate( attachment, { silent: true });
+               }, this );
 
-                               if ( full ) {
-                                       this.set( 'aspectRatio', full.width / full.height );
-                                       return;
-                               }
-                       }
+               if ( ! options.silent ) {
+                       this.trigger( 'reset', this, options );
+               }
+               return this;
+       },
+       /**
+        * Start observing another attachments collection change events
+        * and replicate them on this collection.
+        *
+        * @param {wp.media.model.Attachments} The attachments collection to observe.
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining.
+        */
+       observe: function( attachments ) {
+               this.observers = this.observers || [];
+               this.observers.push( attachments );
+
+               attachments.on( 'add change remove', this._validateHandler, this );
+               attachments.on( 'reset', this._validateAllHandler, this );
+               this.validateAll( attachments );
+               return this;
+       },
+       /**
+        * Stop replicating collection change events from another attachments collection.
+        *
+        * @param {wp.media.model.Attachments} The attachments collection to stop observing.
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+        */
+       unobserve: function( attachments ) {
+               if ( attachments ) {
+                       attachments.off( null, null, this );
+                       this.observers = _.without( this.observers, attachments );
 
-                       this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );
+               } else {
+                       _.each( this.observers, function( attachments ) {
+                               attachments.off( null, null, this );
+                       }, this );
+                       delete this.observers;
                }
-       });
 
+               return this;
+       },
        /**
-        * wp.media.model.Attachments
-        *
-        * A collection of attachments.
+        * @access private
         *
-        * This collection has no persistence with the server without supplying
-        * 'options.props.query = true', which will mirror the collection
-        * to an Attachments Query collection - @see wp.media.model.Attachments.mirror().
+        * @param {wp.media.model.Attachments} attachment
+        * @param {wp.media.model.Attachments} attachments
+        * @param {Object} options
         *
-        * @class
-        * @augments Backbone.Collection
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+        */
+       _validateHandler: function( attachment, attachments, options ) {
+               // If we're not mirroring this `attachments` collection,
+               // only retain the `silent` option.
+               options = attachments === this.mirroring ? options : {
+                       silent: options && options.silent
+               };
+
+               return this.validate( attachment, options );
+       },
+       /**
+        * @access private
         *
-        * @param {array}  [models]                Models to initialize with the collection.
-        * @param {object} [options]               Options hash for the collection.
-        * @param {string} [options.props]         Options hash for the initial query properties.
-        * @param {string} [options.props.order]   Initial order (ASC or DESC) for the collection.
-        * @param {string} [options.props.orderby] Initial attribute key to order the collection by.
-        * @param {string} [options.props.query]   Whether the collection is linked to an attachments query.
-        * @param {string} [options.observe]
-        * @param {string} [options.filters]
+        * @param {wp.media.model.Attachments} attachments
+        * @param {Object} options
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+        */
+       _validateAllHandler: function( attachments, options ) {
+               return this.validateAll( attachments, options );
+       },
+       /**
+        * Start mirroring another attachments collection, clearing out any models already
+        * in the collection.
         *
+        * @param {wp.media.model.Attachments} The attachments collection to mirror.
+        * @returns {wp.media.model.Attachments} Returns itself to allow chaining
         */
-       Attachments = media.model.Attachments = Backbone.Collection.extend({
-               /**
-                * @type {wp.media.model.Attachment}
-                */
-               model: Attachment,
-               /**
-                * @param {Array} [models=[]] Array of models used to populate the collection.
-                * @param {Object} [options={}]
-                */
-               initialize: function( models, options ) {
-                       options = options || {};
-
-                       this.props   = new Backbone.Model();
-                       this.filters = options.filters || {};
+       mirror: function( attachments ) {
+               if ( this.mirroring && this.mirroring === attachments ) {
+                       return this;
+               }
 
-                       // Bind default `change` events to the `props` model.
-                       this.props.on( 'change', this._changeFilteredProps, this );
+               this.unmirror();
+               this.mirroring = attachments;
 
-                       this.props.on( 'change:order',   this._changeOrder,   this );
-                       this.props.on( 'change:orderby', this._changeOrderby, this );
-                       this.props.on( 'change:query',   this._changeQuery,   this );
+               // Clear the collection silently. A `reset` event will be fired
+               // when `observe()` calls `validateAll()`.
+               this.reset( [], { silent: true } );
+               this.observe( attachments );
 
-                       this.props.set( _.defaults( options.props || {} ) );
+               return this;
+       },
+       /**
+        * Stop mirroring another attachments collection.
+        */
+       unmirror: function() {
+               if ( ! this.mirroring ) {
+                       return;
+               }
 
-                       if ( options.observe ) {
-                               this.observe( options.observe );
-                       }
-               },
-               /**
-                * Sort the collection when the order attribute changes.
-                *
-                * @access private
-                */
-               _changeOrder: function() {
-                       if ( this.comparator ) {
-                               this.sort();
-                       }
-               },
-               /**
-                * Set the default comparator only when the `orderby` property is set.
-                *
-                * @access private
-                *
-                * @param {Backbone.Model} model
-                * @param {string} orderby
-                */
-               _changeOrderby: function( model, orderby ) {
-                       // If a different comparator is defined, bail.
-                       if ( this.comparator && this.comparator !== Attachments.comparator ) {
-                               return;
-                       }
+               this.unobserve( this.mirroring );
+               delete 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
+        * @returns {Promise}
+        */
+       more: function( options ) {
+               var deferred = jQuery.Deferred(),
+                       mirroring = this.mirroring,
+                       attachments = this;
 
-                       if ( orderby && 'post__in' !== orderby ) {
-                               this.comparator = Attachments.comparator;
-                       } else {
-                               delete this.comparator;
-                       }
-               },
-               /**
-                * If the `query` property is set to true, query the server using
-                * the `props` values, and sync the results to this collection.
-                *
-                * @access private
-                *
-                * @param {Backbone.Model} model
-                * @param {Boolean} query
-                */
-               _changeQuery: function( model, query ) {
-                       if ( query ) {
-                               this.props.on( 'change', this._requery, this );
-                               this._requery();
-                       } else {
-                               this.props.off( 'change', this._requery, this );
-                       }
-               },
-               /**
-                * @access private
-                *
-                * @param {Backbone.Model} model
-                */
-               _changeFilteredProps: function( model ) {
-                       // If this is a query, updating the collection will be handled by
-                       // `this._requery()`.
-                       if ( this.props.get('query') ) {
-                               return;
+               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
+               // the request resolves.
+               mirroring.more( options ).done( function() {
+                       if ( this === attachments.mirroring ) {
+                               deferred.resolveWith( this );
                        }
+               });
 
-                       var changed = _.chain( model.changed ).map( function( t, prop ) {
-                               var filter = Attachments.filters[ prop ],
-                                       term = model.get( prop );
-
-                               if ( ! filter ) {
-                                       return;
-                               }
-
-                               if ( term && ! this.filters[ prop ] ) {
-                                       this.filters[ prop ] = filter;
-                               } else if ( ! term && this.filters[ prop ] === filter ) {
-                                       delete this.filters[ prop ];
-                               } else {
-                                       return;
-                               }
-
-                               // Record the change.
-                               return true;
-                       }, this ).any().value();
+               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.
+        *
+        * @returns {boolean}
+        */
+       hasMore: function() {
+               return this.mirroring ? this.mirroring.hasMore() : false;
+       },
+       /**
+        * A custom AJAX-response parser.
+        *
+        * See trac ticket #24753
+        *
+        * @param {Object|Array} resp The raw response Object/Array.
+        * @param {Object} xhr
+        * @returns {Array} The array of model attributes to be added to the collection
+        */
+       parse: function( resp, xhr ) {
+               if ( ! _.isArray( resp ) ) {
+                       resp = [resp];
+               }
 
-                       if ( ! changed ) {
-                               return;
-                       }
+               return _.map( resp, function( attrs ) {
+                       var id, attachment, newAttributes;
 
-                       // If no `Attachments` model is provided to source the searches
-                       // from, then automatically generate a source from the existing
-                       // models.
-                       if ( ! this._source ) {
-                               this._source = new Attachments( this.models );
+                       if ( attrs instanceof Backbone.Model ) {
+                               id = attrs.get( 'id' );
+                               attrs = attrs.attributes;
+                       } else {
+                               id = attrs.id;
                        }
 
-                       this.reset( this._source.filter( this.validator, this ) );
-               },
+                       attachment = wp.media.model.Attachment.get( id );
+                       newAttributes = attachment.parse( attrs, xhr );
 
-               validateDestroyed: false,
-               /**
-                * Checks whether an attachment is valid.
-                *
-                * @param {wp.media.model.Attachment} attachment
-                * @returns {Boolean}
-                */
-               validator: function( attachment ) {
-                       if ( ! this.validateDestroyed && attachment.destroyed ) {
-                               return false;
+                       if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
+                               attachment.set( newAttributes );
                        }
-                       return _.all( this.filters, function( filter ) {
-                               return !! filter.call( this, attachment );
-                       }, this );
-               },
-               /**
-                * Add or remove an attachment to the collection depending on its validity.
-                *
-                * @param {wp.media.model.Attachment} attachment
-                * @param {Object} options
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
-                */
-               validate: function( attachment, options ) {
-                       var valid = this.validator( attachment ),
-                               hasAttachment = !! this.get( attachment.cid );
-
-                       if ( ! valid && hasAttachment ) {
-                               this.remove( attachment, options );
-                       } else if ( valid && ! hasAttachment ) {
-                               this.add( attachment, options );
-                       }
-
-                       return this;
-               },
-
-               /**
-                * Add or remove all attachments from another collection depending on each one's validity.
-                *
-                * @param {wp.media.model.Attachments} attachments
-                * @param {object} [options={}]
-                *
-                * @fires wp.media.model.Attachments#reset
-                *
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
-                */
-               validateAll: function( attachments, options ) {
-                       options = options || {};
 
-                       _.each( attachments.models, function( attachment ) {
-                               this.validate( attachment, { silent: true });
-                       }, this );
+                       return attachment;
+               });
+       },
+       /**
+        * If the collection is a query, create and mirror an Attachments Query collection.
+        *
+        * @access private
+        */
+       _requery: function( refresh ) {
+               var props;
+               if ( this.props.get('query') ) {
+                       props = this.props.toJSON();
+                       props.cache = ( true !== refresh );
+                       this.mirror( wp.media.model.Query.get( props ) );
+               }
+       },
+       /**
+        * If this collection is sorted by `menuOrder`, recalculates and saves
+        * the menu order to the database.
+        *
+        * @returns {undefined|Promise}
+        */
+       saveMenuOrder: function() {
+               if ( 'menuOrder' !== this.props.get('orderby') ) {
+                       return;
+               }
 
-                       if ( ! options.silent ) {
-                               this.trigger( 'reset', this, options );
-                       }
-                       return this;
-               },
-               /**
-                * Start observing another attachments collection change events
-                * and replicate them on this collection.
-                *
-                * @param {wp.media.model.Attachments} The attachments collection to observe.
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining.
-                */
-               observe: function( attachments ) {
-                       this.observers = this.observers || [];
-                       this.observers.push( attachments );
+               // 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 ) {
+                       // Indices start at 1.
+                       index = index + 1;
+                       attachment.set( 'menuOrder', index );
+                       return [ attachment.id, index ];
+               }).object().value();
+
+               if ( _.isEmpty( attachments ) ) {
+                       return;
+               }
 
-                       attachments.on( 'add change remove', this._validateHandler, this );
-                       attachments.on( 'reset', this._validateAllHandler, this );
-                       this.validateAll( attachments );
-                       return this;
-               },
-               /**
-                * Stop replicating collection change events from another attachments collection.
-                *
-                * @param {wp.media.model.Attachments} The attachments collection to stop observing.
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
-                */
-               unobserve: function( attachments ) {
-                       if ( attachments ) {
-                               attachments.off( null, null, this );
-                               this.observers = _.without( this.observers, attachments );
+               return wp.media.post( 'save-attachment-order', {
+                       nonce:       wp.media.model.settings.post.nonce,
+                       post_id:     wp.media.model.settings.post.id,
+                       attachments: 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().
+        *
+        * @static
+        *
+        * @param {Backbone.Model} a
+        * @param {Backbone.Model} b
+        * @param {Object} options
+        * @returns {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',
+                       ac    = a.cid,
+                       bc    = b.cid;
+
+               a = a.get( key );
+               b = b.get( key );
+
+               if ( 'date' === key || 'modified' === key ) {
+                       a = a || new Date();
+                       b = b || new Date();
+               }
 
-                       } else {
-                               _.each( this.observers, function( attachments ) {
-                                       attachments.off( null, null, this );
-                               }, this );
-                               delete this.observers;
-                       }
+               // If `options.ties` is set, don't enforce the `cid` tiebreaker.
+               if ( options && options.ties ) {
+                       ac = bc = null;
+               }
 
-                       return this;
-               },
+               return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
+       },
+       /**
+        * @namespace
+        */
+       filters: {
                /**
-                * @access private
+                * @static
+                * Note that this client-side searching is *not* equivalent
+                * to our server-side searching.
+                *
+                * @param {wp.media.model.Attachment} attachment
                 *
-                * @param {wp.media.model.Attachments} attachment
-                * @param {wp.media.model.Attachments} attachments
-                * @param {Object} options
+                * @this wp.media.model.Attachments
                 *
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+                * @returns {Boolean}
                 */
-               _validateHandler: function( attachment, attachments, options ) {
-                       // If we're not mirroring this `attachments` collection,
-                       // only retain the `silent` option.
-                       options = attachments === this.mirroring ? options : {
-                               silent: options && options.silent
-                       };
+               search: function( attachment ) {
+                       if ( ! this.props.get('search') ) {
+                               return true;
+                       }
 
-                       return this.validate( attachment, options );
+                       return _.any(['title','filename','description','caption','name'], function( key ) {
+                               var value = attachment.get( key );
+                               return value && -1 !== value.search( this.props.get('search') );
+                       }, this );
                },
                /**
-                * @access private
+                * @static
+                * @param {wp.media.model.Attachment} attachment
                 *
-                * @param {wp.media.model.Attachments} attachments
-                * @param {Object} options
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
-                */
-               _validateAllHandler: function( attachments, options ) {
-                       return this.validateAll( attachments, options );
-               },
-               /**
-                * Start mirroring another attachments collection, clearing out any models already
-                * in the collection.
+                * @this wp.media.model.Attachments
                 *
-                * @param {wp.media.model.Attachments} The attachments collection to mirror.
-                * @returns {wp.media.model.Attachments} Returns itself to allow chaining
+                * @returns {Boolean}
                 */
-               mirror: function( attachments ) {
-                       if ( this.mirroring && this.mirroring === attachments ) {
-                               return this;
-                       }
+               type: function( attachment ) {
+                       var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
 
-                       this.unmirror();
-                       this.mirroring = attachments;
+                       if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
+                               return true;
+                       }
 
-                       // Clear the collection silently. A `reset` event will be fired
-                       // when `observe()` calls `validateAll()`.
-                       this.reset( [], { silent: true } );
-                       this.observe( attachments );
+                       mime = atts.mime || ( atts.file && atts.file.type ) || '';
 
-                       return this;
-               },
-               /**
-                * Stop mirroring another attachments collection.
-                */
-               unmirror: function() {
-                       if ( ! this.mirroring ) {
-                               return;
+                       if ( _.isArray( type ) ) {
+                               found = _.find( type, function (t) {
+                                       return -1 !== mime.indexOf( t );
+                               } );
+                       } else {
+                               found = -1 !== mime.indexOf( type );
                        }
 
-                       this.unobserve( this.mirroring );
-                       delete this.mirroring;
+                       return found;
                },
                /**
-                * Retrive more attachments from the server for the collection.
+                * @static
+                * @param {wp.media.model.Attachment} attachment
                 *
-                * 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.
+                * @this wp.media.model.Attachments
                 *
-                * @param {object} options
-                * @returns {Promise}
+                * @returns {Boolean}
                 */
-               more: function( options ) {
-                       var deferred = $.Deferred(),
-                               mirroring = this.mirroring,
-                               attachments = this;
-
-                       if ( ! mirroring || ! mirroring.more ) {
-                               return deferred.resolveWith( this ).promise();
+               uploadedTo: function( attachment ) {
+                       var uploadedTo = this.props.get('uploadedTo');
+                       if ( _.isUndefined( uploadedTo ) ) {
+                               return true;
                        }
-                       // 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
-                       // the request resolves.
-                       mirroring.more( options ).done( function() {
-                               if ( this === attachments.mirroring )
-                                       deferred.resolveWith( this );
-                       });
 
-                       return deferred.promise();
+                       return uploadedTo === attachment.get('uploadedTo');
                },
                /**
-                * 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.
-                *
-                * @returns {boolean}
-                */
-               hasMore: function() {
-                       return this.mirroring ? this.mirroring.hasMore() : false;
-               },
-               /**
-                * A custom AJAX-response parser.
+                * @static
+                * @param {wp.media.model.Attachment} attachment
                 *
-                * See trac ticket #24753
+                * @this wp.media.model.Attachments
                 *
-                * @param {Object|Array} resp The raw response Object/Array.
-                * @param {Object} xhr
-                * @returns {Array} The array of model attributes to be added to the collection
+                * @returns {Boolean}
                 */
-               parse: function( resp, xhr ) {
-                       if ( ! _.isArray( resp ) ) {
-                               resp = [resp];
+               status: function( attachment ) {
+                       var status = this.props.get('status');
+                       if ( _.isUndefined( status ) ) {
+                               return true;
                        }
 
-                       return _.map( resp, function( attrs ) {
-                               var id, attachment, newAttributes;
-
-                               if ( attrs instanceof Backbone.Model ) {
-                                       id = attrs.get( 'id' );
-                                       attrs = attrs.attributes;
-                               } else {
-                                       id = attrs.id;
-                               }
+                       return status === attachment.get('status');
+               }
+       }
+});
+
+module.exports = Attachments;
+
+},{}],4:[function(require,module,exports){
+/**
+ * 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.
+ *
+ * @class
+ * @augments Backbone.Model
+ *
+ * @param {int} [attributes]               Initial model attributes.
+ * @param {int} [attributes.attachment_id] ID of the attachment.
+ **/
+var PostImage = Backbone.Model.extend({
+
+       initialize: function( attributes ) {
+               var Attachment = wp.media.model.Attachment;
+               this.attachment = false;
+
+               if ( attributes.attachment_id ) {
+                       this.attachment = Attachment.get( attributes.attachment_id );
+                       if ( this.attachment.get( 'url' ) ) {
+                               this.dfd = jQuery.Deferred();
+                               this.dfd.resolve();
+                       } else {
+                               this.dfd = this.attachment.fetch();
+                       }
+                       this.bindAttachmentListeners();
+               }
 
-                               attachment = Attachment.get( id );
-                               newAttributes = attachment.parse( attrs, xhr );
+               // 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.setAspectRatio();
+
+               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' ) );
+               this.updateLinkUrl();
+               this.updateSize();
+       },
+
+       setLinkTypeFromUrl: function() {
+               var linkUrl = this.get( 'linkUrl' ),
+                       type;
+
+               if ( ! linkUrl ) {
+                       this.set( 'link', 'none' );
+                       return;
+               }
 
-                               if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
-                                       attachment.set( newAttributes );
-                               }
+               // default to custom if there is a linkUrl
+               type = 'custom';
 
-                               return attachment;
-                       });
-               },
-               /**
-                * If the collection is a query, create and mirror an Attachments Query collection.
-                *
-                * @access private
-                */
-               _requery: function( refresh ) {
-                       var props;
-                       if ( this.props.get('query') ) {
-                               props = this.props.toJSON();
-                               props.cache = ( true !== refresh );
-                               this.mirror( Query.get( props ) );
+               if ( this.attachment ) {
+                       if ( this.attachment.get( 'url' ) === linkUrl ) {
+                               type = 'file';
+                       } else if ( this.attachment.get( 'link' ) === linkUrl ) {
+                               type = 'post';
                        }
-               },
-               /**
-                * If this collection is sorted by `menuOrder`, recalculates and saves
-                * the menu order to the database.
-                *
-                * @returns {undefined|Promise}
-                */
-               saveMenuOrder: function() {
-                       if ( 'menuOrder' !== this.props.get('orderby') ) {
-                               return;
+               } else {
+                       if ( this.get( 'url' ) === linkUrl ) {
+                               type = 'file';
                        }
+               }
 
-                       // 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 ) {
-                               // Indices start at 1.
-                               index = index + 1;
-                               attachment.set( 'menuOrder', index );
-                               return [ attachment.id, index ];
-                       }).object().value();
-
-                       if ( _.isEmpty( attachments ) ) {
-                               return;
-                       }
+               this.set( 'link', type );
+       },
 
-                       return media.post( 'save-attachment-order', {
-                               nonce:       media.model.settings.post.nonce,
-                               post_id:     media.model.settings.post.id,
-                               attachments: attachments
-                       });
+       updateLinkUrl: function() {
+               var link = this.get( 'link' ),
+                       url;
+
+               switch( link ) {
+                       case 'file':
+                               if ( this.attachment ) {
+                                       url = this.attachment.get( 'url' );
+                               } else {
+                                       url = this.get( 'url' );
+                               }
+                               this.set( 'linkUrl', url );
+                               break;
+                       case 'post':
+                               this.set( 'linkUrl', this.attachment.get( 'link' ) );
+                               break;
+                       case 'none':
+                               this.set( 'linkUrl', '' );
+                               break;
                }
-       }, {
-               /**
-                * 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().
-                *
-                * @static
-                *
-                * @param {Backbone.Model} a
-                * @param {Backbone.Model} b
-                * @param {Object} options
-                * @returns {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',
-                               ac    = a.cid,
-                               bc    = b.cid;
-
-                       a = a.get( key );
-                       b = b.get( key );
-
-                       if ( 'date' === key || 'modified' === key ) {
-                               a = a || new Date();
-                               b = b || new Date();
-                       }
+       },
 
-                       // If `options.ties` is set, don't enforce the `cid` tiebreaker.
-                       if ( options && options.ties ) {
-                               ac = bc = null;
-                       }
+       updateSize: function() {
+               var size;
 
-                       return ( 'DESC' === order ) ? compare( a, b, ac, bc ) : compare( b, a, bc, ac );
-               },
-               /**
-                * @namespace
-                */
-               filters: {
-                       /**
-                        * @static
-                        * 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
-                        *
-                        * @returns {Boolean}
-                        */
-                       search: function( attachment ) {
-                               if ( ! this.props.get('search') ) {
-                                       return true;
-                               }
+               if ( ! this.attachment ) {
+                       return;
+               }
 
-                               return _.any(['title','filename','description','caption','name'], function( key ) {
-                                       var value = attachment.get( key );
-                                       return value && -1 !== value.search( this.props.get('search') );
-                               }, this );
-                       },
-                       /**
-                        * @static
-                        * @param {wp.media.model.Attachment} attachment
-                        *
-                        * @this wp.media.model.Attachments
-                        *
-                        * @returns {Boolean}
-                        */
-                       type: function( attachment ) {
-                               var type = this.props.get('type');
-                               return ! type || -1 !== type.indexOf( attachment.get('type') );
-                       },
-                       /**
-                        * @static
-                        * @param {wp.media.model.Attachment} attachment
-                        *
-                        * @this wp.media.model.Attachments
-                        *
-                        * @returns {Boolean}
-                        */
-                       uploadedTo: function( attachment ) {
-                               var uploadedTo = this.props.get('uploadedTo');
-                               if ( _.isUndefined( uploadedTo ) ) {
-                                       return true;
-                               }
+               if ( this.get( 'size' ) === 'custom' ) {
+                       this.set( 'width', this.get( 'customWidth' ) );
+                       this.set( 'height', this.get( 'customHeight' ) );
+                       this.set( 'url', this.get( 'originalUrl' ) );
+                       return;
+               }
 
-                               return uploadedTo === attachment.get('uploadedTo');
-                       },
-                       /**
-                        * @static
-                        * @param {wp.media.model.Attachment} attachment
-                        *
-                        * @this wp.media.model.Attachments
-                        *
-                        * @returns {Boolean}
-                        */
-                       status: function( attachment ) {
-                               var status = this.props.get('status');
-                               if ( _.isUndefined( status ) ) {
-                                       return true;
-                               }
+               size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
+
+               if ( ! size ) {
+                       return;
+               }
 
-                               return status === attachment.get('status');
+               this.set( 'url', size.url );
+               this.set( 'width', size.width );
+               this.set( 'height', size.height );
+       },
+
+       setAspectRatio: function() {
+               var full;
+
+               if ( this.attachment && this.attachment.get( 'sizes' ) ) {
+                       full = this.attachment.get( 'sizes' ).full;
+
+                       if ( full ) {
+                               this.set( 'aspectRatio', full.width / full.height );
+                               return;
                        }
                }
-       });
 
+               this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );
+       }
+});
+
+module.exports = PostImage;
+
+},{}],5:[function(require,module,exports){
+/**
+ * wp.media.model.Query
+ *
+ * A collection of attachments that match the supplied query arguments.
+ *
+ * Note: Do NOT change this.args after the query has been initialized.
+ *       Things will break.
+ *
+ * @class
+ * @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]
+ */
+var Attachments = wp.media.model.Attachments,
+       Query;
+
+Query = Attachments.extend({
        /**
-        * A collection of all attachments that have been fetched from the server.
+        * @global wp.Uploader
         *
-        * @static
-        * @member {wp.media.model.Attachments}
+        * @param {array}  [models=[]]  Array of initial models to populate the collection.
+        * @param {object} [options={}]
         */
-       Attachments.all = new Attachments();
+       initialize: function( models, options ) {
+               var allowed;
+
+               options = options || {};
+               Attachments.prototype.initialize.apply( this, arguments );
+
+               this.args     = options.args;
+               this._hasMore = true;
+               this.created  = new Date();
+
+               this.filters.order = function( attachment ) {
+                       var orderby = this.props.get('orderby'),
+                               order = this.props.get('order');
+
+                       if ( ! this.comparator ) {
+                               return true;
+                       }
 
+                       // 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.
+                       if ( this.length ) {
+                               return 1 !== this.comparator( attachment, this.last(), { ties: true });
+
+                       // Handle the case where there are no items yet and
+                       // we're sorting for recent items. In that case, we want
+                       // changes that occurred after we created the query.
+                       } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) {
+                               return attachment.get( orderby ) >= this.created;
+
+                       // If we're sorting by menu order and we have no items,
+                       // accept any items that have the default menu order (0).
+                       } else if ( 'ASC' === order && 'menuOrder' === orderby ) {
+                               return attachment.get( orderby ) === 0;
+                       }
+
+                       // Otherwise, we don't want any items yet.
+                       return false;
+               };
+
+               // Observe the central `wp.Uploader.queue` collection to watch for
+               // new matches for the query.
+               //
+               // Only observe when a limited number of query args are set. There
+               // are no filters for other properties, so observing will result in
+               // false positives in those queries.
+               allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent' ];
+               if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) {
+                       this.observe( wp.Uploader.queue );
+               }
+       },
        /**
-        * wp.media.query
-        *
-        * Shorthand for creating a new Attachments Query.
+        * Whether there are more attachments that haven't been sync'd from the server
+        * that match the collection's query.
         *
-        * @param {object} [props]
-        * @returns {wp.media.model.Attachments}
+        * @returns {boolean}
         */
-       media.query = function( props ) {
-               return new Attachments( null, {
-                       props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
-               });
-       };
-
+       hasMore: function() {
+               return this._hasMore;
+       },
        /**
-        * wp.media.model.Query
-        *
-        * A collection of attachments that match the supplied query arguments.
+        * Fetch more attachments from the server for the collection.
         *
-        * Note: Do NOT change this.args after the query has been initialized.
-        *       Things will break.
-        *
-        * @class
-        * @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]
+        * @param   {object}  [options={}]
+        * @returns {Promise}
         */
-       Query = media.model.Query = Attachments.extend({
-               /**
-                * @global wp.Uploader
-                *
-                * @param {array}  [models=[]]  Array of initial models to populate the collection.
-                * @param {object} [options={}]
-                */
-               initialize: function( models, options ) {
-                       var allowed;
-
-                       options = options || {};
-                       Attachments.prototype.initialize.apply( this, arguments );
-
-                       this.args     = options.args;
-                       this._hasMore = true;
-                       this.created  = new Date();
+       more: function( options ) {
+               var query = this;
 
-                       this.filters.order = function( attachment ) {
-                               var orderby = this.props.get('orderby'),
-                                       order = this.props.get('order');
-
-                               if ( ! this.comparator ) {
-                                       return true;
-                               }
-
-                               // 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.
-                               if ( this.length ) {
-                                       return 1 !== this.comparator( attachment, this.last(), { ties: true });
-
-                               // Handle the case where there are no items yet and
-                               // we're sorting for recent items. In that case, we want
-                               // changes that occurred after we created the query.
-                               } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) {
-                                       return attachment.get( orderby ) >= this.created;
-
-                               // If we're sorting by menu order and we have no items,
-                               // accept any items that have the default menu order (0).
-                               } else if ( 'ASC' === order && 'menuOrder' === orderby ) {
-                                       return attachment.get( orderby ) === 0;
-                               }
-
-                               // Otherwise, we don't want any items yet.
-                               return false;
-                       };
+               // If there is already a request pending, return early with the Deferred object.
+               if ( this._more && 'pending' === this._more.state() ) {
+                       return this._more;
+               }
 
-                       // Observe the central `wp.Uploader.queue` collection to watch for
-                       // new matches for the query.
-                       //
-                       // Only observe when a limited number of query args are set. There
-                       // are no filters for other properties, so observing will result in
-                       // false positives in those queries.
-                       allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent' ];
-                       if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) {
-                               this.observe( wp.Uploader.queue );
-                       }
-               },
-               /**
-                * Whether there are more attachments that haven't been sync'd from the server
-                * that match the collection's query.
-                *
-                * @returns {boolean}
-                */
-               hasMore: function() {
-                       return this._hasMore;
-               },
-               /**
-                * Fetch more attachments from the server for the collection.
-                *
-                * @param   {object}  [options={}]
-                * @returns {Promise}
-                */
-               more: function( options ) {
-                       var query = this;
+               if ( ! this.hasMore() ) {
+                       return jQuery.Deferred().resolveWith( this ).promise();
+               }
 
-                       // If there is already a request pending, return early with the Deferred object.
-                       if ( this._more && 'pending' === this._more.state() ) {
-                               return this._more;
-                       }
+               options = options || {};
+               options.remove = false;
 
-                       if ( ! this.hasMore() ) {
-                               return $.Deferred().resolveWith( this ).promise();
+               return this._more = this.fetch( options ).done( function( resp ) {
+                       if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
+                               query._hasMore = false;
                        }
+               });
+       },
+       /**
+        * Overrides Backbone.Collection.sync
+        * Overrides wp.media.model.Attachments.sync
+        *
+        * @param {String} method
+        * @param {Backbone.Model} model
+        * @param {Object} [options={}]
+        * @returns {Promise}
+        */
+       sync: function( method, model, options ) {
+               var args, fallback;
 
+               // Overload the read method so Attachment.fetch() functions correctly.
+               if ( 'read' === method ) {
                        options = options || {};
-                       options.remove = false;
-
-                       return this._more = this.fetch( options ).done( function( resp ) {
-                               if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
-                                       query._hasMore = false;
-                               }
+                       options.context = this;
+                       options.data = _.extend( options.data || {}, {
+                               action:  'query-attachments',
+                               post_id: wp.media.model.settings.post.id
                        });
-               },
-               /**
-                * Overrides Backbone.Collection.sync
-                * Overrides wp.media.model.Attachments.sync
-                *
-                * @param {String} method
-                * @param {Backbone.Model} model
-                * @param {Object} [options={}]
-                * @returns {Promise}
-                */
-               sync: function( method, model, options ) {
-                       var args, fallback;
-
-                       // Overload the read method so Attachment.fetch() functions correctly.
-                       if ( 'read' === method ) {
-                               options = options || {};
-                               options.context = this;
-                               options.data = _.extend( options.data || {}, {
-                                       action:  'query-attachments',
-                                       post_id: media.model.settings.post.id
-                               });
 
-                               // Clone the args so manipulation is non-destructive.
-                               args = _.clone( this.args );
+                       // Clone the args so manipulation is non-destructive.
+                       args = _.clone( this.args );
 
-                               // Determine which page to query.
-                               if ( -1 !== args.posts_per_page ) {
-                                       args.paged = Math.floor( this.length / args.posts_per_page ) + 1;
-                               }
+                       // Determine which page to query.
+                       if ( -1 !== args.posts_per_page ) {
+                               args.paged = Math.round( this.length / args.posts_per_page ) + 1;
+                       }
 
-                               options.data.query = args;
-                               return media.ajax( options );
+                       options.data.query = args;
+                       return wp.media.ajax( options );
 
-                       // Otherwise, fall back to Backbone.sync()
-                       } else {
-                               /**
-                                * Call wp.media.model.Attachments.sync or Backbone.sync
-                                */
-                               fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone;
-                               return fallback.sync.apply( this, arguments );
-                       }
+               // Otherwise, fall back to Backbone.sync()
+               } else {
+                       /**
+                        * Call wp.media.model.Attachments.sync or Backbone.sync
+                        */
+                       fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone;
+                       return fallback.sync.apply( this, arguments );
                }
-       }, {
+       }
+}, {
+       /**
+        * @readonly
+        */
+       defaultProps: {
+               orderby: 'date',
+               order:   'DESC'
+       },
+       /**
+        * @readonly
+        */
+       defaultArgs: {
+               posts_per_page: 40
+       },
+       /**
+        * @readonly
+        */
+       orderby: {
+               allowed:  [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ],
                /**
-                * @readonly
+                * A map of JavaScript orderby values to their WP_Query equivalents.
+                * @type {Object}
                 */
-               defaultProps: {
-                       orderby: 'date',
-                       order:   'DESC'
-               },
+               valuemap: {
+                       'id':         'ID',
+                       'uploadedTo': 'parent',
+                       'menuOrder':  'menu_order ID'
+               }
+       },
+       /**
+        * A map of JavaScript query properties to their WP_Query equivalents.
+        *
+        * @readonly
+        */
+       propmap: {
+               'search':    's',
+               'type':      'post_mime_type',
+               'perPage':   'posts_per_page',
+               'menuOrder': 'menu_order',
+               'uploadedTo': 'post_parent',
+               'status':     'post_status',
+               'include':    'post__in',
+               'exclude':    'post__not_in'
+       },
+       /**
+        * Creates and returns an Attachments Query collection given the properties.
+        *
+        * Caches query objects and reuses where possible.
+        *
+        * @static
+        * @method
+        *
+        * @param {object} [props]
+        * @param {Object} [props.cache=true]   Whether to use the query cache or not.
+        * @param {Object} [props.order]
+        * @param {Object} [props.orderby]
+        * @param {Object} [props.include]
+        * @param {Object} [props.exclude]
+        * @param {Object} [props.s]
+        * @param {Object} [props.post_mime_type]
+        * @param {Object} [props.posts_per_page]
+        * @param {Object} [props.menu_order]
+        * @param {Object} [props.post_parent]
+        * @param {Object} [props.post_status]
+        * @param {Object} [options]
+        *
+        * @returns {wp.media.model.Query} A new Attachments Query collection.
+        */
+       get: (function(){
                /**
-                * @readonly
+                * @static
+                * @type Array
                 */
-               defaultArgs: {
-                       posts_per_page: 40
-               },
+               var queries = [];
+
                /**
-                * @readonly
+                * @returns {Query}
                 */
-               orderby: {
-                       allowed:  [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ],
-                       /**
-                        * A map of JavaScript orderby values to their WP_Query equivalents.
-                        * @type {Object}
-                        */
-                       valuemap: {
-                               'id':         'ID',
-                               'uploadedTo': 'parent',
-                               'menuOrder':  'menu_order ID'
+               return function( props, options ) {
+                       var args     = {},
+                               orderby  = Query.orderby,
+                               defaults = Query.defaultProps,
+                               query,
+                               cache    = !! props.cache || _.isUndefined( props.cache );
+
+                       // Remove the `query` property. This isn't linked to a query,
+                       // this *is* the query.
+                       delete props.query;
+                       delete props.cache;
+
+                       // Fill default args.
+                       _.defaults( props, defaults );
+
+                       // Normalize the order.
+                       props.order = props.order.toUpperCase();
+                       if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
+                               props.order = defaults.order.toUpperCase();
                        }
-               },
-               /**
-                * A map of JavaScript query properties to their WP_Query equivalents.
-                *
-                * @readonly
-                */
-               propmap: {
-                       'search':    's',
-                       'type':      'post_mime_type',
-                       'perPage':   'posts_per_page',
-                       'menuOrder': 'menu_order',
-                       'uploadedTo': 'post_parent',
-                       'status':     'post_status',
-                       'include':    'post__in',
-                       'exclude':    'post__not_in'
-               },
-               /**
-                * Creates and returns an Attachments Query collection given the properties.
-                *
-                * Caches query objects and reuses where possible.
-                *
-                * @static
-                * @method
-                *
-                * @param {object} [props]
-                * @param {Object} [props.cache=true]   Whether to use the query cache or not.
-                * @param {Object} [props.order]
-                * @param {Object} [props.orderby]
-                * @param {Object} [props.include]
-                * @param {Object} [props.exclude]
-                * @param {Object} [props.s]
-                * @param {Object} [props.post_mime_type]
-                * @param {Object} [props.posts_per_page]
-                * @param {Object} [props.menu_order]
-                * @param {Object} [props.post_parent]
-                * @param {Object} [props.post_status]
-                * @param {Object} [options]
-                *
-                * @returns {wp.media.model.Query} A new Attachments Query collection.
-                */
-               get: (function(){
-                       /**
-                        * @static
-                        * @type Array
-                        */
-                       var queries = [];
 
-                       /**
-                        * @returns {Query}
-                        */
-                       return function( props, options ) {
-                               var args     = {},
-                                       orderby  = Query.orderby,
-                                       defaults = Query.defaultProps,
-                                       query,
-                                       cache    = !! props.cache || _.isUndefined( props.cache );
-
-                               // Remove the `query` property. This isn't linked to a query,
-                               // this *is* the query.
-                               delete props.query;
-                               delete props.cache;
-
-                               // Fill default args.
-                               _.defaults( props, defaults );
-
-                               // Normalize the order.
-                               props.order = props.order.toUpperCase();
-                               if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
-                                       props.order = defaults.order.toUpperCase();
-                               }
+                       // Ensure we have a valid orderby value.
+                       if ( ! _.contains( orderby.allowed, props.orderby ) ) {
+                               props.orderby = defaults.orderby;
+                       }
 
-                               // Ensure we have a valid orderby value.
-                               if ( ! _.contains( orderby.allowed, props.orderby ) ) {
-                                       props.orderby = defaults.orderby;
+                       _.each( [ 'include', 'exclude' ], function( prop ) {
+                               if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
+                                       props[ prop ] = [ props[ prop ] ];
                                }
+                       } );
 
-                               _.each( [ 'include', 'exclude' ], function( prop ) {
-                                       if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
-                                               props[ prop ] = [ props[ prop ] ];
-                                       }
-                               } );
-
-                               // Generate the query `args` object.
-                               // Correct any differing property names.
-                               _.each( props, function( value, prop ) {
-                                       if ( _.isNull( value ) ) {
-                                               return;
-                                       }
-
-                                       args[ Query.propmap[ prop ] || prop ] = value;
-                               });
+                       // Generate the query `args` object.
+                       // Correct any differing property names.
+                       _.each( props, function( value, prop ) {
+                               if ( _.isNull( value ) ) {
+                                       return;
+                               }
 
-                               // Fill any other default query args.
-                               _.defaults( args, Query.defaultArgs );
+                               args[ Query.propmap[ prop ] || prop ] = value;
+                       });
 
-                               // `props.orderby` does not always map directly to `args.orderby`.
-                               // Substitute exceptions specified in orderby.keymap.
-                               args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
+                       // Fill any other default query args.
+                       _.defaults( args, Query.defaultArgs );
 
-                               // Search the query cache for a matching query.
-                               if ( cache ) {
-                                       query = _.find( queries, function( query ) {
-                                               return _.isEqual( query.args, args );
-                                       });
-                               } else {
-                                       queries = [];
-                               }
+                       // `props.orderby` does not always map directly to `args.orderby`.
+                       // Substitute exceptions specified in orderby.keymap.
+                       args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
 
-                               // Otherwise, create a new query and add it to the cache.
-                               if ( ! query ) {
-                                       query = new Query( [], _.extend( options || {}, {
-                                               props: props,
-                                               args:  args
-                                       } ) );
-                                       queries.push( query );
-                               }
+                       // Search the query cache for a matching query.
+                       if ( cache ) {
+                               query = _.find( queries, function( query ) {
+                                       return _.isEqual( query.args, args );
+                               });
+                       } else {
+                               queries = [];
+                       }
 
-                               return query;
-                       };
-               }())
-       });
+                       // Otherwise, create a new query and add it to the cache.
+                       if ( ! query ) {
+                               query = new Query( [], _.extend( options || {}, {
+                                       props: props,
+                                       args:  args
+                               } ) );
+                               queries.push( query );
+                       }
 
+                       return query;
+               };
+       }())
+});
+
+module.exports = Query;
+
+},{}],6:[function(require,module,exports){
+/**
+ * wp.media.model.Selection
+ *
+ * A selection of attachments.
+ *
+ * @class
+ * @augments wp.media.model.Attachments
+ * @augments Backbone.Collection
+ */
+var Attachments = wp.media.model.Attachments,
+       Selection;
+
+Selection = Attachments.extend({
        /**
-        * wp.media.model.Selection
+        * Refresh the `single` model whenever the selection changes.
+        * Binds `single` instead of using the context argument to ensure
+        * it receives no parameters.
         *
-        * A selection of attachments.
-        *
-        * @class
-        * @augments wp.media.model.Attachments
-        * @augments Backbone.Collection
+        * @param {Array} [models=[]] Array of models used to populate the collection.
+        * @param {Object} [options={}]
         */
-       media.model.Selection = Attachments.extend({
+       initialize: function( models, options ) {
                /**
-                * Refresh the `single` model whenever the selection changes.
-                * Binds `single` instead of using the context argument to ensure
-                * it receives no parameters.
-                *
-                * @param {Array} [models=[]] Array of models used to populate the collection.
-                * @param {Object} [options={}]
+                * call 'initialize' directly on the parent class
                 */
-               initialize: function( models, options ) {
-                       /**
-                        * call 'initialize' directly on the parent class
-                        */
-                       Attachments.prototype.initialize.apply( this, arguments );
-                       this.multiple = options && options.multiple;
+               Attachments.prototype.initialize.apply( this, arguments );
+               this.multiple = options && options.multiple;
 
-                       this.on( 'add remove reset', _.bind( this.single, this, false ) );
-               },
+               this.on( 'add remove reset', _.bind( this.single, this, false ) );
+       },
 
+       /**
+        * If the workflow does not support multi-select, clear out the selection
+        * before adding a new attachment to it.
+        *
+        * @param {Array} models
+        * @param {Object} options
+        * @returns {wp.media.model.Attachment[]}
+        */
+       add: function( models, options ) {
+               if ( ! this.multiple ) {
+                       this.remove( this.models );
+               }
                /**
-                * If the workflow does not support multi-select, clear out the selection
-                * before adding a new attachment to it.
-                *
-                * @param {Array} models
-                * @param {Object} options
-                * @returns {wp.media.model.Attachment[]}
+                * call 'add' directly on the parent class
                 */
-               add: function( models, options ) {
-                       if ( ! this.multiple ) {
-                               this.remove( this.models );
-                       }
-                       /**
-                        * call 'add' directly on the parent class
-                        */
-                       return Attachments.prototype.add.call( this, models, options );
-               },
+               return Attachments.prototype.add.call( this, models, options );
+       },
 
-               /**
-                * Fired when toggling (clicking on) an attachment in the modal.
-                *
-                * @param {undefined|boolean|wp.media.model.Attachment} model
-                *
-                * @fires wp.media.model.Selection#selection:single
-                * @fires wp.media.model.Selection#selection:unsingle
-                *
-                * @returns {Backbone.Model}
-                */
-               single: function( model ) {
-                       var previous = this._single;
+       /**
+        * Fired when toggling (clicking on) an attachment in the modal.
+        *
+        * @param {undefined|boolean|wp.media.model.Attachment} model
+        *
+        * @fires wp.media.model.Selection#selection:single
+        * @fires wp.media.model.Selection#selection:unsingle
+        *
+        * @returns {Backbone.Model}
+        */
+       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.get( this._single.cid ) ) {
-                               delete 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.get( this._single.cid ) ) {
+                       delete this._single;
+               }
 
-                       this._single = this._single || this.last();
+               this._single = this._single || this.last();
 
-                       // If single has changed, fire an event.
-                       if ( this._single !== previous ) {
-                               if ( previous ) {
-                                       previous.trigger( 'selection:unsingle', previous, this );
+               // If single has changed, fire an event.
+               if ( this._single !== previous ) {
+                       if ( previous ) {
+                               previous.trigger( 'selection:unsingle', previous, this );
 
-                                       // If the model was already removed, trigger the collection
-                                       // event manually.
-                                       if ( ! this.get( previous.cid ) ) {
-                                               this.trigger( 'selection:unsingle', previous, this );
-                                       }
-                               }
-                               if ( this._single ) {
-                                       this._single.trigger( 'selection:single', this._single, this );
+                               // If the model was already removed, trigger the collection
+                               // event manually.
+                               if ( ! this.get( previous.cid ) ) {
+                                       this.trigger( 'selection:unsingle', previous, this );
                                }
                        }
-
-                       // Return the single model, or the last model as a fallback.
-                       return this._single;
+                       if ( this._single ) {
+                               this._single.trigger( 'selection:single', this._single, this );
+                       }
                }
-       });
 
-       // Clean up. Prevents mobile browsers caching
-       $(window).on('unload', function(){
-               window.wp = null;
-       });
+               // Return the single model, or the last model as a fallback.
+               return this._single;
+       }
+});
+
+module.exports = Selection;
 
-}(jQuery));
+},{}]},{},[1]);