You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10497 lines
265 KiB

1 year ago
  1. /******/ (function() { // webpackBootstrap
  2. /******/ var __webpack_modules__ = ({
  3. /***/ 1517:
  4. /***/ (function(module) {
  5. var Selection = wp.media.model.Selection,
  6. Library = wp.media.controller.Library,
  7. CollectionAdd;
  8. /**
  9. * wp.media.controller.CollectionAdd
  10. *
  11. * A state for adding attachments to a collection (e.g. video playlist).
  12. *
  13. * @memberOf wp.media.controller
  14. *
  15. * @class
  16. * @augments wp.media.controller.Library
  17. * @augments wp.media.controller.State
  18. * @augments Backbone.Model
  19. *
  20. * @param {object} [attributes] The attributes hash passed to the state.
  21. * @param {string} [attributes.id=library] Unique identifier.
  22. * @param {string} attributes.title Title for the state. Displays in the frame's title region.
  23. * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
  24. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  25. * If one is not supplied, a collection of attachments of the specified type will be created.
  26. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  27. * Accepts 'all', 'uploaded', or 'unattached'.
  28. * @param {string} [attributes.menu=gallery] Initial mode for the menu region.
  29. * @param {string} [attributes.content=upload] Initial mode for the content region.
  30. * Overridden by persistent user setting if 'contentUserSetting' is true.
  31. * @param {string} [attributes.router=browse] Initial mode for the router region.
  32. * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region.
  33. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  34. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  35. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  36. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  37. * @param {int} [attributes.priority=100] The priority for the state link in the media menu.
  38. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  39. * Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
  40. * @param {string} attributes.type The collection's media type. (e.g. 'video').
  41. * @param {string} attributes.collectionType The collection type. (e.g. 'playlist').
  42. */
  43. CollectionAdd = Library.extend(/** @lends wp.media.controller.CollectionAdd.prototype */{
  44. defaults: _.defaults( {
  45. // Selection defaults. @see media.model.Selection
  46. multiple: 'add',
  47. // Attachments browser defaults. @see media.view.AttachmentsBrowser
  48. filterable: 'uploaded',
  49. priority: 100,
  50. syncSelection: false
  51. }, Library.prototype.defaults ),
  52. /**
  53. * @since 3.9.0
  54. */
  55. initialize: function() {
  56. var collectionType = this.get('collectionType');
  57. if ( 'video' === this.get( 'type' ) ) {
  58. collectionType = 'video-' + collectionType;
  59. }
  60. this.set( 'id', collectionType + '-library' );
  61. this.set( 'toolbar', collectionType + '-add' );
  62. this.set( 'menu', collectionType );
  63. // If we haven't been provided a `library`, create a `Selection`.
  64. if ( ! this.get('library') ) {
  65. this.set( 'library', wp.media.query({ type: this.get('type') }) );
  66. }
  67. Library.prototype.initialize.apply( this, arguments );
  68. },
  69. /**
  70. * @since 3.9.0
  71. */
  72. activate: function() {
  73. var library = this.get('library'),
  74. editLibrary = this.get('editLibrary'),
  75. edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library');
  76. if ( editLibrary && editLibrary !== edit ) {
  77. library.unobserve( editLibrary );
  78. }
  79. // Accepts attachments that exist in the original library and
  80. // that do not exist in gallery's library.
  81. library.validator = function( attachment ) {
  82. return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
  83. };
  84. /*
  85. * Reset the library to ensure that all attachments are re-added
  86. * to the collection. Do so silently, as calling `observe` will
  87. * trigger the `reset` event.
  88. */
  89. library.reset( library.mirroring.models, { silent: true });
  90. library.observe( edit );
  91. this.set('editLibrary', edit);
  92. Library.prototype.activate.apply( this, arguments );
  93. }
  94. });
  95. module.exports = CollectionAdd;
  96. /***/ }),
  97. /***/ 1817:
  98. /***/ (function(module) {
  99. var Library = wp.media.controller.Library,
  100. l10n = wp.media.view.l10n,
  101. $ = jQuery,
  102. CollectionEdit;
  103. /**
  104. * wp.media.controller.CollectionEdit
  105. *
  106. * A state for editing a collection, which is used by audio and video playlists,
  107. * and can be used for other collections.
  108. *
  109. * @memberOf wp.media.controller
  110. *
  111. * @class
  112. * @augments wp.media.controller.Library
  113. * @augments wp.media.controller.State
  114. * @augments Backbone.Model
  115. *
  116. * @param {object} [attributes] The attributes hash passed to the state.
  117. * @param {string} attributes.title Title for the state. Displays in the media menu and the frame's title region.
  118. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to edit.
  119. * If one is not supplied, an empty media.model.Selection collection is created.
  120. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  121. * @param {string} [attributes.content=browse] Initial mode for the content region.
  122. * @param {string} attributes.menu Initial mode for the menu region. @todo this needs a better explanation.
  123. * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
  124. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  125. * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar.
  126. * @param {boolean} [attributes.describe=true] Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
  127. * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
  128. * @param {boolean} [attributes.dragInfoText] Instructional text about the attachments being sortable.
  129. * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
  130. * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance.
  131. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  132. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  133. * Defaults to false for this state, because the library passed in *is* the selection.
  134. * @param {view} [attributes.SettingsView] The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
  135. * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
  136. * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
  137. * @param {string} attributes.type The collection's media type. (e.g. 'video').
  138. * @param {string} attributes.collectionType The collection type. (e.g. 'playlist').
  139. */
  140. CollectionEdit = Library.extend(/** @lends wp.media.controller.CollectionEdit.prototype */{
  141. defaults: {
  142. multiple: false,
  143. sortable: true,
  144. date: false,
  145. searchable: false,
  146. content: 'browse',
  147. describe: true,
  148. dragInfo: true,
  149. idealColumnWidth: 170,
  150. editing: false,
  151. priority: 60,
  152. SettingsView: false,
  153. syncSelection: false
  154. },
  155. /**
  156. * @since 3.9.0
  157. */
  158. initialize: function() {
  159. var collectionType = this.get('collectionType');
  160. if ( 'video' === this.get( 'type' ) ) {
  161. collectionType = 'video-' + collectionType;
  162. }
  163. this.set( 'id', collectionType + '-edit' );
  164. this.set( 'toolbar', collectionType + '-edit' );
  165. // If we haven't been provided a `library`, create a `Selection`.
  166. if ( ! this.get('library') ) {
  167. this.set( 'library', new wp.media.model.Selection() );
  168. }
  169. // The single `Attachment` view to be used in the `Attachments` view.
  170. if ( ! this.get('AttachmentView') ) {
  171. this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
  172. }
  173. Library.prototype.initialize.apply( this, arguments );
  174. },
  175. /**
  176. * @since 3.9.0
  177. */
  178. activate: function() {
  179. var library = this.get('library');
  180. // Limit the library to images only.
  181. library.props.set( 'type', this.get( 'type' ) );
  182. // Watch for uploaded attachments.
  183. this.get('library').observe( wp.Uploader.queue );
  184. this.frame.on( 'content:render:browse', this.renderSettings, this );
  185. Library.prototype.activate.apply( this, arguments );
  186. },
  187. /**
  188. * @since 3.9.0
  189. */
  190. deactivate: function() {
  191. // Stop watching for uploaded attachments.
  192. this.get('library').unobserve( wp.Uploader.queue );
  193. this.frame.off( 'content:render:browse', this.renderSettings, this );
  194. Library.prototype.deactivate.apply( this, arguments );
  195. },
  196. /**
  197. * Render the collection embed settings view in the browser sidebar.
  198. *
  199. * @todo This is against the pattern elsewhere in media. Typically the frame
  200. * is responsible for adding region mode callbacks. Explain.
  201. *
  202. * @since 3.9.0
  203. *
  204. * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
  205. */
  206. renderSettings: function( attachmentsBrowserView ) {
  207. var library = this.get('library'),
  208. collectionType = this.get('collectionType'),
  209. dragInfoText = this.get('dragInfoText'),
  210. SettingsView = this.get('SettingsView'),
  211. obj = {};
  212. if ( ! library || ! attachmentsBrowserView ) {
  213. return;
  214. }
  215. library[ collectionType ] = library[ collectionType ] || new Backbone.Model();
  216. obj[ collectionType ] = new SettingsView({
  217. controller: this,
  218. model: library[ collectionType ],
  219. priority: 40
  220. });
  221. attachmentsBrowserView.sidebar.set( obj );
  222. if ( dragInfoText ) {
  223. attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({
  224. el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
  225. priority: -40
  226. }) );
  227. }
  228. // Add the 'Reverse order' button to the toolbar.
  229. attachmentsBrowserView.toolbar.set( 'reverse', {
  230. text: l10n.reverseOrder,
  231. priority: 80,
  232. click: function() {
  233. library.reset( library.toArray().reverse() );
  234. }
  235. });
  236. }
  237. });
  238. module.exports = CollectionEdit;
  239. /***/ }),
  240. /***/ 2288:
  241. /***/ (function(module) {
  242. var l10n = wp.media.view.l10n,
  243. Cropper;
  244. /**
  245. * wp.media.controller.Cropper
  246. *
  247. * A class for cropping an image when called from the header media customization panel.
  248. *
  249. * @memberOf wp.media.controller
  250. *
  251. * @class
  252. * @augments wp.media.controller.State
  253. * @augments Backbone.Model
  254. */
  255. Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{
  256. defaults: {
  257. id: 'cropper',
  258. title: l10n.cropImage,
  259. // Region mode defaults.
  260. toolbar: 'crop',
  261. content: 'crop',
  262. router: false,
  263. canSkipCrop: false,
  264. // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state.
  265. doCropArgs: {}
  266. },
  267. /**
  268. * Shows the crop image window when called from the Add new image button.
  269. *
  270. * @since 4.2.0
  271. *
  272. * @return {void}
  273. */
  274. activate: function() {
  275. this.frame.on( 'content:create:crop', this.createCropContent, this );
  276. this.frame.on( 'close', this.removeCropper, this );
  277. this.set('selection', new Backbone.Collection(this.frame._selection.single));
  278. },
  279. /**
  280. * Changes the state of the toolbar window to browse mode.
  281. *
  282. * @since 4.2.0
  283. *
  284. * @return {void}
  285. */
  286. deactivate: function() {
  287. this.frame.toolbar.mode('browse');
  288. },
  289. /**
  290. * Creates the crop image window.
  291. *
  292. * Initialized when clicking on the Select and Crop button.
  293. *
  294. * @since 4.2.0
  295. *
  296. * @fires crop window
  297. *
  298. * @return {void}
  299. */
  300. createCropContent: function() {
  301. this.cropperView = new wp.media.view.Cropper({
  302. controller: this,
  303. attachment: this.get('selection').first()
  304. });
  305. this.cropperView.on('image-loaded', this.createCropToolbar, this);
  306. this.frame.content.set(this.cropperView);
  307. },
  308. /**
  309. * Removes the image selection and closes the cropping window.
  310. *
  311. * @since 4.2.0
  312. *
  313. * @return {void}
  314. */
  315. removeCropper: function() {
  316. this.imgSelect.cancelSelection();
  317. this.imgSelect.setOptions({remove: true});
  318. this.imgSelect.update();
  319. this.cropperView.remove();
  320. },
  321. /**
  322. * Checks if cropping can be skipped and creates crop toolbar accordingly.
  323. *
  324. * @since 4.2.0
  325. *
  326. * @return {void}
  327. */
  328. createCropToolbar: function() {
  329. var canSkipCrop, toolbarOptions;
  330. canSkipCrop = this.get('canSkipCrop') || false;
  331. toolbarOptions = {
  332. controller: this.frame,
  333. items: {
  334. insert: {
  335. style: 'primary',
  336. text: l10n.cropImage,
  337. priority: 80,
  338. requires: { library: false, selection: false },
  339. click: function() {
  340. var controller = this.controller,
  341. selection;
  342. selection = controller.state().get('selection').first();
  343. selection.set({cropDetails: controller.state().imgSelect.getSelection()});
  344. this.$el.text(l10n.cropping);
  345. this.$el.attr('disabled', true);
  346. controller.state().doCrop( selection ).done( function( croppedImage ) {
  347. controller.trigger('cropped', croppedImage );
  348. controller.close();
  349. }).fail( function() {
  350. controller.trigger('content:error:crop');
  351. });
  352. }
  353. }
  354. }
  355. };
  356. if ( canSkipCrop ) {
  357. _.extend( toolbarOptions.items, {
  358. skip: {
  359. style: 'secondary',
  360. text: l10n.skipCropping,
  361. priority: 70,
  362. requires: { library: false, selection: false },
  363. click: function() {
  364. var selection = this.controller.state().get('selection').first();
  365. this.controller.state().cropperView.remove();
  366. this.controller.trigger('skippedcrop', selection);
  367. this.controller.close();
  368. }
  369. }
  370. });
  371. }
  372. this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) );
  373. },
  374. /**
  375. * Creates an object with the image attachment and crop properties.
  376. *
  377. * @since 4.2.0
  378. *
  379. * @return {$.promise} A jQuery promise with the custom header crop details.
  380. */
  381. doCrop: function( attachment ) {
  382. return wp.ajax.post( 'custom-header-crop', _.extend(
  383. {},
  384. this.defaults.doCropArgs,
  385. {
  386. nonce: attachment.get( 'nonces' ).edit,
  387. id: attachment.get( 'id' ),
  388. cropDetails: attachment.get( 'cropDetails' )
  389. }
  390. ) );
  391. }
  392. });
  393. module.exports = Cropper;
  394. /***/ }),
  395. /***/ 6934:
  396. /***/ (function(module) {
  397. var Controller = wp.media.controller,
  398. CustomizeImageCropper;
  399. /**
  400. * A state for cropping an image in the customizer.
  401. *
  402. * @since 4.3.0
  403. *
  404. * @constructs wp.media.controller.CustomizeImageCropper
  405. * @memberOf wp.media.controller
  406. * @augments wp.media.controller.CustomizeImageCropper.Cropper
  407. * @inheritDoc
  408. */
  409. CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{
  410. /**
  411. * Posts the crop details to the admin.
  412. *
  413. * Uses crop measurements when flexible in both directions.
  414. * Constrains flexible side based on image ratio and size of the fixed side.
  415. *
  416. * @since 4.3.0
  417. *
  418. * @param {Object} attachment The attachment to crop.
  419. *
  420. * @return {$.promise} A jQuery promise that represents the crop image request.
  421. */
  422. doCrop: function( attachment ) {
  423. var cropDetails = attachment.get( 'cropDetails' ),
  424. control = this.get( 'control' ),
  425. ratio = cropDetails.width / cropDetails.height;
  426. // Use crop measurements when flexible in both directions.
  427. if ( control.params.flex_width && control.params.flex_height ) {
  428. cropDetails.dst_width = cropDetails.width;
  429. cropDetails.dst_height = cropDetails.height;
  430. // Constrain flexible side based on image ratio and size of the fixed side.
  431. } else {
  432. cropDetails.dst_width = control.params.flex_width ? control.params.height * ratio : control.params.width;
  433. cropDetails.dst_height = control.params.flex_height ? control.params.width / ratio : control.params.height;
  434. }
  435. return wp.ajax.post( 'crop-image', {
  436. wp_customize: 'on',
  437. nonce: attachment.get( 'nonces' ).edit,
  438. id: attachment.get( 'id' ),
  439. context: control.id,
  440. cropDetails: cropDetails
  441. } );
  442. }
  443. });
  444. module.exports = CustomizeImageCropper;
  445. /***/ }),
  446. /***/ 7658:
  447. /***/ (function(module) {
  448. var l10n = wp.media.view.l10n,
  449. EditImage;
  450. /**
  451. * wp.media.controller.EditImage
  452. *
  453. * A state for editing (cropping, etc.) an image.
  454. *
  455. * @memberOf wp.media.controller
  456. *
  457. * @class
  458. * @augments wp.media.controller.State
  459. * @augments Backbone.Model
  460. *
  461. * @param {object} attributes The attributes hash passed to the state.
  462. * @param {wp.media.model.Attachment} attributes.model The attachment.
  463. * @param {string} [attributes.id=edit-image] Unique identifier.
  464. * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region.
  465. * @param {string} [attributes.content=edit-image] Initial mode for the content region.
  466. * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region.
  467. * @param {string} [attributes.menu=false] Initial mode for the menu region.
  468. * @param {string} [attributes.url] Unused. @todo Consider removal.
  469. */
  470. EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{
  471. defaults: {
  472. id: 'edit-image',
  473. title: l10n.editImage,
  474. menu: false,
  475. toolbar: 'edit-image',
  476. content: 'edit-image',
  477. url: ''
  478. },
  479. /**
  480. * Activates a frame for editing a featured image.
  481. *
  482. * @since 3.9.0
  483. *
  484. * @return {void}
  485. */
  486. activate: function() {
  487. this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) );
  488. },
  489. /**
  490. * Deactivates a frame for editing a featured image.
  491. *
  492. * @since 3.9.0
  493. *
  494. * @return {void}
  495. */
  496. deactivate: function() {
  497. this.frame.off( 'toolbar:render:edit-image' );
  498. },
  499. /**
  500. * Adds a toolbar with a back button.
  501. *
  502. * When the back button is pressed it checks whether there is a previous state.
  503. * In case there is a previous state it sets that previous state otherwise it
  504. * closes the frame.
  505. *
  506. * @since 3.9.0
  507. *
  508. * @return {void}
  509. */
  510. toolbar: function() {
  511. var frame = this.frame,
  512. lastState = frame.lastState(),
  513. previous = lastState && lastState.id;
  514. frame.toolbar.set( new wp.media.view.Toolbar({
  515. controller: frame,
  516. items: {
  517. back: {
  518. style: 'primary',
  519. text: l10n.back,
  520. priority: 20,
  521. click: function() {
  522. if ( previous ) {
  523. frame.setState( previous );
  524. } else {
  525. frame.close();
  526. }
  527. }
  528. }
  529. }
  530. }) );
  531. }
  532. });
  533. module.exports = EditImage;
  534. /***/ }),
  535. /***/ 9067:
  536. /***/ (function(module) {
  537. var l10n = wp.media.view.l10n,
  538. $ = Backbone.$,
  539. Embed;
  540. /**
  541. * wp.media.controller.Embed
  542. *
  543. * A state for embedding media from a URL.
  544. *
  545. * @memberOf wp.media.controller
  546. *
  547. * @class
  548. * @augments wp.media.controller.State
  549. * @augments Backbone.Model
  550. *
  551. * @param {object} attributes The attributes hash passed to the state.
  552. * @param {string} [attributes.id=embed] Unique identifier.
  553. * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
  554. * @param {string} [attributes.content=embed] Initial mode for the content region.
  555. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  556. * @param {string} [attributes.toolbar=main-embed] Initial mode for the toolbar region.
  557. * @param {string} [attributes.menu=false] Initial mode for the menu region.
  558. * @param {int} [attributes.priority=120] The priority for the state link in the media menu.
  559. * @param {string} [attributes.type=link] The type of embed. Currently only link is supported.
  560. * @param {string} [attributes.url] The embed URL.
  561. * @param {object} [attributes.metadata={}] Properties of the embed, which will override attributes.url if set.
  562. */
  563. Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{
  564. defaults: {
  565. id: 'embed',
  566. title: l10n.insertFromUrlTitle,
  567. content: 'embed',
  568. menu: 'default',
  569. toolbar: 'main-embed',
  570. priority: 120,
  571. type: 'link',
  572. url: '',
  573. metadata: {}
  574. },
  575. // The amount of time used when debouncing the scan.
  576. sensitivity: 400,
  577. initialize: function(options) {
  578. this.metadata = options.metadata;
  579. this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
  580. this.props = new Backbone.Model( this.metadata || { url: '' });
  581. this.props.on( 'change:url', this.debouncedScan, this );
  582. this.props.on( 'change:url', this.refresh, this );
  583. this.on( 'scan', this.scanImage, this );
  584. },
  585. /**
  586. * Trigger a scan of the embedded URL's content for metadata required to embed.
  587. *
  588. * @fires wp.media.controller.Embed#scan
  589. */
  590. scan: function() {
  591. var scanners,
  592. embed = this,
  593. attributes = {
  594. type: 'link',
  595. scanners: []
  596. };
  597. /*
  598. * Scan is triggered with the list of `attributes` to set on the
  599. * state, useful for the 'type' attribute and 'scanners' attribute,
  600. * an array of promise objects for asynchronous scan operations.
  601. */
  602. if ( this.props.get('url') ) {
  603. this.trigger( 'scan', attributes );
  604. }
  605. if ( attributes.scanners.length ) {
  606. scanners = attributes.scanners = $.when.apply( $, attributes.scanners );
  607. scanners.always( function() {
  608. if ( embed.get('scanners') === scanners ) {
  609. embed.set( 'loading', false );
  610. }
  611. });
  612. } else {
  613. attributes.scanners = null;
  614. }
  615. attributes.loading = !! attributes.scanners;
  616. this.set( attributes );
  617. },
  618. /**
  619. * Try scanning the embed as an image to discover its dimensions.
  620. *
  621. * @param {Object} attributes
  622. */
  623. scanImage: function( attributes ) {
  624. var frame = this.frame,
  625. state = this,
  626. url = this.props.get('url'),
  627. image = new Image(),
  628. deferred = $.Deferred();
  629. attributes.scanners.push( deferred.promise() );
  630. // Try to load the image and find its width/height.
  631. image.onload = function() {
  632. deferred.resolve();
  633. if ( state !== frame.state() || url !== state.props.get('url') ) {
  634. return;
  635. }
  636. state.set({
  637. type: 'image'
  638. });
  639. state.props.set({
  640. width: image.width,
  641. height: image.height
  642. });
  643. };
  644. image.onerror = deferred.reject;
  645. image.src = url;
  646. },
  647. refresh: function() {
  648. this.frame.toolbar.get().refresh();
  649. },
  650. reset: function() {
  651. this.props.clear().set({ url: '' });
  652. if ( this.active ) {
  653. this.refresh();
  654. }
  655. }
  656. });
  657. module.exports = Embed;
  658. /***/ }),
  659. /***/ 5095:
  660. /***/ (function(module) {
  661. var Attachment = wp.media.model.Attachment,
  662. Library = wp.media.controller.Library,
  663. l10n = wp.media.view.l10n,
  664. FeaturedImage;
  665. /**
  666. * wp.media.controller.FeaturedImage
  667. *
  668. * A state for selecting a featured image for a post.
  669. *
  670. * @memberOf wp.media.controller
  671. *
  672. * @class
  673. * @augments wp.media.controller.Library
  674. * @augments wp.media.controller.State
  675. * @augments Backbone.Model
  676. *
  677. * @param {object} [attributes] The attributes hash passed to the state.
  678. * @param {string} [attributes.id=featured-image] Unique identifier.
  679. * @param {string} [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
  680. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  681. * If one is not supplied, a collection of all images will be created.
  682. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  683. * @param {string} [attributes.content=upload] Initial mode for the content region.
  684. * Overridden by persistent user setting if 'contentUserSetting' is true.
  685. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  686. * @param {string} [attributes.router=browse] Initial mode for the router region.
  687. * @param {string} [attributes.toolbar=featured-image] Initial mode for the toolbar region.
  688. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  689. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  690. * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown.
  691. * Accepts 'all', 'uploaded', or 'unattached'.
  692. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  693. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  694. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  695. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  696. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  697. */
  698. FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{
  699. defaults: _.defaults({
  700. id: 'featured-image',
  701. title: l10n.setFeaturedImageTitle,
  702. multiple: false,
  703. filterable: 'uploaded',
  704. toolbar: 'featured-image',
  705. priority: 60,
  706. syncSelection: true
  707. }, Library.prototype.defaults ),
  708. /**
  709. * @since 3.5.0
  710. */
  711. initialize: function() {
  712. var library, comparator;
  713. // If we haven't been provided a `library`, create a `Selection`.
  714. if ( ! this.get('library') ) {
  715. this.set( 'library', wp.media.query({ type: 'image' }) );
  716. }
  717. Library.prototype.initialize.apply( this, arguments );
  718. library = this.get('library');
  719. comparator = library.comparator;
  720. // Overload the library's comparator to push items that are not in
  721. // the mirrored query to the front of the aggregate collection.
  722. library.comparator = function( a, b ) {
  723. var aInQuery = !! this.mirroring.get( a.cid ),
  724. bInQuery = !! this.mirroring.get( b.cid );
  725. if ( ! aInQuery && bInQuery ) {
  726. return -1;
  727. } else if ( aInQuery && ! bInQuery ) {
  728. return 1;
  729. } else {
  730. return comparator.apply( this, arguments );
  731. }
  732. };
  733. // Add all items in the selection to the library, so any featured
  734. // images that are not initially loaded still appear.
  735. library.observe( this.get('selection') );
  736. },
  737. /**
  738. * @since 3.5.0
  739. */
  740. activate: function() {
  741. this.frame.on( 'open', this.updateSelection, this );
  742. Library.prototype.activate.apply( this, arguments );
  743. },
  744. /**
  745. * @since 3.5.0
  746. */
  747. deactivate: function() {
  748. this.frame.off( 'open', this.updateSelection, this );
  749. Library.prototype.deactivate.apply( this, arguments );
  750. },
  751. /**
  752. * @since 3.5.0
  753. */
  754. updateSelection: function() {
  755. var selection = this.get('selection'),
  756. id = wp.media.view.settings.post.featuredImageId,
  757. attachment;
  758. if ( '' !== id && -1 !== id ) {
  759. attachment = Attachment.get( id );
  760. attachment.fetch();
  761. }
  762. selection.reset( attachment ? [ attachment ] : [] );
  763. }
  764. });
  765. module.exports = FeaturedImage;
  766. /***/ }),
  767. /***/ 7323:
  768. /***/ (function(module) {
  769. var Selection = wp.media.model.Selection,
  770. Library = wp.media.controller.Library,
  771. l10n = wp.media.view.l10n,
  772. GalleryAdd;
  773. /**
  774. * wp.media.controller.GalleryAdd
  775. *
  776. * A state for selecting more images to add to a gallery.
  777. *
  778. * @since 3.5.0
  779. *
  780. * @class
  781. * @augments wp.media.controller.Library
  782. * @augments wp.media.controller.State
  783. * @augments Backbone.Model
  784. *
  785. * @memberof wp.media.controller
  786. *
  787. * @param {Object} [attributes] The attributes hash passed to the state.
  788. * @param {string} [attributes.id=gallery-library] Unique identifier.
  789. * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region.
  790. * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
  791. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  792. * If one is not supplied, a collection of all images will be created.
  793. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  794. * Accepts 'all', 'uploaded', or 'unattached'.
  795. * @param {string} [attributes.menu=gallery] Initial mode for the menu region.
  796. * @param {string} [attributes.content=upload] Initial mode for the content region.
  797. * Overridden by persistent user setting if 'contentUserSetting' is true.
  798. * @param {string} [attributes.router=browse] Initial mode for the router region.
  799. * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region.
  800. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  801. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  802. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  803. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  804. * @param {number} [attributes.priority=100] The priority for the state link in the media menu.
  805. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  806. * Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
  807. */
  808. GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{
  809. defaults: _.defaults({
  810. id: 'gallery-library',
  811. title: l10n.addToGalleryTitle,
  812. multiple: 'add',
  813. filterable: 'uploaded',
  814. menu: 'gallery',
  815. toolbar: 'gallery-add',
  816. priority: 100,
  817. syncSelection: false
  818. }, Library.prototype.defaults ),
  819. /**
  820. * Initializes the library. Creates a library of images if a library isn't supplied.
  821. *
  822. * @since 3.5.0
  823. *
  824. * @return {void}
  825. */
  826. initialize: function() {
  827. if ( ! this.get('library') ) {
  828. this.set( 'library', wp.media.query({ type: 'image' }) );
  829. }
  830. Library.prototype.initialize.apply( this, arguments );
  831. },
  832. /**
  833. * Activates the library.
  834. *
  835. * Removes all event listeners if in edit mode. Creates a validator to check an attachment.
  836. * Resets library and re-enables event listeners. Activates edit mode. Calls the parent's activate method.
  837. *
  838. * @since 3.5.0
  839. *
  840. * @return {void}
  841. */
  842. activate: function() {
  843. var library = this.get('library'),
  844. edit = this.frame.state('gallery-edit').get('library');
  845. if ( this.editLibrary && this.editLibrary !== edit ) {
  846. library.unobserve( this.editLibrary );
  847. }
  848. /*
  849. * Accept attachments that exist in the original library but
  850. * that do not exist in gallery's library yet.
  851. */
  852. library.validator = function( attachment ) {
  853. return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
  854. };
  855. /*
  856. * Reset the library to ensure that all attachments are re-added
  857. * to the collection. Do so silently, as calling `observe` will
  858. * trigger the `reset` event.
  859. */
  860. library.reset( library.mirroring.models, { silent: true });
  861. library.observe( edit );
  862. this.editLibrary = edit;
  863. Library.prototype.activate.apply( this, arguments );
  864. }
  865. });
  866. module.exports = GalleryAdd;
  867. /***/ }),
  868. /***/ 6328:
  869. /***/ (function(module) {
  870. var Library = wp.media.controller.Library,
  871. l10n = wp.media.view.l10n,
  872. GalleryEdit;
  873. /**
  874. * wp.media.controller.GalleryEdit
  875. *
  876. * A state for editing a gallery's images and settings.
  877. *
  878. * @since 3.5.0
  879. *
  880. * @class
  881. * @augments wp.media.controller.Library
  882. * @augments wp.media.controller.State
  883. * @augments Backbone.Model
  884. *
  885. * @memberOf wp.media.controller
  886. *
  887. * @param {Object} [attributes] The attributes hash passed to the state.
  888. * @param {string} [attributes.id=gallery-edit] Unique identifier.
  889. * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region.
  890. * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery.
  891. * If one is not supplied, an empty media.model.Selection collection is created.
  892. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  893. * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
  894. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  895. * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar.
  896. * @param {string|false} [attributes.content=browse] Initial mode for the content region.
  897. * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
  898. * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  899. * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface.
  900. * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
  901. * @param {number} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
  902. * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance.
  903. * @param {number} [attributes.priority=60] The priority for the state link in the media menu.
  904. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  905. * Defaults to false for this state, because the library passed in *is* the selection.
  906. * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
  907. * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
  908. */
  909. GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{
  910. defaults: {
  911. id: 'gallery-edit',
  912. title: l10n.editGalleryTitle,
  913. multiple: false,
  914. searchable: false,
  915. sortable: true,
  916. date: false,
  917. display: false,
  918. content: 'browse',
  919. toolbar: 'gallery-edit',
  920. describe: true,
  921. displaySettings: true,
  922. dragInfo: true,
  923. idealColumnWidth: 170,
  924. editing: false,
  925. priority: 60,
  926. syncSelection: false
  927. },
  928. /**
  929. * Initializes the library.
  930. *
  931. * Creates a selection if a library isn't supplied and creates an attachment
  932. * view if no attachment view is supplied.
  933. *
  934. * @since 3.5.0
  935. *
  936. * @return {void}
  937. */
  938. initialize: function() {
  939. // If we haven't been provided a `library`, create a `Selection`.
  940. if ( ! this.get('library') ) {
  941. this.set( 'library', new wp.media.model.Selection() );
  942. }
  943. // The single `Attachment` view to be used in the `Attachments` view.
  944. if ( ! this.get('AttachmentView') ) {
  945. this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
  946. }
  947. Library.prototype.initialize.apply( this, arguments );
  948. },
  949. /**
  950. * Activates the library.
  951. *
  952. * Limits the library to images, watches for uploaded attachments. Watches for
  953. * the browse event on the frame and binds it to gallerySettings.
  954. *
  955. * @since 3.5.0
  956. *
  957. * @return {void}
  958. */
  959. activate: function() {
  960. var library = this.get('library');
  961. // Limit the library to images only.
  962. library.props.set( 'type', 'image' );
  963. // Watch for uploaded attachments.
  964. this.get('library').observe( wp.Uploader.queue );
  965. this.frame.on( 'content:render:browse', this.gallerySettings, this );
  966. Library.prototype.activate.apply( this, arguments );
  967. },
  968. /**
  969. * Deactivates the library.
  970. *
  971. * Stops watching for uploaded attachments and browse events.
  972. *
  973. * @since 3.5.0
  974. *
  975. * @return {void}
  976. */
  977. deactivate: function() {
  978. // Stop watching for uploaded attachments.
  979. this.get('library').unobserve( wp.Uploader.queue );
  980. this.frame.off( 'content:render:browse', this.gallerySettings, this );
  981. Library.prototype.deactivate.apply( this, arguments );
  982. },
  983. /**
  984. * Adds the gallery settings to the sidebar and adds a reverse button to the
  985. * toolbar.
  986. *
  987. * @since 3.5.0
  988. *
  989. * @param {wp.media.view.Frame} browser The file browser.
  990. *
  991. * @return {void}
  992. */
  993. gallerySettings: function( browser ) {
  994. if ( ! this.get('displaySettings') ) {
  995. return;
  996. }
  997. var library = this.get('library');
  998. if ( ! library || ! browser ) {
  999. return;
  1000. }
  1001. library.gallery = library.gallery || new Backbone.Model();
  1002. browser.sidebar.set({
  1003. gallery: new wp.media.view.Settings.Gallery({
  1004. controller: this,
  1005. model: library.gallery,
  1006. priority: 40
  1007. })
  1008. });
  1009. browser.toolbar.set( 'reverse', {
  1010. text: l10n.reverseOrder,
  1011. priority: 80,
  1012. click: function() {
  1013. library.reset( library.toArray().reverse() );
  1014. }
  1015. });
  1016. }
  1017. });
  1018. module.exports = GalleryEdit;
  1019. /***/ }),
  1020. /***/ 3849:
  1021. /***/ (function(module) {
  1022. var State = wp.media.controller.State,
  1023. Library = wp.media.controller.Library,
  1024. l10n = wp.media.view.l10n,
  1025. ImageDetails;
  1026. /**
  1027. * wp.media.controller.ImageDetails
  1028. *
  1029. * A state for editing the attachment display settings of an image that's been
  1030. * inserted into the editor.
  1031. *
  1032. * @memberOf wp.media.controller
  1033. *
  1034. * @class
  1035. * @augments wp.media.controller.State
  1036. * @augments Backbone.Model
  1037. *
  1038. * @param {object} [attributes] The attributes hash passed to the state.
  1039. * @param {string} [attributes.id=image-details] Unique identifier.
  1040. * @param {string} [attributes.title=Image Details] Title for the state. Displays in the frame's title region.
  1041. * @param {wp.media.model.Attachment} attributes.image The image's model.
  1042. * @param {string|false} [attributes.content=image-details] Initial mode for the content region.
  1043. * @param {string|false} [attributes.menu=false] Initial mode for the menu region.
  1044. * @param {string|false} [attributes.router=false] Initial mode for the router region.
  1045. * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
  1046. * @param {boolean} [attributes.editing=false] Unused.
  1047. * @param {int} [attributes.priority=60] Unused.
  1048. *
  1049. * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
  1050. * however this may not do anything.
  1051. */
  1052. ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{
  1053. defaults: _.defaults({
  1054. id: 'image-details',
  1055. title: l10n.imageDetailsTitle,
  1056. content: 'image-details',
  1057. menu: false,
  1058. router: false,
  1059. toolbar: 'image-details',
  1060. editing: false,
  1061. priority: 60
  1062. }, Library.prototype.defaults ),
  1063. /**
  1064. * @since 3.9.0
  1065. *
  1066. * @param options Attributes
  1067. */
  1068. initialize: function( options ) {
  1069. this.image = options.image;
  1070. State.prototype.initialize.apply( this, arguments );
  1071. },
  1072. /**
  1073. * @since 3.9.0
  1074. */
  1075. activate: function() {
  1076. this.frame.modal.$el.addClass('image-details');
  1077. }
  1078. });
  1079. module.exports = ImageDetails;
  1080. /***/ }),
  1081. /***/ 9024:
  1082. /***/ (function(module) {
  1083. var l10n = wp.media.view.l10n,
  1084. getUserSetting = window.getUserSetting,
  1085. setUserSetting = window.setUserSetting,
  1086. Library;
  1087. /**
  1088. * wp.media.controller.Library
  1089. *
  1090. * A state for choosing an attachment or group of attachments from the media library.
  1091. *
  1092. * @memberOf wp.media.controller
  1093. *
  1094. * @class
  1095. * @augments wp.media.controller.State
  1096. * @augments Backbone.Model
  1097. * @mixes media.selectionSync
  1098. *
  1099. * @param {object} [attributes] The attributes hash passed to the state.
  1100. * @param {string} [attributes.id=library] Unique identifier.
  1101. * @param {string} [attributes.title=Media library] Title for the state. Displays in the media menu and the frame's title region.
  1102. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1103. * If one is not supplied, a collection of all attachments will be created.
  1104. * @param {wp.media.model.Selection|object} [attributes.selection] A collection to contain attachment selections within the state.
  1105. * If the 'selection' attribute is a plain JS object,
  1106. * a Selection will be created using its values as the selection instance's `props` model.
  1107. * Otherwise, it will copy the library's `props` model.
  1108. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1109. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1110. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1111. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  1112. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1113. * @param {string} [attributes.toolbar=select] Initial mode for the toolbar region.
  1114. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1115. * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown.
  1116. * Accepts 'all', 'uploaded', or 'unattached'.
  1117. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1118. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1119. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  1120. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1121. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  1122. */
  1123. Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{
  1124. defaults: {
  1125. id: 'library',
  1126. title: l10n.mediaLibraryTitle,
  1127. multiple: false,
  1128. content: 'upload',
  1129. menu: 'default',
  1130. router: 'browse',
  1131. toolbar: 'select',
  1132. searchable: true,
  1133. filterable: false,
  1134. sortable: true,
  1135. autoSelect: true,
  1136. describe: false,
  1137. contentUserSetting: true,
  1138. syncSelection: true
  1139. },
  1140. /**
  1141. * If a library isn't provided, query all media items.
  1142. * If a selection instance isn't provided, create one.
  1143. *
  1144. * @since 3.5.0
  1145. */
  1146. initialize: function() {
  1147. var selection = this.get('selection'),
  1148. props;
  1149. if ( ! this.get('library') ) {
  1150. this.set( 'library', wp.media.query() );
  1151. }
  1152. if ( ! ( selection instanceof wp.media.model.Selection ) ) {
  1153. props = selection;
  1154. if ( ! props ) {
  1155. props = this.get('library').props.toJSON();
  1156. props = _.omit( props, 'orderby', 'query' );
  1157. }
  1158. this.set( 'selection', new wp.media.model.Selection( null, {
  1159. multiple: this.get('multiple'),
  1160. props: props
  1161. }) );
  1162. }
  1163. this.resetDisplays();
  1164. },
  1165. /**
  1166. * @since 3.5.0
  1167. */
  1168. activate: function() {
  1169. this.syncSelection();
  1170. wp.Uploader.queue.on( 'add', this.uploading, this );
  1171. this.get('selection').on( 'add remove reset', this.refreshContent, this );
  1172. if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
  1173. this.frame.on( 'content:activate', this.saveContentMode, this );
  1174. this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
  1175. }
  1176. },
  1177. /**
  1178. * @since 3.5.0
  1179. */
  1180. deactivate: function() {
  1181. this.recordSelection();
  1182. this.frame.off( 'content:activate', this.saveContentMode, this );
  1183. // Unbind all event handlers that use this state as the context
  1184. // from the selection.
  1185. this.get('selection').off( null, null, this );
  1186. wp.Uploader.queue.off( null, null, this );
  1187. },
  1188. /**
  1189. * Reset the library to its initial state.
  1190. *
  1191. * @since 3.5.0
  1192. */
  1193. reset: function() {
  1194. this.get('selection').reset();
  1195. this.resetDisplays();
  1196. this.refreshContent();
  1197. },
  1198. /**
  1199. * Reset the attachment display settings defaults to the site options.
  1200. *
  1201. * If site options don't define them, fall back to a persistent user setting.
  1202. *
  1203. * @since 3.5.0
  1204. */
  1205. resetDisplays: function() {
  1206. var defaultProps = wp.media.view.settings.defaultProps;
  1207. this._displays = [];
  1208. this._defaultDisplaySettings = {
  1209. align: getUserSetting( 'align', defaultProps.align ) || 'none',
  1210. size: getUserSetting( 'imgsize', defaultProps.size ) || 'medium',
  1211. link: getUserSetting( 'urlbutton', defaultProps.link ) || 'none'
  1212. };
  1213. },
  1214. /**
  1215. * Create a model to represent display settings (alignment, etc.) for an attachment.
  1216. *
  1217. * @since 3.5.0
  1218. *
  1219. * @param {wp.media.model.Attachment} attachment
  1220. * @return {Backbone.Model}
  1221. */
  1222. display: function( attachment ) {
  1223. var displays = this._displays;
  1224. if ( ! displays[ attachment.cid ] ) {
  1225. displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
  1226. }
  1227. return displays[ attachment.cid ];
  1228. },
  1229. /**
  1230. * Given an attachment, create attachment display settings properties.
  1231. *
  1232. * @since 3.6.0
  1233. *
  1234. * @param {wp.media.model.Attachment} attachment
  1235. * @return {Object}
  1236. */
  1237. defaultDisplaySettings: function( attachment ) {
  1238. var settings = _.clone( this._defaultDisplaySettings );
  1239. settings.canEmbed = this.canEmbed( attachment );
  1240. if ( settings.canEmbed ) {
  1241. settings.link = 'embed';
  1242. } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) {
  1243. settings.link = 'file';
  1244. }
  1245. return settings;
  1246. },
  1247. /**
  1248. * Whether an attachment is image.
  1249. *
  1250. * @since 4.4.1
  1251. *
  1252. * @param {wp.media.model.Attachment} attachment
  1253. * @return {boolean}
  1254. */
  1255. isImageAttachment: function( attachment ) {
  1256. // If uploading, we know the filename but not the mime type.
  1257. if ( attachment.get('uploading') ) {
  1258. return /\.(jpe?g|png|gif|webp)$/i.test( attachment.get('filename') );
  1259. }
  1260. return attachment.get('type') === 'image';
  1261. },
  1262. /**
  1263. * Whether an attachment can be embedded (audio or video).
  1264. *
  1265. * @since 3.6.0
  1266. *
  1267. * @param {wp.media.model.Attachment} attachment
  1268. * @return {boolean}
  1269. */
  1270. canEmbed: function( attachment ) {
  1271. // If uploading, we know the filename but not the mime type.
  1272. if ( ! attachment.get('uploading') ) {
  1273. var type = attachment.get('type');
  1274. if ( type !== 'audio' && type !== 'video' ) {
  1275. return false;
  1276. }
  1277. }
  1278. return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
  1279. },
  1280. /**
  1281. * If the state is active, no items are selected, and the current
  1282. * content mode is not an option in the state's router (provided
  1283. * the state has a router), reset the content mode to the default.
  1284. *
  1285. * @since 3.5.0
  1286. */
  1287. refreshContent: function() {
  1288. var selection = this.get('selection'),
  1289. frame = this.frame,
  1290. router = frame.router.get(),
  1291. mode = frame.content.mode();
  1292. if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
  1293. this.frame.content.render( this.get('content') );
  1294. }
  1295. },
  1296. /**
  1297. * Callback handler when an attachment is uploaded.
  1298. *
  1299. * Switch to the Media Library if uploaded from the 'Upload Files' tab.
  1300. *
  1301. * Adds any uploading attachments to the selection.
  1302. *
  1303. * If the state only supports one attachment to be selected and multiple
  1304. * attachments are uploaded, the last attachment in the upload queue will
  1305. * be selected.
  1306. *
  1307. * @since 3.5.0
  1308. *
  1309. * @param {wp.media.model.Attachment} attachment
  1310. */
  1311. uploading: function( attachment ) {
  1312. var content = this.frame.content;
  1313. if ( 'upload' === content.mode() ) {
  1314. this.frame.content.mode('browse');
  1315. }
  1316. if ( this.get( 'autoSelect' ) ) {
  1317. this.get('selection').add( attachment );
  1318. this.frame.trigger( 'library:selection:add' );
  1319. }
  1320. },
  1321. /**
  1322. * Persist the mode of the content region as a user setting.
  1323. *
  1324. * @since 3.5.0
  1325. */
  1326. saveContentMode: function() {
  1327. if ( 'browse' !== this.get('router') ) {
  1328. return;
  1329. }
  1330. var mode = this.frame.content.mode(),
  1331. view = this.frame.router.get();
  1332. if ( view && view.get( mode ) ) {
  1333. setUserSetting( 'libraryContent', mode );
  1334. }
  1335. }
  1336. });
  1337. // Make selectionSync available on any Media Library state.
  1338. _.extend( Library.prototype, wp.media.selectionSync );
  1339. module.exports = Library;
  1340. /***/ }),
  1341. /***/ 3742:
  1342. /***/ (function(module) {
  1343. /**
  1344. * wp.media.controller.MediaLibrary
  1345. *
  1346. * @memberOf wp.media.controller
  1347. *
  1348. * @class
  1349. * @augments wp.media.controller.Library
  1350. * @augments wp.media.controller.State
  1351. * @augments Backbone.Model
  1352. */
  1353. var Library = wp.media.controller.Library,
  1354. MediaLibrary;
  1355. MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{
  1356. defaults: _.defaults({
  1357. // Attachments browser defaults. @see media.view.AttachmentsBrowser
  1358. filterable: 'uploaded',
  1359. displaySettings: false,
  1360. priority: 80,
  1361. syncSelection: false
  1362. }, Library.prototype.defaults ),
  1363. /**
  1364. * @since 3.9.0
  1365. *
  1366. * @param options
  1367. */
  1368. initialize: function( options ) {
  1369. this.media = options.media;
  1370. this.type = options.type;
  1371. this.set( 'library', wp.media.query({ type: this.type }) );
  1372. Library.prototype.initialize.apply( this, arguments );
  1373. },
  1374. /**
  1375. * @since 3.9.0
  1376. */
  1377. activate: function() {
  1378. // @todo this should use this.frame.
  1379. if ( wp.media.frame.lastMime ) {
  1380. this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
  1381. delete wp.media.frame.lastMime;
  1382. }
  1383. Library.prototype.activate.apply( this, arguments );
  1384. }
  1385. });
  1386. module.exports = MediaLibrary;
  1387. /***/ }),
  1388. /***/ 4903:
  1389. /***/ (function(module) {
  1390. /**
  1391. * wp.media.controller.Region
  1392. *
  1393. * A region is a persistent application layout area.
  1394. *
  1395. * A region assumes one mode at any time, and can be switched to another.
  1396. *
  1397. * When mode changes, events are triggered on the region's parent view.
  1398. * The parent view will listen to specific events and fill the region with an
  1399. * appropriate view depending on mode. For example, a frame listens for the
  1400. * 'browse' mode t be activated on the 'content' view and then fills the region
  1401. * with an AttachmentsBrowser view.
  1402. *
  1403. * @memberOf wp.media.controller
  1404. *
  1405. * @class
  1406. *
  1407. * @param {Object} options Options hash for the region.
  1408. * @param {string} options.id Unique identifier for the region.
  1409. * @param {Backbone.View} options.view A parent view the region exists within.
  1410. * @param {string} options.selector jQuery selector for the region within the parent view.
  1411. */
  1412. var Region = function( options ) {
  1413. _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
  1414. };
  1415. // Use Backbone's self-propagating `extend` inheritance method.
  1416. Region.extend = Backbone.Model.extend;
  1417. _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{
  1418. /**
  1419. * Activate a mode.
  1420. *
  1421. * @since 3.5.0
  1422. *
  1423. * @param {string} mode
  1424. *
  1425. * @fires Region#activate
  1426. * @fires Region#deactivate
  1427. *
  1428. * @return {wp.media.controller.Region} Returns itself to allow chaining.
  1429. */
  1430. mode: function( mode ) {
  1431. if ( ! mode ) {
  1432. return this._mode;
  1433. }
  1434. // Bail if we're trying to change to the current mode.
  1435. if ( mode === this._mode ) {
  1436. return this;
  1437. }
  1438. /**
  1439. * Region mode deactivation event.
  1440. *
  1441. * @event wp.media.controller.Region#deactivate
  1442. */
  1443. this.trigger('deactivate');
  1444. this._mode = mode;
  1445. this.render( mode );
  1446. /**
  1447. * Region mode activation event.
  1448. *
  1449. * @event wp.media.controller.Region#activate
  1450. */
  1451. this.trigger('activate');
  1452. return this;
  1453. },
  1454. /**
  1455. * Render a mode.
  1456. *
  1457. * @since 3.5.0
  1458. *
  1459. * @param {string} mode
  1460. *
  1461. * @fires Region#create
  1462. * @fires Region#render
  1463. *
  1464. * @return {wp.media.controller.Region} Returns itself to allow chaining.
  1465. */
  1466. render: function( mode ) {
  1467. // If the mode isn't active, activate it.
  1468. if ( mode && mode !== this._mode ) {
  1469. return this.mode( mode );
  1470. }
  1471. var set = { view: null },
  1472. view;
  1473. /**
  1474. * Create region view event.
  1475. *
  1476. * Region view creation takes place in an event callback on the frame.
  1477. *
  1478. * @event wp.media.controller.Region#create
  1479. * @type {object}
  1480. * @property {object} view
  1481. */
  1482. this.trigger( 'create', set );
  1483. view = set.view;
  1484. /**
  1485. * Render region view event.
  1486. *
  1487. * Region view creation takes place in an event callback on the frame.
  1488. *
  1489. * @event wp.media.controller.Region#render
  1490. * @type {object}
  1491. */
  1492. this.trigger( 'render', view );
  1493. if ( view ) {
  1494. this.set( view );
  1495. }
  1496. return this;
  1497. },
  1498. /**
  1499. * Get the region's view.
  1500. *
  1501. * @since 3.5.0
  1502. *
  1503. * @return {wp.media.View}
  1504. */
  1505. get: function() {
  1506. return this.view.views.first( this.selector );
  1507. },
  1508. /**
  1509. * Set the region's view as a subview of the frame.
  1510. *
  1511. * @since 3.5.0
  1512. *
  1513. * @param {Array|Object} views
  1514. * @param {Object} [options={}]
  1515. * @return {wp.Backbone.Subviews} Subviews is returned to allow chaining.
  1516. */
  1517. set: function( views, options ) {
  1518. if ( options ) {
  1519. options.add = false;
  1520. }
  1521. return this.view.views.set( this.selector, views, options );
  1522. },
  1523. /**
  1524. * Trigger regional view events on the frame.
  1525. *
  1526. * @since 3.5.0
  1527. *
  1528. * @param {string} event
  1529. * @return {undefined|wp.media.controller.Region} Returns itself to allow chaining.
  1530. */
  1531. trigger: function( event ) {
  1532. var base, args;
  1533. if ( ! this._mode ) {
  1534. return;
  1535. }
  1536. args = _.toArray( arguments );
  1537. base = this.id + ':' + event;
  1538. // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
  1539. args[0] = base + ':' + this._mode;
  1540. this.view.trigger.apply( this.view, args );
  1541. // Trigger `{this.id}:{event}` event on the frame.
  1542. args[0] = base;
  1543. this.view.trigger.apply( this.view, args );
  1544. return this;
  1545. }
  1546. });
  1547. module.exports = Region;
  1548. /***/ }),
  1549. /***/ 8493:
  1550. /***/ (function(module) {
  1551. var Library = wp.media.controller.Library,
  1552. l10n = wp.media.view.l10n,
  1553. ReplaceImage;
  1554. /**
  1555. * wp.media.controller.ReplaceImage
  1556. *
  1557. * A state for replacing an image.
  1558. *
  1559. * @memberOf wp.media.controller
  1560. *
  1561. * @class
  1562. * @augments wp.media.controller.Library
  1563. * @augments wp.media.controller.State
  1564. * @augments Backbone.Model
  1565. *
  1566. * @param {object} [attributes] The attributes hash passed to the state.
  1567. * @param {string} [attributes.id=replace-image] Unique identifier.
  1568. * @param {string} [attributes.title=Replace Image] Title for the state. Displays in the media menu and the frame's title region.
  1569. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1570. * If one is not supplied, a collection of all images will be created.
  1571. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1572. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1573. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1574. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  1575. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1576. * @param {string} [attributes.toolbar=replace] Initial mode for the toolbar region.
  1577. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  1578. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1579. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  1580. * Accepts 'all', 'uploaded', or 'unattached'.
  1581. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1582. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1583. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  1584. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1585. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  1586. */
  1587. ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{
  1588. defaults: _.defaults({
  1589. id: 'replace-image',
  1590. title: l10n.replaceImageTitle,
  1591. multiple: false,
  1592. filterable: 'uploaded',
  1593. toolbar: 'replace',
  1594. menu: false,
  1595. priority: 60,
  1596. syncSelection: true
  1597. }, Library.prototype.defaults ),
  1598. /**
  1599. * @since 3.9.0
  1600. *
  1601. * @param options
  1602. */
  1603. initialize: function( options ) {
  1604. var library, comparator;
  1605. this.image = options.image;
  1606. // If we haven't been provided a `library`, create a `Selection`.
  1607. if ( ! this.get('library') ) {
  1608. this.set( 'library', wp.media.query({ type: 'image' }) );
  1609. }
  1610. Library.prototype.initialize.apply( this, arguments );
  1611. library = this.get('library');
  1612. comparator = library.comparator;
  1613. // Overload the library's comparator to push items that are not in
  1614. // the mirrored query to the front of the aggregate collection.
  1615. library.comparator = function( a, b ) {
  1616. var aInQuery = !! this.mirroring.get( a.cid ),
  1617. bInQuery = !! this.mirroring.get( b.cid );
  1618. if ( ! aInQuery && bInQuery ) {
  1619. return -1;
  1620. } else if ( aInQuery && ! bInQuery ) {
  1621. return 1;
  1622. } else {
  1623. return comparator.apply( this, arguments );
  1624. }
  1625. };
  1626. // Add all items in the selection to the library, so any featured
  1627. // images that are not initially loaded still appear.
  1628. library.observe( this.get('selection') );
  1629. },
  1630. /**
  1631. * @since 3.9.0
  1632. */
  1633. activate: function() {
  1634. this.frame.on( 'content:render:browse', this.updateSelection, this );
  1635. Library.prototype.activate.apply( this, arguments );
  1636. },
  1637. /**
  1638. * @since 5.9.0
  1639. */
  1640. deactivate: function() {
  1641. this.frame.off( 'content:render:browse', this.updateSelection, this );
  1642. Library.prototype.deactivate.apply( this, arguments );
  1643. },
  1644. /**
  1645. * @since 3.9.0
  1646. */
  1647. updateSelection: function() {
  1648. var selection = this.get('selection'),
  1649. attachment = this.image.attachment;
  1650. selection.reset( attachment ? [ attachment ] : [] );
  1651. }
  1652. });
  1653. module.exports = ReplaceImage;
  1654. /***/ }),
  1655. /***/ 5274:
  1656. /***/ (function(module) {
  1657. var Controller = wp.media.controller,
  1658. SiteIconCropper;
  1659. /**
  1660. * wp.media.controller.SiteIconCropper
  1661. *
  1662. * A state for cropping a Site Icon.
  1663. *
  1664. * @memberOf wp.media.controller
  1665. *
  1666. * @class
  1667. * @augments wp.media.controller.Cropper
  1668. * @augments wp.media.controller.State
  1669. * @augments Backbone.Model
  1670. */
  1671. SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{
  1672. activate: function() {
  1673. this.frame.on( 'content:create:crop', this.createCropContent, this );
  1674. this.frame.on( 'close', this.removeCropper, this );
  1675. this.set('selection', new Backbone.Collection(this.frame._selection.single));
  1676. },
  1677. createCropContent: function() {
  1678. this.cropperView = new wp.media.view.SiteIconCropper({
  1679. controller: this,
  1680. attachment: this.get('selection').first()
  1681. });
  1682. this.cropperView.on('image-loaded', this.createCropToolbar, this);
  1683. this.frame.content.set(this.cropperView);
  1684. },
  1685. doCrop: function( attachment ) {
  1686. var cropDetails = attachment.get( 'cropDetails' ),
  1687. control = this.get( 'control' );
  1688. cropDetails.dst_width = control.params.width;
  1689. cropDetails.dst_height = control.params.height;
  1690. return wp.ajax.post( 'crop-image', {
  1691. nonce: attachment.get( 'nonces' ).edit,
  1692. id: attachment.get( 'id' ),
  1693. context: 'site-icon',
  1694. cropDetails: cropDetails
  1695. } );
  1696. }
  1697. });
  1698. module.exports = SiteIconCropper;
  1699. /***/ }),
  1700. /***/ 5466:
  1701. /***/ (function(module) {
  1702. /**
  1703. * wp.media.controller.StateMachine
  1704. *
  1705. * A state machine keeps track of state. It is in one state at a time,
  1706. * and can change from one state to another.
  1707. *
  1708. * States are stored as models in a Backbone collection.
  1709. *
  1710. * @memberOf wp.media.controller
  1711. *
  1712. * @since 3.5.0
  1713. *
  1714. * @class
  1715. * @augments Backbone.Model
  1716. * @mixin
  1717. * @mixes Backbone.Events
  1718. */
  1719. var StateMachine = function() {
  1720. return {
  1721. // Use Backbone's self-propagating `extend` inheritance method.
  1722. extend: Backbone.Model.extend
  1723. };
  1724. };
  1725. _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{
  1726. /**
  1727. * Fetch a state.
  1728. *
  1729. * If no `id` is provided, returns the active state.
  1730. *
  1731. * Implicitly creates states.
  1732. *
  1733. * Ensure that the `states` collection exists so the `StateMachine`
  1734. * can be used as a mixin.
  1735. *
  1736. * @since 3.5.0
  1737. *
  1738. * @param {string} id
  1739. * @return {wp.media.controller.State} Returns a State model from
  1740. * the StateMachine collection.
  1741. */
  1742. state: function( id ) {
  1743. this.states = this.states || new Backbone.Collection();
  1744. // Default to the active state.
  1745. id = id || this._state;
  1746. if ( id && ! this.states.get( id ) ) {
  1747. this.states.add({ id: id });
  1748. }
  1749. return this.states.get( id );
  1750. },
  1751. /**
  1752. * Sets the active state.
  1753. *
  1754. * Bail if we're trying to select the current state, if we haven't
  1755. * created the `states` collection, or are trying to select a state
  1756. * that does not exist.
  1757. *
  1758. * @since 3.5.0
  1759. *
  1760. * @param {string} id
  1761. *
  1762. * @fires wp.media.controller.State#deactivate
  1763. * @fires wp.media.controller.State#activate
  1764. *
  1765. * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
  1766. */
  1767. setState: function( id ) {
  1768. var previous = this.state();
  1769. if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
  1770. return this;
  1771. }
  1772. if ( previous ) {
  1773. previous.trigger('deactivate');
  1774. this._lastState = previous.id;
  1775. }
  1776. this._state = id;
  1777. this.state().trigger('activate');
  1778. return this;
  1779. },
  1780. /**
  1781. * Returns the previous active state.
  1782. *
  1783. * Call the `state()` method with no parameters to retrieve the current
  1784. * active state.
  1785. *
  1786. * @since 3.5.0
  1787. *
  1788. * @return {wp.media.controller.State} Returns a State model from
  1789. * the StateMachine collection.
  1790. */
  1791. lastState: function() {
  1792. if ( this._lastState ) {
  1793. return this.state( this._lastState );
  1794. }
  1795. }
  1796. });
  1797. // Map all event binding and triggering on a StateMachine to its `states` collection.
  1798. _.each([ 'on', 'off', 'trigger' ], function( method ) {
  1799. /**
  1800. * @function on
  1801. * @memberOf wp.media.controller.StateMachine
  1802. * @instance
  1803. * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
  1804. */
  1805. /**
  1806. * @function off
  1807. * @memberOf wp.media.controller.StateMachine
  1808. * @instance
  1809. * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
  1810. */
  1811. /**
  1812. * @function trigger
  1813. * @memberOf wp.media.controller.StateMachine
  1814. * @instance
  1815. * @return {wp.media.controller.StateMachine} Returns itself to allow chaining.
  1816. */
  1817. StateMachine.prototype[ method ] = function() {
  1818. // Ensure that the `states` collection exists so the `StateMachine`
  1819. // can be used as a mixin.
  1820. this.states = this.states || new Backbone.Collection();
  1821. // Forward the method to the `states` collection.
  1822. this.states[ method ].apply( this.states, arguments );
  1823. return this;
  1824. };
  1825. });
  1826. module.exports = StateMachine;
  1827. /***/ }),
  1828. /***/ 5826:
  1829. /***/ (function(module) {
  1830. /**
  1831. * wp.media.controller.State
  1832. *
  1833. * A state is a step in a workflow that when set will trigger the controllers
  1834. * for the regions to be updated as specified in the frame.
  1835. *
  1836. * A state has an event-driven lifecycle:
  1837. *
  1838. * 'ready' triggers when a state is added to a state machine's collection.
  1839. * 'activate' triggers when a state is activated by a state machine.
  1840. * 'deactivate' triggers when a state is deactivated by a state machine.
  1841. * 'reset' is not triggered automatically. It should be invoked by the
  1842. * proper controller to reset the state to its default.
  1843. *
  1844. * @memberOf wp.media.controller
  1845. *
  1846. * @class
  1847. * @augments Backbone.Model
  1848. */
  1849. var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{
  1850. /**
  1851. * Constructor.
  1852. *
  1853. * @since 3.5.0
  1854. */
  1855. constructor: function() {
  1856. this.on( 'activate', this._preActivate, this );
  1857. this.on( 'activate', this.activate, this );
  1858. this.on( 'activate', this._postActivate, this );
  1859. this.on( 'deactivate', this._deactivate, this );
  1860. this.on( 'deactivate', this.deactivate, this );
  1861. this.on( 'reset', this.reset, this );
  1862. this.on( 'ready', this._ready, this );
  1863. this.on( 'ready', this.ready, this );
  1864. /**
  1865. * Call parent constructor with passed arguments
  1866. */
  1867. Backbone.Model.apply( this, arguments );
  1868. this.on( 'change:menu', this._updateMenu, this );
  1869. },
  1870. /**
  1871. * Ready event callback.
  1872. *
  1873. * @abstract
  1874. * @since 3.5.0
  1875. */
  1876. ready: function() {},
  1877. /**
  1878. * Activate event callback.
  1879. *
  1880. * @abstract
  1881. * @since 3.5.0
  1882. */
  1883. activate: function() {},
  1884. /**
  1885. * Deactivate event callback.
  1886. *
  1887. * @abstract
  1888. * @since 3.5.0
  1889. */
  1890. deactivate: function() {},
  1891. /**
  1892. * Reset event callback.
  1893. *
  1894. * @abstract
  1895. * @since 3.5.0
  1896. */
  1897. reset: function() {},
  1898. /**
  1899. * @since 3.5.0
  1900. * @access private
  1901. */
  1902. _ready: function() {
  1903. this._updateMenu();
  1904. },
  1905. /**
  1906. * @since 3.5.0
  1907. * @access private
  1908. */
  1909. _preActivate: function() {
  1910. this.active = true;
  1911. },
  1912. /**
  1913. * @since 3.5.0
  1914. * @access private
  1915. */
  1916. _postActivate: function() {
  1917. this.on( 'change:menu', this._menu, this );
  1918. this.on( 'change:titleMode', this._title, this );
  1919. this.on( 'change:content', this._content, this );
  1920. this.on( 'change:toolbar', this._toolbar, this );
  1921. this.frame.on( 'title:render:default', this._renderTitle, this );
  1922. this._title();
  1923. this._menu();
  1924. this._toolbar();
  1925. this._content();
  1926. this._router();
  1927. },
  1928. /**
  1929. * @since 3.5.0
  1930. * @access private
  1931. */
  1932. _deactivate: function() {
  1933. this.active = false;
  1934. this.frame.off( 'title:render:default', this._renderTitle, this );
  1935. this.off( 'change:menu', this._menu, this );
  1936. this.off( 'change:titleMode', this._title, this );
  1937. this.off( 'change:content', this._content, this );
  1938. this.off( 'change:toolbar', this._toolbar, this );
  1939. },
  1940. /**
  1941. * @since 3.5.0
  1942. * @access private
  1943. */
  1944. _title: function() {
  1945. this.frame.title.render( this.get('titleMode') || 'default' );
  1946. },
  1947. /**
  1948. * @since 3.5.0
  1949. * @access private
  1950. */
  1951. _renderTitle: function( view ) {
  1952. view.$el.text( this.get('title') || '' );
  1953. },
  1954. /**
  1955. * @since 3.5.0
  1956. * @access private
  1957. */
  1958. _router: function() {
  1959. var router = this.frame.router,
  1960. mode = this.get('router'),
  1961. view;
  1962. this.frame.$el.toggleClass( 'hide-router', ! mode );
  1963. if ( ! mode ) {
  1964. return;
  1965. }
  1966. this.frame.router.render( mode );
  1967. view = router.get();
  1968. if ( view && view.select ) {
  1969. view.select( this.frame.content.mode() );
  1970. }
  1971. },
  1972. /**
  1973. * @since 3.5.0
  1974. * @access private
  1975. */
  1976. _menu: function() {
  1977. var menu = this.frame.menu,
  1978. mode = this.get('menu'),
  1979. view;
  1980. this.frame.$el.toggleClass( 'hide-menu', ! mode );
  1981. if ( ! mode ) {
  1982. return;
  1983. }
  1984. menu.mode( mode );
  1985. view = menu.get();
  1986. if ( view && view.select ) {
  1987. view.select( this.id );
  1988. }
  1989. },
  1990. /**
  1991. * @since 3.5.0
  1992. * @access private
  1993. */
  1994. _updateMenu: function() {
  1995. var previous = this.previous('menu'),
  1996. menu = this.get('menu');
  1997. if ( previous ) {
  1998. this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
  1999. }
  2000. if ( menu ) {
  2001. this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
  2002. }
  2003. },
  2004. /**
  2005. * Create a view in the media menu for the state.
  2006. *
  2007. * @since 3.5.0
  2008. * @access private
  2009. *
  2010. * @param {media.view.Menu} view The menu view.
  2011. */
  2012. _renderMenu: function( view ) {
  2013. var menuItem = this.get('menuItem'),
  2014. title = this.get('title'),
  2015. priority = this.get('priority');
  2016. if ( ! menuItem && title ) {
  2017. menuItem = { text: title };
  2018. if ( priority ) {
  2019. menuItem.priority = priority;
  2020. }
  2021. }
  2022. if ( ! menuItem ) {
  2023. return;
  2024. }
  2025. view.set( this.id, menuItem );
  2026. }
  2027. });
  2028. _.each(['toolbar','content'], function( region ) {
  2029. /**
  2030. * @access private
  2031. */
  2032. State.prototype[ '_' + region ] = function() {
  2033. var mode = this.get( region );
  2034. if ( mode ) {
  2035. this.frame[ region ].render( mode );
  2036. }
  2037. };
  2038. });
  2039. module.exports = State;
  2040. /***/ }),
  2041. /***/ 3526:
  2042. /***/ (function(module) {
  2043. /**
  2044. * wp.media.selectionSync
  2045. *
  2046. * Sync an attachments selection in a state with another state.
  2047. *
  2048. * Allows for selecting multiple images in the Add Media workflow, and then
  2049. * switching to the Insert Gallery workflow while preserving the attachments selection.
  2050. *
  2051. * @memberOf wp.media
  2052. *
  2053. * @mixin
  2054. */
  2055. var selectionSync = {
  2056. /**
  2057. * @since 3.5.0
  2058. */
  2059. syncSelection: function() {
  2060. var selection = this.get('selection'),
  2061. manager = this.frame._selection;
  2062. if ( ! this.get('syncSelection') || ! manager || ! selection ) {
  2063. return;
  2064. }
  2065. /*
  2066. * If the selection supports multiple items, validate the stored
  2067. * attachments based on the new selection's conditions. Record
  2068. * the attachments that are not included; we'll maintain a
  2069. * reference to those. Other attachments are considered in flux.
  2070. */
  2071. if ( selection.multiple ) {
  2072. selection.reset( [], { silent: true });
  2073. selection.validateAll( manager.attachments );
  2074. manager.difference = _.difference( manager.attachments.models, selection.models );
  2075. }
  2076. // Sync the selection's single item with the master.
  2077. selection.single( manager.single );
  2078. },
  2079. /**
  2080. * Record the currently active attachments, which is a combination
  2081. * of the selection's attachments and the set of selected
  2082. * attachments that this specific selection considered invalid.
  2083. * Reset the difference and record the single attachment.
  2084. *
  2085. * @since 3.5.0
  2086. */
  2087. recordSelection: function() {
  2088. var selection = this.get('selection'),
  2089. manager = this.frame._selection;
  2090. if ( ! this.get('syncSelection') || ! manager || ! selection ) {
  2091. return;
  2092. }
  2093. if ( selection.multiple ) {
  2094. manager.attachments.reset( selection.toArray().concat( manager.difference ) );
  2095. manager.difference = [];
  2096. } else {
  2097. manager.attachments.add( selection.toArray() );
  2098. }
  2099. manager.single = selection._single;
  2100. }
  2101. };
  2102. module.exports = selectionSync;
  2103. /***/ }),
  2104. /***/ 8093:
  2105. /***/ (function(module) {
  2106. var View = wp.media.View,
  2107. AttachmentCompat;
  2108. /**
  2109. * wp.media.view.AttachmentCompat
  2110. *
  2111. * A view to display fields added via the `attachment_fields_to_edit` filter.
  2112. *
  2113. * @memberOf wp.media.view
  2114. *
  2115. * @class
  2116. * @augments wp.media.View
  2117. * @augments wp.Backbone.View
  2118. * @augments Backbone.View
  2119. */
  2120. AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{
  2121. tagName: 'form',
  2122. className: 'compat-item',
  2123. events: {
  2124. 'submit': 'preventDefault',
  2125. 'change input': 'save',
  2126. 'change select': 'save',
  2127. 'change textarea': 'save'
  2128. },
  2129. initialize: function() {
  2130. // Render the view when a new item is added.
  2131. this.listenTo( this.model, 'add', this.render );
  2132. },
  2133. /**
  2134. * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining.
  2135. */
  2136. dispose: function() {
  2137. if ( this.$(':focus').length ) {
  2138. this.save();
  2139. }
  2140. /**
  2141. * call 'dispose' directly on the parent class
  2142. */
  2143. return View.prototype.dispose.apply( this, arguments );
  2144. },
  2145. /**
  2146. * @return {wp.media.view.AttachmentCompat} Returns itself to allow chaining.
  2147. */
  2148. render: function() {
  2149. var compat = this.model.get('compat');
  2150. if ( ! compat || ! compat.item ) {
  2151. return;
  2152. }
  2153. this.views.detach();
  2154. this.$el.html( compat.item );
  2155. this.views.render();
  2156. return this;
  2157. },
  2158. /**
  2159. * @param {Object} event
  2160. */
  2161. preventDefault: function( event ) {
  2162. event.preventDefault();
  2163. },
  2164. /**
  2165. * @param {Object} event
  2166. */
  2167. save: function( event ) {
  2168. var data = {};
  2169. if ( event ) {
  2170. event.preventDefault();
  2171. }
  2172. _.each( this.$el.serializeArray(), function( pair ) {
  2173. data[ pair.name ] = pair.value;
  2174. });
  2175. this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
  2176. this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
  2177. },
  2178. postSave: function() {
  2179. this.controller.trigger( 'attachment:compat:ready', ['ready'] );
  2180. }
  2181. });
  2182. module.exports = AttachmentCompat;
  2183. /***/ }),
  2184. /***/ 4906:
  2185. /***/ (function(module) {
  2186. var $ = jQuery,
  2187. AttachmentFilters;
  2188. /**
  2189. * wp.media.view.AttachmentFilters
  2190. *
  2191. * @memberOf wp.media.view
  2192. *
  2193. * @class
  2194. * @augments wp.media.View
  2195. * @augments wp.Backbone.View
  2196. * @augments Backbone.View
  2197. */
  2198. AttachmentFilters = wp.media.View.extend(/** @lends wp.media.view.AttachmentFilters.prototype */{
  2199. tagName: 'select',
  2200. className: 'attachment-filters',
  2201. id: 'media-attachment-filters',
  2202. events: {
  2203. change: 'change'
  2204. },
  2205. keys: [],
  2206. initialize: function() {
  2207. this.createFilters();
  2208. _.extend( this.filters, this.options.filters );
  2209. // Build `<option>` elements.
  2210. this.$el.html( _.chain( this.filters ).map( function( filter, value ) {
  2211. return {
  2212. el: $( '<option></option>' ).val( value ).html( filter.text )[0],
  2213. priority: filter.priority || 50
  2214. };
  2215. }, this ).sortBy('priority').pluck('el').value() );
  2216. this.listenTo( this.model, 'change', this.select );
  2217. this.select();
  2218. },
  2219. /**
  2220. * @abstract
  2221. */
  2222. createFilters: function() {
  2223. this.filters = {};
  2224. },
  2225. /**
  2226. * When the selected filter changes, update the Attachment Query properties to match.
  2227. */
  2228. change: function() {
  2229. var filter = this.filters[ this.el.value ];
  2230. if ( filter ) {
  2231. this.model.set( filter.props );
  2232. }
  2233. },
  2234. select: function() {
  2235. var model = this.model,
  2236. value = 'all',
  2237. props = model.toJSON();
  2238. _.find( this.filters, function( filter, id ) {
  2239. var equal = _.all( filter.props, function( prop, key ) {
  2240. return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] );
  2241. });
  2242. if ( equal ) {
  2243. return value = id;
  2244. }
  2245. });
  2246. this.$el.val( value );
  2247. }
  2248. });
  2249. module.exports = AttachmentFilters;
  2250. /***/ }),
  2251. /***/ 2868:
  2252. /***/ (function(module) {
  2253. var l10n = wp.media.view.l10n,
  2254. All;
  2255. /**
  2256. * wp.media.view.AttachmentFilters.All
  2257. *
  2258. * @memberOf wp.media.view.AttachmentFilters
  2259. *
  2260. * @class
  2261. * @augments wp.media.view.AttachmentFilters
  2262. * @augments wp.media.View
  2263. * @augments wp.Backbone.View
  2264. * @augments Backbone.View
  2265. */
  2266. All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{
  2267. createFilters: function() {
  2268. var filters = {},
  2269. uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0;
  2270. _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
  2271. filters[ key ] = {
  2272. text: text,
  2273. props: {
  2274. status: null,
  2275. type: key,
  2276. uploadedTo: null,
  2277. orderby: 'date',
  2278. order: 'DESC',
  2279. author: null
  2280. }
  2281. };
  2282. });
  2283. filters.all = {
  2284. text: l10n.allMediaItems,
  2285. props: {
  2286. status: null,
  2287. type: null,
  2288. uploadedTo: null,
  2289. orderby: 'date',
  2290. order: 'DESC',
  2291. author: null
  2292. },
  2293. priority: 10
  2294. };
  2295. if ( wp.media.view.settings.post.id ) {
  2296. filters.uploaded = {
  2297. text: l10n.uploadedToThisPost,
  2298. props: {
  2299. status: null,
  2300. type: null,
  2301. uploadedTo: wp.media.view.settings.post.id,
  2302. orderby: 'menuOrder',
  2303. order: 'ASC',
  2304. author: null
  2305. },
  2306. priority: 20
  2307. };
  2308. }
  2309. filters.unattached = {
  2310. text: l10n.unattached,
  2311. props: {
  2312. status: null,
  2313. uploadedTo: 0,
  2314. type: null,
  2315. orderby: 'menuOrder',
  2316. order: 'ASC',
  2317. author: null
  2318. },
  2319. priority: 50
  2320. };
  2321. if ( uid ) {
  2322. filters.mine = {
  2323. text: l10n.mine,
  2324. props: {
  2325. status: null,
  2326. type: null,
  2327. uploadedTo: null,
  2328. orderby: 'date',
  2329. order: 'DESC',
  2330. author: uid
  2331. },
  2332. priority: 50
  2333. };
  2334. }
  2335. if ( wp.media.view.settings.mediaTrash &&
  2336. this.controller.isModeActive( 'grid' ) ) {
  2337. filters.trash = {
  2338. text: l10n.trash,
  2339. props: {
  2340. uploadedTo: null,
  2341. status: 'trash',
  2342. type: null,
  2343. orderby: 'date',
  2344. order: 'DESC',
  2345. author: null
  2346. },
  2347. priority: 50
  2348. };
  2349. }
  2350. this.filters = filters;
  2351. }
  2352. });
  2353. module.exports = All;
  2354. /***/ }),
  2355. /***/ 9663:
  2356. /***/ (function(module) {
  2357. var l10n = wp.media.view.l10n,
  2358. DateFilter;
  2359. /**
  2360. * A filter dropdown for month/dates.
  2361. *
  2362. * @memberOf wp.media.view.AttachmentFilters
  2363. *
  2364. * @class
  2365. * @augments wp.media.view.AttachmentFilters
  2366. * @augments wp.media.View
  2367. * @augments wp.Backbone.View
  2368. * @augments Backbone.View
  2369. */
  2370. DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{
  2371. id: 'media-attachment-date-filters',
  2372. createFilters: function() {
  2373. var filters = {};
  2374. _.each( wp.media.view.settings.months || {}, function( value, index ) {
  2375. filters[ index ] = {
  2376. text: value.text,
  2377. props: {
  2378. year: value.year,
  2379. monthnum: value.month
  2380. }
  2381. };
  2382. });
  2383. filters.all = {
  2384. text: l10n.allDates,
  2385. props: {
  2386. monthnum: false,
  2387. year: false
  2388. },
  2389. priority: 10
  2390. };
  2391. this.filters = filters;
  2392. }
  2393. });
  2394. module.exports = DateFilter;
  2395. /***/ }),
  2396. /***/ 7040:
  2397. /***/ (function(module) {
  2398. var l10n = wp.media.view.l10n,
  2399. Uploaded;
  2400. /**
  2401. * wp.media.view.AttachmentFilters.Uploaded
  2402. *
  2403. * @memberOf wp.media.view.AttachmentFilters
  2404. *
  2405. * @class
  2406. * @augments wp.media.view.AttachmentFilters
  2407. * @augments wp.media.View
  2408. * @augments wp.Backbone.View
  2409. * @augments Backbone.View
  2410. */
  2411. Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{
  2412. createFilters: function() {
  2413. var type = this.model.get('type'),
  2414. types = wp.media.view.settings.mimeTypes,
  2415. uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0,
  2416. text;
  2417. if ( types && type ) {
  2418. text = types[ type ];
  2419. }
  2420. this.filters = {
  2421. all: {
  2422. text: text || l10n.allMediaItems,
  2423. props: {
  2424. uploadedTo: null,
  2425. orderby: 'date',
  2426. order: 'DESC',
  2427. author: null
  2428. },
  2429. priority: 10
  2430. },
  2431. uploaded: {
  2432. text: l10n.uploadedToThisPost,
  2433. props: {
  2434. uploadedTo: wp.media.view.settings.post.id,
  2435. orderby: 'menuOrder',
  2436. order: 'ASC',
  2437. author: null
  2438. },
  2439. priority: 20
  2440. },
  2441. unattached: {
  2442. text: l10n.unattached,
  2443. props: {
  2444. uploadedTo: 0,
  2445. orderby: 'menuOrder',
  2446. order: 'ASC',
  2447. author: null
  2448. },
  2449. priority: 50
  2450. }
  2451. };
  2452. if ( uid ) {
  2453. this.filters.mine = {
  2454. text: l10n.mine,
  2455. props: {
  2456. orderby: 'date',
  2457. order: 'DESC',
  2458. author: uid
  2459. },
  2460. priority: 50
  2461. };
  2462. }
  2463. }
  2464. });
  2465. module.exports = Uploaded;
  2466. /***/ }),
  2467. /***/ 5019:
  2468. /***/ (function(module) {
  2469. var View = wp.media.View,
  2470. $ = jQuery,
  2471. Attachment;
  2472. /**
  2473. * wp.media.view.Attachment
  2474. *
  2475. * @memberOf wp.media.view
  2476. *
  2477. * @class
  2478. * @augments wp.media.View
  2479. * @augments wp.Backbone.View
  2480. * @augments Backbone.View
  2481. */
  2482. Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{
  2483. tagName: 'li',
  2484. className: 'attachment',
  2485. template: wp.template('attachment'),
  2486. attributes: function() {
  2487. return {
  2488. 'tabIndex': 0,
  2489. 'role': 'checkbox',
  2490. 'aria-label': this.model.get( 'title' ),
  2491. 'aria-checked': false,
  2492. 'data-id': this.model.get( 'id' )
  2493. };
  2494. },
  2495. events: {
  2496. 'click': 'toggleSelectionHandler',
  2497. 'change [data-setting]': 'updateSetting',
  2498. 'change [data-setting] input': 'updateSetting',
  2499. 'change [data-setting] select': 'updateSetting',
  2500. 'change [data-setting] textarea': 'updateSetting',
  2501. 'click .attachment-close': 'removeFromLibrary',
  2502. 'click .check': 'checkClickHandler',
  2503. 'keydown': 'toggleSelectionHandler'
  2504. },
  2505. buttons: {},
  2506. initialize: function() {
  2507. var selection = this.options.selection,
  2508. options = _.defaults( this.options, {
  2509. rerenderOnModelChange: true
  2510. } );
  2511. if ( options.rerenderOnModelChange ) {
  2512. this.listenTo( this.model, 'change', this.render );
  2513. } else {
  2514. this.listenTo( this.model, 'change:percent', this.progress );
  2515. }
  2516. this.listenTo( this.model, 'change:title', this._syncTitle );
  2517. this.listenTo( this.model, 'change:caption', this._syncCaption );
  2518. this.listenTo( this.model, 'change:artist', this._syncArtist );
  2519. this.listenTo( this.model, 'change:album', this._syncAlbum );
  2520. // Update the selection.
  2521. this.listenTo( this.model, 'add', this.select );
  2522. this.listenTo( this.model, 'remove', this.deselect );
  2523. if ( selection ) {
  2524. selection.on( 'reset', this.updateSelect, this );
  2525. // Update the model's details view.
  2526. this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
  2527. this.details( this.model, this.controller.state().get('selection') );
  2528. }
  2529. this.listenTo( this.controller.states, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
  2530. },
  2531. /**
  2532. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2533. */
  2534. dispose: function() {
  2535. var selection = this.options.selection;
  2536. // Make sure all settings are saved before removing the view.
  2537. this.updateAll();
  2538. if ( selection ) {
  2539. selection.off( null, null, this );
  2540. }
  2541. /**
  2542. * call 'dispose' directly on the parent class
  2543. */
  2544. View.prototype.dispose.apply( this, arguments );
  2545. return this;
  2546. },
  2547. /**
  2548. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2549. */
  2550. render: function() {
  2551. var options = _.defaults( this.model.toJSON(), {
  2552. orientation: 'landscape',
  2553. uploading: false,
  2554. type: '',
  2555. subtype: '',
  2556. icon: '',
  2557. filename: '',
  2558. caption: '',
  2559. title: '',
  2560. dateFormatted: '',
  2561. width: '',
  2562. height: '',
  2563. compat: false,
  2564. alt: '',
  2565. description: ''
  2566. }, this.options );
  2567. options.buttons = this.buttons;
  2568. options.describe = this.controller.state().get('describe');
  2569. if ( 'image' === options.type ) {
  2570. options.size = this.imageSize();
  2571. }
  2572. options.can = {};
  2573. if ( options.nonces ) {
  2574. options.can.remove = !! options.nonces['delete'];
  2575. options.can.save = !! options.nonces.update;
  2576. }
  2577. if ( this.controller.state().get('allowLocalEdits') ) {
  2578. options.allowLocalEdits = true;
  2579. }
  2580. if ( options.uploading && ! options.percent ) {
  2581. options.percent = 0;
  2582. }
  2583. this.views.detach();
  2584. this.$el.html( this.template( options ) );
  2585. this.$el.toggleClass( 'uploading', options.uploading );
  2586. if ( options.uploading ) {
  2587. this.$bar = this.$('.media-progress-bar div');
  2588. } else {
  2589. delete this.$bar;
  2590. }
  2591. // Check if the model is selected.
  2592. this.updateSelect();
  2593. // Update the save status.
  2594. this.updateSave();
  2595. this.views.render();
  2596. return this;
  2597. },
  2598. progress: function() {
  2599. if ( this.$bar && this.$bar.length ) {
  2600. this.$bar.width( this.model.get('percent') + '%' );
  2601. }
  2602. },
  2603. /**
  2604. * @param {Object} event
  2605. */
  2606. toggleSelectionHandler: function( event ) {
  2607. var method;
  2608. // Don't do anything inside inputs and on the attachment check and remove buttons.
  2609. if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) {
  2610. return;
  2611. }
  2612. // Catch arrow events.
  2613. if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
  2614. this.controller.trigger( 'attachment:keydown:arrow', event );
  2615. return;
  2616. }
  2617. // Catch enter and space events.
  2618. if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
  2619. return;
  2620. }
  2621. event.preventDefault();
  2622. // In the grid view, bubble up an edit:attachment event to the controller.
  2623. if ( this.controller.isModeActive( 'grid' ) ) {
  2624. if ( this.controller.isModeActive( 'edit' ) ) {
  2625. // Pass the current target to restore focus when closing.
  2626. this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
  2627. return;
  2628. }
  2629. if ( this.controller.isModeActive( 'select' ) ) {
  2630. method = 'toggle';
  2631. }
  2632. }
  2633. if ( event.shiftKey ) {
  2634. method = 'between';
  2635. } else if ( event.ctrlKey || event.metaKey ) {
  2636. method = 'toggle';
  2637. }
  2638. this.toggleSelection({
  2639. method: method
  2640. });
  2641. this.controller.trigger( 'selection:toggle' );
  2642. },
  2643. /**
  2644. * @param {Object} options
  2645. */
  2646. toggleSelection: function( options ) {
  2647. var collection = this.collection,
  2648. selection = this.options.selection,
  2649. model = this.model,
  2650. method = options && options.method,
  2651. single, models, singleIndex, modelIndex;
  2652. if ( ! selection ) {
  2653. return;
  2654. }
  2655. single = selection.single();
  2656. method = _.isUndefined( method ) ? selection.multiple : method;
  2657. // If the `method` is set to `between`, select all models that
  2658. // exist between the current and the selected model.
  2659. if ( 'between' === method && single && selection.multiple ) {
  2660. // If the models are the same, short-circuit.
  2661. if ( single === model ) {
  2662. return;
  2663. }
  2664. singleIndex = collection.indexOf( single );
  2665. modelIndex = collection.indexOf( this.model );
  2666. if ( singleIndex < modelIndex ) {
  2667. models = collection.models.slice( singleIndex, modelIndex + 1 );
  2668. } else {
  2669. models = collection.models.slice( modelIndex, singleIndex + 1 );
  2670. }
  2671. selection.add( models );
  2672. selection.single( model );
  2673. return;
  2674. // If the `method` is set to `toggle`, just flip the selection
  2675. // status, regardless of whether the model is the single model.
  2676. } else if ( 'toggle' === method ) {
  2677. selection[ this.selected() ? 'remove' : 'add' ]( model );
  2678. selection.single( model );
  2679. return;
  2680. } else if ( 'add' === method ) {
  2681. selection.add( model );
  2682. selection.single( model );
  2683. return;
  2684. }
  2685. // Fixes bug that loses focus when selecting a featured image.
  2686. if ( ! method ) {
  2687. method = 'add';
  2688. }
  2689. if ( method !== 'add' ) {
  2690. method = 'reset';
  2691. }
  2692. if ( this.selected() ) {
  2693. /*
  2694. * If the model is the single model, remove it.
  2695. * If it is not the same as the single model,
  2696. * it now becomes the single model.
  2697. */
  2698. selection[ single === model ? 'remove' : 'single' ]( model );
  2699. } else {
  2700. /*
  2701. * If the model is not selected, run the `method` on the
  2702. * selection. By default, we `reset` the selection, but the
  2703. * `method` can be set to `add` the model to the selection.
  2704. */
  2705. selection[ method ]( model );
  2706. selection.single( model );
  2707. }
  2708. },
  2709. updateSelect: function() {
  2710. this[ this.selected() ? 'select' : 'deselect' ]();
  2711. },
  2712. /**
  2713. * @return {unresolved|boolean}
  2714. */
  2715. selected: function() {
  2716. var selection = this.options.selection;
  2717. if ( selection ) {
  2718. return !! selection.get( this.model.cid );
  2719. }
  2720. },
  2721. /**
  2722. * @param {Backbone.Model} model
  2723. * @param {Backbone.Collection} collection
  2724. */
  2725. select: function( model, collection ) {
  2726. var selection = this.options.selection,
  2727. controller = this.controller;
  2728. /*
  2729. * Check if a selection exists and if it's the collection provided.
  2730. * If they're not the same collection, bail; we're in another
  2731. * selection's event loop.
  2732. */
  2733. if ( ! selection || ( collection && collection !== selection ) ) {
  2734. return;
  2735. }
  2736. // Bail if the model is already selected.
  2737. if ( this.$el.hasClass( 'selected' ) ) {
  2738. return;
  2739. }
  2740. // Add 'selected' class to model, set aria-checked to true.
  2741. this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
  2742. // Make the checkbox tabable, except in media grid (bulk select mode).
  2743. if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
  2744. this.$( '.check' ).attr( 'tabindex', '0' );
  2745. }
  2746. },
  2747. /**
  2748. * @param {Backbone.Model} model
  2749. * @param {Backbone.Collection} collection
  2750. */
  2751. deselect: function( model, collection ) {
  2752. var selection = this.options.selection;
  2753. /*
  2754. * Check if a selection exists and if it's the collection provided.
  2755. * If they're not the same collection, bail; we're in another
  2756. * selection's event loop.
  2757. */
  2758. if ( ! selection || ( collection && collection !== selection ) ) {
  2759. return;
  2760. }
  2761. this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
  2762. .find( '.check' ).attr( 'tabindex', '-1' );
  2763. },
  2764. /**
  2765. * @param {Backbone.Model} model
  2766. * @param {Backbone.Collection} collection
  2767. */
  2768. details: function( model, collection ) {
  2769. var selection = this.options.selection,
  2770. details;
  2771. if ( selection !== collection ) {
  2772. return;
  2773. }
  2774. details = selection.single();
  2775. this.$el.toggleClass( 'details', details === this.model );
  2776. },
  2777. /**
  2778. * @param {string} size
  2779. * @return {Object}
  2780. */
  2781. imageSize: function( size ) {
  2782. var sizes = this.model.get('sizes'), matched = false;
  2783. size = size || 'medium';
  2784. // Use the provided image size if possible.
  2785. if ( sizes ) {
  2786. if ( sizes[ size ] ) {
  2787. matched = sizes[ size ];
  2788. } else if ( sizes.large ) {
  2789. matched = sizes.large;
  2790. } else if ( sizes.thumbnail ) {
  2791. matched = sizes.thumbnail;
  2792. } else if ( sizes.full ) {
  2793. matched = sizes.full;
  2794. }
  2795. if ( matched ) {
  2796. return _.clone( matched );
  2797. }
  2798. }
  2799. return {
  2800. url: this.model.get('url'),
  2801. width: this.model.get('width'),
  2802. height: this.model.get('height'),
  2803. orientation: this.model.get('orientation')
  2804. };
  2805. },
  2806. /**
  2807. * @param {Object} event
  2808. */
  2809. updateSetting: function( event ) {
  2810. var $setting = $( event.target ).closest('[data-setting]'),
  2811. setting, value;
  2812. if ( ! $setting.length ) {
  2813. return;
  2814. }
  2815. setting = $setting.data('setting');
  2816. value = event.target.value;
  2817. if ( this.model.get( setting ) !== value ) {
  2818. this.save( setting, value );
  2819. }
  2820. },
  2821. /**
  2822. * Pass all the arguments to the model's save method.
  2823. *
  2824. * Records the aggregate status of all save requests and updates the
  2825. * view's classes accordingly.
  2826. */
  2827. save: function() {
  2828. var view = this,
  2829. save = this._save = this._save || { status: 'ready' },
  2830. request = this.model.save.apply( this.model, arguments ),
  2831. requests = save.requests ? $.when( request, save.requests ) : request;
  2832. // If we're waiting to remove 'Saved.', stop.
  2833. if ( save.savedTimer ) {
  2834. clearTimeout( save.savedTimer );
  2835. }
  2836. this.updateSave('waiting');
  2837. save.requests = requests;
  2838. requests.always( function() {
  2839. // If we've performed another request since this one, bail.
  2840. if ( save.requests !== requests ) {
  2841. return;
  2842. }
  2843. view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
  2844. save.savedTimer = setTimeout( function() {
  2845. view.updateSave('ready');
  2846. delete save.savedTimer;
  2847. }, 2000 );
  2848. });
  2849. },
  2850. /**
  2851. * @param {string} status
  2852. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2853. */
  2854. updateSave: function( status ) {
  2855. var save = this._save = this._save || { status: 'ready' };
  2856. if ( status && status !== save.status ) {
  2857. this.$el.removeClass( 'save-' + save.status );
  2858. save.status = status;
  2859. }
  2860. this.$el.addClass( 'save-' + save.status );
  2861. return this;
  2862. },
  2863. updateAll: function() {
  2864. var $settings = this.$('[data-setting]'),
  2865. model = this.model,
  2866. changed;
  2867. changed = _.chain( $settings ).map( function( el ) {
  2868. var $input = $('input, textarea, select, [value]', el ),
  2869. setting, value;
  2870. if ( ! $input.length ) {
  2871. return;
  2872. }
  2873. setting = $(el).data('setting');
  2874. value = $input.val();
  2875. // Record the value if it changed.
  2876. if ( model.get( setting ) !== value ) {
  2877. return [ setting, value ];
  2878. }
  2879. }).compact().object().value();
  2880. if ( ! _.isEmpty( changed ) ) {
  2881. model.save( changed );
  2882. }
  2883. },
  2884. /**
  2885. * @param {Object} event
  2886. */
  2887. removeFromLibrary: function( event ) {
  2888. // Catch enter and space events.
  2889. if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
  2890. return;
  2891. }
  2892. // Stop propagation so the model isn't selected.
  2893. event.stopPropagation();
  2894. this.collection.remove( this.model );
  2895. },
  2896. /**
  2897. * Add the model if it isn't in the selection, if it is in the selection,
  2898. * remove it.
  2899. *
  2900. * @param {[type]} event [description]
  2901. * @return {[type]} [description]
  2902. */
  2903. checkClickHandler: function ( event ) {
  2904. var selection = this.options.selection;
  2905. if ( ! selection ) {
  2906. return;
  2907. }
  2908. event.stopPropagation();
  2909. if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
  2910. selection.remove( this.model );
  2911. // Move focus back to the attachment tile (from the check).
  2912. this.$el.focus();
  2913. } else {
  2914. selection.add( this.model );
  2915. }
  2916. // Trigger an action button update.
  2917. this.controller.trigger( 'selection:toggle' );
  2918. }
  2919. });
  2920. // Ensure settings remain in sync between attachment views.
  2921. _.each({
  2922. caption: '_syncCaption',
  2923. title: '_syncTitle',
  2924. artist: '_syncArtist',
  2925. album: '_syncAlbum'
  2926. }, function( method, setting ) {
  2927. /**
  2928. * @function _syncCaption
  2929. * @memberOf wp.media.view.Attachment
  2930. * @instance
  2931. *
  2932. * @param {Backbone.Model} model
  2933. * @param {string} value
  2934. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2935. */
  2936. /**
  2937. * @function _syncTitle
  2938. * @memberOf wp.media.view.Attachment
  2939. * @instance
  2940. *
  2941. * @param {Backbone.Model} model
  2942. * @param {string} value
  2943. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2944. */
  2945. /**
  2946. * @function _syncArtist
  2947. * @memberOf wp.media.view.Attachment
  2948. * @instance
  2949. *
  2950. * @param {Backbone.Model} model
  2951. * @param {string} value
  2952. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2953. */
  2954. /**
  2955. * @function _syncAlbum
  2956. * @memberOf wp.media.view.Attachment
  2957. * @instance
  2958. *
  2959. * @param {Backbone.Model} model
  2960. * @param {string} value
  2961. * @return {wp.media.view.Attachment} Returns itself to allow chaining.
  2962. */
  2963. Attachment.prototype[ method ] = function( model, value ) {
  2964. var $setting = this.$('[data-setting="' + setting + '"]');
  2965. if ( ! $setting.length ) {
  2966. return this;
  2967. }
  2968. /*
  2969. * If the updated value is in sync with the value in the DOM, there
  2970. * is no need to re-render. If we're currently editing the value,
  2971. * it will automatically be in sync, suppressing the re-render for
  2972. * the view we're editing, while updating any others.
  2973. */
  2974. if ( value === $setting.find('input, textarea, select, [value]').val() ) {
  2975. return this;
  2976. }
  2977. return this.render();
  2978. };
  2979. });
  2980. module.exports = Attachment;
  2981. /***/ }),
  2982. /***/ 7274:
  2983. /***/ (function(module) {
  2984. /* global ClipboardJS */
  2985. var Attachment = wp.media.view.Attachment,
  2986. l10n = wp.media.view.l10n,
  2987. $ = jQuery,
  2988. Details,
  2989. __ = wp.i18n.__;
  2990. Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{
  2991. tagName: 'div',
  2992. className: 'attachment-details',
  2993. template: wp.template('attachment-details'),
  2994. /*
  2995. * Reset all the attributes inherited from Attachment including role=checkbox,
  2996. * tabindex, etc., as they are inappropriate for this view. See #47458 and [30483] / #30390.
  2997. */
  2998. attributes: {},
  2999. events: {
  3000. 'change [data-setting]': 'updateSetting',
  3001. 'change [data-setting] input': 'updateSetting',
  3002. 'change [data-setting] select': 'updateSetting',
  3003. 'change [data-setting] textarea': 'updateSetting',
  3004. 'click .delete-attachment': 'deleteAttachment',
  3005. 'click .trash-attachment': 'trashAttachment',
  3006. 'click .untrash-attachment': 'untrashAttachment',
  3007. 'click .edit-attachment': 'editAttachment',
  3008. 'keydown': 'toggleSelectionHandler'
  3009. },
  3010. /**
  3011. * Copies the attachment URL to the clipboard.
  3012. *
  3013. * @since 5.5.0
  3014. *
  3015. * @param {MouseEvent} event A click event.
  3016. *
  3017. * @return {void}
  3018. */
  3019. copyAttachmentDetailsURLClipboard: function() {
  3020. var clipboard = new ClipboardJS( '.copy-attachment-url' ),
  3021. successTimeout;
  3022. clipboard.on( 'success', function( event ) {
  3023. var triggerElement = $( event.trigger ),
  3024. successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) );
  3025. // Clear the selection and move focus back to the trigger.
  3026. event.clearSelection();
  3027. // Handle ClipboardJS focus bug, see https://github.com/zenorocha/clipboard.js/issues/680
  3028. triggerElement.trigger( 'focus' );
  3029. // Show success visual feedback.
  3030. clearTimeout( successTimeout );
  3031. successElement.removeClass( 'hidden' );
  3032. // Hide success visual feedback after 3 seconds since last success.
  3033. successTimeout = setTimeout( function() {
  3034. successElement.addClass( 'hidden' );
  3035. }, 3000 );
  3036. // Handle success audible feedback.
  3037. wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) );
  3038. } );
  3039. },
  3040. /**
  3041. * Shows the details of an attachment.
  3042. *
  3043. * @since 3.5.0
  3044. *
  3045. * @constructs wp.media.view.Attachment.Details
  3046. * @augments wp.media.view.Attachment
  3047. *
  3048. * @return {void}
  3049. */
  3050. initialize: function() {
  3051. this.options = _.defaults( this.options, {
  3052. rerenderOnModelChange: false
  3053. });
  3054. // Call 'initialize' directly on the parent class.
  3055. Attachment.prototype.initialize.apply( this, arguments );
  3056. this.copyAttachmentDetailsURLClipboard();
  3057. },
  3058. /**
  3059. * Gets the focusable elements to move focus to.
  3060. *
  3061. * @since 5.3.0
  3062. */
  3063. getFocusableElements: function() {
  3064. var editedAttachment = $( 'li[data-id="' + this.model.id + '"]' );
  3065. this.previousAttachment = editedAttachment.prev();
  3066. this.nextAttachment = editedAttachment.next();
  3067. },
  3068. /**
  3069. * Moves focus to the previous or next attachment in the grid.
  3070. * Fallbacks to the upload button or media frame when there are no attachments.
  3071. *
  3072. * @since 5.3.0
  3073. */
  3074. moveFocus: function() {
  3075. if ( this.previousAttachment.length ) {
  3076. this.previousAttachment.trigger( 'focus' );
  3077. return;
  3078. }
  3079. if ( this.nextAttachment.length ) {
  3080. this.nextAttachment.trigger( 'focus' );
  3081. return;
  3082. }
  3083. // Fallback: move focus to the "Select Files" button in the media modal.
  3084. if ( this.controller.uploader && this.controller.uploader.$browser ) {
  3085. this.controller.uploader.$browser.trigger( 'focus' );
  3086. return;
  3087. }
  3088. // Last fallback.
  3089. this.moveFocusToLastFallback();
  3090. },
  3091. /**
  3092. * Moves focus to the media frame as last fallback.
  3093. *
  3094. * @since 5.3.0
  3095. */
  3096. moveFocusToLastFallback: function() {
  3097. // Last fallback: make the frame focusable and move focus to it.
  3098. $( '.media-frame' )
  3099. .attr( 'tabindex', '-1' )
  3100. .trigger( 'focus' );
  3101. },
  3102. /**
  3103. * Deletes an attachment.
  3104. *
  3105. * Deletes an attachment after asking for confirmation. After deletion,
  3106. * keeps focus in the modal.
  3107. *
  3108. * @since 3.5.0
  3109. *
  3110. * @param {MouseEvent} event A click event.
  3111. *
  3112. * @return {void}
  3113. */
  3114. deleteAttachment: function( event ) {
  3115. event.preventDefault();
  3116. this.getFocusableElements();
  3117. if ( window.confirm( l10n.warnDelete ) ) {
  3118. this.model.destroy( {
  3119. wait: true,
  3120. error: function() {
  3121. window.alert( l10n.errorDeleting );
  3122. }
  3123. } );
  3124. this.moveFocus();
  3125. }
  3126. },
  3127. /**
  3128. * Sets the Trash state on an attachment, or destroys the model itself.
  3129. *
  3130. * If the mediaTrash setting is set to true, trashes the attachment.
  3131. * Otherwise, the model itself is destroyed.
  3132. *
  3133. * @since 3.9.0
  3134. *
  3135. * @param {MouseEvent} event A click event.
  3136. *
  3137. * @return {void}
  3138. */
  3139. trashAttachment: function( event ) {
  3140. var library = this.controller.library,
  3141. self = this;
  3142. event.preventDefault();
  3143. this.getFocusableElements();
  3144. // When in the Media Library and the Media Trash is enabled.
  3145. if ( wp.media.view.settings.mediaTrash &&
  3146. 'edit-metadata' === this.controller.content.mode() ) {
  3147. this.model.set( 'status', 'trash' );
  3148. this.model.save().done( function() {
  3149. library._requery( true );
  3150. /*
  3151. * @todo We need to move focus back to the previous, next, or first
  3152. * attachment but the library gets re-queried and refreshed.
  3153. * Thus, the references to the previous attachments are lost.
  3154. * We need an alternate method.
  3155. */
  3156. self.moveFocusToLastFallback();
  3157. } );
  3158. } else {
  3159. this.model.destroy();
  3160. this.moveFocus();
  3161. }
  3162. },
  3163. /**
  3164. * Untrashes an attachment.
  3165. *
  3166. * @since 4.0.0
  3167. *
  3168. * @param {MouseEvent} event A click event.
  3169. *
  3170. * @return {void}
  3171. */
  3172. untrashAttachment: function( event ) {
  3173. var library = this.controller.library;
  3174. event.preventDefault();
  3175. this.model.set( 'status', 'inherit' );
  3176. this.model.save().done( function() {
  3177. library._requery( true );
  3178. } );
  3179. },
  3180. /**
  3181. * Opens the edit page for a specific attachment.
  3182. *
  3183. * @since 3.5.0
  3184. *
  3185. * @param {MouseEvent} event A click event.
  3186. *
  3187. * @return {void}
  3188. */
  3189. editAttachment: function( event ) {
  3190. var editState = this.controller.states.get( 'edit-image' );
  3191. if ( window.imageEdit && editState ) {
  3192. event.preventDefault();
  3193. editState.set( 'image', this.model );
  3194. this.controller.setState( 'edit-image' );
  3195. } else {
  3196. this.$el.addClass('needs-refresh');
  3197. }
  3198. },
  3199. /**
  3200. * Triggers an event on the controller when reverse tabbing (shift+tab).
  3201. *
  3202. * This event can be used to make sure to move the focus correctly.
  3203. *
  3204. * @since 4.0.0
  3205. *
  3206. * @fires wp.media.controller.MediaLibrary#attachment:details:shift-tab
  3207. * @fires wp.media.controller.MediaLibrary#attachment:keydown:arrow
  3208. *
  3209. * @param {KeyboardEvent} event A keyboard event.
  3210. *
  3211. * @return {boolean|void} Returns false or undefined.
  3212. */
  3213. toggleSelectionHandler: function( event ) {
  3214. if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
  3215. this.controller.trigger( 'attachment:details:shift-tab', event );
  3216. return false;
  3217. }
  3218. },
  3219. render: function() {
  3220. Attachment.prototype.render.apply( this, arguments );
  3221. wp.media.mixin.removeAllPlayers();
  3222. this.$( 'audio, video' ).each( function (i, elem) {
  3223. var el = wp.media.view.MediaDetails.prepareSrc( elem );
  3224. new window.MediaElementPlayer( el, wp.media.mixin.mejsSettings );
  3225. } );
  3226. }
  3227. });
  3228. module.exports = Details;
  3229. /***/ }),
  3230. /***/ 4640:
  3231. /***/ (function(module) {
  3232. /**
  3233. * wp.media.view.Attachment.EditLibrary
  3234. *
  3235. * @memberOf wp.media.view.Attachment
  3236. *
  3237. * @class
  3238. * @augments wp.media.view.Attachment
  3239. * @augments wp.media.View
  3240. * @augments wp.Backbone.View
  3241. * @augments Backbone.View
  3242. */
  3243. var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{
  3244. buttons: {
  3245. close: true
  3246. }
  3247. });
  3248. module.exports = EditLibrary;
  3249. /***/ }),
  3250. /***/ 1009:
  3251. /***/ (function(module) {
  3252. /**
  3253. * wp.media.view.Attachment.EditSelection
  3254. *
  3255. * @memberOf wp.media.view.Attachment
  3256. *
  3257. * @class
  3258. * @augments wp.media.view.Attachment.Selection
  3259. * @augments wp.media.view.Attachment
  3260. * @augments wp.media.View
  3261. * @augments wp.Backbone.View
  3262. * @augments Backbone.View
  3263. */
  3264. var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{
  3265. buttons: {
  3266. close: true
  3267. }
  3268. });
  3269. module.exports = EditSelection;
  3270. /***/ }),
  3271. /***/ 9254:
  3272. /***/ (function(module) {
  3273. /**
  3274. * wp.media.view.Attachment.Library
  3275. *
  3276. * @memberOf wp.media.view.Attachment
  3277. *
  3278. * @class
  3279. * @augments wp.media.view.Attachment
  3280. * @augments wp.media.View
  3281. * @augments wp.Backbone.View
  3282. * @augments Backbone.View
  3283. */
  3284. var Library = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Library.prototype */{
  3285. buttons: {
  3286. check: true
  3287. }
  3288. });
  3289. module.exports = Library;
  3290. /***/ }),
  3291. /***/ 9003:
  3292. /***/ (function(module) {
  3293. /**
  3294. * wp.media.view.Attachment.Selection
  3295. *
  3296. * @memberOf wp.media.view.Attachment
  3297. *
  3298. * @class
  3299. * @augments wp.media.view.Attachment
  3300. * @augments wp.media.View
  3301. * @augments wp.Backbone.View
  3302. * @augments Backbone.View
  3303. */
  3304. var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{
  3305. className: 'attachment selection',
  3306. // On click, just select the model, instead of removing the model from
  3307. // the selection.
  3308. toggleSelection: function() {
  3309. this.options.selection.single( this.model );
  3310. }
  3311. });
  3312. module.exports = Selection;
  3313. /***/ }),
  3314. /***/ 8408:
  3315. /***/ (function(module) {
  3316. var View = wp.media.View,
  3317. $ = jQuery,
  3318. Attachments,
  3319. infiniteScrolling = wp.media.view.settings.infiniteScrolling;
  3320. Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
  3321. tagName: 'ul',
  3322. className: 'attachments',
  3323. attributes: {
  3324. tabIndex: -1
  3325. },
  3326. /**
  3327. * Represents the overview of attachments in the Media Library.
  3328. *
  3329. * The constructor binds events to the collection this view represents when
  3330. * adding or removing attachments or resetting the entire collection.
  3331. *
  3332. * @since 3.5.0
  3333. *
  3334. * @constructs
  3335. * @memberof wp.media.view
  3336. *
  3337. * @augments wp.media.View
  3338. *
  3339. * @listens collection:add
  3340. * @listens collection:remove
  3341. * @listens collection:reset
  3342. * @listens controller:library:selection:add
  3343. * @listens scrollElement:scroll
  3344. * @listens this:ready
  3345. * @listens controller:open
  3346. */
  3347. initialize: function() {
  3348. this.el.id = _.uniqueId('__attachments-view-');
  3349. /**
  3350. * @since 5.8.0 Added the `infiniteScrolling` parameter.
  3351. *
  3352. * @param infiniteScrolling Whether to enable infinite scrolling or use
  3353. * the default "load more" button.
  3354. * @param refreshSensitivity The time in milliseconds to throttle the scroll
  3355. * handler.
  3356. * @param refreshThreshold The amount of pixels that should be scrolled before
  3357. * loading more attachments from the server.
  3358. * @param AttachmentView The view class to be used for models in the
  3359. * collection.
  3360. * @param sortable A jQuery sortable options object
  3361. * ( http://api.jqueryui.com/sortable/ ).
  3362. * @param resize A boolean indicating whether or not to listen to
  3363. * resize events.
  3364. * @param idealColumnWidth The width in pixels which a column should have when
  3365. * calculating the total number of columns.
  3366. */
  3367. _.defaults( this.options, {
  3368. infiniteScrolling: infiniteScrolling || false,
  3369. refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
  3370. refreshThreshold: 3,
  3371. AttachmentView: wp.media.view.Attachment,
  3372. sortable: false,
  3373. resize: true,
  3374. idealColumnWidth: $( window ).width() < 640 ? 135 : 150
  3375. });
  3376. this._viewsByCid = {};
  3377. this.$window = $( window );
  3378. this.resizeEvent = 'resize.media-modal-columns';
  3379. this.collection.on( 'add', function( attachment ) {
  3380. this.views.add( this.createAttachmentView( attachment ), {
  3381. at: this.collection.indexOf( attachment )
  3382. });
  3383. }, this );
  3384. /*
  3385. * Find the view to be removed, delete it and call the remove function to clear
  3386. * any set event handlers.
  3387. */
  3388. this.collection.on( 'remove', function( attachment ) {
  3389. var view = this._viewsByCid[ attachment.cid ];
  3390. delete this._viewsByCid[ attachment.cid ];
  3391. if ( view ) {
  3392. view.remove();
  3393. }
  3394. }, this );
  3395. this.collection.on( 'reset', this.render, this );
  3396. this.controller.on( 'library:selection:add', this.attachmentFocus, this );
  3397. if ( this.options.infiniteScrolling ) {
  3398. // Throttle the scroll handler and bind this.
  3399. this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
  3400. this.options.scrollElement = this.options.scrollElement || this.el;
  3401. $( this.options.scrollElement ).on( 'scroll', this.scroll );
  3402. }
  3403. this.initSortable();
  3404. _.bindAll( this, 'setColumns' );
  3405. if ( this.options.resize ) {
  3406. this.on( 'ready', this.bindEvents );
  3407. this.controller.on( 'open', this.setColumns );
  3408. /*
  3409. * Call this.setColumns() after this view has been rendered in the
  3410. * DOM so attachments get proper width applied.
  3411. */
  3412. _.defer( this.setColumns, this );
  3413. }
  3414. },
  3415. /**
  3416. * Listens to the resizeEvent on the window.
  3417. *
  3418. * Adjusts the amount of columns accordingly. First removes any existing event
  3419. * handlers to prevent duplicate listeners.
  3420. *
  3421. * @since 4.0.0
  3422. *
  3423. * @listens window:resize
  3424. *
  3425. * @return {void}
  3426. */
  3427. bindEvents: function() {
  3428. this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
  3429. },
  3430. /**
  3431. * Focuses the first item in the collection.
  3432. *
  3433. * @since 4.0.0
  3434. *
  3435. * @return {void}
  3436. */
  3437. attachmentFocus: function() {
  3438. /*
  3439. * @todo When uploading new attachments, this tries to move focus to
  3440. * the attachments grid. Actually, a progress bar gets initially displayed
  3441. * and then updated when uploading completes, so focus is lost.
  3442. * Additionally: this view is used for both the attachments list and
  3443. * the list of selected attachments in the bottom media toolbar. Thus, when
  3444. * uploading attachments, it is called twice and returns two different `this`.
  3445. * `this.columns` is truthy within the modal.
  3446. */
  3447. if ( this.columns ) {
  3448. // Move focus to the grid list within the modal.
  3449. this.$el.focus();
  3450. }
  3451. },
  3452. /**
  3453. * Restores focus to the selected item in the collection.
  3454. *
  3455. * Moves focus back to the first selected attachment in the grid. Used when
  3456. * tabbing backwards from the attachment details sidebar.
  3457. * See media.view.AttachmentsBrowser.
  3458. *
  3459. * @since 4.0.0
  3460. *
  3461. * @return {void}
  3462. */
  3463. restoreFocus: function() {
  3464. this.$( 'li.selected:first' ).focus();
  3465. },
  3466. /**
  3467. * Handles events for arrow key presses.
  3468. *
  3469. * Focuses the attachment in the direction of the used arrow key if it exists.
  3470. *
  3471. * @since 4.0.0
  3472. *
  3473. * @param {KeyboardEvent} event The keyboard event that triggered this function.
  3474. *
  3475. * @return {void}
  3476. */
  3477. arrowEvent: function( event ) {
  3478. var attachments = this.$el.children( 'li' ),
  3479. perRow = this.columns,
  3480. index = attachments.filter( ':focus' ).index(),
  3481. row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
  3482. if ( index === -1 ) {
  3483. return;
  3484. }
  3485. // Left arrow = 37.
  3486. if ( 37 === event.keyCode ) {
  3487. if ( 0 === index ) {
  3488. return;
  3489. }
  3490. attachments.eq( index - 1 ).focus();
  3491. }
  3492. // Up arrow = 38.
  3493. if ( 38 === event.keyCode ) {
  3494. if ( 1 === row ) {
  3495. return;
  3496. }
  3497. attachments.eq( index - perRow ).focus();
  3498. }
  3499. // Right arrow = 39.
  3500. if ( 39 === event.keyCode ) {
  3501. if ( attachments.length === index ) {
  3502. return;
  3503. }
  3504. attachments.eq( index + 1 ).focus();
  3505. }
  3506. // Down arrow = 40.
  3507. if ( 40 === event.keyCode ) {
  3508. if ( Math.ceil( attachments.length / perRow ) === row ) {
  3509. return;
  3510. }
  3511. attachments.eq( index + perRow ).focus();
  3512. }
  3513. },
  3514. /**
  3515. * Clears any set event handlers.
  3516. *
  3517. * @since 3.5.0
  3518. *
  3519. * @return {void}
  3520. */
  3521. dispose: function() {
  3522. this.collection.props.off( null, null, this );
  3523. if ( this.options.resize ) {
  3524. this.$window.off( this.resizeEvent );
  3525. }
  3526. // Call 'dispose' directly on the parent class.
  3527. View.prototype.dispose.apply( this, arguments );
  3528. },
  3529. /**
  3530. * Calculates the amount of columns.
  3531. *
  3532. * Calculates the amount of columns and sets it on the data-columns attribute
  3533. * of .media-frame-content.
  3534. *
  3535. * @since 4.0.0
  3536. *
  3537. * @return {void}
  3538. */
  3539. setColumns: function() {
  3540. var prev = this.columns,
  3541. width = this.$el.width();
  3542. if ( width ) {
  3543. this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
  3544. if ( ! prev || prev !== this.columns ) {
  3545. this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
  3546. }
  3547. }
  3548. },
  3549. /**
  3550. * Initializes jQuery sortable on the attachment list.
  3551. *
  3552. * Fails gracefully if jQuery sortable doesn't exist or isn't passed
  3553. * in the options.
  3554. *
  3555. * @since 3.5.0
  3556. *
  3557. * @fires collection:reset
  3558. *
  3559. * @return {void}
  3560. */
  3561. initSortable: function() {
  3562. var collection = this.collection;
  3563. if ( ! this.options.sortable || ! $.fn.sortable ) {
  3564. return;
  3565. }
  3566. this.$el.sortable( _.extend({
  3567. // If the `collection` has a `comparator`, disable sorting.
  3568. disabled: !! collection.comparator,
  3569. /*
  3570. * Change the position of the attachment as soon as the mouse pointer
  3571. * overlaps a thumbnail.
  3572. */
  3573. tolerance: 'pointer',
  3574. // Record the initial `index` of the dragged model.
  3575. start: function( event, ui ) {
  3576. ui.item.data('sortableIndexStart', ui.item.index());
  3577. },
  3578. /*
  3579. * Update the model's index in the collection. Do so silently, as the view
  3580. * is already accurate.
  3581. */
  3582. update: function( event, ui ) {
  3583. var model = collection.at( ui.item.data('sortableIndexStart') ),
  3584. comparator = collection.comparator;
  3585. // Temporarily disable the comparator to prevent `add`
  3586. // from re-sorting.
  3587. delete collection.comparator;
  3588. // Silently shift the model to its new index.
  3589. collection.remove( model, {
  3590. silent: true
  3591. });
  3592. collection.add( model, {
  3593. silent: true,
  3594. at: ui.item.index()
  3595. });
  3596. // Restore the comparator.
  3597. collection.comparator = comparator;
  3598. // Fire the `reset` event to ensure other collections sync.
  3599. collection.trigger( 'reset', collection );
  3600. // If the collection is sorted by menu order, update the menu order.
  3601. collection.saveMenuOrder();
  3602. }
  3603. }, this.options.sortable ) );
  3604. /*
  3605. * If the `orderby` property is changed on the `collection`,
  3606. * check to see if we have a `comparator`. If so, disable sorting.
  3607. */
  3608. collection.props.on( 'change:orderby', function() {
  3609. this.$el.sortable( 'option', 'disabled', !! collection.comparator );
  3610. }, this );
  3611. this.collection.props.on( 'change:orderby', this.refreshSortable, this );
  3612. this.refreshSortable();
  3613. },
  3614. /**
  3615. * Disables jQuery sortable if collection has a comparator or collection.orderby
  3616. * equals menuOrder.
  3617. *
  3618. * @since 3.5.0
  3619. *
  3620. * @return {void}
  3621. */
  3622. refreshSortable: function() {
  3623. if ( ! this.options.sortable || ! $.fn.sortable ) {
  3624. return;
  3625. }
  3626. var collection = this.collection,
  3627. orderby = collection.props.get('orderby'),
  3628. enabled = 'menuOrder' === orderby || ! collection.comparator;
  3629. this.$el.sortable( 'option', 'disabled', ! enabled );
  3630. },
  3631. /**
  3632. * Creates a new view for an attachment and adds it to _viewsByCid.
  3633. *
  3634. * @since 3.5.0
  3635. *
  3636. * @param {wp.media.model.Attachment} attachment
  3637. *
  3638. * @return {wp.media.View} The created view.
  3639. */
  3640. createAttachmentView: function( attachment ) {
  3641. var view = new this.options.AttachmentView({
  3642. controller: this.controller,
  3643. model: attachment,
  3644. collection: this.collection,
  3645. selection: this.options.selection
  3646. });
  3647. return this._viewsByCid[ attachment.cid ] = view;
  3648. },
  3649. /**
  3650. * Prepares view for display.
  3651. *
  3652. * Creates views for every attachment in collection if the collection is not
  3653. * empty, otherwise clears all views and loads more attachments.
  3654. *
  3655. * @since 3.5.0
  3656. *
  3657. * @return {void}
  3658. */
  3659. prepare: function() {
  3660. if ( this.collection.length ) {
  3661. this.views.set( this.collection.map( this.createAttachmentView, this ) );
  3662. } else {
  3663. this.views.unset();
  3664. if ( this.options.infiniteScrolling ) {
  3665. this.collection.more().done( this.scroll );
  3666. }
  3667. }
  3668. },
  3669. /**
  3670. * Triggers the scroll function to check if we should query for additional
  3671. * attachments right away.
  3672. *
  3673. * @since 3.5.0
  3674. *
  3675. * @return {void}
  3676. */
  3677. ready: function() {
  3678. if ( this.options.infiniteScrolling ) {
  3679. this.scroll();
  3680. }
  3681. },
  3682. /**
  3683. * Handles scroll events.
  3684. *
  3685. * Shows the spinner if we're close to the bottom. Loads more attachments from
  3686. * server if we're {refreshThreshold} times away from the bottom.
  3687. *
  3688. * @since 3.5.0
  3689. *
  3690. * @return {void}
  3691. */
  3692. scroll: function() {
  3693. var view = this,
  3694. el = this.options.scrollElement,
  3695. scrollTop = el.scrollTop,
  3696. toolbar;
  3697. /*
  3698. * The scroll event occurs on the document, but the element that should be
  3699. * checked is the document body.
  3700. */
  3701. if ( el === document ) {
  3702. el = document.body;
  3703. scrollTop = $(document).scrollTop();
  3704. }
  3705. if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
  3706. return;
  3707. }
  3708. toolbar = this.views.parent.toolbar;
  3709. // Show the spinner only if we are close to the bottom.
  3710. if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
  3711. toolbar.get('spinner').show();
  3712. }
  3713. if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
  3714. this.collection.more().done(function() {
  3715. view.scroll();
  3716. toolbar.get('spinner').hide();
  3717. });
  3718. }
  3719. }
  3720. });
  3721. module.exports = Attachments;
  3722. /***/ }),
  3723. /***/ 9239:
  3724. /***/ (function(module) {
  3725. var View = wp.media.View,
  3726. mediaTrash = wp.media.view.settings.mediaTrash,
  3727. l10n = wp.media.view.l10n,
  3728. $ = jQuery,
  3729. AttachmentsBrowser,
  3730. infiniteScrolling = wp.media.view.settings.infiniteScrolling,
  3731. __ = wp.i18n.__,
  3732. sprintf = wp.i18n.sprintf;
  3733. /**
  3734. * wp.media.view.AttachmentsBrowser
  3735. *
  3736. * @memberOf wp.media.view
  3737. *
  3738. * @class
  3739. * @augments wp.media.View
  3740. * @augments wp.Backbone.View
  3741. * @augments Backbone.View
  3742. *
  3743. * @param {object} [options] The options hash passed to the view.
  3744. * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar.
  3745. * Accepts 'uploaded' and 'all'.
  3746. * @param {boolean} [options.search=true] Whether to show the search interface in the
  3747. * browser's toolbar.
  3748. * @param {boolean} [options.date=true] Whether to show the date filter in the
  3749. * browser's toolbar.
  3750. * @param {boolean} [options.display=false] Whether to show the attachments display settings
  3751. * view in the sidebar.
  3752. * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser.
  3753. * Accepts true, false, and 'errors'.
  3754. */
  3755. AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{
  3756. tagName: 'div',
  3757. className: 'attachments-browser',
  3758. initialize: function() {
  3759. _.defaults( this.options, {
  3760. filters: false,
  3761. search: true,
  3762. date: true,
  3763. display: false,
  3764. sidebar: true,
  3765. AttachmentView: wp.media.view.Attachment.Library
  3766. });
  3767. this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this );
  3768. this.controller.on( 'edit:selection', this.editSelection );
  3769. // In the Media Library, the sidebar is used to display errors before the attachments grid.
  3770. if ( this.options.sidebar && 'errors' === this.options.sidebar ) {
  3771. this.createSidebar();
  3772. }
  3773. /*
  3774. * In the grid mode (the Media Library), place the Inline Uploader before
  3775. * other sections so that the visual order and the DOM order match. This way,
  3776. * the Inline Uploader in the Media Library is right after the "Add New"
  3777. * button, see ticket #37188.
  3778. */
  3779. if ( this.controller.isModeActive( 'grid' ) ) {
  3780. this.createUploader();
  3781. /*
  3782. * Create a multi-purpose toolbar. Used as main toolbar in the Media Library
  3783. * and also for other things, for example the "Drag and drop to reorder" and
  3784. * "Suggested dimensions" info in the media modal.
  3785. */
  3786. this.createToolbar();
  3787. } else {
  3788. this.createToolbar();
  3789. this.createUploader();
  3790. }
  3791. // Add a heading before the attachments list.
  3792. this.createAttachmentsHeading();
  3793. // Create the attachments wrapper view.
  3794. this.createAttachmentsWrapperView();
  3795. if ( ! infiniteScrolling ) {
  3796. this.$el.addClass( 'has-load-more' );
  3797. this.createLoadMoreView();
  3798. }
  3799. // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
  3800. if ( this.options.sidebar && 'errors' !== this.options.sidebar ) {
  3801. this.createSidebar();
  3802. }
  3803. this.updateContent();
  3804. if ( ! infiniteScrolling ) {
  3805. this.updateLoadMoreView();
  3806. }
  3807. if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) {
  3808. this.$el.addClass( 'hide-sidebar' );
  3809. if ( 'errors' === this.options.sidebar ) {
  3810. this.$el.addClass( 'sidebar-for-errors' );
  3811. }
  3812. }
  3813. this.collection.on( 'add remove reset', this.updateContent, this );
  3814. if ( ! infiniteScrolling ) {
  3815. this.collection.on( 'add remove reset', this.updateLoadMoreView, this );
  3816. }
  3817. // The non-cached or cached attachments query has completed.
  3818. this.collection.on( 'attachments:received', this.announceSearchResults, this );
  3819. },
  3820. /**
  3821. * Updates the `wp.a11y.speak()` ARIA live region with a message to communicate
  3822. * the number of search results to screen reader users. This function is
  3823. * debounced because the collection updates multiple times.
  3824. *
  3825. * @since 5.3.0
  3826. *
  3827. * @return {void}
  3828. */
  3829. announceSearchResults: _.debounce( function() {
  3830. var count,
  3831. /* translators: Accessibility text. %d: Number of attachments found in a search. */
  3832. mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' );
  3833. if ( infiniteScrolling ) {
  3834. /* translators: Accessibility text. %d: Number of attachments found in a search. */
  3835. mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' );
  3836. }
  3837. if ( this.collection.mirroring && this.collection.mirroring.args.s ) {
  3838. count = this.collection.length;
  3839. if ( 0 === count ) {
  3840. wp.a11y.speak( l10n.noMediaTryNewSearch );
  3841. return;
  3842. }
  3843. if ( this.collection.hasMore() ) {
  3844. wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) );
  3845. return;
  3846. }
  3847. wp.a11y.speak( l10n.mediaFound.replace( '%d', count ) );
  3848. }
  3849. }, 200 ),
  3850. editSelection: function( modal ) {
  3851. // When editing a selection, move focus to the "Go to library" button.
  3852. modal.$( '.media-button-backToLibrary' ).focus();
  3853. },
  3854. /**
  3855. * @return {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining.
  3856. */
  3857. dispose: function() {
  3858. this.options.selection.off( null, null, this );
  3859. View.prototype.dispose.apply( this, arguments );
  3860. return this;
  3861. },
  3862. createToolbar: function() {
  3863. var LibraryViewSwitcher, Filters, toolbarOptions,
  3864. showFilterByType = -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] );
  3865. toolbarOptions = {
  3866. controller: this.controller
  3867. };
  3868. if ( this.controller.isModeActive( 'grid' ) ) {
  3869. toolbarOptions.className = 'media-toolbar wp-filter';
  3870. }
  3871. /**
  3872. * @member {wp.media.view.Toolbar}
  3873. */
  3874. this.toolbar = new wp.media.view.Toolbar( toolbarOptions );
  3875. this.views.add( this.toolbar );
  3876. this.toolbar.set( 'spinner', new wp.media.view.Spinner({
  3877. priority: -20
  3878. }) );
  3879. if ( showFilterByType || this.options.date ) {
  3880. /*
  3881. * Create a h2 heading before the select elements that filter attachments.
  3882. * This heading is visible in the modal and visually hidden in the grid.
  3883. */
  3884. this.toolbar.set( 'filters-heading', new wp.media.view.Heading( {
  3885. priority: -100,
  3886. text: l10n.filterAttachments,
  3887. level: 'h2',
  3888. className: 'media-attachments-filter-heading'
  3889. }).render() );
  3890. }
  3891. if ( showFilterByType ) {
  3892. // "Filters" is a <select>, a visually hidden label element needs to be rendered before.
  3893. this.toolbar.set( 'filtersLabel', new wp.media.view.Label({
  3894. value: l10n.filterByType,
  3895. attributes: {
  3896. 'for': 'media-attachment-filters'
  3897. },
  3898. priority: -80
  3899. }).render() );
  3900. if ( 'uploaded' === this.options.filters ) {
  3901. this.toolbar.set( 'filters', new wp.media.view.AttachmentFilters.Uploaded({
  3902. controller: this.controller,
  3903. model: this.collection.props,
  3904. priority: -80
  3905. }).render() );
  3906. } else {
  3907. Filters = new wp.media.view.AttachmentFilters.All({
  3908. controller: this.controller,
  3909. model: this.collection.props,
  3910. priority: -80
  3911. });
  3912. this.toolbar.set( 'filters', Filters.render() );
  3913. }
  3914. }
  3915. /*
  3916. * Feels odd to bring the global media library switcher into the Attachment browser view.
  3917. * Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
  3918. * which the controller can tap into and add this view?
  3919. */
  3920. if ( this.controller.isModeActive( 'grid' ) ) {
  3921. LibraryViewSwitcher = View.extend({
  3922. className: 'view-switch media-grid-view-switch',
  3923. template: wp.template( 'media-library-view-switcher')
  3924. });
  3925. this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({
  3926. controller: this.controller,
  3927. priority: -90
  3928. }).render() );
  3929. // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
  3930. this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
  3931. value: l10n.filterByDate,
  3932. attributes: {
  3933. 'for': 'media-attachment-date-filters'
  3934. },
  3935. priority: -75
  3936. }).render() );
  3937. this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
  3938. controller: this.controller,
  3939. model: this.collection.props,
  3940. priority: -75
  3941. }).render() );
  3942. // BulkSelection is a <div> with subviews, including screen reader text.
  3943. this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({
  3944. text: l10n.bulkSelect,
  3945. controller: this.controller,
  3946. priority: -70
  3947. }).render() );
  3948. this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({
  3949. filters: Filters,
  3950. style: 'primary',
  3951. disabled: true,
  3952. text: mediaTrash ? l10n.trashSelected : l10n.deletePermanently,
  3953. controller: this.controller,
  3954. priority: -80,
  3955. click: function() {
  3956. var changed = [], removed = [],
  3957. selection = this.controller.state().get( 'selection' ),
  3958. library = this.controller.state().get( 'library' );
  3959. if ( ! selection.length ) {
  3960. return;
  3961. }
  3962. if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) {
  3963. return;
  3964. }
  3965. if ( mediaTrash &&
  3966. 'trash' !== selection.at( 0 ).get( 'status' ) &&
  3967. ! window.confirm( l10n.warnBulkTrash ) ) {
  3968. return;
  3969. }
  3970. selection.each( function( model ) {
  3971. if ( ! model.get( 'nonces' )['delete'] ) {
  3972. removed.push( model );
  3973. return;
  3974. }
  3975. if ( mediaTrash && 'trash' === model.get( 'status' ) ) {
  3976. model.set( 'status', 'inherit' );
  3977. changed.push( model.save() );
  3978. removed.push( model );
  3979. } else if ( mediaTrash ) {
  3980. model.set( 'status', 'trash' );
  3981. changed.push( model.save() );
  3982. removed.push( model );
  3983. } else {
  3984. model.destroy({wait: true});
  3985. }
  3986. } );
  3987. if ( changed.length ) {
  3988. selection.remove( removed );
  3989. $.when.apply( null, changed ).then( _.bind( function() {
  3990. library._requery( true );
  3991. this.controller.trigger( 'selection:action:done' );
  3992. }, this ) );
  3993. } else {
  3994. this.controller.trigger( 'selection:action:done' );
  3995. }
  3996. }
  3997. }).render() );
  3998. if ( mediaTrash ) {
  3999. this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({
  4000. filters: Filters,
  4001. style: 'link button-link-delete',
  4002. disabled: true,
  4003. text: l10n.deletePermanently,
  4004. controller: this.controller,
  4005. priority: -55,
  4006. click: function() {
  4007. var removed = [],
  4008. destroy = [],
  4009. selection = this.controller.state().get( 'selection' );
  4010. if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) {
  4011. return;
  4012. }
  4013. selection.each( function( model ) {
  4014. if ( ! model.get( 'nonces' )['delete'] ) {
  4015. removed.push( model );
  4016. return;
  4017. }
  4018. destroy.push( model );
  4019. } );
  4020. if ( removed.length ) {
  4021. selection.remove( removed );
  4022. }
  4023. if ( destroy.length ) {
  4024. $.when.apply( null, destroy.map( function (item) {
  4025. return item.destroy();
  4026. } ) ).then( _.bind( function() {
  4027. this.controller.trigger( 'selection:action:done' );
  4028. }, this ) );
  4029. }
  4030. }
  4031. }).render() );
  4032. }
  4033. } else if ( this.options.date ) {
  4034. // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
  4035. this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
  4036. value: l10n.filterByDate,
  4037. attributes: {
  4038. 'for': 'media-attachment-date-filters'
  4039. },
  4040. priority: -75
  4041. }).render() );
  4042. this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
  4043. controller: this.controller,
  4044. model: this.collection.props,
  4045. priority: -75
  4046. }).render() );
  4047. }
  4048. if ( this.options.search ) {
  4049. // Search is an input, a visually hidden label element needs to be rendered before.
  4050. this.toolbar.set( 'searchLabel', new wp.media.view.Label({
  4051. value: l10n.searchLabel,
  4052. className: 'media-search-input-label',
  4053. attributes: {
  4054. 'for': 'media-search-input'
  4055. },
  4056. priority: 60
  4057. }).render() );
  4058. this.toolbar.set( 'search', new wp.media.view.Search({
  4059. controller: this.controller,
  4060. model: this.collection.props,
  4061. priority: 60
  4062. }).render() );
  4063. }
  4064. if ( this.options.dragInfo ) {
  4065. this.toolbar.set( 'dragInfo', new View({
  4066. el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
  4067. priority: -40
  4068. }) );
  4069. }
  4070. if ( this.options.suggestedWidth && this.options.suggestedHeight ) {
  4071. this.toolbar.set( 'suggestedDimensions', new View({
  4072. el: $( '<div class="instructions">' + l10n.suggestedDimensions.replace( '%1$s', this.options.suggestedWidth ).replace( '%2$s', this.options.suggestedHeight ) + '</div>' )[0],
  4073. priority: -40
  4074. }) );
  4075. }
  4076. },
  4077. updateContent: function() {
  4078. var view = this,
  4079. noItemsView;
  4080. if ( this.controller.isModeActive( 'grid' ) ) {
  4081. // Usually the media library.
  4082. noItemsView = view.attachmentsNoResults;
  4083. } else {
  4084. // Usually the media modal.
  4085. noItemsView = view.uploader;
  4086. }
  4087. if ( ! this.collection.length ) {
  4088. this.toolbar.get( 'spinner' ).show();
  4089. this.dfd = this.collection.more().done( function() {
  4090. if ( ! view.collection.length ) {
  4091. noItemsView.$el.removeClass( 'hidden' );
  4092. } else {
  4093. noItemsView.$el.addClass( 'hidden' );
  4094. }
  4095. view.toolbar.get( 'spinner' ).hide();
  4096. } );
  4097. } else {
  4098. noItemsView.$el.addClass( 'hidden' );
  4099. view.toolbar.get( 'spinner' ).hide();
  4100. }
  4101. },
  4102. createUploader: function() {
  4103. this.uploader = new wp.media.view.UploaderInline({
  4104. controller: this.controller,
  4105. status: false,
  4106. message: this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound,
  4107. canClose: this.controller.isModeActive( 'grid' )
  4108. });
  4109. this.uploader.$el.addClass( 'hidden' );
  4110. this.views.add( this.uploader );
  4111. },
  4112. toggleUploader: function() {
  4113. if ( this.uploader.$el.hasClass( 'hidden' ) ) {
  4114. this.uploader.show();
  4115. } else {
  4116. this.uploader.hide();
  4117. }
  4118. },
  4119. /**
  4120. * Creates the Attachments wrapper view.
  4121. *
  4122. * @since 5.8.0
  4123. *
  4124. * @return {void}
  4125. */
  4126. createAttachmentsWrapperView: function() {
  4127. this.attachmentsWrapper = new wp.media.View( {
  4128. className: 'attachments-wrapper'
  4129. } );
  4130. // Create the list of attachments.
  4131. this.views.add( this.attachmentsWrapper );
  4132. this.createAttachments();
  4133. },
  4134. createAttachments: function() {
  4135. this.attachments = new wp.media.view.Attachments({
  4136. controller: this.controller,
  4137. collection: this.collection,
  4138. selection: this.options.selection,
  4139. model: this.model,
  4140. sortable: this.options.sortable,
  4141. scrollElement: this.options.scrollElement,
  4142. idealColumnWidth: this.options.idealColumnWidth,
  4143. // The single `Attachment` view to be used in the `Attachments` view.
  4144. AttachmentView: this.options.AttachmentView
  4145. });
  4146. // Add keydown listener to the instance of the Attachments view.
  4147. this.controller.on( 'attachment:keydown:arrow', _.bind( this.attachments.arrowEvent, this.attachments ) );
  4148. this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
  4149. this.views.add( '.attachments-wrapper', this.attachments );
  4150. if ( this.controller.isModeActive( 'grid' ) ) {
  4151. this.attachmentsNoResults = new View({
  4152. controller: this.controller,
  4153. tagName: 'p'
  4154. });
  4155. this.attachmentsNoResults.$el.addClass( 'hidden no-media' );
  4156. this.attachmentsNoResults.$el.html( l10n.noMedia );
  4157. this.views.add( this.attachmentsNoResults );
  4158. }
  4159. },
  4160. /**
  4161. * Creates the load more button and attachments counter view.
  4162. *
  4163. * @since 5.8.0
  4164. *
  4165. * @return {void}
  4166. */
  4167. createLoadMoreView: function() {
  4168. var view = this;
  4169. this.loadMoreWrapper = new View( {
  4170. controller: this.controller,
  4171. className: 'load-more-wrapper'
  4172. } );
  4173. this.loadMoreCount = new View( {
  4174. controller: this.controller,
  4175. tagName: 'p',
  4176. className: 'load-more-count hidden'
  4177. } );
  4178. this.loadMoreButton = new wp.media.view.Button( {
  4179. text: __( 'Load more' ),
  4180. className: 'load-more hidden',
  4181. style: 'primary',
  4182. size: '',
  4183. click: function() {
  4184. view.loadMoreAttachments();
  4185. }
  4186. } );
  4187. this.loadMoreSpinner = new wp.media.view.Spinner();
  4188. this.loadMoreJumpToFirst = new wp.media.view.Button( {
  4189. text: __( 'Jump to first loaded item' ),
  4190. className: 'load-more-jump hidden',
  4191. size: '',
  4192. click: function() {
  4193. view.jumpToFirstAddedItem();
  4194. }
  4195. } );
  4196. this.views.add( '.attachments-wrapper', this.loadMoreWrapper );
  4197. this.views.add( '.load-more-wrapper', this.loadMoreSpinner );
  4198. this.views.add( '.load-more-wrapper', this.loadMoreCount );
  4199. this.views.add( '.load-more-wrapper', this.loadMoreButton );
  4200. this.views.add( '.load-more-wrapper', this.loadMoreJumpToFirst );
  4201. },
  4202. /**
  4203. * Updates the Load More view. This function is debounced because the
  4204. * collection updates multiple times at the add, remove, and reset events.
  4205. * We need it to run only once, after all attachments are added or removed.
  4206. *
  4207. * @since 5.8.0
  4208. *
  4209. * @return {void}
  4210. */
  4211. updateLoadMoreView: _.debounce( function() {
  4212. // Ensure the load more view elements are initially hidden at each update.
  4213. this.loadMoreButton.$el.addClass( 'hidden' );
  4214. this.loadMoreCount.$el.addClass( 'hidden' );
  4215. this.loadMoreJumpToFirst.$el.addClass( 'hidden' ).prop( 'disabled', true );
  4216. if ( ! this.collection.getTotalAttachments() ) {
  4217. return;
  4218. }
  4219. if ( this.collection.length ) {
  4220. this.loadMoreCount.$el.text(
  4221. /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */
  4222. sprintf(
  4223. __( 'Showing %1$s of %2$s media items' ),
  4224. this.collection.length,
  4225. this.collection.getTotalAttachments()
  4226. )
  4227. );
  4228. this.loadMoreCount.$el.removeClass( 'hidden' );
  4229. }
  4230. /*
  4231. * Notice that while the collection updates multiple times hasMore() may
  4232. * return true when it's actually not true.
  4233. */
  4234. if ( this.collection.hasMore() ) {
  4235. this.loadMoreButton.$el.removeClass( 'hidden' );
  4236. }
  4237. // Find the media item to move focus to. The jQuery `eq()` index is zero-based.
  4238. this.firstAddedMediaItem = this.$el.find( '.attachment' ).eq( this.firstAddedMediaItemIndex );
  4239. // If there's a media item to move focus to, make the "Jump to" button available.
  4240. if ( this.firstAddedMediaItem.length ) {
  4241. this.firstAddedMediaItem.addClass( 'new-media' );
  4242. this.loadMoreJumpToFirst.$el.removeClass( 'hidden' ).prop( 'disabled', false );
  4243. }
  4244. // If there are new items added, but no more to be added, move focus to Jump button.
  4245. if ( this.firstAddedMediaItem.length && ! this.collection.hasMore() ) {
  4246. this.loadMoreJumpToFirst.$el.trigger( 'focus' );
  4247. }
  4248. }, 10 ),
  4249. /**
  4250. * Loads more attachments.
  4251. *
  4252. * @since 5.8.0
  4253. *
  4254. * @return {void}
  4255. */
  4256. loadMoreAttachments: function() {
  4257. var view = this;
  4258. if ( ! this.collection.hasMore() ) {
  4259. return;
  4260. }
  4261. /*
  4262. * The collection index is zero-based while the length counts the actual
  4263. * amount of items. Thus the length is equivalent to the position of the
  4264. * first added item.
  4265. */
  4266. this.firstAddedMediaItemIndex = this.collection.length;
  4267. this.$el.addClass( 'more-loaded' );
  4268. this.collection.each( function( attachment ) {
  4269. var attach_id = attachment.attributes.id;
  4270. $( '[data-id="' + attach_id + '"]' ).addClass( 'found-media' );
  4271. });
  4272. view.loadMoreSpinner.show();
  4273. this.collection.once( 'attachments:received', function() {
  4274. view.loadMoreSpinner.hide();
  4275. } );
  4276. this.collection.more();
  4277. },
  4278. /**
  4279. * Moves focus to the first new added item. .
  4280. *
  4281. * @since 5.8.0
  4282. *
  4283. * @return {void}
  4284. */
  4285. jumpToFirstAddedItem: function() {
  4286. // Set focus on first added item.
  4287. this.firstAddedMediaItem.focus();
  4288. },
  4289. createAttachmentsHeading: function() {
  4290. this.attachmentsHeading = new wp.media.view.Heading( {
  4291. text: l10n.attachmentsList,
  4292. level: 'h2',
  4293. className: 'media-views-heading screen-reader-text'
  4294. } );
  4295. this.views.add( this.attachmentsHeading );
  4296. },
  4297. createSidebar: function() {
  4298. var options = this.options,
  4299. selection = options.selection,
  4300. sidebar = this.sidebar = new wp.media.view.Sidebar({
  4301. controller: this.controller
  4302. });
  4303. this.views.add( sidebar );
  4304. if ( this.controller.uploader ) {
  4305. sidebar.set( 'uploads', new wp.media.view.UploaderStatus({
  4306. controller: this.controller,
  4307. priority: 40
  4308. }) );
  4309. }
  4310. selection.on( 'selection:single', this.createSingle, this );
  4311. selection.on( 'selection:unsingle', this.disposeSingle, this );
  4312. if ( selection.single() ) {
  4313. this.createSingle();
  4314. }
  4315. },
  4316. createSingle: function() {
  4317. var sidebar = this.sidebar,
  4318. single = this.options.selection.single();
  4319. sidebar.set( 'details', new wp.media.view.Attachment.Details({
  4320. controller: this.controller,
  4321. model: single,
  4322. priority: 80
  4323. }) );
  4324. sidebar.set( 'compat', new wp.media.view.AttachmentCompat({
  4325. controller: this.controller,
  4326. model: single,
  4327. priority: 120
  4328. }) );
  4329. if ( this.options.display ) {
  4330. sidebar.set( 'display', new wp.media.view.Settings.AttachmentDisplay({
  4331. controller: this.controller,
  4332. model: this.model.display( single ),
  4333. attachment: single,
  4334. priority: 160,
  4335. userSettings: this.model.get('displayUserSettings')
  4336. }) );
  4337. }
  4338. // Show the sidebar on mobile.
  4339. if ( this.model.id === 'insert' ) {
  4340. sidebar.$el.addClass( 'visible' );
  4341. }
  4342. },
  4343. disposeSingle: function() {
  4344. var sidebar = this.sidebar;
  4345. sidebar.unset('details');
  4346. sidebar.unset('compat');
  4347. sidebar.unset('display');
  4348. // Hide the sidebar on mobile.
  4349. sidebar.$el.removeClass( 'visible' );
  4350. }
  4351. });
  4352. module.exports = AttachmentsBrowser;
  4353. /***/ }),
  4354. /***/ 1223:
  4355. /***/ (function(module) {
  4356. var Attachments = wp.media.view.Attachments,
  4357. Selection;
  4358. /**
  4359. * wp.media.view.Attachments.Selection
  4360. *
  4361. * @memberOf wp.media.view.Attachments
  4362. *
  4363. * @class
  4364. * @augments wp.media.view.Attachments
  4365. * @augments wp.media.View
  4366. * @augments wp.Backbone.View
  4367. * @augments Backbone.View
  4368. */
  4369. Selection = Attachments.extend(/** @lends wp.media.view.Attachments.Selection.prototype */{
  4370. events: {},
  4371. initialize: function() {
  4372. _.defaults( this.options, {
  4373. sortable: false,
  4374. resize: false,
  4375. // The single `Attachment` view to be used in the `Attachments` view.
  4376. AttachmentView: wp.media.view.Attachment.Selection
  4377. });
  4378. // Call 'initialize' directly on the parent class.
  4379. return Attachments.prototype.initialize.apply( this, arguments );
  4380. }
  4381. });
  4382. module.exports = Selection;
  4383. /***/ }),
  4384. /***/ 4094:
  4385. /***/ (function(module) {
  4386. var $ = Backbone.$,
  4387. ButtonGroup;
  4388. /**
  4389. * wp.media.view.ButtonGroup
  4390. *
  4391. * @memberOf wp.media.view
  4392. *
  4393. * @class
  4394. * @augments wp.media.View
  4395. * @augments wp.Backbone.View
  4396. * @augments Backbone.View
  4397. */
  4398. ButtonGroup = wp.media.View.extend(/** @lends wp.media.view.ButtonGroup.prototype */{
  4399. tagName: 'div',
  4400. className: 'button-group button-large media-button-group',
  4401. initialize: function() {
  4402. /**
  4403. * @member {wp.media.view.Button[]}
  4404. */
  4405. this.buttons = _.map( this.options.buttons || [], function( button ) {
  4406. if ( button instanceof Backbone.View ) {
  4407. return button;
  4408. } else {
  4409. return new wp.media.view.Button( button ).render();
  4410. }
  4411. });
  4412. delete this.options.buttons;
  4413. if ( this.options.classes ) {
  4414. this.$el.addClass( this.options.classes );
  4415. }
  4416. },
  4417. /**
  4418. * @return {wp.media.view.ButtonGroup}
  4419. */
  4420. render: function() {
  4421. this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
  4422. return this;
  4423. }
  4424. });
  4425. module.exports = ButtonGroup;
  4426. /***/ }),
  4427. /***/ 3157:
  4428. /***/ (function(module) {
  4429. /**
  4430. * wp.media.view.Button
  4431. *
  4432. * @memberOf wp.media.view
  4433. *
  4434. * @class
  4435. * @augments wp.media.View
  4436. * @augments wp.Backbone.View
  4437. * @augments Backbone.View
  4438. */
  4439. var Button = wp.media.View.extend(/** @lends wp.media.view.Button.prototype */{
  4440. tagName: 'button',
  4441. className: 'media-button',
  4442. attributes: { type: 'button' },
  4443. events: {
  4444. 'click': 'click'
  4445. },
  4446. defaults: {
  4447. text: '',
  4448. style: '',
  4449. size: 'large',
  4450. disabled: false
  4451. },
  4452. initialize: function() {
  4453. /**
  4454. * Create a model with the provided `defaults`.
  4455. *
  4456. * @member {Backbone.Model}
  4457. */
  4458. this.model = new Backbone.Model( this.defaults );
  4459. // If any of the `options` have a key from `defaults`, apply its
  4460. // value to the `model` and remove it from the `options object.
  4461. _.each( this.defaults, function( def, key ) {
  4462. var value = this.options[ key ];
  4463. if ( _.isUndefined( value ) ) {
  4464. return;
  4465. }
  4466. this.model.set( key, value );
  4467. delete this.options[ key ];
  4468. }, this );
  4469. this.listenTo( this.model, 'change', this.render );
  4470. },
  4471. /**
  4472. * @return {wp.media.view.Button} Returns itself to allow chaining.
  4473. */
  4474. render: function() {
  4475. var classes = [ 'button', this.className ],
  4476. model = this.model.toJSON();
  4477. if ( model.style ) {
  4478. classes.push( 'button-' + model.style );
  4479. }
  4480. if ( model.size ) {
  4481. classes.push( 'button-' + model.size );
  4482. }
  4483. classes = _.uniq( classes.concat( this.options.classes ) );
  4484. this.el.className = classes.join(' ');
  4485. this.$el.attr( 'disabled', model.disabled );
  4486. this.$el.text( this.model.get('text') );
  4487. return this;
  4488. },
  4489. /**
  4490. * @param {Object} event
  4491. */
  4492. click: function( event ) {
  4493. if ( '#' === this.attributes.href ) {
  4494. event.preventDefault();
  4495. }
  4496. if ( this.options.click && ! this.model.get('disabled') ) {
  4497. this.options.click.apply( this, arguments );
  4498. }
  4499. }
  4500. });
  4501. module.exports = Button;
  4502. /***/ }),
  4503. /***/ 7137:
  4504. /***/ (function(module) {
  4505. var View = wp.media.View,
  4506. UploaderStatus = wp.media.view.UploaderStatus,
  4507. l10n = wp.media.view.l10n,
  4508. $ = jQuery,
  4509. Cropper;
  4510. /**
  4511. * wp.media.view.Cropper
  4512. *
  4513. * Uses the imgAreaSelect plugin to allow a user to crop an image.
  4514. *
  4515. * Takes imgAreaSelect options from
  4516. * wp.customize.HeaderControl.calculateImageSelectOptions via
  4517. * wp.customize.HeaderControl.openMM.
  4518. *
  4519. * @memberOf wp.media.view
  4520. *
  4521. * @class
  4522. * @augments wp.media.View
  4523. * @augments wp.Backbone.View
  4524. * @augments Backbone.View
  4525. */
  4526. Cropper = View.extend(/** @lends wp.media.view.Cropper.prototype */{
  4527. className: 'crop-content',
  4528. template: wp.template('crop-content'),
  4529. initialize: function() {
  4530. _.bindAll(this, 'onImageLoad');
  4531. },
  4532. ready: function() {
  4533. this.controller.frame.on('content:error:crop', this.onError, this);
  4534. this.$image = this.$el.find('.crop-image');
  4535. this.$image.on('load', this.onImageLoad);
  4536. $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
  4537. },
  4538. remove: function() {
  4539. $(window).off('resize.cropper');
  4540. this.$el.remove();
  4541. this.$el.off();
  4542. View.prototype.remove.apply(this, arguments);
  4543. },
  4544. prepare: function() {
  4545. return {
  4546. title: l10n.cropYourImage,
  4547. url: this.options.attachment.get('url')
  4548. };
  4549. },
  4550. onImageLoad: function() {
  4551. var imgOptions = this.controller.get('imgSelectOptions'),
  4552. imgSelect;
  4553. if (typeof imgOptions === 'function') {
  4554. imgOptions = imgOptions(this.options.attachment, this.controller);
  4555. }
  4556. imgOptions = _.extend(imgOptions, {
  4557. parent: this.$el,
  4558. onInit: function() {
  4559. // Store the set ratio.
  4560. var setRatio = imgSelect.getOptions().aspectRatio;
  4561. // On mousedown, if no ratio is set and the Shift key is down, use a 1:1 ratio.
  4562. this.parent.children().on( 'mousedown touchstart', function( e ) {
  4563. // If no ratio is set and the shift key is down, use a 1:1 ratio.
  4564. if ( ! setRatio && e.shiftKey ) {
  4565. imgSelect.setOptions( {
  4566. aspectRatio: '1:1'
  4567. } );
  4568. }
  4569. } );
  4570. this.parent.children().on( 'mouseup touchend', function() {
  4571. // Restore the set ratio.
  4572. imgSelect.setOptions( {
  4573. aspectRatio: setRatio ? setRatio : false
  4574. } );
  4575. } );
  4576. }
  4577. } );
  4578. this.trigger('image-loaded');
  4579. imgSelect = this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
  4580. },
  4581. onError: function() {
  4582. var filename = this.options.attachment.get('filename');
  4583. this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
  4584. filename: UploaderStatus.prototype.filename(filename),
  4585. message: window._wpMediaViewsL10n.cropError
  4586. }), { at: 0 });
  4587. }
  4588. });
  4589. module.exports = Cropper;
  4590. /***/ }),
  4591. /***/ 5970:
  4592. /***/ (function(module) {
  4593. var View = wp.media.View,
  4594. EditImage;
  4595. /**
  4596. * wp.media.view.EditImage
  4597. *
  4598. * @memberOf wp.media.view
  4599. *
  4600. * @class
  4601. * @augments wp.media.View
  4602. * @augments wp.Backbone.View
  4603. * @augments Backbone.View
  4604. */
  4605. EditImage = View.extend(/** @lends wp.media.view.EditImage.prototype */{
  4606. className: 'image-editor',
  4607. template: wp.template('image-editor'),
  4608. initialize: function( options ) {
  4609. this.editor = window.imageEdit;
  4610. this.controller = options.controller;
  4611. View.prototype.initialize.apply( this, arguments );
  4612. },
  4613. prepare: function() {
  4614. return this.model.toJSON();
  4615. },
  4616. loadEditor: function() {
  4617. this.editor.open( this.model.get( 'id' ), this.model.get( 'nonces' ).edit, this );
  4618. },
  4619. back: function() {
  4620. var lastState = this.controller.lastState();
  4621. this.controller.setState( lastState );
  4622. },
  4623. refresh: function() {
  4624. this.model.fetch();
  4625. },
  4626. save: function() {
  4627. var lastState = this.controller.lastState();
  4628. this.model.fetch().done( _.bind( function() {
  4629. this.controller.setState( lastState );
  4630. }, this ) );
  4631. }
  4632. });
  4633. module.exports = EditImage;
  4634. /***/ }),
  4635. /***/ 5138:
  4636. /***/ (function(module) {
  4637. /**
  4638. * wp.media.view.Embed
  4639. *
  4640. * @memberOf wp.media.view
  4641. *
  4642. * @class
  4643. * @augments wp.media.View
  4644. * @augments wp.Backbone.View
  4645. * @augments Backbone.View
  4646. */
  4647. var Embed = wp.media.View.extend(/** @lends wp.media.view.Ember.prototype */{
  4648. className: 'media-embed',
  4649. initialize: function() {
  4650. /**
  4651. * @member {wp.media.view.EmbedUrl}
  4652. */
  4653. this.url = new wp.media.view.EmbedUrl({
  4654. controller: this.controller,
  4655. model: this.model.props
  4656. }).render();
  4657. this.views.set([ this.url ]);
  4658. this.refresh();
  4659. this.listenTo( this.model, 'change:type', this.refresh );
  4660. this.listenTo( this.model, 'change:loading', this.loading );
  4661. },
  4662. /**
  4663. * @param {Object} view
  4664. */
  4665. settings: function( view ) {
  4666. if ( this._settings ) {
  4667. this._settings.remove();
  4668. }
  4669. this._settings = view;
  4670. this.views.add( view );
  4671. },
  4672. refresh: function() {
  4673. var type = this.model.get('type'),
  4674. constructor;
  4675. if ( 'image' === type ) {
  4676. constructor = wp.media.view.EmbedImage;
  4677. } else if ( 'link' === type ) {
  4678. constructor = wp.media.view.EmbedLink;
  4679. } else {
  4680. return;
  4681. }
  4682. this.settings( new constructor({
  4683. controller: this.controller,
  4684. model: this.model.props,
  4685. priority: 40
  4686. }) );
  4687. },
  4688. loading: function() {
  4689. this.$el.toggleClass( 'embed-loading', this.model.get('loading') );
  4690. }
  4691. });
  4692. module.exports = Embed;
  4693. /***/ }),
  4694. /***/ 1338:
  4695. /***/ (function(module) {
  4696. var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
  4697. EmbedImage;
  4698. /**
  4699. * wp.media.view.EmbedImage
  4700. *
  4701. * @memberOf wp.media.view
  4702. *
  4703. * @class
  4704. * @augments wp.media.view.Settings.AttachmentDisplay
  4705. * @augments wp.media.view.Settings
  4706. * @augments wp.media.View
  4707. * @augments wp.Backbone.View
  4708. * @augments Backbone.View
  4709. */
  4710. EmbedImage = AttachmentDisplay.extend(/** @lends wp.media.view.EmbedImage.prototype */{
  4711. className: 'embed-media-settings',
  4712. template: wp.template('embed-image-settings'),
  4713. initialize: function() {
  4714. /**
  4715. * Call `initialize` directly on parent class with passed arguments
  4716. */
  4717. AttachmentDisplay.prototype.initialize.apply( this, arguments );
  4718. this.listenTo( this.model, 'change:url', this.updateImage );
  4719. },
  4720. updateImage: function() {
  4721. this.$('img').attr( 'src', this.model.get('url') );
  4722. }
  4723. });
  4724. module.exports = EmbedImage;
  4725. /***/ }),
  4726. /***/ 6959:
  4727. /***/ (function(module) {
  4728. var $ = jQuery,
  4729. EmbedLink;
  4730. /**
  4731. * wp.media.view.EmbedLink
  4732. *
  4733. * @memberOf wp.media.view
  4734. *
  4735. * @class
  4736. * @augments wp.media.view.Settings
  4737. * @augments wp.media.View
  4738. * @augments wp.Backbone.View
  4739. * @augments Backbone.View
  4740. */
  4741. EmbedLink = wp.media.view.Settings.extend(/** @lends wp.media.view.EmbedLink.prototype */{
  4742. className: 'embed-link-settings',
  4743. template: wp.template('embed-link-settings'),
  4744. initialize: function() {
  4745. this.listenTo( this.model, 'change:url', this.updateoEmbed );
  4746. },
  4747. updateoEmbed: _.debounce( function() {
  4748. var url = this.model.get( 'url' );
  4749. // Clear out previous results.
  4750. this.$('.embed-container').hide().find('.embed-preview').empty();
  4751. this.$( '.setting' ).hide();
  4752. // Only proceed with embed if the field contains more than 11 characters.
  4753. // Example: http://a.io is 11 chars
  4754. if ( url && ( url.length < 11 || ! url.match(/^http(s)?:\/\//) ) ) {
  4755. return;
  4756. }
  4757. this.fetch();
  4758. }, wp.media.controller.Embed.sensitivity ),
  4759. fetch: function() {
  4760. var url = this.model.get( 'url' ), re, youTubeEmbedMatch;
  4761. // Check if they haven't typed in 500 ms.
  4762. if ( $('#embed-url-field').val() !== url ) {
  4763. return;
  4764. }
  4765. if ( this.dfd && 'pending' === this.dfd.state() ) {
  4766. this.dfd.abort();
  4767. }
  4768. // Support YouTube embed urls, since they work once in the editor.
  4769. re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/;
  4770. youTubeEmbedMatch = re.exec( url );
  4771. if ( youTubeEmbedMatch ) {
  4772. url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ];
  4773. }
  4774. this.dfd = wp.apiRequest({
  4775. url: wp.media.view.settings.oEmbedProxyUrl,
  4776. data: {
  4777. url: url,
  4778. maxwidth: this.model.get( 'width' ),
  4779. maxheight: this.model.get( 'height' )
  4780. },
  4781. type: 'GET',
  4782. dataType: 'json',
  4783. context: this
  4784. })
  4785. .done( function( response ) {
  4786. this.renderoEmbed( {
  4787. data: {
  4788. body: response.html || ''
  4789. }
  4790. } );
  4791. } )
  4792. .fail( this.renderFail );
  4793. },
  4794. renderFail: function ( response, status ) {
  4795. if ( 'abort' === status ) {
  4796. return;
  4797. }
  4798. this.$( '.link-text' ).show();
  4799. },
  4800. renderoEmbed: function( response ) {
  4801. var html = ( response && response.data && response.data.body ) || '';
  4802. if ( html ) {
  4803. this.$('.embed-container').show().find('.embed-preview').html( html );
  4804. } else {
  4805. this.renderFail();
  4806. }
  4807. }
  4808. });
  4809. module.exports = EmbedLink;
  4810. /***/ }),
  4811. /***/ 4848:
  4812. /***/ (function(module) {
  4813. var View = wp.media.View,
  4814. $ = jQuery,
  4815. l10n = wp.media.view.l10n,
  4816. EmbedUrl;
  4817. /**
  4818. * wp.media.view.EmbedUrl
  4819. *
  4820. * @memberOf wp.media.view
  4821. *
  4822. * @class
  4823. * @augments wp.media.View
  4824. * @augments wp.Backbone.View
  4825. * @augments Backbone.View
  4826. */
  4827. EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{
  4828. tagName: 'span',
  4829. className: 'embed-url',
  4830. events: {
  4831. 'input': 'url'
  4832. },
  4833. initialize: function() {
  4834. this.$input = $( '<input id="embed-url-field" type="url" />' )
  4835. .attr( 'aria-label', l10n.insertFromUrlTitle )
  4836. .val( this.model.get('url') );
  4837. this.input = this.$input[0];
  4838. this.spinner = $('<span class="spinner" />')[0];
  4839. this.$el.append([ this.input, this.spinner ]);
  4840. this.listenTo( this.model, 'change:url', this.render );
  4841. if ( this.model.get( 'url' ) ) {
  4842. _.delay( _.bind( function () {
  4843. this.model.trigger( 'change:url' );
  4844. }, this ), 500 );
  4845. }
  4846. },
  4847. /**
  4848. * @return {wp.media.view.EmbedUrl} Returns itself to allow chaining.
  4849. */
  4850. render: function() {
  4851. var $input = this.$input;
  4852. if ( $input.is(':focus') ) {
  4853. return;
  4854. }
  4855. if ( this.model.get( 'url' ) ) {
  4856. this.input.value = this.model.get('url');
  4857. } else {
  4858. this.input.setAttribute( 'placeholder', 'https://' );
  4859. }
  4860. /**
  4861. * Call `render` directly on parent class with passed arguments
  4862. */
  4863. View.prototype.render.apply( this, arguments );
  4864. return this;
  4865. },
  4866. url: function( event ) {
  4867. var url = event.target.value || '';
  4868. this.model.set( 'url', url.trim() );
  4869. }
  4870. });
  4871. module.exports = EmbedUrl;
  4872. /***/ }),
  4873. /***/ 6557:
  4874. /***/ (function(module) {
  4875. var $ = jQuery;
  4876. /**
  4877. * wp.media.view.FocusManager
  4878. *
  4879. * @memberOf wp.media.view
  4880. *
  4881. * @class
  4882. * @augments wp.media.View
  4883. * @augments wp.Backbone.View
  4884. * @augments Backbone.View
  4885. */
  4886. var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{
  4887. events: {
  4888. 'keydown': 'focusManagementMode'
  4889. },
  4890. /**
  4891. * Initializes the Focus Manager.
  4892. *
  4893. * @param {Object} options The Focus Manager options.
  4894. *
  4895. * @since 5.3.0
  4896. *
  4897. * @return {void}
  4898. */
  4899. initialize: function( options ) {
  4900. this.mode = options.mode || 'constrainTabbing';
  4901. this.tabsAutomaticActivation = options.tabsAutomaticActivation || false;
  4902. },
  4903. /**
  4904. * Determines which focus management mode to use.
  4905. *
  4906. * @since 5.3.0
  4907. *
  4908. * @param {Object} event jQuery event object.
  4909. *
  4910. * @return {void}
  4911. */
  4912. focusManagementMode: function( event ) {
  4913. if ( this.mode === 'constrainTabbing' ) {
  4914. this.constrainTabbing( event );
  4915. }
  4916. if ( this.mode === 'tabsNavigation' ) {
  4917. this.tabsNavigation( event );
  4918. }
  4919. },
  4920. /**
  4921. * Gets all the tabbable elements.
  4922. *
  4923. * @since 5.3.0
  4924. *
  4925. * @return {Object} A jQuery collection of tabbable elements.
  4926. */
  4927. getTabbables: function() {
  4928. // Skip the file input added by Plupload.
  4929. return this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
  4930. },
  4931. /**
  4932. * Moves focus to the modal dialog.
  4933. *
  4934. * @since 3.5.0
  4935. *
  4936. * @return {void}
  4937. */
  4938. focus: function() {
  4939. this.$( '.media-modal' ).trigger( 'focus' );
  4940. },
  4941. /**
  4942. * Constrains navigation with the Tab key within the media view element.
  4943. *
  4944. * @since 4.0.0
  4945. *
  4946. * @param {Object} event A keydown jQuery event.
  4947. *
  4948. * @return {void}
  4949. */
  4950. constrainTabbing: function( event ) {
  4951. var tabbables;
  4952. // Look for the tab key.
  4953. if ( 9 !== event.keyCode ) {
  4954. return;
  4955. }
  4956. tabbables = this.getTabbables();
  4957. // Keep tab focus within media modal while it's open.
  4958. if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
  4959. tabbables.first().focus();
  4960. return false;
  4961. } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
  4962. tabbables.last().focus();
  4963. return false;
  4964. }
  4965. },
  4966. /**
  4967. * Hides from assistive technologies all the body children.
  4968. *
  4969. * Sets an `aria-hidden="true"` attribute on all the body children except
  4970. * the provided element and other elements that should not be hidden.
  4971. *
  4972. * The reason why we use `aria-hidden` is that `aria-modal="true"` is buggy
  4973. * in Safari 11.1 and support is spotty in other browsers. Also, `aria-modal="true"`
  4974. * prevents the `wp.a11y.speak()` ARIA live regions to work as they're outside
  4975. * of the modal dialog and get hidden from assistive technologies.
  4976. *
  4977. * @since 5.2.3
  4978. *
  4979. * @param {Object} visibleElement The jQuery object representing the element that should not be hidden.
  4980. *
  4981. * @return {void}
  4982. */
  4983. setAriaHiddenOnBodyChildren: function( visibleElement ) {
  4984. var bodyChildren,
  4985. self = this;
  4986. if ( this.isBodyAriaHidden ) {
  4987. return;
  4988. }
  4989. // Get all the body children.
  4990. bodyChildren = document.body.children;
  4991. // Loop through the body children and hide the ones that should be hidden.
  4992. _.each( bodyChildren, function( element ) {
  4993. // Don't hide the modal element.
  4994. if ( element === visibleElement[0] ) {
  4995. return;
  4996. }
  4997. // Determine the body children to hide.
  4998. if ( self.elementShouldBeHidden( element ) ) {
  4999. element.setAttribute( 'aria-hidden', 'true' );
  5000. // Store the hidden elements.
  5001. self.ariaHiddenElements.push( element );
  5002. }
  5003. } );
  5004. this.isBodyAriaHidden = true;
  5005. },
  5006. /**
  5007. * Unhides from assistive technologies all the body children.
  5008. *
  5009. * Makes visible again to assistive technologies all the body children
  5010. * previously hidden and stored in this.ariaHiddenElements.
  5011. *
  5012. * @since 5.2.3
  5013. *
  5014. * @return {void}
  5015. */
  5016. removeAriaHiddenFromBodyChildren: function() {
  5017. _.each( this.ariaHiddenElements, function( element ) {
  5018. element.removeAttribute( 'aria-hidden' );
  5019. } );
  5020. this.ariaHiddenElements = [];
  5021. this.isBodyAriaHidden = false;
  5022. },
  5023. /**
  5024. * Determines if the passed element should not be hidden from assistive technologies.
  5025. *
  5026. * @since 5.2.3
  5027. *
  5028. * @param {Object} element The DOM element that should be checked.
  5029. *
  5030. * @return {boolean} Whether the element should not be hidden from assistive technologies.
  5031. */
  5032. elementShouldBeHidden: function( element ) {
  5033. var role = element.getAttribute( 'role' ),
  5034. liveRegionsRoles = [ 'alert', 'status', 'log', 'marquee', 'timer' ];
  5035. /*
  5036. * Don't hide scripts, elements that already have `aria-hidden`, and
  5037. * ARIA live regions.
  5038. */
  5039. return ! (
  5040. element.tagName === 'SCRIPT' ||
  5041. element.hasAttribute( 'aria-hidden' ) ||
  5042. element.hasAttribute( 'aria-live' ) ||
  5043. liveRegionsRoles.indexOf( role ) !== -1
  5044. );
  5045. },
  5046. /**
  5047. * Whether the body children are hidden from assistive technologies.
  5048. *
  5049. * @since 5.2.3
  5050. */
  5051. isBodyAriaHidden: false,
  5052. /**
  5053. * Stores an array of DOM elements that should be hidden from assistive
  5054. * technologies, for example when the media modal dialog opens.
  5055. *
  5056. * @since 5.2.3
  5057. */
  5058. ariaHiddenElements: [],
  5059. /**
  5060. * Holds the jQuery collection of ARIA tabs.
  5061. *
  5062. * @since 5.3.0
  5063. */
  5064. tabs: $(),
  5065. /**
  5066. * Sets up tabs in an ARIA tabbed interface.
  5067. *
  5068. * @since 5.3.0
  5069. *
  5070. * @param {Object} event jQuery event object.
  5071. *
  5072. * @return {void}
  5073. */
  5074. setupAriaTabs: function() {
  5075. this.tabs = this.$( '[role="tab"]' );
  5076. // Set up initial attributes.
  5077. this.tabs.attr( {
  5078. 'aria-selected': 'false',
  5079. tabIndex: '-1'
  5080. } );
  5081. // Set up attributes on the initially active tab.
  5082. this.tabs.filter( '.active' )
  5083. .removeAttr( 'tabindex' )
  5084. .attr( 'aria-selected', 'true' );
  5085. },
  5086. /**
  5087. * Enables arrows navigation within the ARIA tabbed interface.
  5088. *
  5089. * @since 5.3.0
  5090. *
  5091. * @param {Object} event jQuery event object.
  5092. *
  5093. * @return {void}
  5094. */
  5095. tabsNavigation: function( event ) {
  5096. var orientation = 'horizontal',
  5097. keys = [ 32, 35, 36, 37, 38, 39, 40 ];
  5098. // Return if not Spacebar, End, Home, or Arrow keys.
  5099. if ( keys.indexOf( event.which ) === -1 ) {
  5100. return;
  5101. }
  5102. // Determine navigation direction.
  5103. if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) {
  5104. orientation = 'vertical';
  5105. }
  5106. // Make Up and Down arrow keys do nothing with horizontal tabs.
  5107. if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) {
  5108. return;
  5109. }
  5110. // Make Left and Right arrow keys do nothing with vertical tabs.
  5111. if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) {
  5112. return;
  5113. }
  5114. this.switchTabs( event, this.tabs );
  5115. },
  5116. /**
  5117. * Switches tabs in the ARIA tabbed interface.
  5118. *
  5119. * @since 5.3.0
  5120. *
  5121. * @param {Object} event jQuery event object.
  5122. *
  5123. * @return {void}
  5124. */
  5125. switchTabs: function( event ) {
  5126. var key = event.which,
  5127. index = this.tabs.index( $( event.target ) ),
  5128. newIndex;
  5129. switch ( key ) {
  5130. // Space bar: Activate current targeted tab.
  5131. case 32: {
  5132. this.activateTab( this.tabs[ index ] );
  5133. break;
  5134. }
  5135. // End key: Activate last tab.
  5136. case 35: {
  5137. event.preventDefault();
  5138. this.activateTab( this.tabs[ this.tabs.length - 1 ] );
  5139. break;
  5140. }
  5141. // Home key: Activate first tab.
  5142. case 36: {
  5143. event.preventDefault();
  5144. this.activateTab( this.tabs[ 0 ] );
  5145. break;
  5146. }
  5147. // Left and up keys: Activate previous tab.
  5148. case 37:
  5149. case 38: {
  5150. event.preventDefault();
  5151. newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1;
  5152. this.activateTab( this.tabs[ newIndex ] );
  5153. break;
  5154. }
  5155. // Right and down keys: Activate next tab.
  5156. case 39:
  5157. case 40: {
  5158. event.preventDefault();
  5159. newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1;
  5160. this.activateTab( this.tabs[ newIndex ] );
  5161. break;
  5162. }
  5163. }
  5164. },
  5165. /**
  5166. * Sets a single tab to be focusable and semantically selected.
  5167. *
  5168. * @since 5.3.0
  5169. *
  5170. * @param {Object} tab The tab DOM element.
  5171. *
  5172. * @return {void}
  5173. */
  5174. activateTab: function( tab ) {
  5175. if ( ! tab ) {
  5176. return;
  5177. }
  5178. // The tab is a DOM element: no need for jQuery methods.
  5179. tab.focus();
  5180. // Handle automatic activation.
  5181. if ( this.tabsAutomaticActivation ) {
  5182. tab.removeAttribute( 'tabindex' );
  5183. tab.setAttribute( 'aria-selected', 'true' );
  5184. tab.click();
  5185. return;
  5186. }
  5187. // Handle manual activation.
  5188. $( tab ).on( 'click', function() {
  5189. tab.removeAttribute( 'tabindex' );
  5190. tab.setAttribute( 'aria-selected', 'true' );
  5191. } );
  5192. }
  5193. });
  5194. module.exports = FocusManager;
  5195. /***/ }),
  5196. /***/ 3647:
  5197. /***/ (function(module) {
  5198. /**
  5199. * wp.media.view.Frame
  5200. *
  5201. * A frame is a composite view consisting of one or more regions and one or more
  5202. * states.
  5203. *
  5204. * @memberOf wp.media.view
  5205. *
  5206. * @see wp.media.controller.State
  5207. * @see wp.media.controller.Region
  5208. *
  5209. * @class
  5210. * @augments wp.media.View
  5211. * @augments wp.Backbone.View
  5212. * @augments Backbone.View
  5213. * @mixes wp.media.controller.StateMachine
  5214. */
  5215. var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{
  5216. initialize: function() {
  5217. _.defaults( this.options, {
  5218. mode: [ 'select' ]
  5219. });
  5220. this._createRegions();
  5221. this._createStates();
  5222. this._createModes();
  5223. },
  5224. _createRegions: function() {
  5225. // Clone the regions array.
  5226. this.regions = this.regions ? this.regions.slice() : [];
  5227. // Initialize regions.
  5228. _.each( this.regions, function( region ) {
  5229. this[ region ] = new wp.media.controller.Region({
  5230. view: this,
  5231. id: region,
  5232. selector: '.media-frame-' + region
  5233. });
  5234. }, this );
  5235. },
  5236. /**
  5237. * Create the frame's states.
  5238. *
  5239. * @see wp.media.controller.State
  5240. * @see wp.media.controller.StateMachine
  5241. *
  5242. * @fires wp.media.controller.State#ready
  5243. */
  5244. _createStates: function() {
  5245. // Create the default `states` collection.
  5246. this.states = new Backbone.Collection( null, {
  5247. model: wp.media.controller.State
  5248. });
  5249. // Ensure states have a reference to the frame.
  5250. this.states.on( 'add', function( model ) {
  5251. model.frame = this;
  5252. model.trigger('ready');
  5253. }, this );
  5254. if ( this.options.states ) {
  5255. this.states.add( this.options.states );
  5256. }
  5257. },
  5258. /**
  5259. * A frame can be in a mode or multiple modes at one time.
  5260. *
  5261. * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
  5262. */
  5263. _createModes: function() {
  5264. // Store active "modes" that the frame is in. Unrelated to region modes.
  5265. this.activeModes = new Backbone.Collection();
  5266. this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
  5267. _.each( this.options.mode, function( mode ) {
  5268. this.activateMode( mode );
  5269. }, this );
  5270. },
  5271. /**
  5272. * Reset all states on the frame to their defaults.
  5273. *
  5274. * @return {wp.media.view.Frame} Returns itself to allow chaining.
  5275. */
  5276. reset: function() {
  5277. this.states.invoke( 'trigger', 'reset' );
  5278. return this;
  5279. },
  5280. /**
  5281. * Map activeMode collection events to the frame.
  5282. */
  5283. triggerModeEvents: function( model, collection, options ) {
  5284. var collectionEvent,
  5285. modeEventMap = {
  5286. add: 'activate',
  5287. remove: 'deactivate'
  5288. },
  5289. eventToTrigger;
  5290. // Probably a better way to do this.
  5291. _.each( options, function( value, key ) {
  5292. if ( value ) {
  5293. collectionEvent = key;
  5294. }
  5295. } );
  5296. if ( ! _.has( modeEventMap, collectionEvent ) ) {
  5297. return;
  5298. }
  5299. eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
  5300. this.trigger( eventToTrigger );
  5301. },
  5302. /**
  5303. * Activate a mode on the frame.
  5304. *
  5305. * @param string mode Mode ID.
  5306. * @return {this} Returns itself to allow chaining.
  5307. */
  5308. activateMode: function( mode ) {
  5309. // Bail if the mode is already active.
  5310. if ( this.isModeActive( mode ) ) {
  5311. return;
  5312. }
  5313. this.activeModes.add( [ { id: mode } ] );
  5314. // Add a CSS class to the frame so elements can be styled for the mode.
  5315. this.$el.addClass( 'mode-' + mode );
  5316. return this;
  5317. },
  5318. /**
  5319. * Deactivate a mode on the frame.
  5320. *
  5321. * @param string mode Mode ID.
  5322. * @return {this} Returns itself to allow chaining.
  5323. */
  5324. deactivateMode: function( mode ) {
  5325. // Bail if the mode isn't active.
  5326. if ( ! this.isModeActive( mode ) ) {
  5327. return this;
  5328. }
  5329. this.activeModes.remove( this.activeModes.where( { id: mode } ) );
  5330. this.$el.removeClass( 'mode-' + mode );
  5331. /**
  5332. * Frame mode deactivation event.
  5333. *
  5334. * @event wp.media.view.Frame#{mode}:deactivate
  5335. */
  5336. this.trigger( mode + ':deactivate' );
  5337. return this;
  5338. },
  5339. /**
  5340. * Check if a mode is enabled on the frame.
  5341. *
  5342. * @param string mode Mode ID.
  5343. * @return bool
  5344. */
  5345. isModeActive: function( mode ) {
  5346. return Boolean( this.activeModes.where( { id: mode } ).length );
  5347. }
  5348. });
  5349. // Make the `Frame` a `StateMachine`.
  5350. _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype );
  5351. module.exports = Frame;
  5352. /***/ }),
  5353. /***/ 9142:
  5354. /***/ (function(module) {
  5355. var Select = wp.media.view.MediaFrame.Select,
  5356. l10n = wp.media.view.l10n,
  5357. ImageDetails;
  5358. /**
  5359. * wp.media.view.MediaFrame.ImageDetails
  5360. *
  5361. * A media frame for manipulating an image that's already been inserted
  5362. * into a post.
  5363. *
  5364. * @memberOf wp.media.view.MediaFrame
  5365. *
  5366. * @class
  5367. * @augments wp.media.view.MediaFrame.Select
  5368. * @augments wp.media.view.MediaFrame
  5369. * @augments wp.media.view.Frame
  5370. * @augments wp.media.View
  5371. * @augments wp.Backbone.View
  5372. * @augments Backbone.View
  5373. * @mixes wp.media.controller.StateMachine
  5374. */
  5375. ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{
  5376. defaults: {
  5377. id: 'image',
  5378. url: '',
  5379. menu: 'image-details',
  5380. content: 'image-details',
  5381. toolbar: 'image-details',
  5382. type: 'link',
  5383. title: l10n.imageDetailsTitle,
  5384. priority: 120
  5385. },
  5386. initialize: function( options ) {
  5387. this.image = new wp.media.model.PostImage( options.metadata );
  5388. this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } );
  5389. Select.prototype.initialize.apply( this, arguments );
  5390. },
  5391. bindHandlers: function() {
  5392. Select.prototype.bindHandlers.apply( this, arguments );
  5393. this.on( 'menu:create:image-details', this.createMenu, this );
  5394. this.on( 'content:create:image-details', this.imageDetailsContent, this );
  5395. this.on( 'content:render:edit-image', this.editImageContent, this );
  5396. this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
  5397. // Override the select toolbar.
  5398. this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
  5399. },
  5400. createStates: function() {
  5401. this.states.add([
  5402. new wp.media.controller.ImageDetails({
  5403. image: this.image,
  5404. editable: false
  5405. }),
  5406. new wp.media.controller.ReplaceImage({
  5407. id: 'replace-image',
  5408. library: wp.media.query( { type: 'image' } ),
  5409. image: this.image,
  5410. multiple: false,
  5411. title: l10n.imageReplaceTitle,
  5412. toolbar: 'replace',
  5413. priority: 80,
  5414. displaySettings: true
  5415. }),
  5416. new wp.media.controller.EditImage( {
  5417. image: this.image,
  5418. selection: this.options.selection
  5419. } )
  5420. ]);
  5421. },
  5422. imageDetailsContent: function( options ) {
  5423. options.view = new wp.media.view.ImageDetails({
  5424. controller: this,
  5425. model: this.state().image,
  5426. attachment: this.state().image.attachment
  5427. });
  5428. },
  5429. editImageContent: function() {
  5430. var state = this.state(),
  5431. model = state.get('image'),
  5432. view;
  5433. if ( ! model ) {
  5434. return;
  5435. }
  5436. view = new wp.media.view.EditImage( { model: model, controller: this } ).render();
  5437. this.content.set( view );
  5438. // After bringing in the frame, load the actual editor via an Ajax call.
  5439. view.loadEditor();
  5440. },
  5441. renderImageDetailsToolbar: function() {
  5442. this.toolbar.set( new wp.media.view.Toolbar({
  5443. controller: this,
  5444. items: {
  5445. select: {
  5446. style: 'primary',
  5447. text: l10n.update,
  5448. priority: 80,
  5449. click: function() {
  5450. var controller = this.controller,
  5451. state = controller.state();
  5452. controller.close();
  5453. // Not sure if we want to use wp.media.string.image which will create a shortcode or
  5454. // perhaps wp.html.string to at least to build the <img />.
  5455. state.trigger( 'update', controller.image.toJSON() );
  5456. // Restore and reset the default state.
  5457. controller.setState( controller.options.state );
  5458. controller.reset();
  5459. }
  5460. }
  5461. }
  5462. }) );
  5463. },
  5464. renderReplaceImageToolbar: function() {
  5465. var frame = this,
  5466. lastState = frame.lastState(),
  5467. previous = lastState && lastState.id;
  5468. this.toolbar.set( new wp.media.view.Toolbar({
  5469. controller: this,
  5470. items: {
  5471. back: {
  5472. text: l10n.back,
  5473. priority: 80,
  5474. click: function() {
  5475. if ( previous ) {
  5476. frame.setState( previous );
  5477. } else {
  5478. frame.close();
  5479. }
  5480. }
  5481. },
  5482. replace: {
  5483. style: 'primary',
  5484. text: l10n.replace,
  5485. priority: 20,
  5486. requires: { selection: true },
  5487. click: function() {
  5488. var controller = this.controller,
  5489. state = controller.state(),
  5490. selection = state.get( 'selection' ),
  5491. attachment = selection.single();
  5492. controller.close();
  5493. controller.image.changeAttachment( attachment, state.display( attachment ) );
  5494. // Not sure if we want to use wp.media.string.image which will create a shortcode or
  5495. // perhaps wp.html.string to at least to build the <img />.
  5496. state.trigger( 'replace', controller.image.toJSON() );
  5497. // Restore and reset the default state.
  5498. controller.setState( controller.options.state );
  5499. controller.reset();
  5500. }
  5501. }
  5502. }
  5503. }) );
  5504. }
  5505. });
  5506. module.exports = ImageDetails;
  5507. /***/ }),
  5508. /***/ 9075:
  5509. /***/ (function(module) {
  5510. var Select = wp.media.view.MediaFrame.Select,
  5511. Library = wp.media.controller.Library,
  5512. l10n = wp.media.view.l10n,
  5513. Post;
  5514. /**
  5515. * wp.media.view.MediaFrame.Post
  5516. *
  5517. * The frame for manipulating media on the Edit Post page.
  5518. *
  5519. * @memberOf wp.media.view.MediaFrame
  5520. *
  5521. * @class
  5522. * @augments wp.media.view.MediaFrame.Select
  5523. * @augments wp.media.view.MediaFrame
  5524. * @augments wp.media.view.Frame
  5525. * @augments wp.media.View
  5526. * @augments wp.Backbone.View
  5527. * @augments Backbone.View
  5528. * @mixes wp.media.controller.StateMachine
  5529. */
  5530. Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{
  5531. initialize: function() {
  5532. this.counts = {
  5533. audio: {
  5534. count: wp.media.view.settings.attachmentCounts.audio,
  5535. state: 'playlist'
  5536. },
  5537. video: {
  5538. count: wp.media.view.settings.attachmentCounts.video,
  5539. state: 'video-playlist'
  5540. }
  5541. };
  5542. _.defaults( this.options, {
  5543. multiple: true,
  5544. editing: false,
  5545. state: 'insert',
  5546. metadata: {}
  5547. });
  5548. // Call 'initialize' directly on the parent class.
  5549. Select.prototype.initialize.apply( this, arguments );
  5550. this.createIframeStates();
  5551. },
  5552. /**
  5553. * Create the default states.
  5554. */
  5555. createStates: function() {
  5556. var options = this.options;
  5557. this.states.add([
  5558. // Main states.
  5559. new Library({
  5560. id: 'insert',
  5561. title: l10n.insertMediaTitle,
  5562. priority: 20,
  5563. toolbar: 'main-insert',
  5564. filterable: 'all',
  5565. library: wp.media.query( options.library ),
  5566. multiple: options.multiple ? 'reset' : false,
  5567. editable: true,
  5568. // If the user isn't allowed to edit fields,
  5569. // can they still edit it locally?
  5570. allowLocalEdits: true,
  5571. // Show the attachment display settings.
  5572. displaySettings: true,
  5573. // Update user settings when users adjust the
  5574. // attachment display settings.
  5575. displayUserSettings: true
  5576. }),
  5577. new Library({
  5578. id: 'gallery',
  5579. title: l10n.createGalleryTitle,
  5580. priority: 40,
  5581. toolbar: 'main-gallery',
  5582. filterable: 'uploaded',
  5583. multiple: 'add',
  5584. editable: false,
  5585. library: wp.media.query( _.defaults({
  5586. type: 'image'
  5587. }, options.library ) )
  5588. }),
  5589. // Embed states.
  5590. new wp.media.controller.Embed( { metadata: options.metadata } ),
  5591. new wp.media.controller.EditImage( { model: options.editImage } ),
  5592. // Gallery states.
  5593. new wp.media.controller.GalleryEdit({
  5594. library: options.selection,
  5595. editing: options.editing,
  5596. menu: 'gallery'
  5597. }),
  5598. new wp.media.controller.GalleryAdd(),
  5599. new Library({
  5600. id: 'playlist',
  5601. title: l10n.createPlaylistTitle,
  5602. priority: 60,
  5603. toolbar: 'main-playlist',
  5604. filterable: 'uploaded',
  5605. multiple: 'add',
  5606. editable: false,
  5607. library: wp.media.query( _.defaults({
  5608. type: 'audio'
  5609. }, options.library ) )
  5610. }),
  5611. // Playlist states.
  5612. new wp.media.controller.CollectionEdit({
  5613. type: 'audio',
  5614. collectionType: 'playlist',
  5615. title: l10n.editPlaylistTitle,
  5616. SettingsView: wp.media.view.Settings.Playlist,
  5617. library: options.selection,
  5618. editing: options.editing,
  5619. menu: 'playlist',
  5620. dragInfoText: l10n.playlistDragInfo,
  5621. dragInfo: false
  5622. }),
  5623. new wp.media.controller.CollectionAdd({
  5624. type: 'audio',
  5625. collectionType: 'playlist',
  5626. title: l10n.addToPlaylistTitle
  5627. }),
  5628. new Library({
  5629. id: 'video-playlist',
  5630. title: l10n.createVideoPlaylistTitle,
  5631. priority: 60,
  5632. toolbar: 'main-video-playlist',
  5633. filterable: 'uploaded',
  5634. multiple: 'add',
  5635. editable: false,
  5636. library: wp.media.query( _.defaults({
  5637. type: 'video'
  5638. }, options.library ) )
  5639. }),
  5640. new wp.media.controller.CollectionEdit({
  5641. type: 'video',
  5642. collectionType: 'playlist',
  5643. title: l10n.editVideoPlaylistTitle,
  5644. SettingsView: wp.media.view.Settings.Playlist,
  5645. library: options.selection,
  5646. editing: options.editing,
  5647. menu: 'video-playlist',
  5648. dragInfoText: l10n.videoPlaylistDragInfo,
  5649. dragInfo: false
  5650. }),
  5651. new wp.media.controller.CollectionAdd({
  5652. type: 'video',
  5653. collectionType: 'playlist',
  5654. title: l10n.addToVideoPlaylistTitle
  5655. })
  5656. ]);
  5657. if ( wp.media.view.settings.post.featuredImageId ) {
  5658. this.states.add( new wp.media.controller.FeaturedImage() );
  5659. }
  5660. },
  5661. bindHandlers: function() {
  5662. var handlers, checkCounts;
  5663. Select.prototype.bindHandlers.apply( this, arguments );
  5664. this.on( 'activate', this.activate, this );
  5665. // Only bother checking media type counts if one of the counts is zero.
  5666. checkCounts = _.find( this.counts, function( type ) {
  5667. return type.count === 0;
  5668. } );
  5669. if ( typeof checkCounts !== 'undefined' ) {
  5670. this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts );
  5671. }
  5672. this.on( 'menu:create:gallery', this.createMenu, this );
  5673. this.on( 'menu:create:playlist', this.createMenu, this );
  5674. this.on( 'menu:create:video-playlist', this.createMenu, this );
  5675. this.on( 'toolbar:create:main-insert', this.createToolbar, this );
  5676. this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
  5677. this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
  5678. this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
  5679. this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
  5680. this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
  5681. handlers = {
  5682. menu: {
  5683. 'default': 'mainMenu',
  5684. 'gallery': 'galleryMenu',
  5685. 'playlist': 'playlistMenu',
  5686. 'video-playlist': 'videoPlaylistMenu'
  5687. },
  5688. content: {
  5689. 'embed': 'embedContent',
  5690. 'edit-image': 'editImageContent',
  5691. 'edit-selection': 'editSelectionContent'
  5692. },
  5693. toolbar: {
  5694. 'main-insert': 'mainInsertToolbar',
  5695. 'main-gallery': 'mainGalleryToolbar',
  5696. 'gallery-edit': 'galleryEditToolbar',
  5697. 'gallery-add': 'galleryAddToolbar',
  5698. 'main-playlist': 'mainPlaylistToolbar',
  5699. 'playlist-edit': 'playlistEditToolbar',
  5700. 'playlist-add': 'playlistAddToolbar',
  5701. 'main-video-playlist': 'mainVideoPlaylistToolbar',
  5702. 'video-playlist-edit': 'videoPlaylistEditToolbar',
  5703. 'video-playlist-add': 'videoPlaylistAddToolbar'
  5704. }
  5705. };
  5706. _.each( handlers, function( regionHandlers, region ) {
  5707. _.each( regionHandlers, function( callback, handler ) {
  5708. this.on( region + ':render:' + handler, this[ callback ], this );
  5709. }, this );
  5710. }, this );
  5711. },
  5712. activate: function() {
  5713. // Hide menu items for states tied to particular media types if there are no items.
  5714. _.each( this.counts, function( type ) {
  5715. if ( type.count < 1 ) {
  5716. this.menuItemVisibility( type.state, 'hide' );
  5717. }
  5718. }, this );
  5719. },
  5720. mediaTypeCounts: function( model, attr ) {
  5721. if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) {
  5722. this.counts[ attr ].count++;
  5723. this.menuItemVisibility( this.counts[ attr ].state, 'show' );
  5724. }
  5725. },
  5726. // Menus.
  5727. /**
  5728. * @param {wp.Backbone.View} view
  5729. */
  5730. mainMenu: function( view ) {
  5731. view.set({
  5732. 'library-separator': new wp.media.View({
  5733. className: 'separator',
  5734. priority: 100,
  5735. attributes: {
  5736. role: 'presentation'
  5737. }
  5738. })
  5739. });
  5740. },
  5741. menuItemVisibility: function( state, visibility ) {
  5742. var menu = this.menu.get();
  5743. if ( visibility === 'hide' ) {
  5744. menu.hide( state );
  5745. } else if ( visibility === 'show' ) {
  5746. menu.show( state );
  5747. }
  5748. },
  5749. /**
  5750. * @param {wp.Backbone.View} view
  5751. */
  5752. galleryMenu: function( view ) {
  5753. var lastState = this.lastState(),
  5754. previous = lastState && lastState.id,
  5755. frame = this;
  5756. view.set({
  5757. cancel: {
  5758. text: l10n.cancelGalleryTitle,
  5759. priority: 20,
  5760. click: function() {
  5761. if ( previous ) {
  5762. frame.setState( previous );
  5763. } else {
  5764. frame.close();
  5765. }
  5766. // Move focus to the modal after canceling a Gallery.
  5767. this.controller.modal.focusManager.focus();
  5768. }
  5769. },
  5770. separateCancel: new wp.media.View({
  5771. className: 'separator',
  5772. priority: 40
  5773. })
  5774. });
  5775. },
  5776. playlistMenu: function( view ) {
  5777. var lastState = this.lastState(),
  5778. previous = lastState && lastState.id,
  5779. frame = this;
  5780. view.set({
  5781. cancel: {
  5782. text: l10n.cancelPlaylistTitle,
  5783. priority: 20,
  5784. click: function() {
  5785. if ( previous ) {
  5786. frame.setState( previous );
  5787. } else {
  5788. frame.close();
  5789. }
  5790. // Move focus to the modal after canceling an Audio Playlist.
  5791. this.controller.modal.focusManager.focus();
  5792. }
  5793. },
  5794. separateCancel: new wp.media.View({
  5795. className: 'separator',
  5796. priority: 40
  5797. })
  5798. });
  5799. },
  5800. videoPlaylistMenu: function( view ) {
  5801. var lastState = this.lastState(),
  5802. previous = lastState && lastState.id,
  5803. frame = this;
  5804. view.set({
  5805. cancel: {
  5806. text: l10n.cancelVideoPlaylistTitle,
  5807. priority: 20,
  5808. click: function() {
  5809. if ( previous ) {
  5810. frame.setState( previous );
  5811. } else {
  5812. frame.close();
  5813. }
  5814. // Move focus to the modal after canceling a Video Playlist.
  5815. this.controller.modal.focusManager.focus();
  5816. }
  5817. },
  5818. separateCancel: new wp.media.View({
  5819. className: 'separator',
  5820. priority: 40
  5821. })
  5822. });
  5823. },
  5824. // Content.
  5825. embedContent: function() {
  5826. var view = new wp.media.view.Embed({
  5827. controller: this,
  5828. model: this.state()
  5829. }).render();
  5830. this.content.set( view );
  5831. },
  5832. editSelectionContent: function() {
  5833. var state = this.state(),
  5834. selection = state.get('selection'),
  5835. view;
  5836. view = new wp.media.view.AttachmentsBrowser({
  5837. controller: this,
  5838. collection: selection,
  5839. selection: selection,
  5840. model: state,
  5841. sortable: true,
  5842. search: false,
  5843. date: false,
  5844. dragInfo: true,
  5845. AttachmentView: wp.media.view.Attachments.EditSelection
  5846. }).render();
  5847. view.toolbar.set( 'backToLibrary', {
  5848. text: l10n.returnToLibrary,
  5849. priority: -100,
  5850. click: function() {
  5851. this.controller.content.mode('browse');
  5852. // Move focus to the modal when jumping back from Edit Selection to Add Media view.
  5853. this.controller.modal.focusManager.focus();
  5854. }
  5855. });
  5856. // Browse our library of attachments.
  5857. this.content.set( view );
  5858. // Trigger the controller to set focus.
  5859. this.trigger( 'edit:selection', this );
  5860. },
  5861. editImageContent: function() {
  5862. var image = this.state().get('image'),
  5863. view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
  5864. this.content.set( view );
  5865. // After creating the wrapper view, load the actual editor via an Ajax call.
  5866. view.loadEditor();
  5867. },
  5868. // Toolbars.
  5869. /**
  5870. * @param {wp.Backbone.View} view
  5871. */
  5872. selectionStatusToolbar: function( view ) {
  5873. var editable = this.state().get('editable');
  5874. view.set( 'selection', new wp.media.view.Selection({
  5875. controller: this,
  5876. collection: this.state().get('selection'),
  5877. priority: -40,
  5878. // If the selection is editable, pass the callback to
  5879. // switch the content mode.
  5880. editable: editable && function() {
  5881. this.controller.content.mode('edit-selection');
  5882. }
  5883. }).render() );
  5884. },
  5885. /**
  5886. * @param {wp.Backbone.View} view
  5887. */
  5888. mainInsertToolbar: function( view ) {
  5889. var controller = this;
  5890. this.selectionStatusToolbar( view );
  5891. view.set( 'insert', {
  5892. style: 'primary',
  5893. priority: 80,
  5894. text: l10n.insertIntoPost,
  5895. requires: { selection: true },
  5896. /**
  5897. * @ignore
  5898. *
  5899. * @fires wp.media.controller.State#insert
  5900. */
  5901. click: function() {
  5902. var state = controller.state(),
  5903. selection = state.get('selection');
  5904. controller.close();
  5905. state.trigger( 'insert', selection ).reset();
  5906. }
  5907. });
  5908. },
  5909. /**
  5910. * @param {wp.Backbone.View} view
  5911. */
  5912. mainGalleryToolbar: function( view ) {
  5913. var controller = this;
  5914. this.selectionStatusToolbar( view );
  5915. view.set( 'gallery', {
  5916. style: 'primary',
  5917. text: l10n.createNewGallery,
  5918. priority: 60,
  5919. requires: { selection: true },
  5920. click: function() {
  5921. var selection = controller.state().get('selection'),
  5922. edit = controller.state('gallery-edit'),
  5923. models = selection.where({ type: 'image' });
  5924. edit.set( 'library', new wp.media.model.Selection( models, {
  5925. props: selection.props.toJSON(),
  5926. multiple: true
  5927. }) );
  5928. // Jump to Edit Gallery view.
  5929. this.controller.setState( 'gallery-edit' );
  5930. // Move focus to the modal after jumping to Edit Gallery view.
  5931. this.controller.modal.focusManager.focus();
  5932. }
  5933. });
  5934. },
  5935. mainPlaylistToolbar: function( view ) {
  5936. var controller = this;
  5937. this.selectionStatusToolbar( view );
  5938. view.set( 'playlist', {
  5939. style: 'primary',
  5940. text: l10n.createNewPlaylist,
  5941. priority: 100,
  5942. requires: { selection: true },
  5943. click: function() {
  5944. var selection = controller.state().get('selection'),
  5945. edit = controller.state('playlist-edit'),
  5946. models = selection.where({ type: 'audio' });
  5947. edit.set( 'library', new wp.media.model.Selection( models, {
  5948. props: selection.props.toJSON(),
  5949. multiple: true
  5950. }) );
  5951. // Jump to Edit Audio Playlist view.
  5952. this.controller.setState( 'playlist-edit' );
  5953. // Move focus to the modal after jumping to Edit Audio Playlist view.
  5954. this.controller.modal.focusManager.focus();
  5955. }
  5956. });
  5957. },
  5958. mainVideoPlaylistToolbar: function( view ) {
  5959. var controller = this;
  5960. this.selectionStatusToolbar( view );
  5961. view.set( 'video-playlist', {
  5962. style: 'primary',
  5963. text: l10n.createNewVideoPlaylist,
  5964. priority: 100,
  5965. requires: { selection: true },
  5966. click: function() {
  5967. var selection = controller.state().get('selection'),
  5968. edit = controller.state('video-playlist-edit'),
  5969. models = selection.where({ type: 'video' });
  5970. edit.set( 'library', new wp.media.model.Selection( models, {
  5971. props: selection.props.toJSON(),
  5972. multiple: true
  5973. }) );
  5974. // Jump to Edit Video Playlist view.
  5975. this.controller.setState( 'video-playlist-edit' );
  5976. // Move focus to the modal after jumping to Edit Video Playlist view.
  5977. this.controller.modal.focusManager.focus();
  5978. }
  5979. });
  5980. },
  5981. featuredImageToolbar: function( toolbar ) {
  5982. this.createSelectToolbar( toolbar, {
  5983. text: l10n.setFeaturedImage,
  5984. state: this.options.state
  5985. });
  5986. },
  5987. mainEmbedToolbar: function( toolbar ) {
  5988. toolbar.view = new wp.media.view.Toolbar.Embed({
  5989. controller: this
  5990. });
  5991. },
  5992. galleryEditToolbar: function() {
  5993. var editing = this.state().get('editing');
  5994. this.toolbar.set( new wp.media.view.Toolbar({
  5995. controller: this,
  5996. items: {
  5997. insert: {
  5998. style: 'primary',
  5999. text: editing ? l10n.updateGallery : l10n.insertGallery,
  6000. priority: 80,
  6001. requires: { library: true },
  6002. /**
  6003. * @fires wp.media.controller.State#update
  6004. */
  6005. click: function() {
  6006. var controller = this.controller,
  6007. state = controller.state();
  6008. controller.close();
  6009. state.trigger( 'update', state.get('library') );
  6010. // Restore and reset the default state.
  6011. controller.setState( controller.options.state );
  6012. controller.reset();
  6013. }
  6014. }
  6015. }
  6016. }) );
  6017. },
  6018. galleryAddToolbar: function() {
  6019. this.toolbar.set( new wp.media.view.Toolbar({
  6020. controller: this,
  6021. items: {
  6022. insert: {
  6023. style: 'primary',
  6024. text: l10n.addToGallery,
  6025. priority: 80,
  6026. requires: { selection: true },
  6027. /**
  6028. * @fires wp.media.controller.State#reset
  6029. */
  6030. click: function() {
  6031. var controller = this.controller,
  6032. state = controller.state(),
  6033. edit = controller.state('gallery-edit');
  6034. edit.get('library').add( state.get('selection').models );
  6035. state.trigger('reset');
  6036. controller.setState('gallery-edit');
  6037. // Move focus to the modal when jumping back from Add to Gallery to Edit Gallery view.
  6038. this.controller.modal.focusManager.focus();
  6039. }
  6040. }
  6041. }
  6042. }) );
  6043. },
  6044. playlistEditToolbar: function() {
  6045. var editing = this.state().get('editing');
  6046. this.toolbar.set( new wp.media.view.Toolbar({
  6047. controller: this,
  6048. items: {
  6049. insert: {
  6050. style: 'primary',
  6051. text: editing ? l10n.updatePlaylist : l10n.insertPlaylist,
  6052. priority: 80,
  6053. requires: { library: true },
  6054. /**
  6055. * @fires wp.media.controller.State#update
  6056. */
  6057. click: function() {
  6058. var controller = this.controller,
  6059. state = controller.state();
  6060. controller.close();
  6061. state.trigger( 'update', state.get('library') );
  6062. // Restore and reset the default state.
  6063. controller.setState( controller.options.state );
  6064. controller.reset();
  6065. }
  6066. }
  6067. }
  6068. }) );
  6069. },
  6070. playlistAddToolbar: function() {
  6071. this.toolbar.set( new wp.media.view.Toolbar({
  6072. controller: this,
  6073. items: {
  6074. insert: {
  6075. style: 'primary',
  6076. text: l10n.addToPlaylist,
  6077. priority: 80,
  6078. requires: { selection: true },
  6079. /**
  6080. * @fires wp.media.controller.State#reset
  6081. */
  6082. click: function() {
  6083. var controller = this.controller,
  6084. state = controller.state(),
  6085. edit = controller.state('playlist-edit');
  6086. edit.get('library').add( state.get('selection').models );
  6087. state.trigger('reset');
  6088. controller.setState('playlist-edit');
  6089. // Move focus to the modal when jumping back from Add to Audio Playlist to Edit Audio Playlist view.
  6090. this.controller.modal.focusManager.focus();
  6091. }
  6092. }
  6093. }
  6094. }) );
  6095. },
  6096. videoPlaylistEditToolbar: function() {
  6097. var editing = this.state().get('editing');
  6098. this.toolbar.set( new wp.media.view.Toolbar({
  6099. controller: this,
  6100. items: {
  6101. insert: {
  6102. style: 'primary',
  6103. text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
  6104. priority: 140,
  6105. requires: { library: true },
  6106. click: function() {
  6107. var controller = this.controller,
  6108. state = controller.state(),
  6109. library = state.get('library');
  6110. library.type = 'video';
  6111. controller.close();
  6112. state.trigger( 'update', library );
  6113. // Restore and reset the default state.
  6114. controller.setState( controller.options.state );
  6115. controller.reset();
  6116. }
  6117. }
  6118. }
  6119. }) );
  6120. },
  6121. videoPlaylistAddToolbar: function() {
  6122. this.toolbar.set( new wp.media.view.Toolbar({
  6123. controller: this,
  6124. items: {
  6125. insert: {
  6126. style: 'primary',
  6127. text: l10n.addToVideoPlaylist,
  6128. priority: 140,
  6129. requires: { selection: true },
  6130. click: function() {
  6131. var controller = this.controller,
  6132. state = controller.state(),
  6133. edit = controller.state('video-playlist-edit');
  6134. edit.get('library').add( state.get('selection').models );
  6135. state.trigger('reset');
  6136. controller.setState('video-playlist-edit');
  6137. // Move focus to the modal when jumping back from Add to Video Playlist to Edit Video Playlist view.
  6138. this.controller.modal.focusManager.focus();
  6139. }
  6140. }
  6141. }
  6142. }) );
  6143. }
  6144. });
  6145. module.exports = Post;
  6146. /***/ }),
  6147. /***/ 8719:
  6148. /***/ (function(module) {
  6149. var MediaFrame = wp.media.view.MediaFrame,
  6150. l10n = wp.media.view.l10n,
  6151. Select;
  6152. /**
  6153. * wp.media.view.MediaFrame.Select
  6154. *
  6155. * A frame for selecting an item or items from the media library.
  6156. *
  6157. * @memberOf wp.media.view.MediaFrame
  6158. *
  6159. * @class
  6160. * @augments wp.media.view.MediaFrame
  6161. * @augments wp.media.view.Frame
  6162. * @augments wp.media.View
  6163. * @augments wp.Backbone.View
  6164. * @augments Backbone.View
  6165. * @mixes wp.media.controller.StateMachine
  6166. */
  6167. Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{
  6168. initialize: function() {
  6169. // Call 'initialize' directly on the parent class.
  6170. MediaFrame.prototype.initialize.apply( this, arguments );
  6171. _.defaults( this.options, {
  6172. selection: [],
  6173. library: {},
  6174. multiple: false,
  6175. state: 'library'
  6176. });
  6177. this.createSelection();
  6178. this.createStates();
  6179. this.bindHandlers();
  6180. },
  6181. /**
  6182. * Attach a selection collection to the frame.
  6183. *
  6184. * A selection is a collection of attachments used for a specific purpose
  6185. * by a media frame. e.g. Selecting an attachment (or many) to insert into
  6186. * post content.
  6187. *
  6188. * @see media.model.Selection
  6189. */
  6190. createSelection: function() {
  6191. var selection = this.options.selection;
  6192. if ( ! (selection instanceof wp.media.model.Selection) ) {
  6193. this.options.selection = new wp.media.model.Selection( selection, {
  6194. multiple: this.options.multiple
  6195. });
  6196. }
  6197. this._selection = {
  6198. attachments: new wp.media.model.Attachments(),
  6199. difference: []
  6200. };
  6201. },
  6202. editImageContent: function() {
  6203. var image = this.state().get('image'),
  6204. view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
  6205. this.content.set( view );
  6206. // After creating the wrapper view, load the actual editor via an Ajax call.
  6207. view.loadEditor();
  6208. },
  6209. /**
  6210. * Create the default states on the frame.
  6211. */
  6212. createStates: function() {
  6213. var options = this.options;
  6214. if ( this.options.states ) {
  6215. return;
  6216. }
  6217. // Add the default states.
  6218. this.states.add([
  6219. // Main states.
  6220. new wp.media.controller.Library({
  6221. library: wp.media.query( options.library ),
  6222. multiple: options.multiple,
  6223. title: options.title,
  6224. priority: 20
  6225. }),
  6226. new wp.media.controller.EditImage( { model: options.editImage } )
  6227. ]);
  6228. },
  6229. /**
  6230. * Bind region mode event callbacks.
  6231. *
  6232. * @see media.controller.Region.render
  6233. */
  6234. bindHandlers: function() {
  6235. this.on( 'router:create:browse', this.createRouter, this );
  6236. this.on( 'router:render:browse', this.browseRouter, this );
  6237. this.on( 'content:create:browse', this.browseContent, this );
  6238. this.on( 'content:render:upload', this.uploadContent, this );
  6239. this.on( 'toolbar:create:select', this.createSelectToolbar, this );
  6240. this.on( 'content:render:edit-image', this.editImageContent, this );
  6241. },
  6242. /**
  6243. * Render callback for the router region in the `browse` mode.
  6244. *
  6245. * @param {wp.media.view.Router} routerView
  6246. */
  6247. browseRouter: function( routerView ) {
  6248. routerView.set({
  6249. upload: {
  6250. text: l10n.uploadFilesTitle,
  6251. priority: 20
  6252. },
  6253. browse: {
  6254. text: l10n.mediaLibraryTitle,
  6255. priority: 40
  6256. }
  6257. });
  6258. },
  6259. /**
  6260. * Render callback for the content region in the `browse` mode.
  6261. *
  6262. * @param {wp.media.controller.Region} contentRegion
  6263. */
  6264. browseContent: function( contentRegion ) {
  6265. var state = this.state();
  6266. this.$el.removeClass('hide-toolbar');
  6267. // Browse our library of attachments.
  6268. contentRegion.view = new wp.media.view.AttachmentsBrowser({
  6269. controller: this,
  6270. collection: state.get('library'),
  6271. selection: state.get('selection'),
  6272. model: state,
  6273. sortable: state.get('sortable'),
  6274. search: state.get('searchable'),
  6275. filters: state.get('filterable'),
  6276. date: state.get('date'),
  6277. display: state.has('display') ? state.get('display') : state.get('displaySettings'),
  6278. dragInfo: state.get('dragInfo'),
  6279. idealColumnWidth: state.get('idealColumnWidth'),
  6280. suggestedWidth: state.get('suggestedWidth'),
  6281. suggestedHeight: state.get('suggestedHeight'),
  6282. AttachmentView: state.get('AttachmentView')
  6283. });
  6284. },
  6285. /**
  6286. * Render callback for the content region in the `upload` mode.
  6287. */
  6288. uploadContent: function() {
  6289. this.$el.removeClass( 'hide-toolbar' );
  6290. this.content.set( new wp.media.view.UploaderInline({
  6291. controller: this
  6292. }) );
  6293. },
  6294. /**
  6295. * Toolbars
  6296. *
  6297. * @param {Object} toolbar
  6298. * @param {Object} [options={}]
  6299. * @this wp.media.controller.Region
  6300. */
  6301. createSelectToolbar: function( toolbar, options ) {
  6302. options = options || this.options.button || {};
  6303. options.controller = this;
  6304. toolbar.view = new wp.media.view.Toolbar.Select( options );
  6305. }
  6306. });
  6307. module.exports = Select;
  6308. /***/ }),
  6309. /***/ 7990:
  6310. /***/ (function(module) {
  6311. /**
  6312. * wp.media.view.Heading
  6313. *
  6314. * A reusable heading component for the media library
  6315. *
  6316. * Used to add accessibility friendly headers in the media library/modal.
  6317. *
  6318. * @class
  6319. * @augments wp.media.View
  6320. * @augments wp.Backbone.View
  6321. * @augments Backbone.View
  6322. */
  6323. var Heading = wp.media.View.extend( {
  6324. tagName: function() {
  6325. return this.options.level || 'h1';
  6326. },
  6327. className: 'media-views-heading',
  6328. initialize: function() {
  6329. if ( this.options.className ) {
  6330. this.$el.addClass( this.options.className );
  6331. }
  6332. this.text = this.options.text;
  6333. },
  6334. render: function() {
  6335. this.$el.html( this.text );
  6336. return this;
  6337. }
  6338. } );
  6339. module.exports = Heading;
  6340. /***/ }),
  6341. /***/ 6217:
  6342. /***/ (function(module) {
  6343. /**
  6344. * wp.media.view.Iframe
  6345. *
  6346. * @memberOf wp.media.view
  6347. *
  6348. * @class
  6349. * @augments wp.media.View
  6350. * @augments wp.Backbone.View
  6351. * @augments Backbone.View
  6352. */
  6353. var Iframe = wp.media.View.extend(/** @lends wp.media.view.Iframe.prototype */{
  6354. className: 'media-iframe',
  6355. /**
  6356. * @return {wp.media.view.Iframe} Returns itself to allow chaining.
  6357. */
  6358. render: function() {
  6359. this.views.detach();
  6360. this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
  6361. this.views.render();
  6362. return this;
  6363. }
  6364. });
  6365. module.exports = Iframe;
  6366. /***/ }),
  6367. /***/ 7598:
  6368. /***/ (function(module) {
  6369. var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
  6370. $ = jQuery,
  6371. ImageDetails;
  6372. /**
  6373. * wp.media.view.ImageDetails
  6374. *
  6375. * @memberOf wp.media.view
  6376. *
  6377. * @class
  6378. * @augments wp.media.view.Settings.AttachmentDisplay
  6379. * @augments wp.media.view.Settings
  6380. * @augments wp.media.View
  6381. * @augments wp.Backbone.View
  6382. * @augments Backbone.View
  6383. */
  6384. ImageDetails = AttachmentDisplay.extend(/** @lends wp.media.view.ImageDetails.prototype */{
  6385. className: 'image-details',
  6386. template: wp.template('image-details'),
  6387. events: _.defaults( AttachmentDisplay.prototype.events, {
  6388. 'click .edit-attachment': 'editAttachment',
  6389. 'click .replace-attachment': 'replaceAttachment',
  6390. 'click .advanced-toggle': 'onToggleAdvanced',
  6391. 'change [data-setting="customWidth"]': 'onCustomSize',
  6392. 'change [data-setting="customHeight"]': 'onCustomSize',
  6393. 'keyup [data-setting="customWidth"]': 'onCustomSize',
  6394. 'keyup [data-setting="customHeight"]': 'onCustomSize'
  6395. } ),
  6396. initialize: function() {
  6397. // Used in AttachmentDisplay.prototype.updateLinkTo.
  6398. this.options.attachment = this.model.attachment;
  6399. this.listenTo( this.model, 'change:url', this.updateUrl );
  6400. this.listenTo( this.model, 'change:link', this.toggleLinkSettings );
  6401. this.listenTo( this.model, 'change:size', this.toggleCustomSize );
  6402. AttachmentDisplay.prototype.initialize.apply( this, arguments );
  6403. },
  6404. prepare: function() {
  6405. var attachment = false;
  6406. if ( this.model.attachment ) {
  6407. attachment = this.model.attachment.toJSON();
  6408. }
  6409. return _.defaults({
  6410. model: this.model.toJSON(),
  6411. attachment: attachment
  6412. }, this.options );
  6413. },
  6414. render: function() {
  6415. var args = arguments;
  6416. if ( this.model.attachment && 'pending' === this.model.dfd.state() ) {
  6417. this.model.dfd
  6418. .done( _.bind( function() {
  6419. AttachmentDisplay.prototype.render.apply( this, args );
  6420. this.postRender();
  6421. }, this ) )
  6422. .fail( _.bind( function() {
  6423. this.model.attachment = false;
  6424. AttachmentDisplay.prototype.render.apply( this, args );
  6425. this.postRender();
  6426. }, this ) );
  6427. } else {
  6428. AttachmentDisplay.prototype.render.apply( this, arguments );
  6429. this.postRender();
  6430. }
  6431. return this;
  6432. },
  6433. postRender: function() {
  6434. setTimeout( _.bind( this.scrollToTop, this ), 10 );
  6435. this.toggleLinkSettings();
  6436. if ( window.getUserSetting( 'advImgDetails' ) === 'show' ) {
  6437. this.toggleAdvanced( true );
  6438. }
  6439. this.trigger( 'post-render' );
  6440. },
  6441. scrollToTop: function() {
  6442. this.$( '.embed-media-settings' ).scrollTop( 0 );
  6443. },
  6444. updateUrl: function() {
  6445. this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) );
  6446. this.$( '.url' ).val( this.model.get( 'url' ) );
  6447. },
  6448. toggleLinkSettings: function() {
  6449. if ( this.model.get( 'link' ) === 'none' ) {
  6450. this.$( '.link-settings' ).addClass('hidden');
  6451. } else {
  6452. this.$( '.link-settings' ).removeClass('hidden');
  6453. }
  6454. },
  6455. toggleCustomSize: function() {
  6456. if ( this.model.get( 'size' ) !== 'custom' ) {
  6457. this.$( '.custom-size' ).addClass('hidden');
  6458. } else {
  6459. this.$( '.custom-size' ).removeClass('hidden');
  6460. }
  6461. },
  6462. onCustomSize: function( event ) {
  6463. var dimension = $( event.target ).data('setting'),
  6464. num = $( event.target ).val(),
  6465. value;
  6466. // Ignore bogus input.
  6467. if ( ! /^\d+/.test( num ) || parseInt( num, 10 ) < 1 ) {
  6468. event.preventDefault();
  6469. return;
  6470. }
  6471. if ( dimension === 'customWidth' ) {
  6472. value = Math.round( 1 / this.model.get( 'aspectRatio' ) * num );
  6473. this.model.set( 'customHeight', value, { silent: true } );
  6474. this.$( '[data-setting="customHeight"]' ).val( value );
  6475. } else {
  6476. value = Math.round( this.model.get( 'aspectRatio' ) * num );
  6477. this.model.set( 'customWidth', value, { silent: true } );
  6478. this.$( '[data-setting="customWidth"]' ).val( value );
  6479. }
  6480. },
  6481. onToggleAdvanced: function( event ) {
  6482. event.preventDefault();
  6483. this.toggleAdvanced();
  6484. },
  6485. toggleAdvanced: function( show ) {
  6486. var $advanced = this.$el.find( '.advanced-section' ),
  6487. mode;
  6488. if ( $advanced.hasClass('advanced-visible') || show === false ) {
  6489. $advanced.removeClass('advanced-visible');
  6490. $advanced.find('.advanced-settings').addClass('hidden');
  6491. mode = 'hide';
  6492. } else {
  6493. $advanced.addClass('advanced-visible');
  6494. $advanced.find('.advanced-settings').removeClass('hidden');
  6495. mode = 'show';
  6496. }
  6497. window.setUserSetting( 'advImgDetails', mode );
  6498. },
  6499. editAttachment: function( event ) {
  6500. var editState = this.controller.states.get( 'edit-image' );
  6501. if ( window.imageEdit && editState ) {
  6502. event.preventDefault();
  6503. editState.set( 'image', this.model.attachment );
  6504. this.controller.setState( 'edit-image' );
  6505. }
  6506. },
  6507. replaceAttachment: function( event ) {
  6508. event.preventDefault();
  6509. this.controller.setState( 'replace-image' );
  6510. }
  6511. });
  6512. module.exports = ImageDetails;
  6513. /***/ }),
  6514. /***/ 6644:
  6515. /***/ (function(module) {
  6516. /**
  6517. * wp.media.view.Label
  6518. *
  6519. * @memberOf wp.media.view
  6520. *
  6521. * @class
  6522. * @augments wp.media.View
  6523. * @augments wp.Backbone.View
  6524. * @augments Backbone.View
  6525. */
  6526. var Label = wp.media.View.extend(/** @lends wp.media.view.Label.prototype */{
  6527. tagName: 'label',
  6528. className: 'screen-reader-text',
  6529. initialize: function() {
  6530. this.value = this.options.value;
  6531. },
  6532. render: function() {
  6533. this.$el.html( this.value );
  6534. return this;
  6535. }
  6536. });
  6537. module.exports = Label;
  6538. /***/ }),
  6539. /***/ 4861:
  6540. /***/ (function(module) {
  6541. var Frame = wp.media.view.Frame,
  6542. l10n = wp.media.view.l10n,
  6543. $ = jQuery,
  6544. MediaFrame;
  6545. /**
  6546. * wp.media.view.MediaFrame
  6547. *
  6548. * The frame used to create the media modal.
  6549. *
  6550. * @memberOf wp.media.view
  6551. *
  6552. * @class
  6553. * @augments wp.media.view.Frame
  6554. * @augments wp.media.View
  6555. * @augments wp.Backbone.View
  6556. * @augments Backbone.View
  6557. * @mixes wp.media.controller.StateMachine
  6558. */
  6559. MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
  6560. className: 'media-frame',
  6561. template: wp.template('media-frame'),
  6562. regions: ['menu','title','content','toolbar','router'],
  6563. events: {
  6564. 'click .media-frame-menu-toggle': 'toggleMenu'
  6565. },
  6566. /**
  6567. * @constructs
  6568. */
  6569. initialize: function() {
  6570. Frame.prototype.initialize.apply( this, arguments );
  6571. _.defaults( this.options, {
  6572. title: l10n.mediaFrameDefaultTitle,
  6573. modal: true,
  6574. uploader: true
  6575. });
  6576. // Ensure core UI is enabled.
  6577. this.$el.addClass('wp-core-ui');
  6578. // Initialize modal container view.
  6579. if ( this.options.modal ) {
  6580. this.modal = new wp.media.view.Modal({
  6581. controller: this,
  6582. title: this.options.title
  6583. });
  6584. this.modal.content( this );
  6585. }
  6586. // Force the uploader off if the upload limit has been exceeded or
  6587. // if the browser isn't supported.
  6588. if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
  6589. this.options.uploader = false;
  6590. }
  6591. // Initialize window-wide uploader.
  6592. if ( this.options.uploader ) {
  6593. this.uploader = new wp.media.view.UploaderWindow({
  6594. controller: this,
  6595. uploader: {
  6596. dropzone: this.modal ? this.modal.$el : this.$el,
  6597. container: this.$el
  6598. }
  6599. });
  6600. this.views.set( '.media-frame-uploader', this.uploader );
  6601. }
  6602. this.on( 'attach', _.bind( this.views.ready, this.views ), this );
  6603. // Bind default title creation.
  6604. this.on( 'title:create:default', this.createTitle, this );
  6605. this.title.mode('default');
  6606. // Bind default menu.
  6607. this.on( 'menu:create:default', this.createMenu, this );
  6608. // Set the menu ARIA tab panel attributes when the modal opens.
  6609. this.on( 'open', this.setMenuTabPanelAriaAttributes, this );
  6610. // Set the router ARIA tab panel attributes when the modal opens.
  6611. this.on( 'open', this.setRouterTabPanelAriaAttributes, this );
  6612. // Update the menu ARIA tab panel attributes when the content updates.
  6613. this.on( 'content:render', this.setMenuTabPanelAriaAttributes, this );
  6614. // Update the router ARIA tab panel attributes when the content updates.
  6615. this.on( 'content:render', this.setRouterTabPanelAriaAttributes, this );
  6616. },
  6617. /**
  6618. * Sets the attributes to be used on the menu ARIA tab panel.
  6619. *
  6620. * @since 5.3.0
  6621. *
  6622. * @return {void}
  6623. */
  6624. setMenuTabPanelAriaAttributes: function() {
  6625. var stateId = this.state().get( 'id' ),
  6626. tabPanelEl = this.$el.find( '.media-frame-tab-panel' ),
  6627. ariaLabelledby;
  6628. tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
  6629. if ( this.state().get( 'menu' ) && this.menuView && this.menuView.isVisible ) {
  6630. ariaLabelledby = 'menu-item-' + stateId;
  6631. // Set the tab panel attributes only if the tabs are visible.
  6632. tabPanelEl
  6633. .attr( {
  6634. role: 'tabpanel',
  6635. 'aria-labelledby': ariaLabelledby,
  6636. tabIndex: '0'
  6637. } );
  6638. }
  6639. },
  6640. /**
  6641. * Sets the attributes to be used on the router ARIA tab panel.
  6642. *
  6643. * @since 5.3.0
  6644. *
  6645. * @return {void}
  6646. */
  6647. setRouterTabPanelAriaAttributes: function() {
  6648. var tabPanelEl = this.$el.find( '.media-frame-content' ),
  6649. ariaLabelledby;
  6650. tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
  6651. // Set the tab panel attributes only if the tabs are visible.
  6652. if ( this.state().get( 'router' ) && this.routerView && this.routerView.isVisible && this.content._mode ) {
  6653. ariaLabelledby = 'menu-item-' + this.content._mode;
  6654. tabPanelEl
  6655. .attr( {
  6656. role: 'tabpanel',
  6657. 'aria-labelledby': ariaLabelledby,
  6658. tabIndex: '0'
  6659. } );
  6660. }
  6661. },
  6662. /**
  6663. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6664. */
  6665. render: function() {
  6666. // Activate the default state if no active state exists.
  6667. if ( ! this.state() && this.options.state ) {
  6668. this.setState( this.options.state );
  6669. }
  6670. /**
  6671. * call 'render' directly on the parent class
  6672. */
  6673. return Frame.prototype.render.apply( this, arguments );
  6674. },
  6675. /**
  6676. * @param {Object} title
  6677. * @this wp.media.controller.Region
  6678. */
  6679. createTitle: function( title ) {
  6680. title.view = new wp.media.View({
  6681. controller: this,
  6682. tagName: 'h1'
  6683. });
  6684. },
  6685. /**
  6686. * @param {Object} menu
  6687. * @this wp.media.controller.Region
  6688. */
  6689. createMenu: function( menu ) {
  6690. menu.view = new wp.media.view.Menu({
  6691. controller: this,
  6692. attributes: {
  6693. role: 'tablist',
  6694. 'aria-orientation': 'vertical'
  6695. }
  6696. });
  6697. this.menuView = menu.view;
  6698. },
  6699. toggleMenu: function( event ) {
  6700. var menu = this.$el.find( '.media-menu' );
  6701. menu.toggleClass( 'visible' );
  6702. $( event.target ).attr( 'aria-expanded', menu.hasClass( 'visible' ) );
  6703. },
  6704. /**
  6705. * @param {Object} toolbar
  6706. * @this wp.media.controller.Region
  6707. */
  6708. createToolbar: function( toolbar ) {
  6709. toolbar.view = new wp.media.view.Toolbar({
  6710. controller: this
  6711. });
  6712. },
  6713. /**
  6714. * @param {Object} router
  6715. * @this wp.media.controller.Region
  6716. */
  6717. createRouter: function( router ) {
  6718. router.view = new wp.media.view.Router({
  6719. controller: this,
  6720. attributes: {
  6721. role: 'tablist',
  6722. 'aria-orientation': 'horizontal'
  6723. }
  6724. });
  6725. this.routerView = router.view;
  6726. },
  6727. /**
  6728. * @param {Object} options
  6729. */
  6730. createIframeStates: function( options ) {
  6731. var settings = wp.media.view.settings,
  6732. tabs = settings.tabs,
  6733. tabUrl = settings.tabUrl,
  6734. $postId;
  6735. if ( ! tabs || ! tabUrl ) {
  6736. return;
  6737. }
  6738. // Add the post ID to the tab URL if it exists.
  6739. $postId = $('#post_ID');
  6740. if ( $postId.length ) {
  6741. tabUrl += '&post_id=' + $postId.val();
  6742. }
  6743. // Generate the tab states.
  6744. _.each( tabs, function( title, id ) {
  6745. this.state( 'iframe:' + id ).set( _.defaults({
  6746. tab: id,
  6747. src: tabUrl + '&tab=' + id,
  6748. title: title,
  6749. content: 'iframe',
  6750. menu: 'default'
  6751. }, options ) );
  6752. }, this );
  6753. this.on( 'content:create:iframe', this.iframeContent, this );
  6754. this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
  6755. this.on( 'menu:render:default', this.iframeMenu, this );
  6756. this.on( 'open', this.hijackThickbox, this );
  6757. this.on( 'close', this.restoreThickbox, this );
  6758. },
  6759. /**
  6760. * @param {Object} content
  6761. * @this wp.media.controller.Region
  6762. */
  6763. iframeContent: function( content ) {
  6764. this.$el.addClass('hide-toolbar');
  6765. content.view = new wp.media.view.Iframe({
  6766. controller: this
  6767. });
  6768. },
  6769. iframeContentCleanup: function() {
  6770. this.$el.removeClass('hide-toolbar');
  6771. },
  6772. iframeMenu: function( view ) {
  6773. var views = {};
  6774. if ( ! view ) {
  6775. return;
  6776. }
  6777. _.each( wp.media.view.settings.tabs, function( title, id ) {
  6778. views[ 'iframe:' + id ] = {
  6779. text: this.state( 'iframe:' + id ).get('title'),
  6780. priority: 200
  6781. };
  6782. }, this );
  6783. view.set( views );
  6784. },
  6785. hijackThickbox: function() {
  6786. var frame = this;
  6787. if ( ! window.tb_remove || this._tb_remove ) {
  6788. return;
  6789. }
  6790. this._tb_remove = window.tb_remove;
  6791. window.tb_remove = function() {
  6792. frame.close();
  6793. frame.reset();
  6794. frame.setState( frame.options.state );
  6795. frame._tb_remove.call( window );
  6796. };
  6797. },
  6798. restoreThickbox: function() {
  6799. if ( ! this._tb_remove ) {
  6800. return;
  6801. }
  6802. window.tb_remove = this._tb_remove;
  6803. delete this._tb_remove;
  6804. }
  6805. });
  6806. // Map some of the modal's methods to the frame.
  6807. _.each(['open','close','attach','detach','escape'], function( method ) {
  6808. /**
  6809. * @function open
  6810. * @memberOf wp.media.view.MediaFrame
  6811. * @instance
  6812. *
  6813. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6814. */
  6815. /**
  6816. * @function close
  6817. * @memberOf wp.media.view.MediaFrame
  6818. * @instance
  6819. *
  6820. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6821. */
  6822. /**
  6823. * @function attach
  6824. * @memberOf wp.media.view.MediaFrame
  6825. * @instance
  6826. *
  6827. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6828. */
  6829. /**
  6830. * @function detach
  6831. * @memberOf wp.media.view.MediaFrame
  6832. * @instance
  6833. *
  6834. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6835. */
  6836. /**
  6837. * @function escape
  6838. * @memberOf wp.media.view.MediaFrame
  6839. * @instance
  6840. *
  6841. * @return {wp.media.view.MediaFrame} Returns itself to allow chaining.
  6842. */
  6843. MediaFrame.prototype[ method ] = function() {
  6844. if ( this.modal ) {
  6845. this.modal[ method ].apply( this.modal, arguments );
  6846. }
  6847. return this;
  6848. };
  6849. });
  6850. module.exports = MediaFrame;
  6851. /***/ }),
  6852. /***/ 917:
  6853. /***/ (function(module) {
  6854. var MenuItem;
  6855. /**
  6856. * wp.media.view.MenuItem
  6857. *
  6858. * @memberOf wp.media.view
  6859. *
  6860. * @class
  6861. * @augments wp.media.View
  6862. * @augments wp.Backbone.View
  6863. * @augments Backbone.View
  6864. */
  6865. MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
  6866. tagName: 'button',
  6867. className: 'media-menu-item',
  6868. attributes: {
  6869. type: 'button',
  6870. role: 'tab'
  6871. },
  6872. events: {
  6873. 'click': '_click'
  6874. },
  6875. /**
  6876. * Allows to override the click event.
  6877. */
  6878. _click: function() {
  6879. var clickOverride = this.options.click;
  6880. if ( clickOverride ) {
  6881. clickOverride.call( this );
  6882. } else {
  6883. this.click();
  6884. }
  6885. },
  6886. click: function() {
  6887. var state = this.options.state;
  6888. if ( state ) {
  6889. this.controller.setState( state );
  6890. // Toggle the menu visibility in the responsive view.
  6891. this.views.parent.$el.removeClass( 'visible' ); // @todo Or hide on any click, see below.
  6892. }
  6893. },
  6894. /**
  6895. * @return {wp.media.view.MenuItem} returns itself to allow chaining.
  6896. */
  6897. render: function() {
  6898. var options = this.options,
  6899. menuProperty = options.state || options.contentMode;
  6900. if ( options.text ) {
  6901. this.$el.text( options.text );
  6902. } else if ( options.html ) {
  6903. this.$el.html( options.html );
  6904. }
  6905. // Set the menu item ID based on the frame state associated to the menu item.
  6906. this.$el.attr( 'id', 'menu-item-' + menuProperty );
  6907. return this;
  6908. }
  6909. });
  6910. module.exports = MenuItem;
  6911. /***/ }),
  6912. /***/ 2596:
  6913. /***/ (function(module) {
  6914. var MenuItem = wp.media.view.MenuItem,
  6915. PriorityList = wp.media.view.PriorityList,
  6916. Menu;
  6917. /**
  6918. * wp.media.view.Menu
  6919. *
  6920. * @memberOf wp.media.view
  6921. *
  6922. * @class
  6923. * @augments wp.media.view.PriorityList
  6924. * @augments wp.media.View
  6925. * @augments wp.Backbone.View
  6926. * @augments Backbone.View
  6927. */
  6928. Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
  6929. tagName: 'div',
  6930. className: 'media-menu',
  6931. property: 'state',
  6932. ItemView: MenuItem,
  6933. region: 'menu',
  6934. attributes: {
  6935. role: 'tablist',
  6936. 'aria-orientation': 'horizontal'
  6937. },
  6938. initialize: function() {
  6939. this._views = {};
  6940. this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
  6941. delete this.options.views;
  6942. if ( ! this.options.silent ) {
  6943. this.render();
  6944. }
  6945. // Initialize the Focus Manager.
  6946. this.focusManager = new wp.media.view.FocusManager( {
  6947. el: this.el,
  6948. mode: 'tabsNavigation'
  6949. } );
  6950. // The menu is always rendered and can be visible or hidden on some frames.
  6951. this.isVisible = true;
  6952. },
  6953. /**
  6954. * @param {Object} options
  6955. * @param {string} id
  6956. * @return {wp.media.View}
  6957. */
  6958. toView: function( options, id ) {
  6959. options = options || {};
  6960. options[ this.property ] = options[ this.property ] || id;
  6961. return new this.ItemView( options ).render();
  6962. },
  6963. ready: function() {
  6964. /**
  6965. * call 'ready' directly on the parent class
  6966. */
  6967. PriorityList.prototype.ready.apply( this, arguments );
  6968. this.visibility();
  6969. // Set up aria tabs initial attributes.
  6970. this.focusManager.setupAriaTabs();
  6971. },
  6972. set: function() {
  6973. /**
  6974. * call 'set' directly on the parent class
  6975. */
  6976. PriorityList.prototype.set.apply( this, arguments );
  6977. this.visibility();
  6978. },
  6979. unset: function() {
  6980. /**
  6981. * call 'unset' directly on the parent class
  6982. */
  6983. PriorityList.prototype.unset.apply( this, arguments );
  6984. this.visibility();
  6985. },
  6986. visibility: function() {
  6987. var region = this.region,
  6988. view = this.controller[ region ].get(),
  6989. views = this.views.get(),
  6990. hide = ! views || views.length < 2;
  6991. if ( this === view ) {
  6992. // Flag this menu as hidden or visible.
  6993. this.isVisible = ! hide;
  6994. // Set or remove a CSS class to hide the menu.
  6995. this.controller.$el.toggleClass( 'hide-' + region, hide );
  6996. }
  6997. },
  6998. /**
  6999. * @param {string} id
  7000. */
  7001. select: function( id ) {
  7002. var view = this.get( id );
  7003. if ( ! view ) {
  7004. return;
  7005. }
  7006. this.deselect();
  7007. view.$el.addClass('active');
  7008. // Set up again the aria tabs initial attributes after the menu updates.
  7009. this.focusManager.setupAriaTabs();
  7010. },
  7011. deselect: function() {
  7012. this.$el.children().removeClass('active');
  7013. },
  7014. hide: function( id ) {
  7015. var view = this.get( id );
  7016. if ( ! view ) {
  7017. return;
  7018. }
  7019. view.$el.addClass('hidden');
  7020. },
  7021. show: function( id ) {
  7022. var view = this.get( id );
  7023. if ( ! view ) {
  7024. return;
  7025. }
  7026. view.$el.removeClass('hidden');
  7027. }
  7028. });
  7029. module.exports = Menu;
  7030. /***/ }),
  7031. /***/ 3939:
  7032. /***/ (function(module) {
  7033. var $ = jQuery,
  7034. Modal;
  7035. /**
  7036. * wp.media.view.Modal
  7037. *
  7038. * A modal view, which the media modal uses as its default container.
  7039. *
  7040. * @memberOf wp.media.view
  7041. *
  7042. * @class
  7043. * @augments wp.media.View
  7044. * @augments wp.Backbone.View
  7045. * @augments Backbone.View
  7046. */
  7047. Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
  7048. tagName: 'div',
  7049. template: wp.template('media-modal'),
  7050. events: {
  7051. 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
  7052. 'keydown': 'keydown'
  7053. },
  7054. clickedOpenerEl: null,
  7055. initialize: function() {
  7056. _.defaults( this.options, {
  7057. container: document.body,
  7058. title: '',
  7059. propagate: true,
  7060. hasCloseButton: true
  7061. });
  7062. this.focusManager = new wp.media.view.FocusManager({
  7063. el: this.el
  7064. });
  7065. },
  7066. /**
  7067. * @return {Object}
  7068. */
  7069. prepare: function() {
  7070. return {
  7071. title: this.options.title,
  7072. hasCloseButton: this.options.hasCloseButton
  7073. };
  7074. },
  7075. /**
  7076. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7077. */
  7078. attach: function() {
  7079. if ( this.views.attached ) {
  7080. return this;
  7081. }
  7082. if ( ! this.views.rendered ) {
  7083. this.render();
  7084. }
  7085. this.$el.appendTo( this.options.container );
  7086. // Manually mark the view as attached and trigger ready.
  7087. this.views.attached = true;
  7088. this.views.ready();
  7089. return this.propagate('attach');
  7090. },
  7091. /**
  7092. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7093. */
  7094. detach: function() {
  7095. if ( this.$el.is(':visible') ) {
  7096. this.close();
  7097. }
  7098. this.$el.detach();
  7099. this.views.attached = false;
  7100. return this.propagate('detach');
  7101. },
  7102. /**
  7103. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7104. */
  7105. open: function() {
  7106. var $el = this.$el,
  7107. mceEditor;
  7108. if ( $el.is(':visible') ) {
  7109. return this;
  7110. }
  7111. this.clickedOpenerEl = document.activeElement;
  7112. if ( ! this.views.attached ) {
  7113. this.attach();
  7114. }
  7115. // Disable page scrolling.
  7116. $( 'body' ).addClass( 'modal-open' );
  7117. $el.show();
  7118. // Try to close the onscreen keyboard.
  7119. if ( 'ontouchend' in document ) {
  7120. if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
  7121. mceEditor.iframeElement.focus();
  7122. mceEditor.iframeElement.blur();
  7123. setTimeout( function() {
  7124. mceEditor.iframeElement.blur();
  7125. }, 100 );
  7126. }
  7127. }
  7128. // Set initial focus on the content instead of this view element, to avoid page scrolling.
  7129. this.$( '.media-modal' ).trigger( 'focus' );
  7130. // Hide the page content from assistive technologies.
  7131. this.focusManager.setAriaHiddenOnBodyChildren( $el );
  7132. return this.propagate('open');
  7133. },
  7134. /**
  7135. * @param {Object} options
  7136. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7137. */
  7138. close: function( options ) {
  7139. if ( ! this.views.attached || ! this.$el.is(':visible') ) {
  7140. return this;
  7141. }
  7142. // Pause current audio/video even after closing the modal.
  7143. $( '.mejs-pause button' ).trigger( 'click' );
  7144. // Enable page scrolling.
  7145. $( 'body' ).removeClass( 'modal-open' );
  7146. // Hide the modal element by adding display:none.
  7147. this.$el.hide();
  7148. /*
  7149. * Make visible again to assistive technologies all body children that
  7150. * have been made hidden when the modal opened.
  7151. */
  7152. this.focusManager.removeAriaHiddenFromBodyChildren();
  7153. // Move focus back in useful location once modal is closed.
  7154. if ( null !== this.clickedOpenerEl ) {
  7155. // Move focus back to the element that opened the modal.
  7156. this.clickedOpenerEl.focus();
  7157. } else {
  7158. // Fallback to the admin page main element.
  7159. $( '#wpbody-content' )
  7160. .attr( 'tabindex', '-1' )
  7161. .trigger( 'focus' );
  7162. }
  7163. this.propagate('close');
  7164. if ( options && options.escape ) {
  7165. this.propagate('escape');
  7166. }
  7167. return this;
  7168. },
  7169. /**
  7170. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7171. */
  7172. escape: function() {
  7173. return this.close({ escape: true });
  7174. },
  7175. /**
  7176. * @param {Object} event
  7177. */
  7178. escapeHandler: function( event ) {
  7179. event.preventDefault();
  7180. this.escape();
  7181. },
  7182. /**
  7183. * @param {Array|Object} content Views to register to '.media-modal-content'
  7184. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7185. */
  7186. content: function( content ) {
  7187. this.views.set( '.media-modal-content', content );
  7188. return this;
  7189. },
  7190. /**
  7191. * Triggers a modal event and if the `propagate` option is set,
  7192. * forwards events to the modal's controller.
  7193. *
  7194. * @param {string} id
  7195. * @return {wp.media.view.Modal} Returns itself to allow chaining.
  7196. */
  7197. propagate: function( id ) {
  7198. this.trigger( id );
  7199. if ( this.options.propagate ) {
  7200. this.controller.trigger( id );
  7201. }
  7202. return this;
  7203. },
  7204. /**
  7205. * @param {Object} event
  7206. */
  7207. keydown: function( event ) {
  7208. // Close the modal when escape is pressed.
  7209. if ( 27 === event.which && this.$el.is(':visible') ) {
  7210. this.escape();
  7211. event.stopImmediatePropagation();
  7212. }
  7213. }
  7214. });
  7215. module.exports = Modal;
  7216. /***/ }),
  7217. /***/ 1993:
  7218. /***/ (function(module) {
  7219. /**
  7220. * wp.media.view.PriorityList
  7221. *
  7222. * @memberOf wp.media.view
  7223. *
  7224. * @class
  7225. * @augments wp.media.View
  7226. * @augments wp.Backbone.View
  7227. * @augments Backbone.View
  7228. */
  7229. var PriorityList = wp.media.View.extend(/** @lends wp.media.view.PriorityList.prototype */{
  7230. tagName: 'div',
  7231. initialize: function() {
  7232. this._views = {};
  7233. this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
  7234. delete this.options.views;
  7235. if ( ! this.options.silent ) {
  7236. this.render();
  7237. }
  7238. },
  7239. /**
  7240. * @param {string} id
  7241. * @param {wp.media.View|Object} view
  7242. * @param {Object} options
  7243. * @return {wp.media.view.PriorityList} Returns itself to allow chaining.
  7244. */
  7245. set: function( id, view, options ) {
  7246. var priority, views, index;
  7247. options = options || {};
  7248. // Accept an object with an `id` : `view` mapping.
  7249. if ( _.isObject( id ) ) {
  7250. _.each( id, function( view, id ) {
  7251. this.set( id, view );
  7252. }, this );
  7253. return this;
  7254. }
  7255. if ( ! (view instanceof Backbone.View) ) {
  7256. view = this.toView( view, id, options );
  7257. }
  7258. view.controller = view.controller || this.controller;
  7259. this.unset( id );
  7260. priority = view.options.priority || 10;
  7261. views = this.views.get() || [];
  7262. _.find( views, function( existing, i ) {
  7263. if ( existing.options.priority > priority ) {
  7264. index = i;
  7265. return true;
  7266. }
  7267. });
  7268. this._views[ id ] = view;
  7269. this.views.add( view, {
  7270. at: _.isNumber( index ) ? index : views.length || 0
  7271. });
  7272. return this;
  7273. },
  7274. /**
  7275. * @param {string} id
  7276. * @return {wp.media.View}
  7277. */
  7278. get: function( id ) {
  7279. return this._views[ id ];
  7280. },
  7281. /**
  7282. * @param {string} id
  7283. * @return {wp.media.view.PriorityList}
  7284. */
  7285. unset: function( id ) {
  7286. var view = this.get( id );
  7287. if ( view ) {
  7288. view.remove();
  7289. }
  7290. delete this._views[ id ];
  7291. return this;
  7292. },
  7293. /**
  7294. * @param {Object} options
  7295. * @return {wp.media.View}
  7296. */
  7297. toView: function( options ) {
  7298. return new wp.media.View( options );
  7299. }
  7300. });
  7301. module.exports = PriorityList;
  7302. /***/ }),
  7303. /***/ 9484:
  7304. /***/ (function(module) {
  7305. /**
  7306. * wp.media.view.RouterItem
  7307. *
  7308. * @memberOf wp.media.view
  7309. *
  7310. * @class
  7311. * @augments wp.media.view.MenuItem
  7312. * @augments wp.media.View
  7313. * @augments wp.Backbone.View
  7314. * @augments Backbone.View
  7315. */
  7316. var RouterItem = wp.media.view.MenuItem.extend(/** @lends wp.media.view.RouterItem.prototype */{
  7317. /**
  7318. * On click handler to activate the content region's corresponding mode.
  7319. */
  7320. click: function() {
  7321. var contentMode = this.options.contentMode;
  7322. if ( contentMode ) {
  7323. this.controller.content.mode( contentMode );
  7324. }
  7325. }
  7326. });
  7327. module.exports = RouterItem;
  7328. /***/ }),
  7329. /***/ 1562:
  7330. /***/ (function(module) {
  7331. var Menu = wp.media.view.Menu,
  7332. Router;
  7333. /**
  7334. * wp.media.view.Router
  7335. *
  7336. * @memberOf wp.media.view
  7337. *
  7338. * @class
  7339. * @augments wp.media.view.Menu
  7340. * @augments wp.media.view.PriorityList
  7341. * @augments wp.media.View
  7342. * @augments wp.Backbone.View
  7343. * @augments Backbone.View
  7344. */
  7345. Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{
  7346. tagName: 'div',
  7347. className: 'media-router',
  7348. property: 'contentMode',
  7349. ItemView: wp.media.view.RouterItem,
  7350. region: 'router',
  7351. attributes: {
  7352. role: 'tablist',
  7353. 'aria-orientation': 'horizontal'
  7354. },
  7355. initialize: function() {
  7356. this.controller.on( 'content:render', this.update, this );
  7357. // Call 'initialize' directly on the parent class.
  7358. Menu.prototype.initialize.apply( this, arguments );
  7359. },
  7360. update: function() {
  7361. var mode = this.controller.content.mode();
  7362. if ( mode ) {
  7363. this.select( mode );
  7364. }
  7365. }
  7366. });
  7367. module.exports = Router;
  7368. /***/ }),
  7369. /***/ 4556:
  7370. /***/ (function(module) {
  7371. var Search;
  7372. /**
  7373. * wp.media.view.Search
  7374. *
  7375. * @memberOf wp.media.view
  7376. *
  7377. * @class
  7378. * @augments wp.media.View
  7379. * @augments wp.Backbone.View
  7380. * @augments Backbone.View
  7381. */
  7382. Search = wp.media.View.extend(/** @lends wp.media.view.Search.prototype */{
  7383. tagName: 'input',
  7384. className: 'search',
  7385. id: 'media-search-input',
  7386. attributes: {
  7387. type: 'search'
  7388. },
  7389. events: {
  7390. 'input': 'search'
  7391. },
  7392. /**
  7393. * @return {wp.media.view.Search} Returns itself to allow chaining.
  7394. */
  7395. render: function() {
  7396. this.el.value = this.model.escape('search');
  7397. return this;
  7398. },
  7399. search: _.debounce( function( event ) {
  7400. var searchTerm = event.target.value.trim();
  7401. // Trigger the search only after 2 ASCII characters.
  7402. if ( searchTerm && searchTerm.length > 1 ) {
  7403. this.model.set( 'search', searchTerm );
  7404. } else {
  7405. this.model.unset( 'search' );
  7406. }
  7407. }, 500 )
  7408. });
  7409. module.exports = Search;
  7410. /***/ }),
  7411. /***/ 6191:
  7412. /***/ (function(module) {
  7413. var _n = wp.i18n._n,
  7414. sprintf = wp.i18n.sprintf,
  7415. Selection;
  7416. /**
  7417. * wp.media.view.Selection
  7418. *
  7419. * @memberOf wp.media.view
  7420. *
  7421. * @class
  7422. * @augments wp.media.View
  7423. * @augments wp.Backbone.View
  7424. * @augments Backbone.View
  7425. */
  7426. Selection = wp.media.View.extend(/** @lends wp.media.view.Selection.prototype */{
  7427. tagName: 'div',
  7428. className: 'media-selection',
  7429. template: wp.template('media-selection'),
  7430. events: {
  7431. 'click .edit-selection': 'edit',
  7432. 'click .clear-selection': 'clear'
  7433. },
  7434. initialize: function() {
  7435. _.defaults( this.options, {
  7436. editable: false,
  7437. clearable: true
  7438. });
  7439. /**
  7440. * @member {wp.media.view.Attachments.Selection}
  7441. */
  7442. this.attachments = new wp.media.view.Attachments.Selection({
  7443. controller: this.controller,
  7444. collection: this.collection,
  7445. selection: this.collection,
  7446. model: new Backbone.Model()
  7447. });
  7448. this.views.set( '.selection-view', this.attachments );
  7449. this.collection.on( 'add remove reset', this.refresh, this );
  7450. this.controller.on( 'content:activate', this.refresh, this );
  7451. },
  7452. ready: function() {
  7453. this.refresh();
  7454. },
  7455. refresh: function() {
  7456. // If the selection hasn't been rendered, bail.
  7457. if ( ! this.$el.children().length ) {
  7458. return;
  7459. }
  7460. var collection = this.collection,
  7461. editing = 'edit-selection' === this.controller.content.mode();
  7462. // If nothing is selected, display nothing.
  7463. this.$el.toggleClass( 'empty', ! collection.length );
  7464. this.$el.toggleClass( 'one', 1 === collection.length );
  7465. this.$el.toggleClass( 'editing', editing );
  7466. this.$( '.count' ).text(
  7467. /* translators: %s: Number of selected media attachments. */
  7468. sprintf( _n( '%s item selected', '%s items selected', collection.length ), collection.length )
  7469. );
  7470. },
  7471. edit: function( event ) {
  7472. event.preventDefault();
  7473. if ( this.options.editable ) {
  7474. this.options.editable.call( this, this.collection );
  7475. }
  7476. },
  7477. clear: function( event ) {
  7478. event.preventDefault();
  7479. this.collection.reset();
  7480. // Move focus to the modal.
  7481. this.controller.modal.focusManager.focus();
  7482. }
  7483. });
  7484. module.exports = Selection;
  7485. /***/ }),
  7486. /***/ 859:
  7487. /***/ (function(module) {
  7488. var View = wp.media.View,
  7489. $ = Backbone.$,
  7490. Settings;
  7491. /**
  7492. * wp.media.view.Settings
  7493. *
  7494. * @memberOf wp.media.view
  7495. *
  7496. * @class
  7497. * @augments wp.media.View
  7498. * @augments wp.Backbone.View
  7499. * @augments Backbone.View
  7500. */
  7501. Settings = View.extend(/** @lends wp.media.view.Settings.prototype */{
  7502. events: {
  7503. 'click button': 'updateHandler',
  7504. 'change input': 'updateHandler',
  7505. 'change select': 'updateHandler',
  7506. 'change textarea': 'updateHandler'
  7507. },
  7508. initialize: function() {
  7509. this.model = this.model || new Backbone.Model();
  7510. this.listenTo( this.model, 'change', this.updateChanges );
  7511. },
  7512. prepare: function() {
  7513. return _.defaults({
  7514. model: this.model.toJSON()
  7515. }, this.options );
  7516. },
  7517. /**
  7518. * @return {wp.media.view.Settings} Returns itself to allow chaining.
  7519. */
  7520. render: function() {
  7521. View.prototype.render.apply( this, arguments );
  7522. // Select the correct values.
  7523. _( this.model.attributes ).chain().keys().each( this.update, this );
  7524. return this;
  7525. },
  7526. /**
  7527. * @param {string} key
  7528. */
  7529. update: function( key ) {
  7530. var value = this.model.get( key ),
  7531. $setting = this.$('[data-setting="' + key + '"]'),
  7532. $buttons, $value;
  7533. // Bail if we didn't find a matching setting.
  7534. if ( ! $setting.length ) {
  7535. return;
  7536. }
  7537. // Attempt to determine how the setting is rendered and update
  7538. // the selected value.
  7539. // Handle dropdowns.
  7540. if ( $setting.is('select') ) {
  7541. $value = $setting.find('[value="' + value + '"]');
  7542. if ( $value.length ) {
  7543. $setting.find('option').prop( 'selected', false );
  7544. $value.prop( 'selected', true );
  7545. } else {
  7546. // If we can't find the desired value, record what *is* selected.
  7547. this.model.set( key, $setting.find(':selected').val() );
  7548. }
  7549. // Handle button groups.
  7550. } else if ( $setting.hasClass('button-group') ) {
  7551. $buttons = $setting.find( 'button' )
  7552. .removeClass( 'active' )
  7553. .attr( 'aria-pressed', 'false' );
  7554. $buttons.filter( '[value="' + value + '"]' )
  7555. .addClass( 'active' )
  7556. .attr( 'aria-pressed', 'true' );
  7557. // Handle text inputs and textareas.
  7558. } else if ( $setting.is('input[type="text"], textarea') ) {
  7559. if ( ! $setting.is(':focus') ) {
  7560. $setting.val( value );
  7561. }
  7562. // Handle checkboxes.
  7563. } else if ( $setting.is('input[type="checkbox"]') ) {
  7564. $setting.prop( 'checked', !! value && 'false' !== value );
  7565. }
  7566. },
  7567. /**
  7568. * @param {Object} event
  7569. */
  7570. updateHandler: function( event ) {
  7571. var $setting = $( event.target ).closest('[data-setting]'),
  7572. value = event.target.value,
  7573. userSetting;
  7574. event.preventDefault();
  7575. if ( ! $setting.length ) {
  7576. return;
  7577. }
  7578. // Use the correct value for checkboxes.
  7579. if ( $setting.is('input[type="checkbox"]') ) {
  7580. value = $setting[0].checked;
  7581. }
  7582. // Update the corresponding setting.
  7583. this.model.set( $setting.data('setting'), value );
  7584. // If the setting has a corresponding user setting,
  7585. // update that as well.
  7586. userSetting = $setting.data('userSetting');
  7587. if ( userSetting ) {
  7588. window.setUserSetting( userSetting, value );
  7589. }
  7590. },
  7591. updateChanges: function( model ) {
  7592. if ( model.hasChanged() ) {
  7593. _( model.changed ).chain().keys().each( this.update, this );
  7594. }
  7595. }
  7596. });
  7597. module.exports = Settings;
  7598. /***/ }),
  7599. /***/ 2176:
  7600. /***/ (function(module) {
  7601. var Settings = wp.media.view.Settings,
  7602. AttachmentDisplay;
  7603. /**
  7604. * wp.media.view.Settings.AttachmentDisplay
  7605. *
  7606. * @memberOf wp.media.view.Settings
  7607. *
  7608. * @class
  7609. * @augments wp.media.view.Settings
  7610. * @augments wp.media.View
  7611. * @augments wp.Backbone.View
  7612. * @augments Backbone.View
  7613. */
  7614. AttachmentDisplay = Settings.extend(/** @lends wp.media.view.Settings.AttachmentDisplay.prototype */{
  7615. className: 'attachment-display-settings',
  7616. template: wp.template('attachment-display-settings'),
  7617. initialize: function() {
  7618. var attachment = this.options.attachment;
  7619. _.defaults( this.options, {
  7620. userSettings: false
  7621. });
  7622. // Call 'initialize' directly on the parent class.
  7623. Settings.prototype.initialize.apply( this, arguments );
  7624. this.listenTo( this.model, 'change:link', this.updateLinkTo );
  7625. if ( attachment ) {
  7626. attachment.on( 'change:uploading', this.render, this );
  7627. }
  7628. },
  7629. dispose: function() {
  7630. var attachment = this.options.attachment;
  7631. if ( attachment ) {
  7632. attachment.off( null, null, this );
  7633. }
  7634. /**
  7635. * call 'dispose' directly on the parent class
  7636. */
  7637. Settings.prototype.dispose.apply( this, arguments );
  7638. },
  7639. /**
  7640. * @return {wp.media.view.AttachmentDisplay} Returns itself to allow chaining.
  7641. */
  7642. render: function() {
  7643. var attachment = this.options.attachment;
  7644. if ( attachment ) {
  7645. _.extend( this.options, {
  7646. sizes: attachment.get('sizes'),
  7647. type: attachment.get('type')
  7648. });
  7649. }
  7650. /**
  7651. * call 'render' directly on the parent class
  7652. */
  7653. Settings.prototype.render.call( this );
  7654. this.updateLinkTo();
  7655. return this;
  7656. },
  7657. updateLinkTo: function() {
  7658. var linkTo = this.model.get('link'),
  7659. $input = this.$('.link-to-custom'),
  7660. attachment = this.options.attachment;
  7661. if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
  7662. $input.closest( '.setting' ).addClass( 'hidden' );
  7663. return;
  7664. }
  7665. if ( attachment ) {
  7666. if ( 'post' === linkTo ) {
  7667. $input.val( attachment.get('link') );
  7668. } else if ( 'file' === linkTo ) {
  7669. $input.val( attachment.get('url') );
  7670. } else if ( ! this.model.get('linkUrl') ) {
  7671. $input.val('http://');
  7672. }
  7673. $input.prop( 'readonly', 'custom' !== linkTo );
  7674. }
  7675. $input.closest( '.setting' ).removeClass( 'hidden' );
  7676. if ( $input.length ) {
  7677. $input[0].scrollIntoView();
  7678. }
  7679. }
  7680. });
  7681. module.exports = AttachmentDisplay;
  7682. /***/ }),
  7683. /***/ 6872:
  7684. /***/ (function(module) {
  7685. /**
  7686. * wp.media.view.Settings.Gallery
  7687. *
  7688. * @memberOf wp.media.view.Settings
  7689. *
  7690. * @class
  7691. * @augments wp.media.view.Settings
  7692. * @augments wp.media.View
  7693. * @augments wp.Backbone.View
  7694. * @augments Backbone.View
  7695. */
  7696. var Gallery = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Gallery.prototype */{
  7697. className: 'collection-settings gallery-settings',
  7698. template: wp.template('gallery-settings')
  7699. });
  7700. module.exports = Gallery;
  7701. /***/ }),
  7702. /***/ 8488:
  7703. /***/ (function(module) {
  7704. /**
  7705. * wp.media.view.Settings.Playlist
  7706. *
  7707. * @memberOf wp.media.view.Settings
  7708. *
  7709. * @class
  7710. * @augments wp.media.view.Settings
  7711. * @augments wp.media.View
  7712. * @augments wp.Backbone.View
  7713. * @augments Backbone.View
  7714. */
  7715. var Playlist = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Playlist.prototype */{
  7716. className: 'collection-settings playlist-settings',
  7717. template: wp.template('playlist-settings')
  7718. });
  7719. module.exports = Playlist;
  7720. /***/ }),
  7721. /***/ 9799:
  7722. /***/ (function(module) {
  7723. /**
  7724. * wp.media.view.Sidebar
  7725. *
  7726. * @memberOf wp.media.view
  7727. *
  7728. * @class
  7729. * @augments wp.media.view.PriorityList
  7730. * @augments wp.media.View
  7731. * @augments wp.Backbone.View
  7732. * @augments Backbone.View
  7733. */
  7734. var Sidebar = wp.media.view.PriorityList.extend(/** @lends wp.media.view.Sidebar.prototype */{
  7735. className: 'media-sidebar'
  7736. });
  7737. module.exports = Sidebar;
  7738. /***/ }),
  7739. /***/ 5187:
  7740. /***/ (function(module) {
  7741. var View = wp.media.view,
  7742. SiteIconCropper;
  7743. /**
  7744. * wp.media.view.SiteIconCropper
  7745. *
  7746. * Uses the imgAreaSelect plugin to allow a user to crop a Site Icon.
  7747. *
  7748. * Takes imgAreaSelect options from
  7749. * wp.customize.SiteIconControl.calculateImageSelectOptions.
  7750. *
  7751. * @memberOf wp.media.view
  7752. *
  7753. * @class
  7754. * @augments wp.media.view.Cropper
  7755. * @augments wp.media.View
  7756. * @augments wp.Backbone.View
  7757. * @augments Backbone.View
  7758. */
  7759. SiteIconCropper = View.Cropper.extend(/** @lends wp.media.view.SiteIconCropper.prototype */{
  7760. className: 'crop-content site-icon',
  7761. ready: function () {
  7762. View.Cropper.prototype.ready.apply( this, arguments );
  7763. this.$( '.crop-image' ).on( 'load', _.bind( this.addSidebar, this ) );
  7764. },
  7765. addSidebar: function() {
  7766. this.sidebar = new wp.media.view.Sidebar({
  7767. controller: this.controller
  7768. });
  7769. this.sidebar.set( 'preview', new wp.media.view.SiteIconPreview({
  7770. controller: this.controller,
  7771. attachment: this.options.attachment
  7772. }) );
  7773. this.controller.cropperView.views.add( this.sidebar );
  7774. }
  7775. });
  7776. module.exports = SiteIconCropper;
  7777. /***/ }),
  7778. /***/ 8260:
  7779. /***/ (function(module) {
  7780. var View = wp.media.View,
  7781. $ = jQuery,
  7782. SiteIconPreview;
  7783. /**
  7784. * wp.media.view.SiteIconPreview
  7785. *
  7786. * Shows a preview of the Site Icon as a favicon and app icon while cropping.
  7787. *
  7788. * @memberOf wp.media.view
  7789. *
  7790. * @class
  7791. * @augments wp.media.View
  7792. * @augments wp.Backbone.View
  7793. * @augments Backbone.View
  7794. */
  7795. SiteIconPreview = View.extend(/** @lends wp.media.view.SiteIconPreview.prototype */{
  7796. className: 'site-icon-preview',
  7797. template: wp.template( 'site-icon-preview' ),
  7798. ready: function() {
  7799. this.controller.imgSelect.setOptions({
  7800. onInit: this.updatePreview,
  7801. onSelectChange: this.updatePreview
  7802. });
  7803. },
  7804. prepare: function() {
  7805. return {
  7806. url: this.options.attachment.get( 'url' )
  7807. };
  7808. },
  7809. updatePreview: function( img, coords ) {
  7810. var rx = 64 / coords.width,
  7811. ry = 64 / coords.height,
  7812. preview_rx = 16 / coords.width,
  7813. preview_ry = 16 / coords.height;
  7814. $( '#preview-app-icon' ).css({
  7815. width: Math.round(rx * this.imageWidth ) + 'px',
  7816. height: Math.round(ry * this.imageHeight ) + 'px',
  7817. marginLeft: '-' + Math.round(rx * coords.x1) + 'px',
  7818. marginTop: '-' + Math.round(ry * coords.y1) + 'px'
  7819. });
  7820. $( '#preview-favicon' ).css({
  7821. width: Math.round( preview_rx * this.imageWidth ) + 'px',
  7822. height: Math.round( preview_ry * this.imageHeight ) + 'px',
  7823. marginLeft: '-' + Math.round( preview_rx * coords.x1 ) + 'px',
  7824. marginTop: '-' + Math.floor( preview_ry* coords.y1 ) + 'px'
  7825. });
  7826. }
  7827. });
  7828. module.exports = SiteIconPreview;
  7829. /***/ }),
  7830. /***/ 2234:
  7831. /***/ (function(module) {
  7832. /**
  7833. * wp.media.view.Spinner
  7834. *
  7835. * Represents a spinner in the Media Library.
  7836. *
  7837. * @since 3.9.0
  7838. *
  7839. * @memberOf wp.media.view
  7840. *
  7841. * @class
  7842. * @augments wp.media.View
  7843. * @augments wp.Backbone.View
  7844. * @augments Backbone.View
  7845. */
  7846. var Spinner = wp.media.View.extend(/** @lends wp.media.view.Spinner.prototype */{
  7847. tagName: 'span',
  7848. className: 'spinner',
  7849. spinnerTimeout: false,
  7850. delay: 400,
  7851. /**
  7852. * Shows the spinner. Delays the visibility by the configured amount.
  7853. *
  7854. * @since 3.9.0
  7855. *
  7856. * @return {wp.media.view.Spinner} The spinner.
  7857. */
  7858. show: function() {
  7859. if ( ! this.spinnerTimeout ) {
  7860. this.spinnerTimeout = _.delay(function( $el ) {
  7861. $el.addClass( 'is-active' );
  7862. }, this.delay, this.$el );
  7863. }
  7864. return this;
  7865. },
  7866. /**
  7867. * Hides the spinner.
  7868. *
  7869. * @since 3.9.0
  7870. *
  7871. * @return {wp.media.view.Spinner} The spinner.
  7872. */
  7873. hide: function() {
  7874. this.$el.removeClass( 'is-active' );
  7875. this.spinnerTimeout = clearTimeout( this.spinnerTimeout );
  7876. return this;
  7877. }
  7878. });
  7879. module.exports = Spinner;
  7880. /***/ }),
  7881. /***/ 9510:
  7882. /***/ (function(module) {
  7883. var View = wp.media.View,
  7884. Toolbar;
  7885. /**
  7886. * wp.media.view.Toolbar
  7887. *
  7888. * A toolbar which consists of a primary and a secondary section. Each sections
  7889. * can be filled with views.
  7890. *
  7891. * @memberOf wp.media.view
  7892. *
  7893. * @class
  7894. * @augments wp.media.View
  7895. * @augments wp.Backbone.View
  7896. * @augments Backbone.View
  7897. */
  7898. Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{
  7899. tagName: 'div',
  7900. className: 'media-toolbar',
  7901. initialize: function() {
  7902. var state = this.controller.state(),
  7903. selection = this.selection = state.get('selection'),
  7904. library = this.library = state.get('library');
  7905. this._views = {};
  7906. // The toolbar is composed of two `PriorityList` views.
  7907. this.primary = new wp.media.view.PriorityList();
  7908. this.secondary = new wp.media.view.PriorityList();
  7909. this.primary.$el.addClass('media-toolbar-primary search-form');
  7910. this.secondary.$el.addClass('media-toolbar-secondary');
  7911. this.views.set([ this.secondary, this.primary ]);
  7912. if ( this.options.items ) {
  7913. this.set( this.options.items, { silent: true });
  7914. }
  7915. if ( ! this.options.silent ) {
  7916. this.render();
  7917. }
  7918. if ( selection ) {
  7919. selection.on( 'add remove reset', this.refresh, this );
  7920. }
  7921. if ( library ) {
  7922. library.on( 'add remove reset', this.refresh, this );
  7923. }
  7924. },
  7925. /**
  7926. * @return {wp.media.view.Toolbar} Returns itsef to allow chaining
  7927. */
  7928. dispose: function() {
  7929. if ( this.selection ) {
  7930. this.selection.off( null, null, this );
  7931. }
  7932. if ( this.library ) {
  7933. this.library.off( null, null, this );
  7934. }
  7935. /**
  7936. * call 'dispose' directly on the parent class
  7937. */
  7938. return View.prototype.dispose.apply( this, arguments );
  7939. },
  7940. ready: function() {
  7941. this.refresh();
  7942. },
  7943. /**
  7944. * @param {string} id
  7945. * @param {Backbone.View|Object} view
  7946. * @param {Object} [options={}]
  7947. * @return {wp.media.view.Toolbar} Returns itself to allow chaining.
  7948. */
  7949. set: function( id, view, options ) {
  7950. var list;
  7951. options = options || {};
  7952. // Accept an object with an `id` : `view` mapping.
  7953. if ( _.isObject( id ) ) {
  7954. _.each( id, function( view, id ) {
  7955. this.set( id, view, { silent: true });
  7956. }, this );
  7957. } else {
  7958. if ( ! ( view instanceof Backbone.View ) ) {
  7959. view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
  7960. view = new wp.media.view.Button( view ).render();
  7961. }
  7962. view.controller = view.controller || this.controller;
  7963. this._views[ id ] = view;
  7964. list = view.options.priority < 0 ? 'secondary' : 'primary';
  7965. this[ list ].set( id, view, options );
  7966. }
  7967. if ( ! options.silent ) {
  7968. this.refresh();
  7969. }
  7970. return this;
  7971. },
  7972. /**
  7973. * @param {string} id
  7974. * @return {wp.media.view.Button}
  7975. */
  7976. get: function( id ) {
  7977. return this._views[ id ];
  7978. },
  7979. /**
  7980. * @param {string} id
  7981. * @param {Object} options
  7982. * @return {wp.media.view.Toolbar} Returns itself to allow chaining.
  7983. */
  7984. unset: function( id, options ) {
  7985. delete this._views[ id ];
  7986. this.primary.unset( id, options );
  7987. this.secondary.unset( id, options );
  7988. if ( ! options || ! options.silent ) {
  7989. this.refresh();
  7990. }
  7991. return this;
  7992. },
  7993. refresh: function() {
  7994. var state = this.controller.state(),
  7995. library = state.get('library'),
  7996. selection = state.get('selection');
  7997. _.each( this._views, function( button ) {
  7998. if ( ! button.model || ! button.options || ! button.options.requires ) {
  7999. return;
  8000. }
  8001. var requires = button.options.requires,
  8002. disabled = false;
  8003. // Prevent insertion of attachments if any of them are still uploading.
  8004. if ( selection && selection.models ) {
  8005. disabled = _.some( selection.models, function( attachment ) {
  8006. return attachment.get('uploading') === true;
  8007. });
  8008. }
  8009. if ( requires.selection && selection && ! selection.length ) {
  8010. disabled = true;
  8011. } else if ( requires.library && library && ! library.length ) {
  8012. disabled = true;
  8013. }
  8014. button.model.set( 'disabled', disabled );
  8015. });
  8016. }
  8017. });
  8018. module.exports = Toolbar;
  8019. /***/ }),
  8020. /***/ 7128:
  8021. /***/ (function(module) {
  8022. var Select = wp.media.view.Toolbar.Select,
  8023. l10n = wp.media.view.l10n,
  8024. Embed;
  8025. /**
  8026. * wp.media.view.Toolbar.Embed
  8027. *
  8028. * @memberOf wp.media.view.Toolbar
  8029. *
  8030. * @class
  8031. * @augments wp.media.view.Toolbar.Select
  8032. * @augments wp.media.view.Toolbar
  8033. * @augments wp.media.View
  8034. * @augments wp.Backbone.View
  8035. * @augments Backbone.View
  8036. */
  8037. Embed = Select.extend(/** @lends wp.media.view.Toolbar.Embed.prototype */{
  8038. initialize: function() {
  8039. _.defaults( this.options, {
  8040. text: l10n.insertIntoPost,
  8041. requires: false
  8042. });
  8043. // Call 'initialize' directly on the parent class.
  8044. Select.prototype.initialize.apply( this, arguments );
  8045. },
  8046. refresh: function() {
  8047. var url = this.controller.state().props.get('url');
  8048. this.get('select').model.set( 'disabled', ! url || url === 'http://' );
  8049. /**
  8050. * call 'refresh' directly on the parent class
  8051. */
  8052. Select.prototype.refresh.apply( this, arguments );
  8053. }
  8054. });
  8055. module.exports = Embed;
  8056. /***/ }),
  8057. /***/ 6850:
  8058. /***/ (function(module) {
  8059. var Toolbar = wp.media.view.Toolbar,
  8060. l10n = wp.media.view.l10n,
  8061. Select;
  8062. /**
  8063. * wp.media.view.Toolbar.Select
  8064. *
  8065. * @memberOf wp.media.view.Toolbar
  8066. *
  8067. * @class
  8068. * @augments wp.media.view.Toolbar
  8069. * @augments wp.media.View
  8070. * @augments wp.Backbone.View
  8071. * @augments Backbone.View
  8072. */
  8073. Select = Toolbar.extend(/** @lends wp.media.view.Toolbar.Select.prototype */{
  8074. initialize: function() {
  8075. var options = this.options;
  8076. _.bindAll( this, 'clickSelect' );
  8077. _.defaults( options, {
  8078. event: 'select',
  8079. state: false,
  8080. reset: true,
  8081. close: true,
  8082. text: l10n.select,
  8083. // Does the button rely on the selection?
  8084. requires: {
  8085. selection: true
  8086. }
  8087. });
  8088. options.items = _.defaults( options.items || {}, {
  8089. select: {
  8090. style: 'primary',
  8091. text: options.text,
  8092. priority: 80,
  8093. click: this.clickSelect,
  8094. requires: options.requires
  8095. }
  8096. });
  8097. // Call 'initialize' directly on the parent class.
  8098. Toolbar.prototype.initialize.apply( this, arguments );
  8099. },
  8100. clickSelect: function() {
  8101. var options = this.options,
  8102. controller = this.controller;
  8103. if ( options.close ) {
  8104. controller.close();
  8105. }
  8106. if ( options.event ) {
  8107. controller.state().trigger( options.event );
  8108. }
  8109. if ( options.state ) {
  8110. controller.setState( options.state );
  8111. }
  8112. if ( options.reset ) {
  8113. controller.reset();
  8114. }
  8115. }
  8116. });
  8117. module.exports = Select;
  8118. /***/ }),
  8119. /***/ 841:
  8120. /***/ (function(module) {
  8121. var View = wp.media.View,
  8122. l10n = wp.media.view.l10n,
  8123. $ = jQuery,
  8124. EditorUploader;
  8125. /**
  8126. * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap)
  8127. * and relays drag'n'dropped files to a media workflow.
  8128. *
  8129. * wp.media.view.EditorUploader
  8130. *
  8131. * @memberOf wp.media.view
  8132. *
  8133. * @class
  8134. * @augments wp.media.View
  8135. * @augments wp.Backbone.View
  8136. * @augments Backbone.View
  8137. */
  8138. EditorUploader = View.extend(/** @lends wp.media.view.EditorUploader.prototype */{
  8139. tagName: 'div',
  8140. className: 'uploader-editor',
  8141. template: wp.template( 'uploader-editor' ),
  8142. localDrag: false,
  8143. overContainer: false,
  8144. overDropzone: false,
  8145. draggingFile: null,
  8146. /**
  8147. * Bind drag'n'drop events to callbacks.
  8148. */
  8149. initialize: function() {
  8150. this.initialized = false;
  8151. // Bail if not enabled or UA does not support drag'n'drop or File API.
  8152. if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) {
  8153. return this;
  8154. }
  8155. this.$document = $(document);
  8156. this.dropzones = [];
  8157. this.files = [];
  8158. this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) );
  8159. this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) );
  8160. this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) );
  8161. this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) );
  8162. this.$document.on( 'dragover', _.bind( this.containerDragover, this ) );
  8163. this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) );
  8164. this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {
  8165. this.localDrag = event.type === 'dragstart';
  8166. if ( event.type === 'drop' ) {
  8167. this.containerDragleave();
  8168. }
  8169. }, this ) );
  8170. this.initialized = true;
  8171. return this;
  8172. },
  8173. /**
  8174. * Check browser support for drag'n'drop.
  8175. *
  8176. * @return {boolean}
  8177. */
  8178. browserSupport: function() {
  8179. var supports = false, div = document.createElement('div');
  8180. supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div );
  8181. supports = supports && !! ( window.File && window.FileList && window.FileReader );
  8182. return supports;
  8183. },
  8184. isDraggingFile: function( event ) {
  8185. if ( this.draggingFile !== null ) {
  8186. return this.draggingFile;
  8187. }
  8188. if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) {
  8189. return false;
  8190. }
  8191. this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 &&
  8192. _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1;
  8193. return this.draggingFile;
  8194. },
  8195. refresh: function( e ) {
  8196. var dropzone_id;
  8197. for ( dropzone_id in this.dropzones ) {
  8198. // Hide the dropzones only if dragging has left the screen.
  8199. this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone );
  8200. }
  8201. if ( ! _.isUndefined( e ) ) {
  8202. $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone );
  8203. }
  8204. if ( ! this.overContainer && ! this.overDropzone ) {
  8205. this.draggingFile = null;
  8206. }
  8207. return this;
  8208. },
  8209. render: function() {
  8210. if ( ! this.initialized ) {
  8211. return this;
  8212. }
  8213. View.prototype.render.apply( this, arguments );
  8214. $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );
  8215. return this;
  8216. },
  8217. attach: function( index, editor ) {
  8218. // Attach a dropzone to an editor.
  8219. var dropzone = this.$el.clone();
  8220. this.dropzones.push( dropzone );
  8221. $( editor ).append( dropzone );
  8222. return this;
  8223. },
  8224. /**
  8225. * When a file is dropped on the editor uploader, open up an editor media workflow
  8226. * and upload the file immediately.
  8227. *
  8228. * @param {jQuery.Event} event The 'drop' event.
  8229. */
  8230. drop: function( event ) {
  8231. var $wrap, uploadView;
  8232. this.containerDragleave( event );
  8233. this.dropzoneDragleave( event );
  8234. this.files = event.originalEvent.dataTransfer.files;
  8235. if ( this.files.length < 1 ) {
  8236. return;
  8237. }
  8238. // Set the active editor to the drop target.
  8239. $wrap = $( event.target ).parents( '.wp-editor-wrap' );
  8240. if ( $wrap.length > 0 && $wrap[0].id ) {
  8241. window.wpActiveEditor = $wrap[0].id.slice( 3, -5 );
  8242. }
  8243. if ( ! this.workflow ) {
  8244. this.workflow = wp.media.editor.open( window.wpActiveEditor, {
  8245. frame: 'post',
  8246. state: 'insert',
  8247. title: l10n.addMedia,
  8248. multiple: true
  8249. });
  8250. uploadView = this.workflow.uploader;
  8251. if ( uploadView.uploader && uploadView.uploader.ready ) {
  8252. this.addFiles.apply( this );
  8253. } else {
  8254. this.workflow.on( 'uploader:ready', this.addFiles, this );
  8255. }
  8256. } else {
  8257. this.workflow.state().reset();
  8258. this.addFiles.apply( this );
  8259. this.workflow.open();
  8260. }
  8261. return false;
  8262. },
  8263. /**
  8264. * Add the files to the uploader.
  8265. */
  8266. addFiles: function() {
  8267. if ( this.files.length ) {
  8268. this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
  8269. this.files = [];
  8270. }
  8271. return this;
  8272. },
  8273. containerDragover: function( event ) {
  8274. if ( this.localDrag || ! this.isDraggingFile( event ) ) {
  8275. return;
  8276. }
  8277. this.overContainer = true;
  8278. this.refresh();
  8279. },
  8280. containerDragleave: function() {
  8281. this.overContainer = false;
  8282. // Throttle dragleave because it's called when bouncing from some elements to others.
  8283. _.delay( _.bind( this.refresh, this ), 50 );
  8284. },
  8285. dropzoneDragover: function( event ) {
  8286. if ( this.localDrag || ! this.isDraggingFile( event ) ) {
  8287. return;
  8288. }
  8289. this.overDropzone = true;
  8290. this.refresh( event );
  8291. return false;
  8292. },
  8293. dropzoneDragleave: function( e ) {
  8294. this.overDropzone = false;
  8295. _.delay( _.bind( this.refresh, this, e ), 50 );
  8296. },
  8297. click: function( e ) {
  8298. // In the rare case where the dropzone gets stuck, hide it on click.
  8299. this.containerDragleave( e );
  8300. this.dropzoneDragleave( e );
  8301. this.localDrag = false;
  8302. }
  8303. });
  8304. module.exports = EditorUploader;
  8305. /***/ }),
  8306. /***/ 6353:
  8307. /***/ (function(module) {
  8308. var View = wp.media.View,
  8309. UploaderInline;
  8310. /**
  8311. * wp.media.view.UploaderInline
  8312. *
  8313. * The inline uploader that shows up in the 'Upload Files' tab.
  8314. *
  8315. * @memberOf wp.media.view
  8316. *
  8317. * @class
  8318. * @augments wp.media.View
  8319. * @augments wp.Backbone.View
  8320. * @augments Backbone.View
  8321. */
  8322. UploaderInline = View.extend(/** @lends wp.media.view.UploaderInline.prototype */{
  8323. tagName: 'div',
  8324. className: 'uploader-inline',
  8325. template: wp.template('uploader-inline'),
  8326. events: {
  8327. 'click .close': 'hide'
  8328. },
  8329. initialize: function() {
  8330. _.defaults( this.options, {
  8331. message: '',
  8332. status: true,
  8333. canClose: false
  8334. });
  8335. if ( ! this.options.$browser && this.controller.uploader ) {
  8336. this.options.$browser = this.controller.uploader.$browser;
  8337. }
  8338. if ( _.isUndefined( this.options.postId ) ) {
  8339. this.options.postId = wp.media.view.settings.post.id;
  8340. }
  8341. if ( this.options.status ) {
  8342. this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({
  8343. controller: this.controller
  8344. }) );
  8345. }
  8346. },
  8347. prepare: function() {
  8348. var suggestedWidth = this.controller.state().get('suggestedWidth'),
  8349. suggestedHeight = this.controller.state().get('suggestedHeight'),
  8350. data = {};
  8351. data.message = this.options.message;
  8352. data.canClose = this.options.canClose;
  8353. if ( suggestedWidth && suggestedHeight ) {
  8354. data.suggestedWidth = suggestedWidth;
  8355. data.suggestedHeight = suggestedHeight;
  8356. }
  8357. return data;
  8358. },
  8359. /**
  8360. * @return {wp.media.view.UploaderInline} Returns itself to allow chaining.
  8361. */
  8362. dispose: function() {
  8363. if ( this.disposing ) {
  8364. /**
  8365. * call 'dispose' directly on the parent class
  8366. */
  8367. return View.prototype.dispose.apply( this, arguments );
  8368. }
  8369. /*
  8370. * Run remove on `dispose`, so we can be sure to refresh the
  8371. * uploader with a view-less DOM. Track whether we're disposing
  8372. * so we don't trigger an infinite loop.
  8373. */
  8374. this.disposing = true;
  8375. return this.remove();
  8376. },
  8377. /**
  8378. * @return {wp.media.view.UploaderInline} Returns itself to allow chaining.
  8379. */
  8380. remove: function() {
  8381. /**
  8382. * call 'remove' directly on the parent class
  8383. */
  8384. var result = View.prototype.remove.apply( this, arguments );
  8385. _.defer( _.bind( this.refresh, this ) );
  8386. return result;
  8387. },
  8388. refresh: function() {
  8389. var uploader = this.controller.uploader;
  8390. if ( uploader ) {
  8391. uploader.refresh();
  8392. }
  8393. },
  8394. /**
  8395. * @return {wp.media.view.UploaderInline}
  8396. */
  8397. ready: function() {
  8398. var $browser = this.options.$browser,
  8399. $placeholder;
  8400. if ( this.controller.uploader ) {
  8401. $placeholder = this.$('.browser');
  8402. // Check if we've already replaced the placeholder.
  8403. if ( $placeholder[0] === $browser[0] ) {
  8404. return;
  8405. }
  8406. $browser.detach().text( $placeholder.text() );
  8407. $browser[0].className = $placeholder[0].className;
  8408. $browser[0].setAttribute( 'aria-labelledby', $browser[0].id + ' ' + $placeholder[0].getAttribute('aria-labelledby') );
  8409. $placeholder.replaceWith( $browser.show() );
  8410. }
  8411. this.refresh();
  8412. return this;
  8413. },
  8414. show: function() {
  8415. this.$el.removeClass( 'hidden' );
  8416. if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
  8417. this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' );
  8418. }
  8419. },
  8420. hide: function() {
  8421. this.$el.addClass( 'hidden' );
  8422. if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
  8423. this.controller.$uploaderToggler
  8424. .attr( 'aria-expanded', 'false' )
  8425. // Move focus back to the toggle button when closing the uploader.
  8426. .trigger( 'focus' );
  8427. }
  8428. }
  8429. });
  8430. module.exports = UploaderInline;
  8431. /***/ }),
  8432. /***/ 9411:
  8433. /***/ (function(module) {
  8434. /**
  8435. * wp.media.view.UploaderStatusError
  8436. *
  8437. * @memberOf wp.media.view
  8438. *
  8439. * @class
  8440. * @augments wp.media.View
  8441. * @augments wp.Backbone.View
  8442. * @augments Backbone.View
  8443. */
  8444. var UploaderStatusError = wp.media.View.extend(/** @lends wp.media.view.UploaderStatusError.prototype */{
  8445. className: 'upload-error',
  8446. template: wp.template('uploader-status-error')
  8447. });
  8448. module.exports = UploaderStatusError;
  8449. /***/ }),
  8450. /***/ 2894:
  8451. /***/ (function(module) {
  8452. var View = wp.media.View,
  8453. UploaderStatus;
  8454. /**
  8455. * wp.media.view.UploaderStatus
  8456. *
  8457. * An uploader status for on-going uploads.
  8458. *
  8459. * @memberOf wp.media.view
  8460. *
  8461. * @class
  8462. * @augments wp.media.View
  8463. * @augments wp.Backbone.View
  8464. * @augments Backbone.View
  8465. */
  8466. UploaderStatus = View.extend(/** @lends wp.media.view.UploaderStatus.prototype */{
  8467. className: 'media-uploader-status',
  8468. template: wp.template('uploader-status'),
  8469. events: {
  8470. 'click .upload-dismiss-errors': 'dismiss'
  8471. },
  8472. initialize: function() {
  8473. this.queue = wp.Uploader.queue;
  8474. this.queue.on( 'add remove reset', this.visibility, this );
  8475. this.queue.on( 'add remove reset change:percent', this.progress, this );
  8476. this.queue.on( 'add remove reset change:uploading', this.info, this );
  8477. this.errors = wp.Uploader.errors;
  8478. this.errors.reset();
  8479. this.errors.on( 'add remove reset', this.visibility, this );
  8480. this.errors.on( 'add', this.error, this );
  8481. },
  8482. /**
  8483. * @return {wp.media.view.UploaderStatus}
  8484. */
  8485. dispose: function() {
  8486. wp.Uploader.queue.off( null, null, this );
  8487. /**
  8488. * call 'dispose' directly on the parent class
  8489. */
  8490. View.prototype.dispose.apply( this, arguments );
  8491. return this;
  8492. },
  8493. visibility: function() {
  8494. this.$el.toggleClass( 'uploading', !! this.queue.length );
  8495. this.$el.toggleClass( 'errors', !! this.errors.length );
  8496. this.$el.toggle( !! this.queue.length || !! this.errors.length );
  8497. },
  8498. ready: function() {
  8499. _.each({
  8500. '$bar': '.media-progress-bar div',
  8501. '$index': '.upload-index',
  8502. '$total': '.upload-total',
  8503. '$filename': '.upload-filename'
  8504. }, function( selector, key ) {
  8505. this[ key ] = this.$( selector );
  8506. }, this );
  8507. this.visibility();
  8508. this.progress();
  8509. this.info();
  8510. },
  8511. progress: function() {
  8512. var queue = this.queue,
  8513. $bar = this.$bar;
  8514. if ( ! $bar || ! queue.length ) {
  8515. return;
  8516. }
  8517. $bar.width( ( queue.reduce( function( memo, attachment ) {
  8518. if ( ! attachment.get('uploading') ) {
  8519. return memo + 100;
  8520. }
  8521. var percent = attachment.get('percent');
  8522. return memo + ( _.isNumber( percent ) ? percent : 100 );
  8523. }, 0 ) / queue.length ) + '%' );
  8524. },
  8525. info: function() {
  8526. var queue = this.queue,
  8527. index = 0, active;
  8528. if ( ! queue.length ) {
  8529. return;
  8530. }
  8531. active = this.queue.find( function( attachment, i ) {
  8532. index = i;
  8533. return attachment.get('uploading');
  8534. });
  8535. if ( this.$index && this.$total && this.$filename ) {
  8536. this.$index.text( index + 1 );
  8537. this.$total.text( queue.length );
  8538. this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
  8539. }
  8540. },
  8541. /**
  8542. * @param {string} filename
  8543. * @return {string}
  8544. */
  8545. filename: function( filename ) {
  8546. return _.escape( filename );
  8547. },
  8548. /**
  8549. * @param {Backbone.Model} error
  8550. */
  8551. error: function( error ) {
  8552. var statusError = new wp.media.view.UploaderStatusError( {
  8553. filename: this.filename( error.get( 'file' ).name ),
  8554. message: error.get( 'message' )
  8555. } );
  8556. var buttonClose = this.$el.find( 'button' );
  8557. // Can show additional info here while retrying to create image sub-sizes.
  8558. this.views.add( '.upload-errors', statusError, { at: 0 } );
  8559. _.delay( function() {
  8560. buttonClose.trigger( 'focus' );
  8561. wp.a11y.speak( error.get( 'message' ), 'assertive' );
  8562. }, 1000 );
  8563. },
  8564. dismiss: function() {
  8565. var errors = this.views.get('.upload-errors');
  8566. if ( errors ) {
  8567. _.invoke( errors, 'remove' );
  8568. }
  8569. wp.Uploader.errors.reset();
  8570. // Move focus to the modal after the dismiss button gets removed from the DOM.
  8571. if ( this.controller.modal ) {
  8572. this.controller.modal.focusManager.focus();
  8573. }
  8574. }
  8575. });
  8576. module.exports = UploaderStatus;
  8577. /***/ }),
  8578. /***/ 5823:
  8579. /***/ (function(module) {
  8580. var $ = jQuery,
  8581. UploaderWindow;
  8582. /**
  8583. * wp.media.view.UploaderWindow
  8584. *
  8585. * An uploader window that allows for dragging and dropping media.
  8586. *
  8587. * @memberOf wp.media.view
  8588. *
  8589. * @class
  8590. * @augments wp.media.View
  8591. * @augments wp.Backbone.View
  8592. * @augments Backbone.View
  8593. *
  8594. * @param {object} [options] Options hash passed to the view.
  8595. * @param {object} [options.uploader] Uploader properties.
  8596. * @param {jQuery} [options.uploader.browser]
  8597. * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
  8598. * @param {object} [options.uploader.params]
  8599. */
  8600. UploaderWindow = wp.media.View.extend(/** @lends wp.media.view.UploaderWindow.prototype */{
  8601. tagName: 'div',
  8602. className: 'uploader-window',
  8603. template: wp.template('uploader-window'),
  8604. initialize: function() {
  8605. var uploader;
  8606. this.$browser = $( '<button type="button" class="browser" />' ).hide().appendTo( 'body' );
  8607. uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
  8608. dropzone: this.$el,
  8609. browser: this.$browser,
  8610. params: {}
  8611. });
  8612. // Ensure the dropzone is a jQuery collection.
  8613. if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
  8614. uploader.dropzone = $( uploader.dropzone );
  8615. }
  8616. this.controller.on( 'activate', this.refresh, this );
  8617. this.controller.on( 'detach', function() {
  8618. this.$browser.remove();
  8619. }, this );
  8620. },
  8621. refresh: function() {
  8622. if ( this.uploader ) {
  8623. this.uploader.refresh();
  8624. }
  8625. },
  8626. ready: function() {
  8627. var postId = wp.media.view.settings.post.id,
  8628. dropzone;
  8629. // If the uploader already exists, bail.
  8630. if ( this.uploader ) {
  8631. return;
  8632. }
  8633. if ( postId ) {
  8634. this.options.uploader.params.post_id = postId;
  8635. }
  8636. this.uploader = new wp.Uploader( this.options.uploader );
  8637. dropzone = this.uploader.dropzone;
  8638. dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
  8639. dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
  8640. $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
  8641. },
  8642. _ready: function() {
  8643. this.controller.trigger( 'uploader:ready' );
  8644. },
  8645. show: function() {
  8646. var $el = this.$el.show();
  8647. // Ensure that the animation is triggered by waiting until
  8648. // the transparent element is painted into the DOM.
  8649. _.defer( function() {
  8650. $el.css({ opacity: 1 });
  8651. });
  8652. },
  8653. hide: function() {
  8654. var $el = this.$el.css({ opacity: 0 });
  8655. wp.media.transition( $el ).done( function() {
  8656. // Transition end events are subject to race conditions.
  8657. // Make sure that the value is set as intended.
  8658. if ( '0' === $el.css('opacity') ) {
  8659. $el.hide();
  8660. }
  8661. });
  8662. // https://core.trac.wordpress.org/ticket/27341
  8663. _.delay( function() {
  8664. if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
  8665. $el.hide();
  8666. }
  8667. }, 500 );
  8668. }
  8669. });
  8670. module.exports = UploaderWindow;
  8671. /***/ }),
  8672. /***/ 487:
  8673. /***/ (function(module) {
  8674. /**
  8675. * wp.media.View
  8676. *
  8677. * The base view class for media.
  8678. *
  8679. * Undelegating events, removing events from the model, and
  8680. * removing events from the controller mirror the code for
  8681. * `Backbone.View.dispose` in Backbone 0.9.8 development.
  8682. *
  8683. * This behavior has since been removed, and should not be used
  8684. * outside of the media manager.
  8685. *
  8686. * @memberOf wp.media
  8687. *
  8688. * @class
  8689. * @augments wp.Backbone.View
  8690. * @augments Backbone.View
  8691. */
  8692. var View = wp.Backbone.View.extend(/** @lends wp.media.View.prototype */{
  8693. constructor: function( options ) {
  8694. if ( options && options.controller ) {
  8695. this.controller = options.controller;
  8696. }
  8697. wp.Backbone.View.apply( this, arguments );
  8698. },
  8699. /**
  8700. * @todo The internal comment mentions this might have been a stop-gap
  8701. * before Backbone 0.9.8 came out. Figure out if Backbone core takes
  8702. * care of this in Backbone.View now.
  8703. *
  8704. * @return {wp.media.View} Returns itself to allow chaining.
  8705. */
  8706. dispose: function() {
  8707. /*
  8708. * Undelegating events, removing events from the model, and
  8709. * removing events from the controller mirror the code for
  8710. * `Backbone.View.dispose` in Backbone 0.9.8 development.
  8711. */
  8712. this.undelegateEvents();
  8713. if ( this.model && this.model.off ) {
  8714. this.model.off( null, null, this );
  8715. }
  8716. if ( this.collection && this.collection.off ) {
  8717. this.collection.off( null, null, this );
  8718. }
  8719. // Unbind controller events.
  8720. if ( this.controller && this.controller.off ) {
  8721. this.controller.off( null, null, this );
  8722. }
  8723. return this;
  8724. },
  8725. /**
  8726. * @return {wp.media.View} Returns itself to allow chaining.
  8727. */
  8728. remove: function() {
  8729. this.dispose();
  8730. /**
  8731. * call 'remove' directly on the parent class
  8732. */
  8733. return wp.Backbone.View.prototype.remove.apply( this, arguments );
  8734. }
  8735. });
  8736. module.exports = View;
  8737. /***/ })
  8738. /******/ });
  8739. /************************************************************************/
  8740. /******/ // The module cache
  8741. /******/ var __webpack_module_cache__ = {};
  8742. /******/
  8743. /******/ // The require function
  8744. /******/ function __webpack_require__(moduleId) {
  8745. /******/ // Check if module is in cache
  8746. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  8747. /******/ if (cachedModule !== undefined) {
  8748. /******/ return cachedModule.exports;
  8749. /******/ }
  8750. /******/ // Create a new module (and put it into the cache)
  8751. /******/ var module = __webpack_module_cache__[moduleId] = {
  8752. /******/ // no module.id needed
  8753. /******/ // no module.loaded needed
  8754. /******/ exports: {}
  8755. /******/ };
  8756. /******/
  8757. /******/ // Execute the module function
  8758. /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  8759. /******/
  8760. /******/ // Return the exports of the module
  8761. /******/ return module.exports;
  8762. /******/ }
  8763. /******/
  8764. /************************************************************************/
  8765. var __webpack_exports__ = {};
  8766. // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  8767. !function() {
  8768. /**
  8769. * @output wp-includes/js/media-views.js
  8770. */
  8771. var media = wp.media,
  8772. $ = jQuery,
  8773. l10n;
  8774. media.isTouchDevice = ( 'ontouchend' in document );
  8775. // Link any localized strings.
  8776. l10n = media.view.l10n = window._wpMediaViewsL10n || {};
  8777. // Link any settings.
  8778. media.view.settings = l10n.settings || {};
  8779. delete l10n.settings;
  8780. // Copy the `post` setting over to the model settings.
  8781. media.model.settings.post = media.view.settings.post;
  8782. // Check if the browser supports CSS 3.0 transitions.
  8783. $.support.transition = (function(){
  8784. var style = document.documentElement.style,
  8785. transitions = {
  8786. WebkitTransition: 'webkitTransitionEnd',
  8787. MozTransition: 'transitionend',
  8788. OTransition: 'oTransitionEnd otransitionend',
  8789. transition: 'transitionend'
  8790. }, transition;
  8791. transition = _.find( _.keys( transitions ), function( transition ) {
  8792. return ! _.isUndefined( style[ transition ] );
  8793. });
  8794. return transition && {
  8795. end: transitions[ transition ]
  8796. };
  8797. }());
  8798. /**
  8799. * A shared event bus used to provide events into
  8800. * the media workflows that 3rd-party devs can use to hook
  8801. * in.
  8802. */
  8803. media.events = _.extend( {}, Backbone.Events );
  8804. /**
  8805. * Makes it easier to bind events using transitions.
  8806. *
  8807. * @param {string} selector
  8808. * @param {number} sensitivity
  8809. * @return {Promise}
  8810. */
  8811. media.transition = function( selector, sensitivity ) {
  8812. var deferred = $.Deferred();
  8813. sensitivity = sensitivity || 2000;
  8814. if ( $.support.transition ) {
  8815. if ( ! (selector instanceof $) ) {
  8816. selector = $( selector );
  8817. }
  8818. // Resolve the deferred when the first element finishes animating.
  8819. selector.first().one( $.support.transition.end, deferred.resolve );
  8820. // Just in case the event doesn't trigger, fire a callback.
  8821. _.delay( deferred.resolve, sensitivity );
  8822. // Otherwise, execute on the spot.
  8823. } else {
  8824. deferred.resolve();
  8825. }
  8826. return deferred.promise();
  8827. };
  8828. media.controller.Region = __webpack_require__( 4903 );
  8829. media.controller.StateMachine = __webpack_require__( 5466 );
  8830. media.controller.State = __webpack_require__( 5826 );
  8831. media.selectionSync = __webpack_require__( 3526 );
  8832. media.controller.Library = __webpack_require__( 9024 );
  8833. media.controller.ImageDetails = __webpack_require__( 3849 );
  8834. media.controller.GalleryEdit = __webpack_require__( 6328 );
  8835. media.controller.GalleryAdd = __webpack_require__( 7323 );
  8836. media.controller.CollectionEdit = __webpack_require__( 1817 );
  8837. media.controller.CollectionAdd = __webpack_require__( 1517 );
  8838. media.controller.FeaturedImage = __webpack_require__( 5095 );
  8839. media.controller.ReplaceImage = __webpack_require__( 8493 );
  8840. media.controller.EditImage = __webpack_require__( 7658 );
  8841. media.controller.MediaLibrary = __webpack_require__( 3742 );
  8842. media.controller.Embed = __webpack_require__( 9067 );
  8843. media.controller.Cropper = __webpack_require__( 2288 );
  8844. media.controller.CustomizeImageCropper = __webpack_require__( 6934 );
  8845. media.controller.SiteIconCropper = __webpack_require__( 5274 );
  8846. media.View = __webpack_require__( 487 );
  8847. media.view.Frame = __webpack_require__( 3647 );
  8848. media.view.MediaFrame = __webpack_require__( 4861 );
  8849. media.view.MediaFrame.Select = __webpack_require__( 8719 );
  8850. media.view.MediaFrame.Post = __webpack_require__( 9075 );
  8851. media.view.MediaFrame.ImageDetails = __webpack_require__( 9142 );
  8852. media.view.Modal = __webpack_require__( 3939 );
  8853. media.view.FocusManager = __webpack_require__( 6557 );
  8854. media.view.UploaderWindow = __webpack_require__( 5823 );
  8855. media.view.EditorUploader = __webpack_require__( 841 );
  8856. media.view.UploaderInline = __webpack_require__( 6353 );
  8857. media.view.UploaderStatus = __webpack_require__( 2894 );
  8858. media.view.UploaderStatusError = __webpack_require__( 9411 );
  8859. media.view.Toolbar = __webpack_require__( 9510 );
  8860. media.view.Toolbar.Select = __webpack_require__( 6850 );
  8861. media.view.Toolbar.Embed = __webpack_require__( 7128 );
  8862. media.view.Button = __webpack_require__( 3157 );
  8863. media.view.ButtonGroup = __webpack_require__( 4094 );
  8864. media.view.PriorityList = __webpack_require__( 1993 );
  8865. media.view.MenuItem = __webpack_require__( 917 );
  8866. media.view.Menu = __webpack_require__( 2596 );
  8867. media.view.RouterItem = __webpack_require__( 9484 );
  8868. media.view.Router = __webpack_require__( 1562 );
  8869. media.view.Sidebar = __webpack_require__( 9799 );
  8870. media.view.Attachment = __webpack_require__( 5019 );
  8871. media.view.Attachment.Library = __webpack_require__( 9254 );
  8872. media.view.Attachment.EditLibrary = __webpack_require__( 4640 );
  8873. media.view.Attachments = __webpack_require__( 8408 );
  8874. media.view.Search = __webpack_require__( 4556 );
  8875. media.view.AttachmentFilters = __webpack_require__( 4906 );
  8876. media.view.DateFilter = __webpack_require__( 9663 );
  8877. media.view.AttachmentFilters.Uploaded = __webpack_require__( 7040 );
  8878. media.view.AttachmentFilters.All = __webpack_require__( 2868 );
  8879. media.view.AttachmentsBrowser = __webpack_require__( 9239 );
  8880. media.view.Selection = __webpack_require__( 6191 );
  8881. media.view.Attachment.Selection = __webpack_require__( 9003 );
  8882. media.view.Attachments.Selection = __webpack_require__( 1223 );
  8883. media.view.Attachment.EditSelection = __webpack_require__( 1009 );
  8884. media.view.Settings = __webpack_require__( 859 );
  8885. media.view.Settings.AttachmentDisplay = __webpack_require__( 2176 );
  8886. media.view.Settings.Gallery = __webpack_require__( 6872 );
  8887. media.view.Settings.Playlist = __webpack_require__( 8488 );
  8888. media.view.Attachment.Details = __webpack_require__( 7274 );
  8889. media.view.AttachmentCompat = __webpack_require__( 8093 );
  8890. media.view.Iframe = __webpack_require__( 6217 );
  8891. media.view.Embed = __webpack_require__( 5138 );
  8892. media.view.Label = __webpack_require__( 6644 );
  8893. media.view.EmbedUrl = __webpack_require__( 4848 );
  8894. media.view.EmbedLink = __webpack_require__( 6959 );
  8895. media.view.EmbedImage = __webpack_require__( 1338 );
  8896. media.view.ImageDetails = __webpack_require__( 7598 );
  8897. media.view.Cropper = __webpack_require__( 7137 );
  8898. media.view.SiteIconCropper = __webpack_require__( 5187 );
  8899. media.view.SiteIconPreview = __webpack_require__( 8260 );
  8900. media.view.EditImage = __webpack_require__( 5970 );
  8901. media.view.Spinner = __webpack_require__( 2234 );
  8902. media.view.Heading = __webpack_require__( 7990 );
  8903. }();
  8904. /******/ })()
  8905. ;