[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Thu Feb 27 01:00:03 2020 Cross-referenced by PHPXref 0.7.1