media-gallery-widget.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /**
  2. * @output wp-admin/js/widgets/media-gallery-widget.js
  3. */
  4. /* eslint consistent-this: [ "error", "control" ] */
  5. (function( component ) {
  6. 'use strict';
  7. var GalleryWidgetModel, GalleryWidgetControl, GalleryDetailsMediaFrame;
  8. /**
  9. * Custom gallery details frame.
  10. *
  11. * @since 4.9.0
  12. * @class wp.mediaWidgets~GalleryDetailsMediaFrame
  13. * @augments wp.media.view.MediaFrame.Post
  14. */
  15. GalleryDetailsMediaFrame = wp.media.view.MediaFrame.Post.extend(/** @lends wp.mediaWidgets~GalleryDetailsMediaFrame.prototype */{
  16. /**
  17. * Create the default states.
  18. *
  19. * @since 4.9.0
  20. * @return {void}
  21. */
  22. createStates: function createStates() {
  23. this.states.add([
  24. new wp.media.controller.Library({
  25. id: 'gallery',
  26. title: wp.media.view.l10n.createGalleryTitle,
  27. priority: 40,
  28. toolbar: 'main-gallery',
  29. filterable: 'uploaded',
  30. multiple: 'add',
  31. editable: true,
  32. library: wp.media.query( _.defaults({
  33. type: 'image'
  34. }, this.options.library ) )
  35. }),
  36. // Gallery states.
  37. new wp.media.controller.GalleryEdit({
  38. library: this.options.selection,
  39. editing: this.options.editing,
  40. menu: 'gallery'
  41. }),
  42. new wp.media.controller.GalleryAdd()
  43. ]);
  44. }
  45. } );
  46. /**
  47. * Gallery widget model.
  48. *
  49. * See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
  50. *
  51. * @since 4.9.0
  52. *
  53. * @class wp.mediaWidgets.modelConstructors.media_gallery
  54. * @augments wp.mediaWidgets.MediaWidgetModel
  55. */
  56. GalleryWidgetModel = component.MediaWidgetModel.extend(/** @lends wp.mediaWidgets.modelConstructors.media_gallery.prototype */{} );
  57. GalleryWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_gallery.prototype */{
  58. /**
  59. * View events.
  60. *
  61. * @since 4.9.0
  62. * @type {object}
  63. */
  64. events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
  65. 'click .media-widget-gallery-preview': 'editMedia'
  66. } ),
  67. /**
  68. * Gallery widget control.
  69. *
  70. * See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
  71. *
  72. * @constructs wp.mediaWidgets.controlConstructors.media_gallery
  73. * @augments wp.mediaWidgets.MediaWidgetControl
  74. *
  75. * @since 4.9.0
  76. * @param {Object} options - Options.
  77. * @param {Backbone.Model} options.model - Model.
  78. * @param {jQuery} options.el - Control field container element.
  79. * @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
  80. * @return {void}
  81. */
  82. initialize: function initialize( options ) {
  83. var control = this;
  84. component.MediaWidgetControl.prototype.initialize.call( control, options );
  85. _.bindAll( control, 'updateSelectedAttachments', 'handleAttachmentDestroy' );
  86. control.selectedAttachments = new wp.media.model.Attachments();
  87. control.model.on( 'change:ids', control.updateSelectedAttachments );
  88. control.selectedAttachments.on( 'change', control.renderPreview );
  89. control.selectedAttachments.on( 'reset', control.renderPreview );
  90. control.updateSelectedAttachments();
  91. /*
  92. * Refresh a Gallery widget partial when the user modifies one of the selected attachments.
  93. * This ensures that when an attachment's caption is updated in the media modal the Gallery
  94. * widget in the preview will then be refreshed to show the change. Normally doing this
  95. * would not be necessary because all of the state should be contained inside the changeset,
  96. * as everything done in the Customizer should not make a change to the site unless the
  97. * changeset itself is published. Attachments are a current exception to this rule.
  98. * For a proposal to include attachments in the customized state, see #37887.
  99. */
  100. if ( wp.customize && wp.customize.previewer ) {
  101. control.selectedAttachments.on( 'change', function() {
  102. wp.customize.previewer.send( 'refresh-widget-partial', control.model.get( 'widget_id' ) );
  103. } );
  104. }
  105. },
  106. /**
  107. * Update the selected attachments if necessary.
  108. *
  109. * @since 4.9.0
  110. * @return {void}
  111. */
  112. updateSelectedAttachments: function updateSelectedAttachments() {
  113. var control = this, newIds, oldIds, removedIds, addedIds, addedQuery;
  114. newIds = control.model.get( 'ids' );
  115. oldIds = _.pluck( control.selectedAttachments.models, 'id' );
  116. removedIds = _.difference( oldIds, newIds );
  117. _.each( removedIds, function( removedId ) {
  118. control.selectedAttachments.remove( control.selectedAttachments.get( removedId ) );
  119. });
  120. addedIds = _.difference( newIds, oldIds );
  121. if ( addedIds.length ) {
  122. addedQuery = wp.media.query({
  123. order: 'ASC',
  124. orderby: 'post__in',
  125. perPage: -1,
  126. post__in: newIds,
  127. query: true,
  128. type: 'image'
  129. });
  130. addedQuery.more().done( function() {
  131. control.selectedAttachments.reset( addedQuery.models );
  132. });
  133. }
  134. },
  135. /**
  136. * Render preview.
  137. *
  138. * @since 4.9.0
  139. * @return {void}
  140. */
  141. renderPreview: function renderPreview() {
  142. var control = this, previewContainer, previewTemplate, data;
  143. previewContainer = control.$el.find( '.media-widget-preview' );
  144. previewTemplate = wp.template( 'wp-media-widget-gallery-preview' );
  145. data = control.previewTemplateProps.toJSON();
  146. data.attachments = {};
  147. control.selectedAttachments.each( function( attachment ) {
  148. data.attachments[ attachment.id ] = attachment.toJSON();
  149. } );
  150. previewContainer.html( previewTemplate( data ) );
  151. },
  152. /**
  153. * Determine whether there are selected attachments.
  154. *
  155. * @since 4.9.0
  156. * @return {boolean} Selected.
  157. */
  158. isSelected: function isSelected() {
  159. var control = this;
  160. if ( control.model.get( 'error' ) ) {
  161. return false;
  162. }
  163. return control.model.get( 'ids' ).length > 0;
  164. },
  165. /**
  166. * Open the media select frame to edit images.
  167. *
  168. * @since 4.9.0
  169. * @return {void}
  170. */
  171. editMedia: function editMedia() {
  172. var control = this, selection, mediaFrame, mediaFrameProps;
  173. selection = new wp.media.model.Selection( control.selectedAttachments.models, {
  174. multiple: true
  175. });
  176. mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
  177. selection.gallery = new Backbone.Model( mediaFrameProps );
  178. if ( mediaFrameProps.size ) {
  179. control.displaySettings.set( 'size', mediaFrameProps.size );
  180. }
  181. mediaFrame = new GalleryDetailsMediaFrame({
  182. frame: 'manage',
  183. text: control.l10n.add_to_widget,
  184. selection: selection,
  185. mimeType: control.mime_type,
  186. selectedDisplaySettings: control.displaySettings,
  187. showDisplaySettings: control.showDisplaySettings,
  188. metadata: mediaFrameProps,
  189. editing: true,
  190. multiple: true,
  191. state: 'gallery-edit'
  192. });
  193. wp.media.frame = mediaFrame; // See wp.media().
  194. // Handle selection of a media item.
  195. mediaFrame.on( 'update', function onUpdate( newSelection ) {
  196. var state = mediaFrame.state(), resultSelection;
  197. resultSelection = newSelection || state.get( 'selection' );
  198. if ( ! resultSelection ) {
  199. return;
  200. }
  201. // Copy orderby_random from gallery state.
  202. if ( resultSelection.gallery ) {
  203. control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
  204. }
  205. // Directly update selectedAttachments to prevent needing to do additional request.
  206. control.selectedAttachments.reset( resultSelection.models );
  207. // Update models in the widget instance.
  208. control.model.set( {
  209. ids: _.pluck( resultSelection.models, 'id' )
  210. } );
  211. } );
  212. mediaFrame.$el.addClass( 'media-widget' );
  213. mediaFrame.open();
  214. if ( selection ) {
  215. selection.on( 'destroy', control.handleAttachmentDestroy );
  216. }
  217. },
  218. /**
  219. * Open the media select frame to chose an item.
  220. *
  221. * @since 4.9.0
  222. * @return {void}
  223. */
  224. selectMedia: function selectMedia() {
  225. var control = this, selection, mediaFrame, mediaFrameProps;
  226. selection = new wp.media.model.Selection( control.selectedAttachments.models, {
  227. multiple: true
  228. });
  229. mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
  230. if ( mediaFrameProps.size ) {
  231. control.displaySettings.set( 'size', mediaFrameProps.size );
  232. }
  233. mediaFrame = new GalleryDetailsMediaFrame({
  234. frame: 'select',
  235. text: control.l10n.add_to_widget,
  236. selection: selection,
  237. mimeType: control.mime_type,
  238. selectedDisplaySettings: control.displaySettings,
  239. showDisplaySettings: control.showDisplaySettings,
  240. metadata: mediaFrameProps,
  241. state: 'gallery'
  242. });
  243. wp.media.frame = mediaFrame; // See wp.media().
  244. // Handle selection of a media item.
  245. mediaFrame.on( 'update', function onUpdate( newSelection ) {
  246. var state = mediaFrame.state(), resultSelection;
  247. resultSelection = newSelection || state.get( 'selection' );
  248. if ( ! resultSelection ) {
  249. return;
  250. }
  251. // Copy orderby_random from gallery state.
  252. if ( resultSelection.gallery ) {
  253. control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
  254. }
  255. // Directly update selectedAttachments to prevent needing to do additional request.
  256. control.selectedAttachments.reset( resultSelection.models );
  257. // Update widget instance.
  258. control.model.set( {
  259. ids: _.pluck( resultSelection.models, 'id' )
  260. } );
  261. } );
  262. mediaFrame.$el.addClass( 'media-widget' );
  263. mediaFrame.open();
  264. if ( selection ) {
  265. selection.on( 'destroy', control.handleAttachmentDestroy );
  266. }
  267. /*
  268. * Make sure focus is set inside of modal so that hitting Esc will close
  269. * the modal and not inadvertently cause the widget to collapse in the customizer.
  270. */
  271. mediaFrame.$el.find( ':focusable:first' ).focus();
  272. },
  273. /**
  274. * Clear the selected attachment when it is deleted in the media select frame.
  275. *
  276. * @since 4.9.0
  277. * @param {wp.media.models.Attachment} attachment - Attachment.
  278. * @return {void}
  279. */
  280. handleAttachmentDestroy: function handleAttachmentDestroy( attachment ) {
  281. var control = this;
  282. control.model.set( {
  283. ids: _.difference(
  284. control.model.get( 'ids' ),
  285. [ attachment.id ]
  286. )
  287. } );
  288. }
  289. } );
  290. // Exports.
  291. component.controlConstructors.media_gallery = GalleryWidgetControl;
  292. component.modelConstructors.media_gallery = GalleryWidgetModel;
  293. })( wp.mediaWidgets );