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


Generated: Tue Sep 17 01:00:03 2019 Cross-referenced by PHPXref 0.7.1