media-video-widget.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 = wp.apiRequest({
  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. discover: false
  106. },
  107. type: 'GET',
  108. dataType: 'json',
  109. context: control
  110. });
  111. control.fetchEmbedDfd.done( function( response ) {
  112. control.oembedResponses[ url ] = response;
  113. control.renderPreview();
  114. });
  115. control.fetchEmbedDfd.fail( function() {
  116. control.oembedResponses[ url ] = null;
  117. });
  118. },
  119. /**
  120. * Whether a url is a supported external host.
  121. *
  122. * @deprecated since 4.9.
  123. *
  124. * @returns {boolean} Whether url is a supported video host.
  125. */
  126. isHostedVideo: function isHostedVideo() {
  127. return true;
  128. },
  129. /**
  130. * Render preview.
  131. *
  132. * @returns {void}
  133. */
  134. renderPreview: function renderPreview() {
  135. var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl, poster, html = '', isOEmbed = false, mime, error, urlParser, matches;
  136. attachmentId = control.model.get( 'attachment_id' );
  137. attachmentUrl = control.model.get( 'url' );
  138. error = control.model.get( 'error' );
  139. if ( ! attachmentId && ! attachmentUrl ) {
  140. return;
  141. }
  142. // Verify the selected attachment mime is supported.
  143. mime = control.selectedAttachment.get( 'mime' );
  144. if ( mime && attachmentId ) {
  145. if ( ! _.contains( _.values( wp.media.view.settings.embedMimes ), mime ) ) {
  146. error = 'unsupported_file_type';
  147. }
  148. } else if ( ! attachmentId ) {
  149. urlParser = document.createElement( 'a' );
  150. urlParser.href = attachmentUrl;
  151. matches = urlParser.pathname.toLowerCase().match( /\.(\w+)$/ );
  152. if ( matches ) {
  153. if ( ! _.contains( _.keys( wp.media.view.settings.embedMimes ), matches[1] ) ) {
  154. error = 'unsupported_file_type';
  155. }
  156. } else {
  157. isOEmbed = true;
  158. }
  159. }
  160. if ( isOEmbed ) {
  161. control.fetchEmbed();
  162. if ( control.oembedResponses[ attachmentUrl ] ) {
  163. poster = control.oembedResponses[ attachmentUrl ].thumbnail_url;
  164. html = control.oembedResponses[ attachmentUrl ].html.replace( /\swidth="\d+"/, ' width="100%"' ).replace( /\sheight="\d+"/, '' );
  165. }
  166. }
  167. previewContainer = control.$el.find( '.media-widget-preview' );
  168. previewTemplate = wp.template( 'wp-media-widget-video-preview' );
  169. previewContainer.html( previewTemplate({
  170. model: {
  171. attachment_id: attachmentId,
  172. html: html,
  173. src: attachmentUrl,
  174. poster: poster
  175. },
  176. is_oembed: isOEmbed,
  177. error: error
  178. }));
  179. wp.mediaelement.initialize();
  180. },
  181. /**
  182. * Open the media image-edit frame to modify the selected item.
  183. *
  184. * @returns {void}
  185. */
  186. editMedia: function editMedia() {
  187. var control = this, mediaFrame, metadata, updateCallback;
  188. metadata = control.mapModelToMediaFrameProps( control.model.toJSON() );
  189. // Set up the media frame.
  190. mediaFrame = new VideoDetailsMediaFrame({
  191. frame: 'video',
  192. state: 'video-details',
  193. metadata: metadata
  194. });
  195. wp.media.frame = mediaFrame;
  196. mediaFrame.$el.addClass( 'media-widget' );
  197. updateCallback = function( mediaFrameProps ) {
  198. // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview.
  199. control.selectedAttachment.set( mediaFrameProps );
  200. control.model.set( _.extend(
  201. _.omit( control.model.defaults(), 'title' ),
  202. control.mapMediaToModelProps( mediaFrameProps ),
  203. { error: false }
  204. ) );
  205. };
  206. mediaFrame.state( 'video-details' ).on( 'update', updateCallback );
  207. mediaFrame.state( 'replace-video' ).on( 'replace', updateCallback );
  208. mediaFrame.on( 'close', function() {
  209. mediaFrame.detach();
  210. });
  211. mediaFrame.open();
  212. }
  213. });
  214. // Exports.
  215. component.controlConstructors.media_video = VideoWidgetControl;
  216. component.modelConstructors.media_video = VideoWidgetModel;
  217. })( wp.mediaWidgets );