media-models.js 43 KB

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