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