[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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          // Used for the search results.
 821          this.trigger( 'attachments:received', this );
 822          return this;
 823      },
 824      /**
 825       * Stop mirroring another attachments collection.
 826       */
 827      unmirror: function() {
 828          if ( ! this.mirroring ) {
 829              return;
 830          }
 831  
 832          this.unobserve( this.mirroring );
 833          delete this.mirroring;
 834      },
 835      /**
 836       * Retrieve more attachments from the server for the collection.
 837       *
 838       * Only works if the collection is mirroring a Query Attachments collection,
 839       * and forwards to its `more` method. This collection class doesn't have
 840       * server persistence by itself.
 841       *
 842       * @param {object} options
 843       * @returns {Promise}
 844       */
 845      more: function( options ) {
 846          var deferred = jQuery.Deferred(),
 847              mirroring = this.mirroring,
 848              attachments = this;
 849  
 850          if ( ! mirroring || ! mirroring.more ) {
 851              return deferred.resolveWith( this ).promise();
 852          }
 853          // If we're mirroring another collection, forward `more` to
 854          // the mirrored collection. Account for a race condition by
 855          // checking if we're still mirroring that collection when
 856          // the request resolves.
 857          mirroring.more( options ).done( function() {
 858              if ( this === attachments.mirroring ) {
 859                  deferred.resolveWith( this );
 860              }
 861  
 862              // Used for the search results.
 863              attachments.trigger( 'attachments:received', this );
 864          });
 865  
 866          return deferred.promise();
 867      },
 868      /**
 869       * Whether there are more attachments that haven't been sync'd from the server
 870       * that match the collection's query.
 871       *
 872       * Only works if the collection is mirroring a Query Attachments collection,
 873       * and forwards to its `hasMore` method. This collection class doesn't have
 874       * server persistence by itself.
 875       *
 876       * @returns {boolean}
 877       */
 878      hasMore: function() {
 879          return this.mirroring ? this.mirroring.hasMore() : false;
 880      },
 881      /**
 882       * A custom AJAX-response parser.
 883       *
 884       * See trac ticket #24753
 885       *
 886       * @param {Object|Array} resp The raw response Object/Array.
 887       * @param {Object} xhr
 888       * @returns {Array} The array of model attributes to be added to the collection
 889       */
 890      parse: function( resp, xhr ) {
 891          if ( ! _.isArray( resp ) ) {
 892              resp = [resp];
 893          }
 894  
 895          return _.map( resp, function( attrs ) {
 896              var id, attachment, newAttributes;
 897  
 898              if ( attrs instanceof Backbone.Model ) {
 899                  id = attrs.get( 'id' );
 900                  attrs = attrs.attributes;
 901              } else {
 902                  id = attrs.id;
 903              }
 904  
 905              attachment = wp.media.model.Attachment.get( id );
 906              newAttributes = attachment.parse( attrs, xhr );
 907  
 908              if ( ! _.isEqual( attachment.attributes, newAttributes ) ) {
 909                  attachment.set( newAttributes );
 910              }
 911  
 912              return attachment;
 913          });
 914      },
 915      /**
 916       * If the collection is a query, create and mirror an Attachments Query collection.
 917       *
 918       * @access private
 919       */
 920      _requery: function( refresh ) {
 921          var props;
 922          if ( this.props.get('query') ) {
 923              props = this.props.toJSON();
 924              props.cache = ( true !== refresh );
 925              this.mirror( wp.media.model.Query.get( props ) );
 926          }
 927      },
 928      /**
 929       * If this collection is sorted by `menuOrder`, recalculates and saves
 930       * the menu order to the database.
 931       *
 932       * @returns {undefined|Promise}
 933       */
 934      saveMenuOrder: function() {
 935          if ( 'menuOrder' !== this.props.get('orderby') ) {
 936              return;
 937          }
 938  
 939          // Removes any uploading attachments, updates each attachment's
 940          // menu order, and returns an object with an { id: menuOrder }
 941          // mapping to pass to the request.
 942          var attachments = this.chain().filter( function( attachment ) {
 943              return ! _.isUndefined( attachment.id );
 944          }).map( function( attachment, index ) {
 945              // Indices start at 1.
 946              index = index + 1;
 947              attachment.set( 'menuOrder', index );
 948              return [ attachment.id, index ];
 949          }).object().value();
 950  
 951          if ( _.isEmpty( attachments ) ) {
 952              return;
 953          }
 954  
 955          return wp.media.post( 'save-attachment-order', {
 956              nonce:       wp.media.model.settings.post.nonce,
 957              post_id:     wp.media.model.settings.post.id,
 958              attachments: attachments
 959          });
 960      }
 961  },/** @lends wp.media.model.Attachments */{
 962      /**
 963       * A function to compare two attachment models in an attachments collection.
 964       *
 965       * Used as the default comparator for instances of wp.media.model.Attachments
 966       * and its subclasses. @see wp.media.model.Attachments._changeOrderby().
 967       *
 968       * @param {Backbone.Model} a
 969       * @param {Backbone.Model} b
 970       * @param {Object} options
 971       * @returns {Number} -1 if the first model should come before the second,
 972       *    0 if they are of the same rank and
 973       *    1 if the first model should come after.
 974       */
 975      comparator: function( a, b, options ) {
 976          var key   = this.props.get('orderby'),
 977              order = this.props.get('order') || 'DESC',
 978              ac    = a.cid,
 979              bc    = b.cid;
 980  
 981          a = a.get( key );
 982          b = b.get( key );
 983  
 984          if ( 'date' === key || 'modified' === key ) {
 985              a = a || new Date();
 986              b = b || new Date();
 987          }
 988  
 989          // If `options.ties` is set, don't enforce the `cid` tiebreaker.
 990          if ( options && options.ties ) {
 991              ac = bc = null;
 992          }
 993  
 994          return ( 'DESC' === order ) ? wp.media.compare( a, b, ac, bc ) : wp.media.compare( b, a, bc, ac );
 995      },
 996      /** @namespace wp.media.model.Attachments.filters */
 997      filters: {
 998          /**
 999           * @static
1000           * Note that this client-side searching is *not* equivalent
1001           * to our server-side searching.
1002           *
1003           * @param {wp.media.model.Attachment} attachment
1004           *
1005           * @this wp.media.model.Attachments
1006           *
1007           * @returns {Boolean}
1008           */
1009          search: function( attachment ) {
1010              if ( ! this.props.get('search') ) {
1011                  return true;
1012              }
1013  
1014              return _.any(['title','filename','description','caption','name'], function( key ) {
1015                  var value = attachment.get( key );
1016                  return value && -1 !== value.search( this.props.get('search') );
1017              }, this );
1018          },
1019          /**
1020           * @static
1021           * @param {wp.media.model.Attachment} attachment
1022           *
1023           * @this wp.media.model.Attachments
1024           *
1025           * @returns {Boolean}
1026           */
1027          type: function( attachment ) {
1028              var type = this.props.get('type'), atts = attachment.toJSON(), mime, found;
1029  
1030              if ( ! type || ( _.isArray( type ) && ! type.length ) ) {
1031                  return true;
1032              }
1033  
1034              mime = atts.mime || ( atts.file && atts.file.type ) || '';
1035  
1036              if ( _.isArray( type ) ) {
1037                  found = _.find( type, function (t) {
1038                      return -1 !== mime.indexOf( t );
1039                  } );
1040              } else {
1041                  found = -1 !== mime.indexOf( type );
1042              }
1043  
1044              return found;
1045          },
1046          /**
1047           * @static
1048           * @param {wp.media.model.Attachment} attachment
1049           *
1050           * @this wp.media.model.Attachments
1051           *
1052           * @returns {Boolean}
1053           */
1054          uploadedTo: function( attachment ) {
1055              var uploadedTo = this.props.get('uploadedTo');
1056              if ( _.isUndefined( uploadedTo ) ) {
1057                  return true;
1058              }
1059  
1060              return uploadedTo === attachment.get('uploadedTo');
1061          },
1062          /**
1063           * @static
1064           * @param {wp.media.model.Attachment} attachment
1065           *
1066           * @this wp.media.model.Attachments
1067           *
1068           * @returns {Boolean}
1069           */
1070          status: function( attachment ) {
1071              var status = this.props.get('status');
1072              if ( _.isUndefined( status ) ) {
1073                  return true;
1074              }
1075  
1076              return status === attachment.get('status');
1077          }
1078      }
1079  });
1080  
1081  module.exports = Attachments;
1082  
1083  
1084  /***/ }),
1085  
1086  /***/ 26:
1087  /***/ (function(module, exports) {
1088  
1089  var Attachments = wp.media.model.Attachments,
1090      Query;
1091  
1092  /**
1093   * wp.media.model.Query
1094   *
1095   * A collection of attachments that match the supplied query arguments.
1096   *
1097   * Note: Do NOT change this.args after the query has been initialized.
1098   *       Things will break.
1099   *
1100   * @memberOf wp.media.model
1101   *
1102   * @class
1103   * @augments wp.media.model.Attachments
1104   * @augments Backbone.Collection
1105   *
1106   * @param {array}  [models]                      Models to initialize with the collection.
1107   * @param {object} [options]                     Options hash.
1108   * @param {object} [options.args]                Attachments query arguments.
1109   * @param {object} [options.args.posts_per_page]
1110   */
1111  Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{
1112      /**
1113       * @param {array}  [models=[]]  Array of initial models to populate the collection.
1114       * @param {object} [options={}]
1115       */
1116      initialize: function( models, options ) {
1117          var allowed;
1118  
1119          options = options || {};
1120          Attachments.prototype.initialize.apply( this, arguments );
1121  
1122          this.args     = options.args;
1123          this._hasMore = true;
1124          this.created  = new Date();
1125  
1126          this.filters.order = function( attachment ) {
1127              var orderby = this.props.get('orderby'),
1128                  order = this.props.get('order');
1129  
1130              if ( ! this.comparator ) {
1131                  return true;
1132              }
1133  
1134              // We want any items that can be placed before the last
1135              // item in the set. If we add any items after the last
1136              // item, then we can't guarantee the set is complete.
1137              if ( this.length ) {
1138                  return 1 !== this.comparator( attachment, this.last(), { ties: true });
1139  
1140              // Handle the case where there are no items yet and
1141              // we're sorting for recent items. In that case, we want
1142              // changes that occurred after we created the query.
1143              } else if ( 'DESC' === order && ( 'date' === orderby || 'modified' === orderby ) ) {
1144                  return attachment.get( orderby ) >= this.created;
1145  
1146              // If we're sorting by menu order and we have no items,
1147              // accept any items that have the default menu order (0).
1148              } else if ( 'ASC' === order && 'menuOrder' === orderby ) {
1149                  return attachment.get( orderby ) === 0;
1150              }
1151  
1152              // Otherwise, we don't want any items yet.
1153              return false;
1154          };
1155  
1156          // Observe the central `wp.Uploader.queue` collection to watch for
1157          // new matches for the query.
1158          //
1159          // Only observe when a limited number of query args are set. There
1160          // are no filters for other properties, so observing will result in
1161          // false positives in those queries.
1162          allowed = [ 's', 'order', 'orderby', 'posts_per_page', 'post_mime_type', 'post_parent', 'author' ];
1163          if ( wp.Uploader && _( this.args ).chain().keys().difference( allowed ).isEmpty().value() ) {
1164              this.observe( wp.Uploader.queue );
1165          }
1166      },
1167      /**
1168       * Whether there are more attachments that haven't been sync'd from the server
1169       * that match the collection's query.
1170       *
1171       * @returns {boolean}
1172       */
1173      hasMore: function() {
1174          return this._hasMore;
1175      },
1176      /**
1177       * Fetch more attachments from the server for the collection.
1178       *
1179       * @param   {object}  [options={}]
1180       * @returns {Promise}
1181       */
1182      more: function( options ) {
1183          var query = this;
1184  
1185          // If there is already a request pending, return early with the Deferred object.
1186          if ( this._more && 'pending' === this._more.state() ) {
1187              return this._more;
1188          }
1189  
1190          if ( ! this.hasMore() ) {
1191              return jQuery.Deferred().resolveWith( this ).promise();
1192          }
1193  
1194          options = options || {};
1195          options.remove = false;
1196  
1197          return this._more = this.fetch( options ).done( function( resp ) {
1198              if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) {
1199                  query._hasMore = false;
1200              }
1201          });
1202      },
1203      /**
1204       * Overrides Backbone.Collection.sync
1205       * Overrides wp.media.model.Attachments.sync
1206       *
1207       * @param {String} method
1208       * @param {Backbone.Model} model
1209       * @param {Object} [options={}]
1210       * @returns {Promise}
1211       */
1212      sync: function( method, model, options ) {
1213          var args, fallback;
1214  
1215          // Overload the read method so Attachment.fetch() functions correctly.
1216          if ( 'read' === method ) {
1217              options = options || {};
1218              options.context = this;
1219              options.data = _.extend( options.data || {}, {
1220                  action:  'query-attachments',
1221                  post_id: wp.media.model.settings.post.id
1222              });
1223  
1224              // Clone the args so manipulation is non-destructive.
1225              args = _.clone( this.args );
1226  
1227              // Determine which page to query.
1228              if ( -1 !== args.posts_per_page ) {
1229                  args.paged = Math.round( this.length / args.posts_per_page ) + 1;
1230              }
1231  
1232              options.data.query = args;
1233              return wp.media.ajax( options );
1234  
1235          // Otherwise, fall back to Backbone.sync()
1236          } else {
1237              /**
1238               * Call wp.media.model.Attachments.sync or Backbone.sync
1239               */
1240              fallback = Attachments.prototype.sync ? Attachments.prototype : Backbone;
1241              return fallback.sync.apply( this, arguments );
1242          }
1243      }
1244  }, /** @lends wp.media.model.Query */{
1245      /**
1246       * @readonly
1247       */
1248      defaultProps: {
1249          orderby: 'date',
1250          order:   'DESC'
1251      },
1252      /**
1253       * @readonly
1254       */
1255      defaultArgs: {
1256          posts_per_page: 40
1257      },
1258      /**
1259       * @readonly
1260       */
1261      orderby: {
1262          allowed:  [ 'name', 'author', 'date', 'title', 'modified', 'uploadedTo', 'id', 'post__in', 'menuOrder' ],
1263          /**
1264           * A map of JavaScript orderby values to their WP_Query equivalents.
1265           * @type {Object}
1266           */
1267          valuemap: {
1268              'id':         'ID',
1269              'uploadedTo': 'parent',
1270              'menuOrder':  'menu_order ID'
1271          }
1272      },
1273      /**
1274       * A map of JavaScript query properties to their WP_Query equivalents.
1275       *
1276       * @readonly
1277       */
1278      propmap: {
1279          'search':        's',
1280          'type':            'post_mime_type',
1281          'perPage':        'posts_per_page',
1282          'menuOrder':    'menu_order',
1283          'uploadedTo':    'post_parent',
1284          'status':        'post_status',
1285          'include':        'post__in',
1286          'exclude':        'post__not_in',
1287          'author':        'author'
1288      },
1289      /**
1290       * Creates and returns an Attachments Query collection given the properties.
1291       *
1292       * Caches query objects and reuses where possible.
1293       *
1294       * @static
1295       * @method
1296       *
1297       * @param {object} [props]
1298       * @param {Object} [props.cache=true]   Whether to use the query cache or not.
1299       * @param {Object} [props.order]
1300       * @param {Object} [props.orderby]
1301       * @param {Object} [props.include]
1302       * @param {Object} [props.exclude]
1303       * @param {Object} [props.s]
1304       * @param {Object} [props.post_mime_type]
1305       * @param {Object} [props.posts_per_page]
1306       * @param {Object} [props.menu_order]
1307       * @param {Object} [props.post_parent]
1308       * @param {Object} [props.post_status]
1309       * @param {Object} [props.author]
1310       * @param {Object} [options]
1311       *
1312       * @returns {wp.media.model.Query} A new Attachments Query collection.
1313       */
1314      get: (function(){
1315          /**
1316           * @static
1317           * @type Array
1318           */
1319          var queries = [];
1320  
1321          /**
1322           * @returns {Query}
1323           */
1324          return function( props, options ) {
1325              var args     = {},
1326                  orderby  = Query.orderby,
1327                  defaults = Query.defaultProps,
1328                  query,
1329                  cache    = !! props.cache || _.isUndefined( props.cache );
1330  
1331              // Remove the `query` property. This isn't linked to a query,
1332              // this *is* the query.
1333              delete props.query;
1334              delete props.cache;
1335  
1336              // Fill default args.
1337              _.defaults( props, defaults );
1338  
1339              // Normalize the order.
1340              props.order = props.order.toUpperCase();
1341              if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
1342                  props.order = defaults.order.toUpperCase();
1343              }
1344  
1345              // Ensure we have a valid orderby value.
1346              if ( ! _.contains( orderby.allowed, props.orderby ) ) {
1347                  props.orderby = defaults.orderby;
1348              }
1349  
1350              _.each( [ 'include', 'exclude' ], function( prop ) {
1351                  if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
1352                      props[ prop ] = [ props[ prop ] ];
1353                  }
1354              } );
1355  
1356              // Generate the query `args` object.
1357              // Correct any differing property names.
1358              _.each( props, function( value, prop ) {
1359                  if ( _.isNull( value ) ) {
1360                      return;
1361                  }
1362  
1363                  args[ Query.propmap[ prop ] || prop ] = value;
1364              });
1365  
1366              // Fill any other default query args.
1367              _.defaults( args, Query.defaultArgs );
1368  
1369              // `props.orderby` does not always map directly to `args.orderby`.
1370              // Substitute exceptions specified in orderby.keymap.
1371              args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
1372  
1373              // Search the query cache for a matching query.
1374              if ( cache ) {
1375                  query = _.find( queries, function( query ) {
1376                      return _.isEqual( query.args, args );
1377                  });
1378              } else {
1379                  queries = [];
1380              }
1381  
1382              // Otherwise, create a new query and add it to the cache.
1383              if ( ! query ) {
1384                  query = new Query( [], _.extend( options || {}, {
1385                      props: props,
1386                      args:  args
1387                  } ) );
1388                  queries.push( query );
1389              }
1390  
1391              return query;
1392          };
1393      }())
1394  });
1395  
1396  module.exports = Query;
1397  
1398  
1399  /***/ }),
1400  
1401  /***/ 27:
1402  /***/ (function(module, exports) {
1403  
1404  /**
1405   * wp.media.model.PostImage
1406   *
1407   * An instance of an image that's been embedded into a post.
1408   *
1409   * Used in the embedded image attachment display settings modal - @see wp.media.view.MediaFrame.ImageDetails.
1410   *
1411   * @memberOf wp.media.model
1412   *
1413   * @class
1414   * @augments Backbone.Model
1415   *
1416   * @param {int} [attributes]               Initial model attributes.
1417   * @param {int} [attributes.attachment_id] ID of the attachment.
1418   **/
1419  var PostImage = Backbone.Model.extend(/** @lends wp.media.model.PostImage.prototype */{
1420  
1421      initialize: function( attributes ) {
1422          var Attachment = wp.media.model.Attachment;
1423          this.attachment = false;
1424  
1425          if ( attributes.attachment_id ) {
1426              this.attachment = Attachment.get( attributes.attachment_id );
1427              if ( this.attachment.get( 'url' ) ) {
1428                  this.dfd = jQuery.Deferred();
1429                  this.dfd.resolve();
1430              } else {
1431                  this.dfd = this.attachment.fetch();
1432              }
1433              this.bindAttachmentListeners();
1434          }
1435  
1436          // keep url in sync with changes to the type of link
1437          this.on( 'change:link', this.updateLinkUrl, this );
1438          this.on( 'change:size', this.updateSize, this );
1439  
1440          this.setLinkTypeFromUrl();
1441          this.setAspectRatio();
1442  
1443          this.set( 'originalUrl', attributes.url );
1444      },
1445  
1446      bindAttachmentListeners: function() {
1447          this.listenTo( this.attachment, 'sync', this.setLinkTypeFromUrl );
1448          this.listenTo( this.attachment, 'sync', this.setAspectRatio );
1449          this.listenTo( this.attachment, 'change', this.updateSize );
1450      },
1451  
1452      changeAttachment: function( attachment, props ) {
1453          this.stopListening( this.attachment );
1454          this.attachment = attachment;
1455          this.bindAttachmentListeners();
1456  
1457          this.set( 'attachment_id', this.attachment.get( 'id' ) );
1458          this.set( 'caption', this.attachment.get( 'caption' ) );
1459          this.set( 'alt', this.attachment.get( 'alt' ) );
1460          this.set( 'size', props.get( 'size' ) );
1461          this.set( 'align', props.get( 'align' ) );
1462          this.set( 'link', props.get( 'link' ) );
1463          this.updateLinkUrl();
1464          this.updateSize();
1465      },
1466  
1467      setLinkTypeFromUrl: function() {
1468          var linkUrl = this.get( 'linkUrl' ),
1469              type;
1470  
1471          if ( ! linkUrl ) {
1472              this.set( 'link', 'none' );
1473              return;
1474          }
1475  
1476          // default to custom if there is a linkUrl
1477          type = 'custom';
1478  
1479          if ( this.attachment ) {
1480              if ( this.attachment.get( 'url' ) === linkUrl ) {
1481                  type = 'file';
1482              } else if ( this.attachment.get( 'link' ) === linkUrl ) {
1483                  type = 'post';
1484              }
1485          } else {
1486              if ( this.get( 'url' ) === linkUrl ) {
1487                  type = 'file';
1488              }
1489          }
1490  
1491          this.set( 'link', type );
1492      },
1493  
1494      updateLinkUrl: function() {
1495          var link = this.get( 'link' ),
1496              url;
1497  
1498          switch( link ) {
1499              case 'file':
1500                  if ( this.attachment ) {
1501                      url = this.attachment.get( 'url' );
1502                  } else {
1503                      url = this.get( 'url' );
1504                  }
1505                  this.set( 'linkUrl', url );
1506                  break;
1507              case 'post':
1508                  this.set( 'linkUrl', this.attachment.get( 'link' ) );
1509                  break;
1510              case 'none':
1511                  this.set( 'linkUrl', '' );
1512                  break;
1513          }
1514      },
1515  
1516      updateSize: function() {
1517          var size;
1518  
1519          if ( ! this.attachment ) {
1520              return;
1521          }
1522  
1523          if ( this.get( 'size' ) === 'custom' ) {
1524              this.set( 'width', this.get( 'customWidth' ) );
1525              this.set( 'height', this.get( 'customHeight' ) );
1526              this.set( 'url', this.get( 'originalUrl' ) );
1527              return;
1528          }
1529  
1530          size = this.attachment.get( 'sizes' )[ this.get( 'size' ) ];
1531  
1532          if ( ! size ) {
1533              return;
1534          }
1535  
1536          this.set( 'url', size.url );
1537          this.set( 'width', size.width );
1538          this.set( 'height', size.height );
1539      },
1540  
1541      setAspectRatio: function() {
1542          var full;
1543  
1544          if ( this.attachment && this.attachment.get( 'sizes' ) ) {
1545              full = this.attachment.get( 'sizes' ).full;
1546  
1547              if ( full ) {
1548                  this.set( 'aspectRatio', full.width / full.height );
1549                  return;
1550              }
1551          }
1552  
1553          this.set( 'aspectRatio', this.get( 'customWidth' ) / this.get( 'customHeight' ) );
1554      }
1555  });
1556  
1557  module.exports = PostImage;
1558  
1559  
1560  /***/ }),
1561  
1562  /***/ 28:
1563  /***/ (function(module, exports) {
1564  
1565  var Attachments = wp.media.model.Attachments,
1566      Selection;
1567  
1568  /**
1569   * wp.media.model.Selection
1570   *
1571   * A selection of attachments.
1572   *
1573   * @memberOf wp.media.model
1574   *
1575   * @class
1576   * @augments wp.media.model.Attachments
1577   * @augments Backbone.Collection
1578   */
1579  Selection = Attachments.extend(/** @lends wp.media.model.Selection.prototype */{
1580      /**
1581       * Refresh the `single` model whenever the selection changes.
1582       * Binds `single` instead of using the context argument to ensure
1583       * it receives no parameters.
1584       *
1585       * @param {Array} [models=[]] Array of models used to populate the collection.
1586       * @param {Object} [options={}]
1587       */
1588      initialize: function( models, options ) {
1589          /**
1590           * call 'initialize' directly on the parent class
1591           */
1592          Attachments.prototype.initialize.apply( this, arguments );
1593          this.multiple = options && options.multiple;
1594  
1595          this.on( 'add remove reset', _.bind( this.single, this, false ) );
1596      },
1597  
1598      /**
1599       * If the workflow does not support multi-select, clear out the selection
1600       * before adding a new attachment to it.
1601       *
1602       * @param {Array} models
1603       * @param {Object} options
1604       * @returns {wp.media.model.Attachment[]}
1605       */
1606      add: function( models, options ) {
1607          if ( ! this.multiple ) {
1608              this.remove( this.models );
1609          }
1610          /**
1611           * call 'add' directly on the parent class
1612           */
1613          return Attachments.prototype.add.call( this, models, options );
1614      },
1615  
1616      /**
1617       * Fired when toggling (clicking on) an attachment in the modal.
1618       *
1619       * @param {undefined|boolean|wp.media.model.Attachment} model
1620       *
1621       * @fires wp.media.model.Selection#selection:single
1622       * @fires wp.media.model.Selection#selection:unsingle
1623       *
1624       * @returns {Backbone.Model}
1625       */
1626      single: function( model ) {
1627          var previous = this._single;
1628  
1629          // If a `model` is provided, use it as the single model.
1630          if ( model ) {
1631              this._single = model;
1632          }
1633          // If the single model isn't in the selection, remove it.
1634          if ( this._single && ! this.get( this._single.cid ) ) {
1635              delete this._single;
1636          }
1637  
1638          this._single = this._single || this.last();
1639  
1640          // If single has changed, fire an event.
1641          if ( this._single !== previous ) {
1642              if ( previous ) {
1643                  previous.trigger( 'selection:unsingle', previous, this );
1644  
1645                  // If the model was already removed, trigger the collection
1646                  // event manually.
1647                  if ( ! this.get( previous.cid ) ) {
1648                      this.trigger( 'selection:unsingle', previous, this );
1649                  }
1650              }
1651              if ( this._single ) {
1652                  this._single.trigger( 'selection:single', this._single, this );
1653              }
1654          }
1655  
1656          // Return the single model, or the last model as a fallback.
1657          return this._single;
1658      }
1659  });
1660  
1661  module.exports = Selection;
1662  
1663  
1664  /***/ })
1665  
1666  /******/ });


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7