[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/js/ -> media-models.js (source)

   1  /******/ (function(modules) { // webpackBootstrap
   2  /******/     // The module cache
   3  /******/     var installedModules = {};
   4  /******/
   5  /******/     // The require function
   6  /******/ 	function __webpack_require__(moduleId) {
   7  /******/
   8  /******/         // Check if module is in cache
   9  /******/         if(installedModules[moduleId]) {
  10  /******/             return installedModules[moduleId].exports;
  11  /******/         }
  12  /******/         // Create a new module (and put it into the cache)
  13  /******/         var module = installedModules[moduleId] = {
  14  /******/             i: moduleId,
  15  /******/             l: false,
  16  /******/             exports: {}
  17  /******/         };
  18  /******/
  19  /******/         // Execute the module function
  20  /******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  21  /******/
  22  /******/         // Flag the module as loaded
  23  /******/         module.l = true;
  24  /******/
  25  /******/         // Return the exports of the module
  26  /******/         return module.exports;
  27  /******/     }
  28  /******/
  29  /******/
  30  /******/     // expose the modules object (__webpack_modules__)
  31  /******/     __webpack_require__.m = modules;
  32  /******/
  33  /******/     // expose the module cache
  34  /******/     __webpack_require__.c = installedModules;
  35  /******/
  36  /******/     // define getter function for harmony exports
  37  /******/     __webpack_require__.d = function(exports, name, getter) {
  38  /******/         if(!__webpack_require__.o(exports, name)) {
  39  /******/             Object.defineProperty(exports, name, { enumerable: true, get: getter });
  40  /******/         }
  41  /******/     };
  42  /******/
  43  /******/     // define __esModule on exports
  44  /******/     __webpack_require__.r = function(exports) {
  45  /******/         if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  46  /******/             Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  47  /******/         }
  48  /******/         Object.defineProperty(exports, '__esModule', { value: true });
  49  /******/     };
  50  /******/
  51  /******/     // create a fake namespace object
  52  /******/     // mode & 1: value is a module id, require it
  53  /******/     // mode & 2: merge all properties of value into the ns
  54  /******/     // mode & 4: return value when already ns object
  55  /******/     // mode & 8|1: behave like require
  56  /******/     __webpack_require__.t = function(value, mode) {
  57  /******/         if(mode & 1) value = __webpack_require__(value);
  58  /******/         if(mode & 8) return value;
  59  /******/         if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  60  /******/         var ns = Object.create(null);
  61  /******/         __webpack_require__.r(ns);
  62  /******/         Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  63  /******/         if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  64  /******/         return ns;
  65  /******/     };
  66  /******/
  67  /******/     // getDefaultExport function for compatibility with non-harmony modules
  68  /******/     __webpack_require__.n = function(module) {
  69  /******/         var getter = module && module.__esModule ?
  70  /******/ 			function getDefault() { return module['default']; } :
  71  /******/ 			function getModuleExports() { return module; };
  72  /******/         __webpack_require__.d(getter, 'a', getter);
  73  /******/         return getter;
  74  /******/     };
  75  /******/
  76  /******/     // Object.prototype.hasOwnProperty.call
  77  /******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  78  /******/
  79  /******/     // __webpack_public_path__
  80  /******/     __webpack_require__.p = "";
  81  /******/
  82  /******/
  83  /******/     // Load entry module and return exports
  84  /******/     return __webpack_require__(__webpack_require__.s = 22);
  85  /******/ })
  86  /************************************************************************/
  87  /******/ ({
  88  
  89  /***/ 22:
  90  /***/ (function(module, exports, __webpack_require__) {
  91  
  92  module.exports = __webpack_require__(23);
  93  
  94  
  95  /***/ }),
  96  
  97  /***/ 23:
  98  /***/ (function(module, exports, __webpack_require__) {
  99  
 100  /**
 101   * @output wp-includes/js/media-models.js
 102   */
 103  
 104  var $ = jQuery,
 105      Attachment, Attachments, l10n, media;
 106  
 107  /** @namespace wp */
 108  window.wp = window.wp || {};
 109  
 110  /**
 111   * Create and return a media frame.
 112   *
 113   * Handles the default media experience.
 114   *
 115   * @alias wp.media
 116   * @memberOf wp
 117   * @namespace
 118   *
 119   * @param {Object} attributes The properties passed to the main media controller.
 120   * @return {wp.media.view.MediaFrame} A media workflow.
 121   */
 122  media = wp.media = function( attributes ) {
 123      var MediaFrame = media.view.MediaFrame,
 124          frame;
 125  
 126      if ( ! MediaFrame ) {
 127          return;
 128      }
 129  
 130      attributes = _.defaults( attributes || {}, {
 131          frame: 'select'
 132      });
 133  
 134      if ( 'select' === attributes.frame && MediaFrame.Select ) {
 135          frame = new MediaFrame.Select( attributes );
 136      } else if ( 'post' === attributes.frame && MediaFrame.Post ) {
 137          frame = new MediaFrame.Post( attributes );
 138      } else if ( 'manage' === attributes.frame && MediaFrame.Manage ) {
 139          frame = new MediaFrame.Manage( attributes );
 140      } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) {
 141          frame = new MediaFrame.ImageDetails( attributes );
 142      } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) {
 143          frame = new MediaFrame.AudioDetails( attributes );
 144      } else if ( 'video' === attributes.frame && MediaFrame.VideoDetails ) {
 145          frame = new MediaFrame.VideoDetails( attributes );
 146      } else if ( 'edit-attachments' === attributes.frame && MediaFrame.EditAttachments ) {
 147          frame = new MediaFrame.EditAttachments( attributes );
 148      }
 149  
 150      delete attributes.frame;
 151  
 152      media.frame = frame;
 153  
 154      return frame;
 155  };
 156  
 157  /** @namespace wp.media.model */
 158  /** @namespace wp.media.view */
 159  /** @namespace wp.media.controller */
 160  /** @namespace wp.media.frames */
 161  _.extend( media, { model: {}, view: {}, controller: {}, frames: {} });
 162  
 163  // Link any localized strings.
 164  l10n = media.model.l10n = window._wpMediaModelsL10n || {};
 165  
 166  // Link any settings.
 167  media.model.settings = l10n.settings || {};
 168  delete l10n.settings;
 169  
 170  Attachment = media.model.Attachment = __webpack_require__( 24 );
 171  Attachments = media.model.Attachments = __webpack_require__( 25 );
 172  
 173  media.model.Query = __webpack_require__( 26 );
 174  media.model.PostImage = __webpack_require__( 27 );
 175  media.model.Selection = __webpack_require__( 28 );
 176  
 177  /**
 178   * ========================================================================
 179   * UTILITIES
 180   * ========================================================================
 181   */
 182  
 183  /**
 184   * A basic equality comparator for Backbone models.
 185   *
 186   * Used to order models within a collection - @see wp.media.model.Attachments.comparator().
 187   *
 188   * @param {mixed}  a  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.
 191   * @param {string} bc The fallback parameter to compare, b's cid.
 192   * @return {number} -1: a should come before b.
 193   *                   0: a and b are of the same rank.
 194   *                   1: b should come before a.
 195   */
 196  media.compare = function( a, b, ac, bc ) {
 197      if ( _.isEqual( a, b ) ) {
 198          return ac === bc ? 0 : (ac > bc ? -1 : 1);
 199      } else {
 200          return a > b ? -1 : 1;
 201      }
 202  };
 203  
 204  _.extend( media, /** @lends wp.media */{
 205      /**
 206       * media.template( id )
 207       *
 208       * Fetch a JavaScript template for an id, and return a templating function for it.
 209       *
 210       * See wp.template() in `wp-includes/js/wp-util.js`.
 211       *
 212       * @borrows wp.template as template
 213       */
 214      template: wp.template,
 215  
 216      /**
 217       * media.post( [action], [data] )
 218       *
 219       * Sends a POST request to WordPress.
 220       * See wp.ajax.post() in `wp-includes/js/wp-util.js`.
 221       *
 222       * @borrows wp.ajax.post as post
 223       */
 224      post: wp.ajax.post,
 225  
 226      /**
 227       * media.ajax( [action], [options] )
 228       *
 229       * Sends an XHR request to WordPress.
 230       * See wp.ajax.send() in `wp-includes/js/wp-util.js`.
 231       *
 232       * @borrows wp.ajax.send as ajax
 233       */
 234      ajax: wp.ajax.send,
 235  
 236      /**
 237       * Scales a set of dimensions to fit within bounding dimensions.
 238       *
 239       * @param {Object} dimensions
 240       * @return {Object}
 241       */
 242      fit: function( dimensions ) {
 243          var width     = dimensions.width,
 244              height    = dimensions.height,
 245              maxWidth  = dimensions.maxWidth,
 246              maxHeight = dimensions.maxHeight,
 247              constraint;
 248  
 249          /*
 250           * Compare ratios between the two values to determine
 251           * which max to constrain by. If a max value doesn't exist,
 252           * then the opposite side is the constraint.
 253           */
 254          if ( ! _.isUndefined( maxWidth ) && ! _.isUndefined( maxHeight ) ) {
 255              constraint = ( width / height > maxWidth / maxHeight ) ? 'width' : 'height';
 256          } else if ( _.isUndefined( maxHeight ) ) {
 257              constraint = 'width';
 258          } else if (  _.isUndefined( maxWidth ) && height > maxHeight ) {
 259              constraint = 'height';
 260          }
 261  
 262          // If the value of the constrained side is larger than the max,
 263          // then scale the values. Otherwise return the originals; they fit.
 264          if ( 'width' === constraint && width > maxWidth ) {
 265              return {
 266                  width : maxWidth,
 267                  height: Math.round( maxWidth * height / width )
 268              };
 269          } else if ( 'height' === constraint && height > maxHeight ) {
 270              return {
 271                  width : Math.round( maxHeight * width / height ),
 272                  height: maxHeight
 273              };
 274          } else {
 275              return {
 276                  width : width,
 277                  height: height
 278              };
 279          }
 280      },
 281      /**
 282       * Truncates a string by injecting an ellipsis into the middle.
 283       * Useful for filenames.
 284       *
 285       * @param {string} string
 286       * @param {number} [length=30]
 287       * @param {string} [replacement=…]
 288       * @return {string} The string, unless length is greater than string.length.
 289       */
 290      truncate: function( string, length, replacement ) {
 291          length = length || 30;
 292          replacement = replacement || '…';
 293  
 294          if ( string.length <= length ) {
 295              return string;
 296          }
 297  
 298          return string.substr( 0, length / 2 ) + replacement + string.substr( -1 * length / 2 );
 299      }
 300  });
 301  
 302  /**
 303   * ========================================================================
 304   * MODELS
 305   * ========================================================================
 306   */
 307  /**
 308   * wp.media.attachment
 309   *
 310   * @static
 311   * @param {string} id A string used to identify a model.
 312   * @return {wp.media.model.Attachment}
 313   */
 314  media.attachment = function( id ) {
 315      return Attachment.get( id );
 316  };
 317  
 318  /**
 319   * A collection of all attachments that have been fetched from the server.
 320   *
 321   * @static
 322   * @member {wp.media.model.Attachments}
 323   */
 324  Attachments.all = new Attachments();
 325  
 326  /**
 327   * wp.media.query
 328   *
 329   * Shorthand for creating a new Attachments Query.
 330   *
 331   * @param {Object} [props]
 332   * @return {wp.media.model.Attachments}
 333   */
 334  media.query = function( props ) {
 335      return new Attachments( null, {
 336          props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
 337      });
 338  };
 339  
 340  // Clean up. Prevents mobile browsers caching.
 341  $(window).on('unload', function(){
 342      window.wp = null;
 343  });
 344  
 345  
 346  /***/ }),
 347  
 348  /***/ 24:
 349  /***/ (function(module, exports) {
 350  
 351  var $ = Backbone.$,
 352      Attachment;
 353  
 354  /**
 355   * wp.media.model.Attachment
 356   *
 357   * @memberOf wp.media.model
 358   *
 359   * @class
 360   * @augments Backbone.Model
 361   */
 362  Attachment = Backbone.Model.extend(/** @lends wp.media.model.Attachment.prototype */{
 363      /**
 364       * Triggered when attachment details change
 365       * Overrides Backbone.Model.sync
 366       *
 367       * @param {string} method
 368       * @param {wp.media.model.Attachment} model
 369       * @param {Object} [options={}]
 370       *
 371       * @return {Promise}
 372       */
 373      sync: function( method, model, options ) {
 374          // If the attachment does not yet have an `id`, return an instantly
 375          // rejected promise. Otherwise, all of our requests will fail.
 376          if ( _.isUndefined( this.id ) ) {
 377              return $.Deferred().rejectWith( this ).promise();
 378          }
 379  
 380          // Overload the `read` request so Attachment.fetch() functions correctly.
 381          if ( 'read' === method ) {
 382              options = options || {};
 383              options.context = this;
 384              options.data = _.extend( options.data || {}, {
 385                  action: 'get-attachment',
 386                  id: this.id
 387              });
 388              return wp.media.ajax( options );
 389  
 390          // Overload the `update` request so properties can be saved.
 391          } else if ( 'update' === method ) {
 392              // If we do not have the necessary nonce, fail immediately.
 393              if ( ! this.get('nonces') || ! this.get('nonces').update ) {
 394                  return $.Deferred().rejectWith( this ).promise();
 395              }
 396  
 397              options = options || {};
 398              options.context = this;
 399  
 400              // Set the action and ID.
 401              options.data = _.extend( options.data || {}, {
 402                  action:  'save-attachment',
 403                  id:      this.id,
 404                  nonce:   this.get('nonces').update,
 405                  post_id: wp.media.model.settings.post.id
 406              });
 407  
 408              // Record the values of the changed attributes.
 409              if ( model.hasChanged() ) {
 410                  options.data.changes = {};
 411  
 412                  _.each( model.changed, function( value, key ) {
 413                      options.data.changes[ key ] = this.get( key );
 414                  }, this );
 415              }
 416  
 417              return wp.media.ajax( options );
 418  
 419          // Overload the `delete` request so attachments can be removed.
 420          // This will permanently delete an attachment.
 421          } else if ( 'delete' === method ) {
 422              options = options || {};
 423  
 424              if ( ! options.wait ) {
 425                  this.destroyed = true;
 426              }
 427  
 428              options.context = this;
 429              options.data = _.extend( options.data || {}, {
 430                  action:   'delete-post',
 431                  id:       this.id,
 432                  _wpnonce: this.get('nonces')['delete']
 433              });
 434  
 435              return wp.media.ajax( options ).done( function() {
 436                  this.destroyed = true;
 437              }).fail( function() {
 438                  this.destroyed = false;
 439              });
 440  
 441          // Otherwise, fall back to `Backbone.sync()`.
 442          } else {
 443              /**
 444               * Call `sync` directly on Backbone.Model
 445               */
 446              return Backbone.Model.prototype.sync.apply( this, arguments );
 447          }
 448      },
 449      /**
 450       * Convert date strings into Date objects.
 451       *
 452       * @param {Object} resp The raw response object, typically returned by fetch()
 453       * @return {Object} The modified response object, which is the attributes hash
 454       *                  to be set on the model.
 455       */
 456      parse: function( resp ) {
 457          if ( ! resp ) {
 458              return resp;
 459          }
 460  
 461          resp.date = new Date( resp.date );
 462          resp.modified = new Date( resp.modified );
 463          return resp;
 464      },
 465      /**
 466       * @param {Object} data The properties to be saved.
 467       * @param {Object} options Sync options. e.g. patch, wait, success, error.
 468       *
 469       * @this Backbone.Model
 470       *
 471       * @return {Promise}
 472       */
 473      saveCompat: function( data, options ) {
 474          var model = this;
 475  
 476          // If we do not have the necessary nonce, fail immediately.
 477          if ( ! this.get('nonces') || ! this.get('nonces').update ) {
 478              return $.Deferred().rejectWith( this ).promise();
 479          }
 480  
 481          return wp.media.post( 'save-attachment-compat', _.defaults({
 482              id:      this.id,
 483              nonce:   this.get('nonces').update,
 484              post_id: wp.media.model.settings.post.id
 485          }, data ) ).done( function( resp, status, xhr ) {
 486              model.set( model.parse( resp, xhr ), options );
 487          });
 488      }
 489  },/** @lends wp.media.model.Attachment */{
 490      /**
 491       * Create a new model on the static 'all' attachments collection and return it.
 492       *
 493       * @static
 494       *
 495       * @param {Object} attrs
 496       * @return {wp.media.model.Attachment}
 497       */
 498      create: function( attrs ) {
 499          var Attachments = wp.media.model.Attachments;
 500          return Attachments.all.push( attrs );
 501      },
 502      /**
 503       * Create a new model on the static 'all' attachments collection and return it.
 504       *
 505       * If this function has already been called for the id,
 506       * it returns the specified attachment.
 507       *
 508       * @static
 509       * @param {string} id A string used to identify a model.
 510       * @param {Backbone.Model|undefined} attachment
 511       * @return {wp.media.model.Attachment}
 512       */
 513      get: _.memoize( function( id, attachment ) {
 514          var Attachments = wp.media.model.Attachments;
 515          return Attachments.all.push( attachment || { id: id } );
 516      })
 517  });
 518  
 519  module.exports = Attachment;
 520  
 521  
 522  /***/ }),
 523  
 524  /***/ 25:
 525  /***/ (function(module, exports) {
 526  
 527  /**
 528   * wp.media.model.Attachments
 529   *
 530   * A collection of attachments.
 531   *
 532   * This collection has no persistence with the server without supplying
 533   * 'options.props.query = true', which will mirror the collection
 534   * to an Attachments Query collection - @see wp.media.model.Attachments.mirror().
 535   *
 536   * @memberOf wp.media.model
 537   *
 538   * @class
 539   * @augments Backbone.Collection
 540   *
 541   * @param {array}  [models]                Models to initialize with the collection.
 542   * @param {object} [options]               Options hash for the collection.
 543   * @param {string} [options.props]         Options hash for the initial query properties.
 544   * @param {string} [options.props.order]   Initial order (ASC or DESC) for the collection.
 545   * @param {string} [options.props.orderby] Initial attribute key to order the collection by.
 546   * @param {string} [options.props.query]   Whether the collection is linked to an attachments query.
 547   * @param {string} [options.observe]
 548   * @param {string} [options.filters]
 549   *
 550   */
 551  var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachments.prototype */{
 552      /**
 553       * @type {wp.media.model.Attachment}
 554       */
 555      model: wp.media.model.Attachment,
 556      /**
 557       * @param {Array} [models=[]] Array of models used to populate the collection.
 558       * @param {Object} [options={}]
 559       */
 560      initialize: function( models, options ) {
 561          options = options || {};
 562  
 563          this.props   = new Backbone.Model();
 564          this.filters = options.filters || {};
 565  
 566          // Bind default `change` events to the `props` model.
 567          this.props.on( 'change', this._changeFilteredProps, this );
 568  
 569          this.props.on( 'change:order',   this._changeOrder,   this );
 570          this.props.on( 'change:orderby', this._changeOrderby, this );
 571          this.props.on( 'change:query',   this._changeQuery,   this );
 572  
 573          this.props.set( _.defaults( options.props || {} ) );
 574  
 575          if ( options.observe ) {
 576              this.observe( options.observe );
 577          }
 578      },
 579      /**
 580       * Sort the collection when the order attribute changes.
 581       *
 582       * @access private
 583       */
 584      _changeOrder: function() {
 585          if ( this.comparator ) {
 586              this.sort();
 587          }
 588      },
 589      /**
 590       * Set the default comparator only when the `orderby` property is set.
 591       *
 592       * @access private
 593       *
 594       * @param {Backbone.Model} model
 595       * @param {string} orderby
 596       */
 597      _changeOrderby: function( model, orderby ) {
 598          // If a different comparator is defined, bail.
 599          if ( this.comparator && this.comparator !== Attachments.comparator ) {
 600              return;
 601          }
 602  
 603          if ( orderby && 'post__in' !== orderby ) {
 604              this.comparator = Attachments.comparator;
 605          } else {
 606              delete this.comparator;
 607          }
 608      },
 609      /**
 610       * If the `query` property is set to true, query the server using
 611       * the `props` values, and sync the results to this collection.
 612       *
 613       * @access private
 614       *
 615       * @param {Backbone.Model} model
 616       * @param {boolean} query
 617       */
 618      _changeQuery: function( model, query ) {
 619          if ( query ) {
 620              this.props.on( 'change', this._requery, this );
 621              this._requery();
 622          } else {
 623              this.props.off( 'change', this._requery, this );
 624          }
 625      },
 626      /**
 627       * @access private
 628       *
 629       * @param {Backbone.Model} model
 630       */
 631      _changeFilteredProps: function( model ) {
 632          // If this is a query, updating the collection will be handled by
 633          // `this._requery()`.
 634          if ( this.props.get('query') ) {
 635              return;
 636          }
 637  
 638          var changed = _.chain( model.changed ).map( function( t, prop ) {
 639              var filter = Attachments.filters[ prop ],
 640                  term = model.get( prop );
 641  
 642              if ( ! filter ) {
 643                  return;
 644              }
 645  
 646              if ( term && ! this.filters[ prop ] ) {
 647                  this.filters[ prop ] = filter;
 648              } else if ( ! term && this.filters[ prop ] === filter ) {
 649                  delete this.filters[ prop ];
 650              } else {
 651                  return;
 652              }
 653  
 654              // Record the change.
 655              return true;
 656          }, this ).any().value();
 657  
 658          if ( ! changed ) {
 659              return;
 660          }
 661  
 662          // If no `Attachments` model is provided to source the searches from,
 663          // then automatically generate a source from the existing models.
 664          if ( ! this._source ) {
 665              this._source = new Attachments( this.models );
 666          }
 667  
 668          this.reset( this._source.filter( this.validator, this ) );
 669      },
 670  
 671      validateDestroyed: false,
 672      /**
 673       * Checks whether an attachment is valid.
 674       *
 675       * @param {wp.media.model.Attachment} attachment
 676       * @return {boolean}
 677       */
 678      validator: function( attachment ) {
 679  
 680          if ( ! this.validateDestroyed && attachment.destroyed ) {
 681              return false;
 682          }
 683          return _.all( this.filters, function( filter ) {
 684              return !! filter.call( this, attachment );
 685          }, this );
 686      },
 687      /**
 688       * Add or remove an attachment to the collection depending on its validity.
 689       *
 690       * @param {wp.media.model.Attachment} attachment
 691       * @param {Object} options
 692       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 693       */
 694      validate: function( attachment, options ) {
 695          var valid = this.validator( attachment ),
 696              hasAttachment = !! this.get( attachment.cid );
 697  
 698          if ( ! valid && hasAttachment ) {
 699              this.remove( attachment, options );
 700          } else if ( valid && ! hasAttachment ) {
 701              this.add( attachment, options );
 702          }
 703  
 704          return this;
 705      },
 706  
 707      /**
 708       * Add or remove all attachments from another collection depending on each one's validity.
 709       *
 710       * @param {wp.media.model.Attachments} attachments
 711       * @param {Object} [options={}]
 712       *
 713       * @fires wp.media.model.Attachments#reset
 714       *
 715       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 716       */
 717      validateAll: function( attachments, options ) {
 718          options = options || {};
 719  
 720          _.each( attachments.models, function( attachment ) {
 721              this.validate( attachment, { silent: true });
 722          }, this );
 723  
 724          if ( ! options.silent ) {
 725              this.trigger( 'reset', this, options );
 726          }
 727          return this;
 728      },
 729      /**
 730       * Start observing another attachments collection change events
 731       * and replicate them on this collection.
 732       *
 733       * @param {wp.media.model.Attachments} The attachments collection to observe.
 734       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 735       */
 736      observe: function( attachments ) {
 737          this.observers = this.observers || [];
 738          this.observers.push( attachments );
 739  
 740          attachments.on( 'add change remove', this._validateHandler, this );
 741          attachments.on( 'reset', this._validateAllHandler, this );
 742          this.validateAll( attachments );
 743          return this;
 744      },
 745      /**
 746       * Stop replicating collection change events from another attachments collection.
 747       *
 748       * @param {wp.media.model.Attachments} The attachments collection to stop observing.
 749       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 750       */
 751      unobserve: function( attachments ) {
 752          if ( attachments ) {
 753              attachments.off( null, null, this );
 754              this.observers = _.without( this.observers, attachments );
 755  
 756          } else {
 757              _.each( this.observers, function( attachments ) {
 758                  attachments.off( null, null, this );
 759              }, this );
 760              delete this.observers;
 761          }
 762  
 763          return this;
 764      },
 765      /**
 766       * @access private
 767       *
 768       * @param {wp.media.model.Attachments} attachment
 769       * @param {wp.media.model.Attachments} attachments
 770       * @param {Object} options
 771       *
 772       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 773       */
 774      _validateHandler: function( attachment, attachments, options ) {
 775          // If we're not mirroring this `attachments` collection,
 776          // only retain the `silent` option.
 777          options = attachments === this.mirroring ? options : {
 778              silent: options && options.silent
 779          };
 780  
 781          return this.validate( attachment, options );
 782      },
 783      /**
 784       * @access private
 785       *
 786       * @param {wp.media.model.Attachments} attachments
 787       * @param {Object} options
 788       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 789       */
 790      _validateAllHandler: function( attachments, options ) {
 791          return this.validateAll( attachments, options );
 792      },
 793      /**
 794       * Start mirroring another attachments collection, clearing out any models already
 795       * in the collection.
 796       *
 797       * @param {wp.media.model.Attachments} The attachments collection to mirror.
 798       * @return {wp.media.model.Attachments} Returns itself to allow chaining.
 799       */
 800      mirror: function( attachments ) {
 801          if ( this.mirroring && this.mirroring === attachments ) {
 802              return this;
 803          }
 804  
 805          this.unmirror();
 806          this.mirroring = attachments;
 807  
 808          // Clear the collection silently. A `reset` event will be fired
 809          // when `observe()` calls `validateAll()`.
 810          this.reset( [], { silent: true } );
 811          this.observe( attachments );
 812  
 813          // Used for the search results.
 814          this.trigger( 'attachments:received', this );
 815          return this;
 816      },
 817      /**
 818       * Stop mirroring another attachments collection.
 819       */
 820      unmirror: function() {
 821          if ( ! this.mirroring ) {
 822              return;
 823          }
 824  
 825          this.unobserve( this.mirroring );
 826          delete this.mirroring;
 827      },
 828      /**
 829       * Retrieve more attachments from the server for the collection.
 830       *
 831       * Only works if the collection is mirroring a Query Attachments collection,
 832       * and forwards to its `more` method. This collection class doesn't have
 833       * server persistence by itself.
 834       *
 835       * @param {Object} options
 836       * @return {Promise}
 837       */
 838      more: function( options ) {
 839          var deferred = jQuery.Deferred(),
 840              mirroring = this.mirroring,
 841              attachments = this;
 842  
 843          if ( ! mirroring || ! mirroring.more ) {
 844              return deferred.resolveWith( this ).promise();
 845          }
 846          /*
 847           * If we're mirroring another collection, forward `more` to
 848           * the mirrored collection. Account for a race condition by
 849           * checking if we're still mirroring that collection when
 850           * the request resolves.
 851           */
 852          mirroring.more( options ).done( function() {
 853              if ( this === attachments.mirroring ) {
 854                  deferred.resolveWith( this );
 855              }
 856  
 857              // Used for the search results.
 858              attachments.trigger( 'attachments:received', this );
 859          });
 860  
 861          return deferred.promise();
 862      },
 863      /**
 864       * Whether there are more attachments that haven't been sync'd from the server
 865       * that match the collection's query.
 866       *
 867       * Only works if the collection is mirroring a Query Attachments collection,
 868       * and forwards to its `hasMore` method. This collection class doesn't have
 869       * server persistence by itself.
 870       *
 871       * @return {boolean}
 872       */
 873      hasMore: function() {
 874          return this.mirroring ? this.mirroring.hasMore() : false;
 875      },
 876      /**
 877       * Holds the total number of attachments.
 878       *
 879       * @since 5.8.0
 880       */
 881      totalAttachments: 0,
 882  
 883      /**
 884       * Gets the total number of attachments.
 885       *
 886       * @since 5.8.0
 887       *
 888       * @return {number} The total number of attachments.
 889       */
 890      getTotalAttachments: function() {
 891          return this.mirroring ? this.mirroring.totalAttachments : 0;
 892      },
 893  
 894      /**
 895       * A custom Ajax-response parser.
 896       *
 897       * See trac ticket #24753.
 898       *
 899       * Called automatically by Backbone whenever a collection's models are returned
 900       * by the server, in fetch. The default implementation is a no-op, simply
 901       * passing through the JSON response. We override this to add attributes to
 902       * the collection items.
 903       *
 904       * @since 5.8.0 The response returns the attachments under `response.attachments` and
 905       *              `response.totalAttachments` holds the total number of attachments found.
 906       *
 907       * @param {Object|Array} response The raw response Object/Array.
 908       * @param {Object} xhr
 909       * @return {Array} The array of model attributes to be added to the collection
 910       */
 911      parse: function( response, xhr ) {
 912          if ( ! _.isArray( response.attachments ) ) {
 913              response = [ response.attachments ];
 914          }
 915  
 916          this.totalAttachments = parseInt( response.totalAttachments, 10 );
 917  
 918          return _.map( response.attachments, function( attrs ) {
 919              var id, attachment, newAttributes;
 920  
 921              if ( attrs instanceof Backbone.Model ) {
 922                  id = attrs.get( 'id' );
 923                  attrs = attrs.attributes;
 924              } else {
 925                  id = attrs.id;
 926              }
 927  
 928              attachment = wp.media.model.Attachment.get( id );
 929              newAttributes = attachment.parse( attrs, xhr );
 930  
 931              if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
 932                  attachment.set( newAttributes );
 933              }
 934  
 935              return attachment;
 936          });
 937      },
 938      /**
 939       * If the collection is a query, create and mirror an Attachments Query collection.
 940       * 
 941       * @access private
 942       * @param {Boolean} refresh Deprecated, refresh parameter no longer used.
 943       */
 944      _requery: function() {
 945          var props;
 946          if ( this.props.get('query') ) {
 947              props = this.props.toJSON();
 948              this.mirror( wp.media.model.Query.get( props ) );
 949          }
 950      },
 951      /**
 952       * If this collection is sorted by `menuOrder`, recalculates and saves
 953       * the menu order to the database.
 954       *
 955       * @return {undefined|Promise}
 956       */
 957      saveMenuOrder: function() {
 958          if ( 'menuOrder' !== this.props.get('orderby') ) {
 959              return;
 960          }
 961  
 962          /*
 963           * Removes any uploading attachments, updates each attachment's
 964           * menu order, and returns an object with an { id: menuOrder }
 965           * mapping to pass to the request.
 966           */
 967          var attachments = this.chain().filter( function( attachment ) {
 968              return ! _.isUndefined( attachment.id );
 969          }).map( function( attachment, index ) {
 970              // Indices start at 1.
 971              index = index + 1;
 972              attachment.set( 'menuOrder', index );
 973              return [ attachment.id, index ];
 974          }).object().value();
 975  
 976          if ( _.isEmpty( attachments ) ) {
 977              return;
 978          }
 979  
 980          return wp.media.post( 'save-attachment-order', {
 981              nonce:       wp.media.model.settings.post.nonce,
 982              post_id:     wp.media.model.settings.post.id,
 983              attachments: attachments
 984          });
 985      }
 986  },/** @lends wp.media.model.Attachments */{
 987      /**
 988       * A function to compare two attachment models in an attachments collection.
 989       *
 990       * Used as the default comparator for instances of wp.media.model.Attachments
 991       * and its subclasses. @see wp.media.model.Attachments._changeOrderby().
 992       *
 993       * @param {Backbone.Model} a
 994       * @param {Backbone.Model} b
 995       * @param {Object} options
 996       * @return {number} -1 if the first model should come before the second,
 997       *                   0 if they are of the same rank and
 998       *                   1 if the first model should come after.
 999       */
1000      comparator: function( a, b, options ) {
1001          var key   = this.props.get('orderby'),
1002              order = this.props.get('order') || 'DESC',
1003              ac    = a.cid,
1004              bc    = b.cid;
1005  
1006          a = a.get( key );
1007          b = b.get( key );
1008  
1009          if ( 'date' === key || 'modified' === key ) {
1010              a = a || new Date();
1011              b = b || new Date();
1012          }
1013  
1014          // If `options.ties` is set, don't enforce the `cid` tiebreaker.
1015          if ( options && options.ties ) {
1016              ac = bc = null;
1017          }
1018  
1019          return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
1020      },
1021      /** @namespace wp.media.model.Attachments.filters */
1022      filters: {
1023          /**
1024           * @static
1025           * Note that this client-side searching is *not* equivalent
1026           * to our server-side searching.
1027           *
1028           * @param {wp.media.model.Attachment} attachment
1029           *
1030           * @this wp.media.model.Attachments
1031           *
1032           * @return {Boolean}
1033           */
1034          search: function( attachment ) {
1035              if ( ! this.props.get('search') ) {
1036                  return true;
1037              }
1038  
1039              return _.any(['title','filename','description','caption','name'], function( key ) {
1040                  var value = attachment.get( key );
1041                  return value && -1 !== value.search( this.props.get('search') );
1042              }, this );
1043          },
1044          /**
1045           * @static
1046           * @param {wp.media.model.Attachment} attachment
1047           *
1048           * @this wp.media.model.Attachments
1049           *
1050           * @return {boolean}
1051           */
1052          type: function( attachment ) {
1053              var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
1054  
1055              if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
1056                  return true;
1057              }
1058  
1059              mime = atts.mime || ( atts.file && atts.file.type ) || '';
1060  
1061              if ( _.isArray( type ) ) {
1062                  found = _.find( type, function (t) {
1063                      return -1 !== mime.indexOf( t );
1064                  } );
1065              } else {
1066                  found = -1 !== mime.indexOf( type );
1067              }
1068  
1069              return found;
1070          },
1071          /**
1072           * @static
1073           * @param {wp.media.model.Attachment} attachment
1074           *
1075           * @this wp.media.model.Attachments
1076           *
1077           * @return {boolean}
1078           */
1079          uploadedTo: function( attachment ) {
1080              var uploadedTo = this.props.get('uploadedTo');
1081              if ( _.isUndefined( uploadedTo ) ) {
1082                  return true;
1083              }
1084  
1085              return uploadedTo === attachment.get('uploadedTo');
1086          },
1087          /**
1088           * @static
1089           * @param {wp.media.model.Attachment} attachment
1090           *
1091           * @this wp.media.model.Attachments
1092           *
1093           * @return {boolean}
1094           */
1095          status: function( attachment ) {
1096              var status = this.props.get('status');
1097              if ( _.isUndefined( status ) ) {
1098                  return true;
1099              }
1100  
1101              return status === attachment.get('status');
1102          }
1103      }
1104  });
1105  
1106  module.exports = Attachments;
1107  
1108  
1109  /***/ }),
1110  
1111  /***/ 26:
1112  /***/ (function(module, exports) {
1113  
1114  var Attachments = wp.media.model.Attachments,
1115      Query;
1116  
1117  /**
1118   * wp.media.model.Query
1119   *
1120   * A collection of attachments that match the supplied query arguments.
1121   *
1122   * Note: Do NOT change this.args after the query has been initialized.
1123   *       Things will break.
1124   *
1125   * @memberOf wp.media.model
1126   *
1127   * @class
1128   * @augments wp.media.model.Attachments
1129   * @augments Backbone.Collection
1130   *
1131   * @param {array}  [models]                      Models to initialize with the collection.
1132   * @param {object} [options]                     Options hash.
1133   * @param {object} [options.args]                Attachments query arguments.
1134   * @param {object} [options.args.posts_per_page]
1135   */
1136  Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{
1137      /**
1138       * @param {Array}  [models=[]]  Array of initial models to populate the collection.
1139       * @param {Object} [options={}]
1140       */
1141      initialize: function( models, options ) {
1142          var allowed;
1143  
1144          options = options || {};
1145          Attachments.prototype.initialize.apply( this, arguments );
1146  
1147          this.args     = options.args;
1148          this._hasMore = true;
1149          this.created  = new Date();
1150  
1151          this.filters.order = function( attachment ) {
1152              var orderby = this.props.get('orderby'),
1153                  order = this.props.get('order');
1154  
1155              if ( ! this.comparator ) {
1156                  return true;
1157              }
1158  
1159              /*
1160               * We want any items that can be placed before the last
1161               * item in the set. If we add any items after the last
1162               * item, then we can't guarantee the set is complete.
1163               */
1164              if ( this.length ) {
1165                  return 1 !== this.comparator( attachment, this.last(), { ties: true });
1166  
1167              /*
1168               * Handle the case where there are no items yet and
1169               * we're sorting for recent items. In that case, we want
1170               * changes that occurred after we created the query.
1171               */
1172              } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) {
1173                  return attachment.get( orderby ) >= this.created;
1174  
1175              // If we're sorting by menu order and we have no items,
1176              // accept any items that have the default menu order (0).
1177              } else if ( 'ASC' === order && 'menuOrder' === orderby ) {
1178                  return attachment.get( orderby ) === 0;
1179              }
1180  
1181              // Otherwise, we don't want any items yet.
1182              return false;
1183          };
1184  
1185          /*
1186           * Observe the central `wp.Uploader.queue` collection to watch for
1187           * new matches for the query.
1188           *
1189           * Only observe when a limited number of query args are set. There
1190           * are no filters for other properties, so observing will result in
1191           * false positives in those queries.
1192           */
1193          allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent', 'author' ];
1194          if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) {
1195              this.observe( wp.Uploader.queue );
1196          }
1197      },
1198      /**
1199       * Whether there are more attachments that haven't been sync'd from the server
1200       * that match the collection's query.
1201       *
1202       * @return {boolean}
1203       */
1204      hasMore: function() {
1205          return this._hasMore;
1206      },
1207      /**
1208       * Fetch more attachments from the server for the collection.
1209       *
1210       * @param {Object} [options={}]
1211       * @return {Promise}
1212       */
1213      more: function( options ) {
1214          var query = this;
1215  
1216          // If there is already a request pending, return early with the Deferred object.
1217          if ( this._more && 'pending' === this._more.state() ) {
1218              return this._more;
1219          }
1220  
1221          if ( ! this.hasMore() ) {
1222              return jQuery.Deferred().resolveWith( this ).promise();
1223          }
1224  
1225          options = options || {};
1226          options.remove = false;
1227  
1228          return this._more = this.fetch( options ).done( function( response ) {
1229              var attachments = response.attachments;
1230  
1231              if ( _.isEmpty( attachments ) || -1 === this.args.posts_per_page || attachments.length < this.args.posts_per_page ) {
1232                  query._hasMore = false;
1233              }
1234          });
1235      },
1236      /**
1237       * Overrides Backbone.Collection.sync
1238       * Overrides wp.media.model.Attachments.sync
1239       *
1240       * @param {string} method
1241       * @param {Backbone.Model} model
1242       * @param {Object} [options={}]
1243       * @return {Promise}
1244       */
1245      sync: function( method, model, options ) {
1246          var args, fallback;
1247  
1248          // Overload the read method so Attachment.fetch() functions correctly.
1249          if ( 'read' === method ) {
1250              options = options || {};
1251              options.context = this;
1252              options.data = _.extend( options.data || {}, {
1253                  action:  'query-attachments',
1254                  post_id: wp.media.model.settings.post.id
1255              });
1256  
1257              // Clone the args so manipulation is non-destructive.
1258              args = _.clone( this.args );
1259  
1260              // Determine which page to query.
1261              if ( -1 !== args.posts_per_page ) {
1262                  args.paged = Math.round( this.length / args.posts_per_page ) + 1;
1263              }
1264  
1265              options.data.query = args;
1266              return wp.media.ajax( options );
1267  
1268          // Otherwise, fall back to `Backbone.sync()`.
1269          } else {
1270              /**
1271               * Call wp.media.model.Attachments.sync or Backbone.sync
1272               */
1273              fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone;
1274              return fallback.sync.apply( this, arguments );
1275          }
1276      }
1277  }, /** @lends wp.media.model.Query */{
1278      /**
1279       * @readonly
1280       */
1281      defaultProps: {
1282          orderby: 'date',
1283          order:   'DESC'
1284      },
1285      /**
1286       * @readonly
1287       */
1288      defaultArgs: {
1289          posts_per_page: 40
1290      },
1291      /**
1292       * @readonly
1293       */
1294      orderby: {
1295          allowed:  [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ],
1296          /**
1297           * A map of JavaScript orderby values to their WP_Query equivalents.
1298           * @type {Object}
1299           */
1300          valuemap: {
1301              'id':         'ID',
1302              'uploadedTo': 'parent',
1303              'menuOrder':  'menu_order ID'
1304          }
1305      },
1306      /**
1307       * A map of JavaScript query properties to their WP_Query equivalents.
1308       *
1309       * @readonly
1310       */
1311      propmap: {
1312          'search':        's',
1313          'type':            'post_mime_type',
1314          'perPage':        'posts_per_page',
1315          'menuOrder':    'menu_order',
1316          'uploadedTo':    'post_parent',
1317          'status':        'post_status',
1318          'include':        'post__in',
1319          'exclude':        'post__not_in',
1320          'author':        'author'
1321      },
1322      /**
1323       * Creates and returns an Attachments Query collection given the properties.
1324       *
1325       * Caches query objects and reuses where possible.
1326       *
1327       * @static
1328       * @method
1329       *
1330       * @param {object} [props]
1331       * @param {Object} [props.order]
1332       * @param {Object} [props.orderby]
1333       * @param {Object} [props.include]
1334       * @param {Object} [props.exclude]
1335       * @param {Object} [props.s]
1336       * @param {Object} [props.post_mime_type]
1337       * @param {Object} [props.posts_per_page]
1338       * @param {Object} [props.menu_order]
1339       * @param {Object} [props.post_parent]
1340       * @param {Object} [props.post_status]
1341       * @param {Object} [props.author]
1342       * @param {Object} [options]
1343       *
1344       * @return {wp.media.model.Query} A new Attachments Query collection.
1345       */
1346      get: (function(){
1347          /**
1348           * @static
1349           * @type Array
1350           */
1351          var queries = [];
1352  
1353          /**
1354           * @return {Query}
1355           */
1356          return function( props, options ) {
1357              var args     = {},
1358                  orderby  = Query.orderby,
1359                  defaults = Query.defaultProps,
1360                  query;
1361  
1362              // Remove the `query` property. This isn't linked to a query,
1363              // this *is* the query.
1364              delete props.query;
1365  
1366              // Fill default args.
1367              _.defaults( props, defaults );
1368  
1369              // Normalize the order.
1370              props.order = props.order.toUpperCase();
1371              if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
1372                  props.order = defaults.order.toUpperCase();
1373              }
1374  
1375              // Ensure we have a valid orderby value.
1376              if ( ! _.contains( orderby.allowed, props.orderby ) ) {
1377                  props.orderby = defaults.orderby;
1378              }
1379  
1380              _.each( [ 'include', 'exclude' ], function( prop ) {
1381                  if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
1382                      props[ prop ] = [ props[ prop ] ];
1383                  }
1384              } );
1385  
1386              // Generate the query `args` object.
1387              // Correct any differing property names.
1388              _.each( props, function( value, prop ) {
1389                  if ( _.isNull( value ) ) {
1390                      return;
1391                  }
1392  
1393                  args[ Query.propmap[ prop ] || prop ] = value;
1394              });
1395  
1396              // Fill any other default query args.
1397              _.defaults( args, Query.defaultArgs );
1398  
1399              // `props.orderby` does not always map directly to `args.orderby`.
1400              // Substitute exceptions specified in orderby.keymap.
1401              args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
1402  
1403              queries = [];
1404  
1405              // Otherwise, create a new query and add it to the cache.
1406              if ( ! query ) {
1407                  query = new Query( [], _.extend( options || {}, {
1408                      props: props,
1409                      args:  args
1410                  } ) );
1411                  queries.push( query );
1412              }
1413  
1414              return query;
1415          };
1416      }())
1417  });
1418  
1419  module.exports = Query;
1420  
1421  
1422  /***/ }),
1423  
1424  /***/ 27:
1425  /***/ (function(module, exports) {
1426  
1427  /**
1428   * wp.media.model.PostImage
1429   *
1430   * An instance of an image that's been embedded into a post.
1431   *
1432   * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails.
1433   *
1434   * @memberOf wp.media.model
1435   *
1436   * @class
1437   * @augments Backbone.Model
1438   *
1439   * @param {int} [attributes]               Initial model attributes.
1440   * @param {int} [attributes.attachment_id] ID of the attachment.
1441   **/
1442  var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{
1443  
1444      initialize: function( attributes ) {
1445          var Attachment = wp.media.model.Attachment;
1446          this.attachment = false;
1447  
1448          if ( attributes.attachment_id ) {
1449              this.attachment = Attachment.get( attributes.attachment_id );
1450              if ( this.attachment.get( 'url' ) ) {
1451                  this.dfd = jQuery.Deferred();
1452                  this.dfd.resolve();
1453              } else {
1454                  this.dfd = this.attachment.fetch();
1455              }
1456              this.bindAttachmentListeners();
1457          }
1458  
1459          // Keep URL in sync with changes to the type of link.
1460          this.on( 'change:link', this.updateLinkUrl, this );
1461          this.on( 'change:size', this.updateSize, this );
1462  
1463          this.setLinkTypeFromUrl();
1464          this.setAspectRatio();
1465  
1466          this.set( 'originalUrl', attributes.url );
1467      },
1468  
1469      bindAttachmentListeners: function() {
1470          this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
1471          this.listenTo( this.attachment, 'sync', this.setAspectRatio );
1472          this.listenTo( this.attachment, 'change', this.updateSize );
1473      },
1474  
1475      changeAttachment: function( attachment, props ) {
1476          this.stopListening( this.attachment );
1477          this.attachment = attachment;
1478          this.bindAttachmentListeners();
1479  
1480          this.set( 'attachment_id', this.attachment.get( 'id' ) );
1481          this.set( 'caption', this.attachment.get( 'caption' ) );
1482          this.set( 'alt', this.attachment.get( 'alt' ) );
1483          this.set( 'size', props.get( 'size' ) );
1484          this.set( 'align', props.get( 'align' ) );
1485          this.set( 'link', props.get( 'link' ) );
1486          this.updateLinkUrl();
1487          this.updateSize();
1488      },
1489  
1490      setLinkTypeFromUrl: function() {
1491          var linkUrl = this.get( 'linkUrl' ),
1492              type;
1493  
1494          if ( ! linkUrl ) {
1495              this.set( 'link', 'none' );
1496              return;
1497          }
1498  
1499          // Default to custom if there is a linkUrl.
1500          type = 'custom';
1501  
1502          if ( this.attachment ) {
1503              if ( this.attachment.get( 'url' ) === linkUrl ) {
1504                  type = 'file';
1505              } else if ( this.attachment.get( 'link' ) === linkUrl ) {
1506                  type = 'post';
1507              }
1508          } else {
1509              if ( this.get( 'url' ) === linkUrl ) {
1510                  type = 'file';
1511              }
1512          }
1513  
1514          this.set( 'link', type );
1515      },
1516  
1517      updateLinkUrl: function() {
1518          var link = this.get( 'link' ),
1519              url;
1520  
1521          switch( link ) {
1522              case 'file':
1523                  if ( this.attachment ) {
1524                      url = this.attachment.get( 'url' );
1525                  } else {
1526                      url = this.get( 'url' );
1527                  }
1528                  this.set( 'linkUrl', url );
1529                  break;
1530              case 'post':
1531                  this.set( 'linkUrl', this.attachment.get( 'link' ) );
1532                  break;
1533              case 'none':
1534                  this.set( 'linkUrl', '' );
1535                  break;
1536          }
1537      },
1538  
1539      updateSize: function() {
1540          var size;
1541  
1542          if ( ! this.attachment ) {
1543              return;
1544          }
1545  
1546          if ( this.get( 'size' ) === 'custom' ) {
1547              this.set( 'width', this.get( 'customWidth' ) );
1548              this.set( 'height', this.get( 'customHeight' ) );
1549              this.set( 'url', this.get( 'originalUrl' ) );
1550              return;
1551          }
1552  
1553          size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
1554  
1555          if ( ! size ) {
1556              return;
1557          }
1558  
1559          this.set( 'url', size.url );
1560          this.set( 'width', size.width );
1561          this.set( 'height', size.height );
1562      },
1563  
1564      setAspectRatio: function() {
1565          var full;
1566  
1567          if ( this.attachment && this.attachment.get( 'sizes' ) ) {
1568              full = this.attachment.get( 'sizes' ).full;
1569  
1570              if ( full ) {
1571                  this.set( 'aspectRatio', full.width / full.height );
1572                  return;
1573              }
1574          }
1575  
1576          this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );
1577      }
1578  });
1579  
1580  module.exports = PostImage;
1581  
1582  
1583  /***/ }),
1584  
1585  /***/ 28:
1586  /***/ (function(module, exports) {
1587  
1588  var Attachments = wp.media.model.Attachments,
1589      Selection;
1590  
1591  /**
1592   * wp.media.model.Selection
1593   *
1594   * A selection of attachments.
1595   *
1596   * @memberOf wp.media.model
1597   *
1598   * @class
1599   * @augments wp.media.model.Attachments
1600   * @augments Backbone.Collection
1601   */
1602  Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{
1603      /**
1604       * Refresh the `single` model whenever the selection changes.
1605       * Binds `single` instead of using the context argument to ensure
1606       * it receives no parameters.
1607       *
1608       * @param {Array} [models=[]] Array of models used to populate the collection.
1609       * @param {Object} [options={}]
1610       */
1611      initialize: function( models, options ) {
1612          /**
1613           * call 'initialize' directly on the parent class
1614           */
1615          Attachments.prototype.initialize.apply( this, arguments );
1616          this.multiple = options && options.multiple;
1617  
1618          this.on( 'add remove reset', _.bind( this.single, this, false ) );
1619      },
1620  
1621      /**
1622       * If the workflow does not support multi-select, clear out the selection
1623       * before adding a new attachment to it.
1624       *
1625       * @param {Array} models
1626       * @param {Object} options
1627       * @return {wp.media.model.Attachment[]}
1628       */
1629      add: function( models, options ) {
1630          if ( ! this.multiple ) {
1631              this.remove( this.models );
1632          }
1633          /**
1634           * call 'add' directly on the parent class
1635           */
1636          return Attachments.prototype.add.call( this, models, options );
1637      },
1638  
1639      /**
1640       * Fired when toggling (clicking on) an attachment in the modal.
1641       *
1642       * @param {undefined|boolean|wp.media.model.Attachment} model
1643       *
1644       * @fires wp.media.model.Selection#selection:single
1645       * @fires wp.media.model.Selection#selection:unsingle
1646       *
1647       * @return {Backbone.Model}
1648       */
1649      single: function( model ) {
1650          var previous = this._single;
1651  
1652          // If a `model` is provided, use it as the single model.
1653          if ( model ) {
1654              this._single = model;
1655          }
1656          // If the single model isn't in the selection, remove it.
1657          if ( this._single && ! this.get( this._single.cid ) ) {
1658              delete this._single;
1659          }
1660  
1661          this._single = this._single || this.last();
1662  
1663          // If single has changed, fire an event.
1664          if ( this._single !== previous ) {
1665              if ( previous ) {
1666                  previous.trigger( 'selection:unsingle', previous, this );
1667  
1668                  // If the model was already removed, trigger the collection
1669                  // event manually.
1670                  if ( ! this.get( previous.cid ) ) {
1671                      this.trigger( 'selection:unsingle', previous, this );
1672                  }
1673              }
1674              if ( this._single ) {
1675                  this._single.trigger( 'selection:single', this._single, this );
1676              }
1677          }
1678  
1679          // Return the single model, or the last model as a fallback.
1680          return this._single;
1681      }
1682  });
1683  
1684  module.exports = Selection;
1685  
1686  
1687  /***/ })
1688  
1689  /******/ });


Generated: Tue May 18 01:00:05 2021 Cross-referenced by PHPXref 0.7.1