114 * |
114 * |
115 * @alias wp.media |
115 * @alias wp.media |
116 * @memberOf wp |
116 * @memberOf wp |
117 * @namespace |
117 * @namespace |
118 * |
118 * |
119 * @param {object} attributes The properties passed to the main media controller. |
119 * @param {Object} attributes The properties passed to the main media controller. |
120 * @return {wp.media.view.MediaFrame} A media workflow. |
120 * @return {wp.media.view.MediaFrame} A media workflow. |
121 */ |
121 */ |
122 media = wp.media = function( attributes ) { |
122 media = wp.media = function( attributes ) { |
123 var MediaFrame = media.view.MediaFrame, |
123 var MediaFrame = media.view.MediaFrame, |
124 frame; |
124 frame; |
183 /** |
183 /** |
184 * A basic equality comparator for Backbone models. |
184 * A basic equality comparator for Backbone models. |
185 * |
185 * |
186 * Used to order models within a collection - @see wp.media.model.Attachments.comparator(). |
186 * Used to order models within a collection - @see wp.media.model.Attachments.comparator(). |
187 * |
187 * |
188 * @param {mixed} a The primary parameter to compare. |
188 * @param {mixed} a The primary parameter to compare. |
189 * @param {mixed} b The primary parameter to compare. |
189 * @param {mixed} b The primary parameter to compare. |
190 * @param {string} ac The fallback parameter to compare, a's cid. |
190 * @param {string} ac The fallback parameter to compare, a's cid. |
191 * @param {string} bc The fallback parameter to compare, b's cid. |
191 * @param {string} bc The fallback parameter to compare, b's cid. |
192 * @return {number} -1: a should come before b. |
192 * @return {number} -1: a should come before b. |
193 * 0: a and b are of the same rank. |
193 * 0: a and b are of the same rank. |
194 * 1: b should come before a. |
194 * 1: b should come before a. |
195 */ |
195 */ |
196 media.compare = function( a, b, ac, bc ) { |
196 media.compare = function( a, b, ac, bc ) { |
197 if ( _.isEqual( a, b ) ) { |
197 if ( _.isEqual( a, b ) ) { |
198 return ac === bc ? 0 : (ac > bc ? -1 : 1); |
198 return ac === bc ? 0 : (ac > bc ? -1 : 1); |
199 } else { |
199 } else { |
235 |
235 |
236 /** |
236 /** |
237 * Scales a set of dimensions to fit within bounding dimensions. |
237 * Scales a set of dimensions to fit within bounding dimensions. |
238 * |
238 * |
239 * @param {Object} dimensions |
239 * @param {Object} dimensions |
240 * @returns {Object} |
240 * @return {Object} |
241 */ |
241 */ |
242 fit: function( dimensions ) { |
242 fit: function( dimensions ) { |
243 var width = dimensions.width, |
243 var width = dimensions.width, |
244 height = dimensions.height, |
244 height = dimensions.height, |
245 maxWidth = dimensions.maxWidth, |
245 maxWidth = dimensions.maxWidth, |
246 maxHeight = dimensions.maxHeight, |
246 maxHeight = dimensions.maxHeight, |
247 constraint; |
247 constraint; |
248 |
248 |
249 // Compare ratios between the two values to determine which |
249 /* |
250 // max to constrain by. If a max value doesn't exist, then the |
250 * Compare ratios between the two values to determine |
251 // opposite side is the constraint. |
251 * which max to constrain by. If a max value doesn't exist, |
|
252 * then the opposite side is the constraint. |
|
253 */ |
252 if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) { |
254 if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) { |
253 constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height'; |
255 constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height'; |
254 } else if ( _.isUndefined( maxHeight ) ) { |
256 } else if ( _.isUndefined( maxHeight ) ) { |
255 constraint = 'width'; |
257 constraint = 'width'; |
256 } else if ( _.isUndefined( maxWidth ) && height > maxHeight ) { |
258 } else if ( _.isUndefined( maxWidth ) && height > maxHeight ) { |
278 }, |
280 }, |
279 /** |
281 /** |
280 * Truncates a string by injecting an ellipsis into the middle. |
282 * Truncates a string by injecting an ellipsis into the middle. |
281 * Useful for filenames. |
283 * Useful for filenames. |
282 * |
284 * |
283 * @param {String} string |
285 * @param {string} string |
284 * @param {Number} [length=30] |
286 * @param {number} [length=30] |
285 * @param {String} [replacement=…] |
287 * @param {string} [replacement=…] |
286 * @returns {String} The string, unless length is greater than string.length. |
288 * @return {string} The string, unless length is greater than string.length. |
287 */ |
289 */ |
288 truncate: function( string, length, replacement ) { |
290 truncate: function( string, length, replacement ) { |
289 length = length || 30; |
291 length = length || 30; |
290 replacement = replacement || '…'; |
292 replacement = replacement || '…'; |
291 |
293 |
304 */ |
306 */ |
305 /** |
307 /** |
306 * wp.media.attachment |
308 * wp.media.attachment |
307 * |
309 * |
308 * @static |
310 * @static |
309 * @param {String} id A string used to identify a model. |
311 * @param {string} id A string used to identify a model. |
310 * @returns {wp.media.model.Attachment} |
312 * @return {wp.media.model.Attachment} |
311 */ |
313 */ |
312 media.attachment = function( id ) { |
314 media.attachment = function( id ) { |
313 return Attachment.get( id ); |
315 return Attachment.get( id ); |
314 }; |
316 }; |
315 |
317 |
324 /** |
326 /** |
325 * wp.media.query |
327 * wp.media.query |
326 * |
328 * |
327 * Shorthand for creating a new Attachments Query. |
329 * Shorthand for creating a new Attachments Query. |
328 * |
330 * |
329 * @param {object} [props] |
331 * @param {Object} [props] |
330 * @returns {wp.media.model.Attachments} |
332 * @return {wp.media.model.Attachments} |
331 */ |
333 */ |
332 media.query = function( props ) { |
334 media.query = function( props ) { |
333 return new Attachments( null, { |
335 return new Attachments( null, { |
334 props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } ) |
336 props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } ) |
335 }); |
337 }); |
336 }; |
338 }; |
337 |
339 |
338 // Clean up. Prevents mobile browsers caching |
340 // Clean up. Prevents mobile browsers caching. |
339 $(window).on('unload', function(){ |
341 $(window).on('unload', function(){ |
340 window.wp = null; |
342 window.wp = null; |
341 }); |
343 }); |
342 |
344 |
343 |
345 |
364 * |
366 * |
365 * @param {string} method |
367 * @param {string} method |
366 * @param {wp.media.model.Attachment} model |
368 * @param {wp.media.model.Attachment} model |
367 * @param {Object} [options={}] |
369 * @param {Object} [options={}] |
368 * |
370 * |
369 * @returns {Promise} |
371 * @return {Promise} |
370 */ |
372 */ |
371 sync: function( method, model, options ) { |
373 sync: function( method, model, options ) { |
372 // If the attachment does not yet have an `id`, return an instantly |
374 // If the attachment does not yet have an `id`, return an instantly |
373 // rejected promise. Otherwise, all of our requests will fail. |
375 // rejected promise. Otherwise, all of our requests will fail. |
374 if ( _.isUndefined( this.id ) ) { |
376 if ( _.isUndefined( this.id ) ) { |
385 }); |
387 }); |
386 return wp.media.ajax( options ); |
388 return wp.media.ajax( options ); |
387 |
389 |
388 // Overload the `update` request so properties can be saved. |
390 // Overload the `update` request so properties can be saved. |
389 } else if ( 'update' === method ) { |
391 } else if ( 'update' === method ) { |
390 // If we do not have the necessary nonce, fail immeditately. |
392 // If we do not have the necessary nonce, fail immediately. |
391 if ( ! this.get('nonces') || ! this.get('nonces').update ) { |
393 if ( ! this.get('nonces') || ! this.get('nonces').update ) { |
392 return $.Deferred().rejectWith( this ).promise(); |
394 return $.Deferred().rejectWith( this ).promise(); |
393 } |
395 } |
394 |
396 |
395 options = options || {}; |
397 options = options || {}; |
446 }, |
448 }, |
447 /** |
449 /** |
448 * Convert date strings into Date objects. |
450 * Convert date strings into Date objects. |
449 * |
451 * |
450 * @param {Object} resp The raw response object, typically returned by fetch() |
452 * @param {Object} resp The raw response object, typically returned by fetch() |
451 * @returns {Object} The modified response object, which is the attributes hash |
453 * @return {Object} The modified response object, which is the attributes hash |
452 * to be set on the model. |
454 * to be set on the model. |
453 */ |
455 */ |
454 parse: function( resp ) { |
456 parse: function( resp ) { |
455 if ( ! resp ) { |
457 if ( ! resp ) { |
456 return resp; |
458 return resp; |
457 } |
459 } |
464 * @param {Object} data The properties to be saved. |
466 * @param {Object} data The properties to be saved. |
465 * @param {Object} options Sync options. e.g. patch, wait, success, error. |
467 * @param {Object} options Sync options. e.g. patch, wait, success, error. |
466 * |
468 * |
467 * @this Backbone.Model |
469 * @this Backbone.Model |
468 * |
470 * |
469 * @returns {Promise} |
471 * @return {Promise} |
470 */ |
472 */ |
471 saveCompat: function( data, options ) { |
473 saveCompat: function( data, options ) { |
472 var model = this; |
474 var model = this; |
473 |
475 |
474 // If we do not have the necessary nonce, fail immeditately. |
476 // If we do not have the necessary nonce, fail immediately. |
475 if ( ! this.get('nonces') || ! this.get('nonces').update ) { |
477 if ( ! this.get('nonces') || ! this.get('nonces').update ) { |
476 return $.Deferred().rejectWith( this ).promise(); |
478 return $.Deferred().rejectWith( this ).promise(); |
477 } |
479 } |
478 |
480 |
479 return wp.media.post( 'save-attachment-compat', _.defaults({ |
481 return wp.media.post( 'save-attachment-compat', _.defaults({ |
489 * Create a new model on the static 'all' attachments collection and return it. |
491 * Create a new model on the static 'all' attachments collection and return it. |
490 * |
492 * |
491 * @static |
493 * @static |
492 * |
494 * |
493 * @param {Object} attrs |
495 * @param {Object} attrs |
494 * @returns {wp.media.model.Attachment} |
496 * @return {wp.media.model.Attachment} |
495 */ |
497 */ |
496 create: function( attrs ) { |
498 create: function( attrs ) { |
497 var Attachments = wp.media.model.Attachments; |
499 var Attachments = wp.media.model.Attachments; |
498 return Attachments.all.push( attrs ); |
500 return Attachments.all.push( attrs ); |
499 }, |
501 }, |
504 * it returns the specified attachment. |
506 * it returns the specified attachment. |
505 * |
507 * |
506 * @static |
508 * @static |
507 * @param {string} id A string used to identify a model. |
509 * @param {string} id A string used to identify a model. |
508 * @param {Backbone.Model|undefined} attachment |
510 * @param {Backbone.Model|undefined} attachment |
509 * @returns {wp.media.model.Attachment} |
511 * @return {wp.media.model.Attachment} |
510 */ |
512 */ |
511 get: _.memoize( function( id, attachment ) { |
513 get: _.memoize( function( id, attachment ) { |
512 var Attachments = wp.media.model.Attachments; |
514 var Attachments = wp.media.model.Attachments; |
513 return Attachments.all.push( attachment || { id: id } ); |
515 return Attachments.all.push( attachment || { id: id } ); |
514 }) |
516 }) |
609 * the `props` values, and sync the results to this collection. |
611 * the `props` values, and sync the results to this collection. |
610 * |
612 * |
611 * @access private |
613 * @access private |
612 * |
614 * |
613 * @param {Backbone.Model} model |
615 * @param {Backbone.Model} model |
614 * @param {Boolean} query |
616 * @param {boolean} query |
615 */ |
617 */ |
616 _changeQuery: function( model, query ) { |
618 _changeQuery: function( model, query ) { |
617 if ( query ) { |
619 if ( query ) { |
618 this.props.on( 'change', this._requery, this ); |
620 this.props.on( 'change', this._requery, this ); |
619 this._requery(); |
621 this._requery(); |
655 |
657 |
656 if ( ! changed ) { |
658 if ( ! changed ) { |
657 return; |
659 return; |
658 } |
660 } |
659 |
661 |
660 // If no `Attachments` model is provided to source the searches |
662 // If no `Attachments` model is provided to source the searches from, |
661 // from, then automatically generate a source from the existing |
663 // then automatically generate a source from the existing models. |
662 // models. |
|
663 if ( ! this._source ) { |
664 if ( ! this._source ) { |
664 this._source = new Attachments( this.models ); |
665 this._source = new Attachments( this.models ); |
665 } |
666 } |
666 |
667 |
667 this.reset( this._source.filter( this.validator, this ) ); |
668 this.reset( this._source.filter( this.validator, this ) ); |
670 validateDestroyed: false, |
671 validateDestroyed: false, |
671 /** |
672 /** |
672 * Checks whether an attachment is valid. |
673 * Checks whether an attachment is valid. |
673 * |
674 * |
674 * @param {wp.media.model.Attachment} attachment |
675 * @param {wp.media.model.Attachment} attachment |
675 * @returns {Boolean} |
676 * @return {boolean} |
676 */ |
677 */ |
677 validator: function( attachment ) { |
678 validator: function( attachment ) { |
678 |
679 |
679 // Filter out contextually created attachments (e.g. headers, logos, etc.). |
680 // Filter out contextually created attachments (e.g. headers, logos, etc.). |
680 if ( |
681 if ( |
694 /** |
695 /** |
695 * Add or remove an attachment to the collection depending on its validity. |
696 * Add or remove an attachment to the collection depending on its validity. |
696 * |
697 * |
697 * @param {wp.media.model.Attachment} attachment |
698 * @param {wp.media.model.Attachment} attachment |
698 * @param {Object} options |
699 * @param {Object} options |
699 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
700 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
700 */ |
701 */ |
701 validate: function( attachment, options ) { |
702 validate: function( attachment, options ) { |
702 var valid = this.validator( attachment ), |
703 var valid = this.validator( attachment ), |
703 hasAttachment = !! this.get( attachment.cid ); |
704 hasAttachment = !! this.get( attachment.cid ); |
704 |
705 |
713 |
714 |
714 /** |
715 /** |
715 * Add or remove all attachments from another collection depending on each one's validity. |
716 * Add or remove all attachments from another collection depending on each one's validity. |
716 * |
717 * |
717 * @param {wp.media.model.Attachments} attachments |
718 * @param {wp.media.model.Attachments} attachments |
718 * @param {object} [options={}] |
719 * @param {Object} [options={}] |
719 * |
720 * |
720 * @fires wp.media.model.Attachments#reset |
721 * @fires wp.media.model.Attachments#reset |
721 * |
722 * |
722 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
723 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
723 */ |
724 */ |
724 validateAll: function( attachments, options ) { |
725 validateAll: function( attachments, options ) { |
725 options = options || {}; |
726 options = options || {}; |
726 |
727 |
727 _.each( attachments.models, function( attachment ) { |
728 _.each( attachments.models, function( attachment ) { |
736 /** |
737 /** |
737 * Start observing another attachments collection change events |
738 * Start observing another attachments collection change events |
738 * and replicate them on this collection. |
739 * and replicate them on this collection. |
739 * |
740 * |
740 * @param {wp.media.model.Attachments} The attachments collection to observe. |
741 * @param {wp.media.model.Attachments} The attachments collection to observe. |
741 * @returns {wp.media.model.Attachments} Returns itself to allow chaining. |
742 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
742 */ |
743 */ |
743 observe: function( attachments ) { |
744 observe: function( attachments ) { |
744 this.observers = this.observers || []; |
745 this.observers = this.observers || []; |
745 this.observers.push( attachments ); |
746 this.observers.push( attachments ); |
746 |
747 |
751 }, |
752 }, |
752 /** |
753 /** |
753 * Stop replicating collection change events from another attachments collection. |
754 * Stop replicating collection change events from another attachments collection. |
754 * |
755 * |
755 * @param {wp.media.model.Attachments} The attachments collection to stop observing. |
756 * @param {wp.media.model.Attachments} The attachments collection to stop observing. |
756 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
757 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
757 */ |
758 */ |
758 unobserve: function( attachments ) { |
759 unobserve: function( attachments ) { |
759 if ( attachments ) { |
760 if ( attachments ) { |
760 attachments.off( null, null, this ); |
761 attachments.off( null, null, this ); |
761 this.observers = _.without( this.observers, attachments ); |
762 this.observers = _.without( this.observers, attachments ); |
774 * |
775 * |
775 * @param {wp.media.model.Attachments} attachment |
776 * @param {wp.media.model.Attachments} attachment |
776 * @param {wp.media.model.Attachments} attachments |
777 * @param {wp.media.model.Attachments} attachments |
777 * @param {Object} options |
778 * @param {Object} options |
778 * |
779 * |
779 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
780 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
780 */ |
781 */ |
781 _validateHandler: function( attachment, attachments, options ) { |
782 _validateHandler: function( attachment, attachments, options ) { |
782 // If we're not mirroring this `attachments` collection, |
783 // If we're not mirroring this `attachments` collection, |
783 // only retain the `silent` option. |
784 // only retain the `silent` option. |
784 options = attachments === this.mirroring ? options : { |
785 options = attachments === this.mirroring ? options : { |
790 /** |
791 /** |
791 * @access private |
792 * @access private |
792 * |
793 * |
793 * @param {wp.media.model.Attachments} attachments |
794 * @param {wp.media.model.Attachments} attachments |
794 * @param {Object} options |
795 * @param {Object} options |
795 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
796 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
796 */ |
797 */ |
797 _validateAllHandler: function( attachments, options ) { |
798 _validateAllHandler: function( attachments, options ) { |
798 return this.validateAll( attachments, options ); |
799 return this.validateAll( attachments, options ); |
799 }, |
800 }, |
800 /** |
801 /** |
801 * Start mirroring another attachments collection, clearing out any models already |
802 * Start mirroring another attachments collection, clearing out any models already |
802 * in the collection. |
803 * in the collection. |
803 * |
804 * |
804 * @param {wp.media.model.Attachments} The attachments collection to mirror. |
805 * @param {wp.media.model.Attachments} The attachments collection to mirror. |
805 * @returns {wp.media.model.Attachments} Returns itself to allow chaining |
806 * @return {wp.media.model.Attachments} Returns itself to allow chaining. |
806 */ |
807 */ |
807 mirror: function( attachments ) { |
808 mirror: function( attachments ) { |
808 if ( this.mirroring && this.mirroring === attachments ) { |
809 if ( this.mirroring && this.mirroring === attachments ) { |
809 return this; |
810 return this; |
810 } |
811 } |
815 // Clear the collection silently. A `reset` event will be fired |
816 // Clear the collection silently. A `reset` event will be fired |
816 // when `observe()` calls `validateAll()`. |
817 // when `observe()` calls `validateAll()`. |
817 this.reset( [], { silent: true } ); |
818 this.reset( [], { silent: true } ); |
818 this.observe( attachments ); |
819 this.observe( attachments ); |
819 |
820 |
|
821 // Used for the search results. |
|
822 this.trigger( 'attachments:received', this ); |
820 return this; |
823 return this; |
821 }, |
824 }, |
822 /** |
825 /** |
823 * Stop mirroring another attachments collection. |
826 * Stop mirroring another attachments collection. |
824 */ |
827 */ |
835 * |
838 * |
836 * Only works if the collection is mirroring a Query Attachments collection, |
839 * Only works if the collection is mirroring a Query Attachments collection, |
837 * and forwards to its `more` method. This collection class doesn't have |
840 * and forwards to its `more` method. This collection class doesn't have |
838 * server persistence by itself. |
841 * server persistence by itself. |
839 * |
842 * |
840 * @param {object} options |
843 * @param {Object} options |
841 * @returns {Promise} |
844 * @return {Promise} |
842 */ |
845 */ |
843 more: function( options ) { |
846 more: function( options ) { |
844 var deferred = jQuery.Deferred(), |
847 var deferred = jQuery.Deferred(), |
845 mirroring = this.mirroring, |
848 mirroring = this.mirroring, |
846 attachments = this; |
849 attachments = this; |
847 |
850 |
848 if ( ! mirroring || ! mirroring.more ) { |
851 if ( ! mirroring || ! mirroring.more ) { |
849 return deferred.resolveWith( this ).promise(); |
852 return deferred.resolveWith( this ).promise(); |
850 } |
853 } |
851 // If we're mirroring another collection, forward `more` to |
854 /* |
852 // the mirrored collection. Account for a race condition by |
855 * If we're mirroring another collection, forward `more` to |
853 // checking if we're still mirroring that collection when |
856 * the mirrored collection. Account for a race condition by |
854 // the request resolves. |
857 * checking if we're still mirroring that collection when |
|
858 * the request resolves. |
|
859 */ |
855 mirroring.more( options ).done( function() { |
860 mirroring.more( options ).done( function() { |
856 if ( this === attachments.mirroring ) { |
861 if ( this === attachments.mirroring ) { |
857 deferred.resolveWith( this ); |
862 deferred.resolveWith( this ); |
858 } |
863 } |
|
864 |
|
865 // Used for the search results. |
|
866 attachments.trigger( 'attachments:received', this ); |
859 }); |
867 }); |
860 |
868 |
861 return deferred.promise(); |
869 return deferred.promise(); |
862 }, |
870 }, |
863 /** |
871 /** |
866 * |
874 * |
867 * Only works if the collection is mirroring a Query Attachments collection, |
875 * Only works if the collection is mirroring a Query Attachments collection, |
868 * and forwards to its `hasMore` method. This collection class doesn't have |
876 * and forwards to its `hasMore` method. This collection class doesn't have |
869 * server persistence by itself. |
877 * server persistence by itself. |
870 * |
878 * |
871 * @returns {boolean} |
879 * @return {boolean} |
872 */ |
880 */ |
873 hasMore: function() { |
881 hasMore: function() { |
874 return this.mirroring ? this.mirroring.hasMore() : false; |
882 return this.mirroring ? this.mirroring.hasMore() : false; |
875 }, |
883 }, |
876 /** |
884 /** |
877 * A custom AJAX-response parser. |
885 * A custom Ajax-response parser. |
878 * |
886 * |
879 * See trac ticket #24753 |
887 * See trac ticket #24753 |
880 * |
888 * |
881 * @param {Object|Array} resp The raw response Object/Array. |
889 * @param {Object|Array} resp The raw response Object/Array. |
882 * @param {Object} xhr |
890 * @param {Object} xhr |
883 * @returns {Array} The array of model attributes to be added to the collection |
891 * @return {Array} The array of model attributes to be added to the collection |
884 */ |
892 */ |
885 parse: function( resp, xhr ) { |
893 parse: function( resp, xhr ) { |
886 if ( ! _.isArray( resp ) ) { |
894 if ( ! _.isArray( resp ) ) { |
887 resp = [resp]; |
895 resp = [resp]; |
888 } |
896 } |
922 }, |
930 }, |
923 /** |
931 /** |
924 * If this collection is sorted by `menuOrder`, recalculates and saves |
932 * If this collection is sorted by `menuOrder`, recalculates and saves |
925 * the menu order to the database. |
933 * the menu order to the database. |
926 * |
934 * |
927 * @returns {undefined|Promise} |
935 * @return {undefined|Promise} |
928 */ |
936 */ |
929 saveMenuOrder: function() { |
937 saveMenuOrder: function() { |
930 if ( 'menuOrder' !== this.props.get('orderby') ) { |
938 if ( 'menuOrder' !== this.props.get('orderby') ) { |
931 return; |
939 return; |
932 } |
940 } |
933 |
941 |
934 // Removes any uploading attachments, updates each attachment's |
942 /* |
935 // menu order, and returns an object with an { id: menuOrder } |
943 * Removes any uploading attachments, updates each attachment's |
936 // mapping to pass to the request. |
944 * menu order, and returns an object with an { id: menuOrder } |
|
945 * mapping to pass to the request. |
|
946 */ |
937 var attachments = this.chain().filter( function( attachment ) { |
947 var attachments = this.chain().filter( function( attachment ) { |
938 return ! _.isUndefined( attachment.id ); |
948 return ! _.isUndefined( attachment.id ); |
939 }).map( function( attachment, index ) { |
949 }).map( function( attachment, index ) { |
940 // Indices start at 1. |
950 // Indices start at 1. |
941 index = index + 1; |
951 index = index + 1; |
961 * and its subclasses. @see wp.media.model.Attachments._changeOrderby(). |
971 * and its subclasses. @see wp.media.model.Attachments._changeOrderby(). |
962 * |
972 * |
963 * @param {Backbone.Model} a |
973 * @param {Backbone.Model} a |
964 * @param {Backbone.Model} b |
974 * @param {Backbone.Model} b |
965 * @param {Object} options |
975 * @param {Object} options |
966 * @returns {Number} -1 if the first model should come before the second, |
976 * @return {number} -1 if the first model should come before the second, |
967 * 0 if they are of the same rank and |
977 * 0 if they are of the same rank and |
968 * 1 if the first model should come after. |
978 * 1 if the first model should come after. |
969 */ |
979 */ |
970 comparator: function( a, b, options ) { |
980 comparator: function( a, b, options ) { |
971 var key = this.props.get('orderby'), |
981 var key = this.props.get('orderby'), |
972 order = this.props.get('order') || 'DESC', |
982 order = this.props.get('order') || 'DESC', |
973 ac = a.cid, |
983 ac = a.cid, |
1015 * @static |
1025 * @static |
1016 * @param {wp.media.model.Attachment} attachment |
1026 * @param {wp.media.model.Attachment} attachment |
1017 * |
1027 * |
1018 * @this wp.media.model.Attachments |
1028 * @this wp.media.model.Attachments |
1019 * |
1029 * |
1020 * @returns {Boolean} |
1030 * @return {boolean} |
1021 */ |
1031 */ |
1022 type: function( attachment ) { |
1032 type: function( attachment ) { |
1023 var type = this.props.get('type'), atts = attachment.toJSON(), mime, found; |
1033 var type = this.props.get('type'), atts = attachment.toJSON(), mime, found; |
1024 |
1034 |
1025 if ( ! type || ( _.isArray( type ) && ! type.length ) ) { |
1035 if ( ! type || ( _.isArray( type ) && ! type.length ) ) { |
1042 * @static |
1052 * @static |
1043 * @param {wp.media.model.Attachment} attachment |
1053 * @param {wp.media.model.Attachment} attachment |
1044 * |
1054 * |
1045 * @this wp.media.model.Attachments |
1055 * @this wp.media.model.Attachments |
1046 * |
1056 * |
1047 * @returns {Boolean} |
1057 * @return {boolean} |
1048 */ |
1058 */ |
1049 uploadedTo: function( attachment ) { |
1059 uploadedTo: function( attachment ) { |
1050 var uploadedTo = this.props.get('uploadedTo'); |
1060 var uploadedTo = this.props.get('uploadedTo'); |
1051 if ( _.isUndefined( uploadedTo ) ) { |
1061 if ( _.isUndefined( uploadedTo ) ) { |
1052 return true; |
1062 return true; |
1058 * @static |
1068 * @static |
1059 * @param {wp.media.model.Attachment} attachment |
1069 * @param {wp.media.model.Attachment} attachment |
1060 * |
1070 * |
1061 * @this wp.media.model.Attachments |
1071 * @this wp.media.model.Attachments |
1062 * |
1072 * |
1063 * @returns {Boolean} |
1073 * @return {boolean} |
1064 */ |
1074 */ |
1065 status: function( attachment ) { |
1075 status: function( attachment ) { |
1066 var status = this.props.get('status'); |
1076 var status = this.props.get('status'); |
1067 if ( _.isUndefined( status ) ) { |
1077 if ( _.isUndefined( status ) ) { |
1068 return true; |
1078 return true; |
1103 * @param {object} [options.args] Attachments query arguments. |
1113 * @param {object} [options.args] Attachments query arguments. |
1104 * @param {object} [options.args.posts_per_page] |
1114 * @param {object} [options.args.posts_per_page] |
1105 */ |
1115 */ |
1106 Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ |
1116 Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ |
1107 /** |
1117 /** |
1108 * @param {array} [models=[]] Array of initial models to populate the collection. |
1118 * @param {Array} [models=[]] Array of initial models to populate the collection. |
1109 * @param {object} [options={}] |
1119 * @param {Object} [options={}] |
1110 */ |
1120 */ |
1111 initialize: function( models, options ) { |
1121 initialize: function( models, options ) { |
1112 var allowed; |
1122 var allowed; |
1113 |
1123 |
1114 options = options || {}; |
1124 options = options || {}; |
1124 |
1134 |
1125 if ( ! this.comparator ) { |
1135 if ( ! this.comparator ) { |
1126 return true; |
1136 return true; |
1127 } |
1137 } |
1128 |
1138 |
1129 // We want any items that can be placed before the last |
1139 /* |
1130 // item in the set. If we add any items after the last |
1140 * We want any items that can be placed before the last |
1131 // item, then we can't guarantee the set is complete. |
1141 * item in the set. If we add any items after the last |
|
1142 * item, then we can't guarantee the set is complete. |
|
1143 */ |
1132 if ( this.length ) { |
1144 if ( this.length ) { |
1133 return 1 !== this.comparator( attachment, this.last(), { ties: true }); |
1145 return 1 !== this.comparator( attachment, this.last(), { ties: true }); |
1134 |
1146 |
1135 // Handle the case where there are no items yet and |
1147 /* |
1136 // we're sorting for recent items. In that case, we want |
1148 * Handle the case where there are no items yet and |
1137 // changes that occurred after we created the query. |
1149 * we're sorting for recent items. In that case, we want |
|
1150 * changes that occurred after we created the query. |
|
1151 */ |
1138 } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) { |
1152 } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) { |
1139 return attachment.get( orderby ) >= this.created; |
1153 return attachment.get( orderby ) >= this.created; |
1140 |
1154 |
1141 // If we're sorting by menu order and we have no items, |
1155 // If we're sorting by menu order and we have no items, |
1142 // accept any items that have the default menu order (0). |
1156 // accept any items that have the default menu order (0). |
1146 |
1160 |
1147 // Otherwise, we don't want any items yet. |
1161 // Otherwise, we don't want any items yet. |
1148 return false; |
1162 return false; |
1149 }; |
1163 }; |
1150 |
1164 |
1151 // Observe the central `wp.Uploader.queue` collection to watch for |
1165 /* |
1152 // new matches for the query. |
1166 * Observe the central `wp.Uploader.queue` collection to watch for |
1153 // |
1167 * new matches for the query. |
1154 // Only observe when a limited number of query args are set. There |
1168 * |
1155 // are no filters for other properties, so observing will result in |
1169 * Only observe when a limited number of query args are set. There |
1156 // false positives in those queries. |
1170 * are no filters for other properties, so observing will result in |
|
1171 * false positives in those queries. |
|
1172 */ |
1157 allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent', 'author' ]; |
1173 allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent', 'author' ]; |
1158 if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) { |
1174 if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) { |
1159 this.observe( wp.Uploader.queue ); |
1175 this.observe( wp.Uploader.queue ); |
1160 } |
1176 } |
1161 }, |
1177 }, |
1162 /** |
1178 /** |
1163 * Whether there are more attachments that haven't been sync'd from the server |
1179 * Whether there are more attachments that haven't been sync'd from the server |
1164 * that match the collection's query. |
1180 * that match the collection's query. |
1165 * |
1181 * |
1166 * @returns {boolean} |
1182 * @return {boolean} |
1167 */ |
1183 */ |
1168 hasMore: function() { |
1184 hasMore: function() { |
1169 return this._hasMore; |
1185 return this._hasMore; |
1170 }, |
1186 }, |
1171 /** |
1187 /** |
1172 * Fetch more attachments from the server for the collection. |
1188 * Fetch more attachments from the server for the collection. |
1173 * |
1189 * |
1174 * @param {object} [options={}] |
1190 * @param {Object} [options={}] |
1175 * @returns {Promise} |
1191 * @return {Promise} |
1176 */ |
1192 */ |
1177 more: function( options ) { |
1193 more: function( options ) { |
1178 var query = this; |
1194 var query = this; |
1179 |
1195 |
1180 // If there is already a request pending, return early with the Deferred object. |
1196 // If there is already a request pending, return early with the Deferred object. |
1197 }, |
1213 }, |
1198 /** |
1214 /** |
1199 * Overrides Backbone.Collection.sync |
1215 * Overrides Backbone.Collection.sync |
1200 * Overrides wp.media.model.Attachments.sync |
1216 * Overrides wp.media.model.Attachments.sync |
1201 * |
1217 * |
1202 * @param {String} method |
1218 * @param {string} method |
1203 * @param {Backbone.Model} model |
1219 * @param {Backbone.Model} model |
1204 * @param {Object} [options={}] |
1220 * @param {Object} [options={}] |
1205 * @returns {Promise} |
1221 * @return {Promise} |
1206 */ |
1222 */ |
1207 sync: function( method, model, options ) { |
1223 sync: function( method, model, options ) { |
1208 var args, fallback; |
1224 var args, fallback; |
1209 |
1225 |
1210 // Overload the read method so Attachment.fetch() functions correctly. |
1226 // Overload the read method so Attachment.fetch() functions correctly. |
1225 } |
1241 } |
1226 |
1242 |
1227 options.data.query = args; |
1243 options.data.query = args; |
1228 return wp.media.ajax( options ); |
1244 return wp.media.ajax( options ); |
1229 |
1245 |
1230 // Otherwise, fall back to Backbone.sync() |
1246 // Otherwise, fall back to `Backbone.sync()`. |
1231 } else { |
1247 } else { |
1232 /** |
1248 /** |
1233 * Call wp.media.model.Attachments.sync or Backbone.sync |
1249 * Call wp.media.model.Attachments.sync or Backbone.sync |
1234 */ |
1250 */ |
1235 fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone; |
1251 fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone; |
1302 * @param {Object} [props.post_parent] |
1318 * @param {Object} [props.post_parent] |
1303 * @param {Object} [props.post_status] |
1319 * @param {Object} [props.post_status] |
1304 * @param {Object} [props.author] |
1320 * @param {Object} [props.author] |
1305 * @param {Object} [options] |
1321 * @param {Object} [options] |
1306 * |
1322 * |
1307 * @returns {wp.media.model.Query} A new Attachments Query collection. |
1323 * @return {wp.media.model.Query} A new Attachments Query collection. |
1308 */ |
1324 */ |
1309 get: (function(){ |
1325 get: (function(){ |
1310 /** |
1326 /** |
1311 * @static |
1327 * @static |
1312 * @type Array |
1328 * @type Array |
1313 */ |
1329 */ |
1314 var queries = []; |
1330 var queries = []; |
1315 |
1331 |
1316 /** |
1332 /** |
1317 * @returns {Query} |
1333 * @return {Query} |
1318 */ |
1334 */ |
1319 return function( props, options ) { |
1335 return function( props, options ) { |
1320 var args = {}, |
1336 var args = {}, |
1321 orderby = Query.orderby, |
1337 orderby = Query.orderby, |
1322 defaults = Query.defaultProps, |
1338 defaults = Query.defaultProps, |
1426 this.dfd = this.attachment.fetch(); |
1442 this.dfd = this.attachment.fetch(); |
1427 } |
1443 } |
1428 this.bindAttachmentListeners(); |
1444 this.bindAttachmentListeners(); |
1429 } |
1445 } |
1430 |
1446 |
1431 // keep url in sync with changes to the type of link |
1447 // Keep URL in sync with changes to the type of link. |
1432 this.on( 'change:link', this.updateLinkUrl, this ); |
1448 this.on( 'change:link', this.updateLinkUrl, this ); |
1433 this.on( 'change:size', this.updateSize, this ); |
1449 this.on( 'change:size', this.updateSize, this ); |
1434 |
1450 |
1435 this.setLinkTypeFromUrl(); |
1451 this.setLinkTypeFromUrl(); |
1436 this.setAspectRatio(); |
1452 this.setAspectRatio(); |
1594 * If the workflow does not support multi-select, clear out the selection |
1610 * If the workflow does not support multi-select, clear out the selection |
1595 * before adding a new attachment to it. |
1611 * before adding a new attachment to it. |
1596 * |
1612 * |
1597 * @param {Array} models |
1613 * @param {Array} models |
1598 * @param {Object} options |
1614 * @param {Object} options |
1599 * @returns {wp.media.model.Attachment[]} |
1615 * @return {wp.media.model.Attachment[]} |
1600 */ |
1616 */ |
1601 add: function( models, options ) { |
1617 add: function( models, options ) { |
1602 if ( ! this.multiple ) { |
1618 if ( ! this.multiple ) { |
1603 this.remove( this.models ); |
1619 this.remove( this.models ); |
1604 } |
1620 } |
1614 * @param {undefined|boolean|wp.media.model.Attachment} model |
1630 * @param {undefined|boolean|wp.media.model.Attachment} model |
1615 * |
1631 * |
1616 * @fires wp.media.model.Selection#selection:single |
1632 * @fires wp.media.model.Selection#selection:single |
1617 * @fires wp.media.model.Selection#selection:unsingle |
1633 * @fires wp.media.model.Selection#selection:unsingle |
1618 * |
1634 * |
1619 * @returns {Backbone.Model} |
1635 * @return {Backbone.Model} |
1620 */ |
1636 */ |
1621 single: function( model ) { |
1637 single: function( model ) { |
1622 var previous = this._single; |
1638 var previous = this._single; |
1623 |
1639 |
1624 // If a `model` is provided, use it as the single model. |
1640 // If a `model` is provided, use it as the single model. |