media-video-widget.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /* eslint consistent-this: [ "error", "control" ] */
  2. (function( component ) {
  3. 'use strict';
  4. var VideoWidgetModel, VideoWidgetControl, VideoDetailsMediaFrame;
  5. /**
  6. * Custom video details frame that removes the replace-video state.
  7. *
  8. * @class VideoDetailsMediaFrame
  9. * @constructor
  10. */
  11. VideoDetailsMediaFrame = wp.media.view.MediaFrame.VideoDetails.extend({
  12. /**
  13. * Create the default states.
  14. *
  15. * @returns {void}
  16. */
  17. createStates: function createStates() {
  18. this.states.add([
  19. new wp.media.controller.VideoDetails({
  20. media: this.media
  21. }),
  22. new wp.media.controller.MediaLibrary({
  23. type: 'video',
  24. id: 'add-video-source',
  25. title: wp.media.view.l10n.videoAddSourceTitle,
  26. toolbar: 'add-video-source',
  27. media: this.media,
  28. menu: false
  29. }),
  30. new wp.media.controller.MediaLibrary({
  31. type: 'text',
  32. id: 'add-track',
  33. title: wp.media.view.l10n.videoAddTrackTitle,
  34. toolbar: 'add-track',
  35. media: this.media,
  36. menu: 'video-details'
  37. })
  38. ]);
  39. }
  40. });
  41. /**
  42. * Video widget model.
  43. *
  44. * See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports.
  45. *
  46. * @class VideoWidgetModel
  47. * @constructor
  48. */
  49. VideoWidgetModel = component.MediaWidgetModel.extend({});
  50. /**
  51. * Video widget control.
  52. *
  53. * See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports.
  54. *
  55. * @class VideoWidgetControl
  56. * @constructor
  57. */
  58. VideoWidgetControl = component.MediaWidgetControl.extend({
  59. /**
  60. * Show display settings.
  61. *
  62. * @type {boolean}
  63. */
  64. showDisplaySettings: false,
  65. /**
  66. * Cache of oembed responses.
  67. *
  68. * @type {Object}
  69. */
  70. oembedResponses: {},
  71. /**
  72. * Map model props to media frame props.
  73. *
  74. * @param {Object} modelProps - Model props.
  75. * @returns {Object} Media frame props.
  76. */
  77. mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) {
  78. var control = this, mediaFrameProps;
  79. mediaFrameProps = component.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call( control, modelProps );
  80. mediaFrameProps.link = 'embed';
  81. return mediaFrameProps;
  82. },
  83. /**
  84. * Fetches embed data for external videos.
  85. *
  86. * @returns {void}
  87. */
  88. fetchEmbed: function fetchEmbed() {
  89. var control = this, url;
  90. url = control.model.get( 'url' );
  91. // If we already have a local cache of the embed response, return.
  92. if ( control.oembedResponses[ url ] ) {
  93. return;
  94. }
  95. // If there is an in-flight embed request, abort it.
  96. if ( control.fetchEmbedDfd && 'pending' === control.fetchEmbedDfd.state() ) {
  97. control.fetchEmbedDfd.abort();
  98. }
  99. control.fetchEmbedDfd = jQuery.ajax({
  100. url: wp.media.view.settings.oEmbedProxyUrl,
  101. data: {
  102. url: control.model.get( 'url' ),
  103. maxwidth: control.model.get( 'width' ),
  104. maxheight: control.model.get( 'height' ),
  105. _wpnonce: wp.media.view.settings.nonce.wpRestApi,
  106. discover: false
  107. },
  108. type: 'GET',
  109. dataType: 'json',
  110. context: control
  111. });
  112. control.fetchEmbedDfd.done( function( response ) {
  113. control.oembedResponses[ url ] = response;
  114. control.renderPreview();
  115. });
  116. control.fetchEmbedDfd.fail( function() {
  117. control.oembedResponses[ url ] = null;
  118. });
  119. },
  120. /**
  121. * Whether a url is a supported external host.
  122. *
  123. * @param {String} url - Video url.
  124. * @returns {boolean} Whether url is a supported video host.
  125. */
  126. isHostedVideo: function isHostedVideo( url ) {
  127. var parsedUrl = document.createElement( 'a' );
  128. parsedUrl.href = url;
  129. return /vimeo|youtu\.?be/.test( parsedUrl.host );
  130. },
  131. /**
  132. * Render preview.
  133. *
  134. * @returns {void}
  135. */
  136. renderPreview: function renderPreview() {
  137. var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl, poster, isHostedEmbed = false, mime, error;
  138. attachmentId = control.model.get( 'attachment_id' );
  139. attachmentUrl = control.model.get( 'url' );
  140. error = control.model.get( 'error' );
  141. if ( ! attachmentId && ! attachmentUrl ) {
  142. return;
  143. }
  144. if ( ! attachmentId && attachmentUrl ) {
  145. isHostedEmbed = control.isHostedVideo( attachmentUrl );
  146. }
  147. if ( isHostedEmbed ) {
  148. control.fetchEmbed();
  149. poster = control.oembedResponses[ attachmentUrl ] ? control.oembedResponses[ attachmentUrl ].thumbnail_url : null;
  150. }
  151. // Verify the selected attachment mime is supported.
  152. mime = control.selectedAttachment.get( 'mime' );
  153. if ( mime && attachmentId ) {
  154. if ( ! _.contains( _.values( wp.media.view.settings.embedMimes ), mime ) ) {
  155. error = 'unsupported_file_type';
  156. }
  157. }
  158. previewContainer = control.$el.find( '.media-widget-preview' );
  159. previewTemplate = wp.template( 'wp-media-widget-video-preview' );
  160. previewContainer.html( previewTemplate({
  161. model: {
  162. attachment_id: control.model.get( 'attachment_id' ),
  163. src: attachmentUrl,
  164. poster: poster
  165. },
  166. is_hosted_embed: isHostedEmbed,
  167. error: error
  168. }));
  169. wp.mediaelement.initialize();
  170. },
  171. /**
  172. * Open the media image-edit frame to modify the selected item.
  173. *
  174. * @returns {void}
  175. */
  176. editMedia: function editMedia() {
  177. var control = this, mediaFrame, metadata, updateCallback;
  178. metadata = control.mapModelToMediaFrameProps( control.model.toJSON() );
  179. // Set up the media frame.
  180. mediaFrame = new VideoDetailsMediaFrame({
  181. frame: 'video',
  182. state: 'video-details',
  183. metadata: metadata
  184. });
  185. wp.media.frame = mediaFrame;
  186. mediaFrame.$el.addClass( 'media-widget' );
  187. updateCallback = function( mediaFrameProps ) {
  188. // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview.
  189. control.selectedAttachment.set( mediaFrameProps );
  190. control.model.set( _.extend(
  191. _.omit( control.model.defaults(), 'title' ),
  192. control.mapMediaToModelProps( mediaFrameProps ),
  193. { error: false }
  194. ) );
  195. };
  196. mediaFrame.state( 'video-details' ).on( 'update', updateCallback );
  197. mediaFrame.state( 'replace-video' ).on( 'replace', updateCallback );
  198. mediaFrame.on( 'close', function() {
  199. mediaFrame.detach();
  200. });
  201. mediaFrame.open();
  202. }
  203. });
  204. // Exports.
  205. component.controlConstructors.media_video = VideoWidgetControl;
  206. component.modelConstructors.media_video = VideoWidgetModel;
  207. })( wp.mediaWidgets );