Audio/Video shortcodes in the media modal:

* Make a generic model, `wp.media.model.PostMedia`, which replaces `wp.media.model.PostAudio` and `wp.media.model.PostVideo`
* Make a generic library, `wp.media.controller.MediaLibrary`, which replaces `wp.media.controller.ReplaceVideo` and `wp.media.controller.ReplaceAudio`
* `wp.media.controller.MediaLibrary` is used to create new states that want to load a library filtered by type, making it incredibly simple to add states to frames. See `wp.media.view.MediaFrame.VideoDetails` and `wp.media.view.MediaFrame.AudioDetails`.

UX changes:

* Add the ability to manage HTML5 fallbacks - add additional `<source>`s or remove specific `<source>`s
* In the Video Details state, add the ability to select a poster image

See #27016.



git-svn-id: https://develop.svn.wordpress.org/trunk@27478 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Scott Taylor 2014-03-09 05:24:10 +00:00
parent 8064f5dbc4
commit 3c8d2a99f2
6 changed files with 272 additions and 194 deletions

View File

@ -453,56 +453,31 @@ window.wp = window.wp || {};
}); });
/** /**
* wp.media.model.PostAudio * wp.media.model.PostMedia
* *
* @constructor * @constructor
* @augments Backbone.Model * @augments Backbone.Model
**/ **/
media.model.PostAudio = Backbone.Model.extend({ media.model.PostMedia = Backbone.Model.extend({
initialize: function() { initialize: function() {
this.attachment = false; this.attachment = false;
}, },
setSource: function ( attachment ) {
this.attachment = attachment;
this.extension = attachment.get('filename' ).split('.').pop();
if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) {
this.set( this.extension, this.attachment.get( 'url' ) );
} else {
this.unset( this.extension );
}
},
changeAttachment: function( attachment ) { changeAttachment: function( attachment ) {
var self = this; var self = this;
this.attachment = attachment; this.setSource( attachment );
this.extension = attachment.get('filename' ).split('.').pop();
if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) {
this.set( this.extension, attachment.get( 'url' ) );
} else {
this.unset( this.extension );
}
_.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) {
self.unset( ext );
} );
}
});
/**
* wp.media.model.PostVideo
*
* @constructor
* @augments Backbone.Model
**/
media.model.PostVideo = Backbone.Model.extend({
initialize: function() {
this.attachment = false;
},
changeAttachment: function( attachment ) {
var self = this;
this.attachment = attachment;
this.extension = attachment.get('filename' ).split('.').pop();
if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) {
this.set( this.extension, attachment.get( 'url' ) );
} else {
this.unset( this.extension );
}
_.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) { _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) {
self.unset( ext ); self.unset( ext );

View File

@ -786,7 +786,7 @@
}, media.controller.Library.prototype.defaults ), }, media.controller.Library.prototype.defaults ),
initialize: function( options ) { initialize: function( options ) {
this.audio = options.audio; this.media = options.media;
media.controller.State.prototype.initialize.apply( this, arguments ); media.controller.State.prototype.initialize.apply( this, arguments );
} }
}); });
@ -812,7 +812,7 @@
}, media.controller.Library.prototype.defaults ), }, media.controller.Library.prototype.defaults ),
initialize: function( options ) { initialize: function( options ) {
this.video = options.video; this.media = options.media;
media.controller.State.prototype.initialize.apply( this, arguments ); media.controller.State.prototype.initialize.apply( this, arguments );
} }
}); });
@ -1183,34 +1183,27 @@
_.extend( media.controller.EditImage.prototype, media.selectionSync ); _.extend( media.controller.EditImage.prototype, media.selectionSync );
/** /**
* wp.media.controller.ReplaceVideo * wp.media.controller.MediaLibrary
*
* Replace a selected single video
* *
* @constructor * @constructor
* @augments wp.media.controller.Library * @augments wp.media.controller.Library
* @augments wp.media.controller.State * @augments wp.media.controller.State
* @augments Backbone.Model * @augments Backbone.Model
*/ */
media.controller.ReplaceVideo = media.controller.Library.extend({ media.controller.MediaLibrary = media.controller.Library.extend({
defaults: _.defaults({ defaults: _.defaults({
id: 'replace-video',
filterable: 'uploaded', filterable: 'uploaded',
multiple: false, multiple: false,
toolbar: 'replace', priority: 80,
title: l10n.replaceVideoTitle, syncSelection: false,
priority: 60, displaySettings: true
syncSelection: false
}, media.controller.Library.prototype.defaults ), }, media.controller.Library.prototype.defaults ),
initialize: function( options ) { initialize: function( options ) {
var library, comparator; var library, comparator;
this.video = options.video; this.media = options.media;
// If we haven't been provided a `library`, create a `Selection`. this.set( 'library', media.query({ type: options.type }) );
if ( ! this.get('library') ) {
this.set( 'library', media.query({ type: 'video' }) );
}
media.controller.Library.prototype.initialize.apply( this, arguments ); media.controller.Library.prototype.initialize.apply( this, arguments );
@ -1244,75 +1237,9 @@
updateSelection: function() { updateSelection: function() {
var selection = this.get('selection'), var selection = this.get('selection'),
attachment = this.video.attachment; attachment;
selection.reset( attachment ? [ attachment ] : [] ); attachment = this.media.attachment;
}
});
/**
* wp.media.controller.ReplaceAudio
*
* Replace a selected single audio file
*
* @constructor
* @augments wp.media.controller.Library
* @augments wp.media.controller.State
* @augments Backbone.Model
*/
media.controller.ReplaceAudio = media.controller.Library.extend({
defaults: _.defaults({
id: 'replace-audio',
filterable: 'uploaded',
multiple: false,
toolbar: 'replace',
title: l10n.replaceAudioTitle,
priority: 60,
syncSelection: false
}, media.controller.Library.prototype.defaults ),
initialize: function( options ) {
var library, comparator;
this.audio = options.audio;
// If we haven't been provided a `library`, create a `Selection`.
if ( ! this.get('library') ) {
this.set( 'library', media.query({ type: 'audio' }) );
}
media.controller.Library.prototype.initialize.apply( this, arguments );
library = this.get('library');
comparator = library.comparator;
// Overload the library's comparator to push items that are not in
// the mirrored query to the front of the aggregate collection.
library.comparator = function( a, b ) {
var aInQuery = !! this.mirroring.get( a.cid ),
bInQuery = !! this.mirroring.get( b.cid );
if ( ! aInQuery && bInQuery ) {
return -1;
} else if ( aInQuery && ! bInQuery ) {
return 1;
} else {
return comparator.apply( this, arguments );
}
};
// Add all items in the selection to the library, so any featured
// images that are not initially loaded still appear.
library.observe( this.get('selection') );
},
activate: function() {
this.updateSelection();
media.controller.Library.prototype.activate.apply( this, arguments );
},
updateSelection: function() {
var selection = this.get('selection'),
attachment = this.audio.attachment;
selection.reset( attachment ? [ attachment ] : [] ); selection.reset( attachment ? [ attachment ] : [] );
} }
@ -2794,8 +2721,8 @@
}, },
initialize: function( options ) { initialize: function( options ) {
this.audio = new media.model.PostAudio( options.metadata ); this.media = new media.model.PostMedia( options.metadata );
this.options.selection = new media.model.Selection( this.audio.attachment, { multiple: false } ); this.options.selection = new media.model.Selection( this.media.attachment, { multiple: false } );
media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments ); media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments );
}, },
@ -2806,35 +2733,44 @@
this.on( 'menu:render:audio-details', this.renderMenu, this ); this.on( 'menu:render:audio-details', this.renderMenu, this );
this.on( 'toolbar:render:audio-details', this.renderAudioDetailsToolbar, this ); this.on( 'toolbar:render:audio-details', this.renderAudioDetailsToolbar, this );
// override the select toolbar // override the select toolbar
this.on( 'toolbar:render:replace', this.renderReplaceAudioToolbar, this ); this.on( 'toolbar:render:replace-audio', this.renderReplaceAudioToolbar, this );
this.on( 'toolbar:render:add-audio-source', this.renderAddAudioSourceToolbar, this );
}, },
createStates: function() { createStates: function() {
this.states.add([ this.states.add([
new media.controller.AudioDetails({ new media.controller.AudioDetails( {
audio: this.audio, media: this.media,
editable: false, editable: false,
menu: 'audio-details' menu: 'audio-details'
}), } ),
new media.controller.ReplaceAudio({
new media.controller.MediaLibrary( {
type: 'audio',
id: 'replace-audio', id: 'replace-audio',
library: media.query( { type: 'audio' } ), title: l10n.audioReplaceTitle,
audio: this.audio, toolbar: 'replace-audio',
multiple: false, media: this.media,
title: l10n.audioReplaceTitle, menu: 'audio-details'
menu: 'audio-details', } ),
toolbar: 'replace',
priority: 80,
displaySettings: true new media.controller.MediaLibrary( {
}) type: 'audio',
id: 'add-audio-source',
title: l10n.audioAddSourceTitle,
toolbar: 'add-audio-source',
media: this.media,
menu: 'audio-details'
} )
]); ]);
}, },
renderAudioDetailsContent: function() { renderAudioDetailsContent: function() {
var view = new media.view.AudioDetails({ var view = new media.view.AudioDetails({
controller: this, controller: this,
model: this.state().audio, model: this.state().media,
attachment: this.state().audio.attachment attachment: this.state().media.attachment
}).render(); }).render();
this.content.set( view ); this.content.set( view );
@ -2880,9 +2816,7 @@
controller.close(); controller.close();
// not sure if we want to use wp.media.string.image which will create a shortcode or state.trigger( 'update', controller.media.toJSON() );
// perhaps wp.html.string to at least to build the <img />
state.trigger( 'update', controller.audio.toJSON() );
// Restore and reset the default state. // Restore and reset the default state.
controller.setState( controller.options.state ); controller.setState( controller.options.state );
@ -2908,11 +2842,39 @@
selection = state.get( 'selection' ), selection = state.get( 'selection' ),
attachment = selection.single(); attachment = selection.single();
controller.audio.changeAttachment( attachment, state.display( attachment ) ); controller.media.changeAttachment( attachment, state.display( attachment ) );
// not sure if we want to use wp.media.string.image which will create a shortcode or // not sure if we want to use wp.media.string.image which will create a shortcode or
// perhaps wp.html.string to at least to build the <img /> // perhaps wp.html.string to at least to build the <img />
state.trigger( 'replace', controller.audio.toJSON() ); state.trigger( 'replace', controller.media.toJSON() );
// Restore and reset the default state.
controller.setState( controller.options.state );
controller.reset();
}
}
}
}) );
},
renderAddAudioSourceToolbar: function() {
this.toolbar.set( new media.view.Toolbar({
controller: this,
items: {
replace: {
style: 'primary',
text: l10n.audioAddSourceTitle,
priority: 80,
click: function() {
var controller = this.controller,
state = controller.state(),
selection = state.get( 'selection' ),
attachment = selection.single();
controller.media.setSource( attachment, state.display( attachment ) );
state.trigger( 'add-source', controller.media.toJSON() );
// Restore and reset the default state. // Restore and reset the default state.
controller.setState( controller.options.state ); controller.setState( controller.options.state );
@ -2949,8 +2911,8 @@
}, },
initialize: function( options ) { initialize: function( options ) {
this.video = new media.model.PostVideo( options.metadata ); this.media = new media.model.PostMedia( options.metadata );
this.options.selection = new media.model.Selection( this.video.attachment, { multiple: false } ); this.options.selection = new media.model.Selection( this.media.attachment, { multiple: false } );
media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments ); media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments );
}, },
@ -2961,35 +2923,53 @@
this.on( 'menu:render:video-details', this.renderMenu, this ); this.on( 'menu:render:video-details', this.renderMenu, this );
this.on( 'toolbar:render:video-details', this.renderVideoDetailsToolbar, this ); this.on( 'toolbar:render:video-details', this.renderVideoDetailsToolbar, this );
// override the select toolbar // override the select toolbar
this.on( 'toolbar:render:replace', this.renderReplaceVideoToolbar, this ); this.on( 'toolbar:render:replace-video', this.renderReplaceVideoToolbar, this );
this.on( 'toolbar:render:add-video-source', this.renderAddVideoSourceToolbar, this );
this.on( 'toolbar:render:select-poster-image', this.renderSelectPosterImageToolbar, this );
}, },
createStates: function() { createStates: function() {
this.states.add([ this.states.add([
new media.controller.VideoDetails({ new media.controller.VideoDetails({
video: this.video, media: this.media,
editable: false, editable: false,
menu: 'video-details' menu: 'video-details'
}), }),
new media.controller.ReplaceVideo({
new media.controller.MediaLibrary( {
type: 'video',
id: 'replace-video', id: 'replace-video',
library: media.query( { type: 'video' } ), title: l10n.videoReplaceTitle,
video: this.video, toolbar: 'replace-video',
multiple: false, media: this.media,
title: l10n.videoReplaceTitle, menu: 'video-details'
menu: 'video-details', } ),
toolbar: 'replace',
priority: 80, new media.controller.MediaLibrary( {
displaySettings: true type: 'video',
}) id: 'add-video-source',
title: l10n.videoAddSourceTitle,
toolbar: 'add-video-source',
media: this.media,
menu: 'video-details'
} ),
new media.controller.MediaLibrary( {
type: 'image',
id: 'select-poster-image',
title: l10n.videoSelectPosterImageTitle,
toolbar: 'select-poster-image',
media: this.media,
menu: 'video-details'
} )
]); ]);
}, },
renderVideoDetailsContent: function() { renderVideoDetailsContent: function() {
var view = new media.view.VideoDetails({ var view = new media.view.VideoDetails({
controller: this, controller: this,
model: this.state().video, model: this.state().media,
attachment: this.state().video.attachment attachment: this.state().media.attachment
}).render(); }).render();
this.content.set( view ); this.content.set( view );
@ -3037,7 +3017,7 @@
// not sure if we want to use wp.media.string.image which will create a shortcode or // not sure if we want to use wp.media.string.image which will create a shortcode or
// perhaps wp.html.string to at least to build the <img /> // perhaps wp.html.string to at least to build the <img />
state.trigger( 'update', controller.video.toJSON() ); state.trigger( 'update', controller.media.toJSON() );
// Restore and reset the default state. // Restore and reset the default state.
controller.setState( controller.options.state ); controller.setState( controller.options.state );
@ -3063,9 +3043,65 @@
selection = state.get( 'selection' ), selection = state.get( 'selection' ),
attachment = selection.single(); attachment = selection.single();
controller.video.changeAttachment( attachment, state.display( attachment ) ); controller.media.changeAttachment( attachment, state.display( attachment ) );
state.trigger( 'replace', controller.video.toJSON() ); state.trigger( 'replace', controller.media.toJSON() );
// Restore and reset the default state.
controller.setState( controller.options.state );
controller.reset();
}
}
}
}) );
},
renderAddVideoSourceToolbar: function() {
this.toolbar.set( new media.view.Toolbar({
controller: this,
items: {
replace: {
style: 'primary',
text: l10n.videoAddSourceTitle,
priority: 80,
click: function() {
var controller = this.controller,
state = controller.state(),
selection = state.get( 'selection' ),
attachment = selection.single();
controller.media.setSource( attachment, state.display( attachment ) );
state.trigger( 'add-source', controller.media.toJSON() );
// Restore and reset the default state.
controller.setState( controller.options.state );
controller.reset();
}
}
}
}) );
},
renderSelectPosterImageToolbar: function() {
this.toolbar.set( new media.view.Toolbar({
controller: this,
items: {
replace: {
style: 'primary',
text: l10n.videoSelectPosterImageTitle,
priority: 80,
click: function() {
var controller = this.controller,
state = controller.state(),
selection = state.get( 'selection' ),
attachment = selection.single();
controller.media.set( 'poster', attachment.get( 'url' ) );
state.trigger( 'set-poster-image', controller.media.toJSON() );
// Restore and reset the default state. // Restore and reset the default state.
controller.setState( controller.options.state ); controller.setState( controller.options.state );
@ -6415,10 +6451,16 @@
*/ */
media.view.MediaDetails = media.view.Settings.AttachmentDisplay.extend({ media.view.MediaDetails = media.view.Settings.AttachmentDisplay.extend({
initialize: function() { initialize: function() {
_.bindAll(this, 'success', 'close'); _.bindAll(this, 'success', 'unsetPlayer');
this.listenTo( this.controller, 'close', this.close ); this.listenTo( this.controller, 'close', this.unsetPlayer );
this.on( 'ready', this.setPlayer ); this.on( 'ready', this.setPlayer );
this.on( 'media:setting:remove', this.unsetPlayer );
this.on( 'media:setting:remove', this.render );
this.on( 'media:setting:remove', this.setPlayer );
this.events = _.extend( this.events, {
'click .remove-setting' : 'removeSetting'
} );
media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments ); media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments );
}, },
@ -6454,6 +6496,16 @@
return media; return media;
}, },
removeSetting : function (e) {
var setting = $( e.currentTarget ).parent();
this.model.unset( setting.find( 'input' ).data( 'setting' ) );
setting.remove();
this.trigger( 'media:setting:remove', this );
},
setPlayer : function () { setPlayer : function () {
if ( ! this.player && this.media ) { if ( ! this.player && this.media ) {
this.player = new MediaElementPlayer( this.media, this.settings ); this.player = new MediaElementPlayer( this.media, this.settings );
@ -6472,7 +6524,7 @@
* MediaElement tries to pull the audio/video tag out of * MediaElement tries to pull the audio/video tag out of
* its container and re-add it to the DOM. * its container and re-add it to the DOM.
*/ */
remove: function() { removePlayer: function() {
var t = this.player, featureIndex, feature; var t = this.player, featureIndex, feature;
// invoke features cleanup // invoke features cleanup
@ -6500,12 +6552,12 @@
delete t.node.player; delete t.node.player;
}, },
close : function() { unsetPlayer : function() {
if ( this.player ) { if ( this.player ) {
if ( _.isUndefined( this.mejs.pluginType ) ) { if ( _.isUndefined( this.mejs.pluginType ) ) {
this.mejs.pause(); this.mejs.pause();
} }
this.remove(); this.removePlayer();
this.player = false; this.player = false;
} }
}, },
@ -6560,9 +6612,17 @@
template: media.template('audio-details'), template: media.template('audio-details'),
setMedia: function() { setMedia: function() {
var audio = this.$('.wp-audio-shortcode').get(0); var audio = this.$('.wp-audio-shortcode');
this.media = this.prepareSrc( audio ); if ( audio.find( 'source' ).length ) {
if ( audio.is(':hidden') ) {
audio.show();
}
this.media = this.prepareSrc( audio.get(0) );
} else {
audio.hide();
this.media = false;
}
return this; return this;
} }
@ -6586,13 +6646,20 @@
setMedia: function() { setMedia: function() {
var video = this.$('.wp-video-shortcode'); var video = this.$('.wp-video-shortcode');
if ( ! video.hasClass('youtube-video') ) { if ( video.find( 'source' ).length ) {
video = this.prepareSrc( video.get(0) ); if ( video.is(':hidden') ) {
} else { video.show();
video = video.get(0); }
}
this.media = video; if ( ! video.hasClass('youtube-video') ) {
this.media = this.prepareSrc( video.get(0) );
} else {
this.media = video.get(0);
}
} else {
video.hide();
this.media = false;
}
return this; return this;
} }

View File

@ -14,18 +14,33 @@
width: auto !important; width: auto !important;
} }
.audio-details .wp-audio-shortcode { .media-embed-details .wp-audio-shortcode {
display: inline-block; display: inline-block;
max-width: 400px; max-width: 400px;
} }
.video-details .embed-video-settings, .media-embed-details .embed-media-settings {
.audio-details .embed-audio-settings { padding-top: 0;
top: 10px;
} }
.video-details .embed-video-settings .checkbox-setting, .media-embed-details .instructions {
.audio-details .embed-audio-settings .checkbox-setting { padding: 16px;
max-width: 600px;
}
.media-embed-details .setting a {
color: #a00;
}
.media-embed-details .setting a:hover {
color: #f00;
}
.media-embed-details .embed-media-settings {
top: 70px;
}
.media-embed-details .embed-media-settings .checkbox-setting {
width: 100px; width: 100px;
clear: none; clear: none;
} }

View File

@ -97,7 +97,7 @@ tinymce.PluginManager.add('wpgallery', function( editor ) {
frame.on( 'close', function () { frame.on( 'close', function () {
frame.detach(); frame.detach();
} ); } );
frame.state( 'video-details' ).on( 'update replace', function ( selection ) { frame.state( 'video-details' ).on( 'update replace add-source select-poster-image', function ( selection ) {
var shortcode = wp.media.video.shortcode( selection ); var shortcode = wp.media.video.shortcode( selection );
editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
frame.detach(); frame.detach();
@ -108,7 +108,7 @@ tinymce.PluginManager.add('wpgallery', function( editor ) {
frame.on( 'close', function () { frame.on( 'close', function () {
frame.detach(); frame.detach();
} ); } );
frame.state( 'audio-details' ).on( 'update replace', function ( selection ) { frame.state( 'audio-details' ).on( 'update replace add-source', function ( selection ) {
var shortcode = wp.media.audio.shortcode( selection ); var shortcode = wp.media.audio.shortcode( selection );
editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
frame.detach(); frame.detach();

View File

@ -663,7 +663,8 @@ function wp_print_media_templates() {
<script type="text/html" id="tmpl-audio-details"> <script type="text/html" id="tmpl-audio-details">
<?php $audio_types = wp_get_audio_extensions(); ?> <?php $audio_types = wp_get_audio_extensions(); ?>
<div class="media-embed"> <div class="media-embed media-embed-details">
<div class="instructions media-instructions">{{{ wp.media.view.l10n.audioDetailsText }}}</div>
<div class="embed-media-settings embed-audio-settings"> <div class="embed-media-settings embed-audio-settings">
<audio controls <audio controls
class="wp-audio-shortcode" class="wp-audio-shortcode"
@ -689,6 +690,7 @@ function wp_print_media_templates() {
<label class="setting"> <label class="setting">
<span>SRC</span> <span>SRC</span>
<input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" /> <input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" />
<a class="remove-setting">{{{ wp.media.view.l10n.audioRemoveSource }}}</a>
</label> </label>
<# } #> <# } #>
<?php <?php
@ -698,6 +700,7 @@ function wp_print_media_templates() {
<label class="setting"> <label class="setting">
<span><?php echo strtoupper( $type ) ?></span> <span><?php echo strtoupper( $type ) ?></span>
<input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" /> <input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" />
<a class="remove-setting">{{{ wp.media.view.l10n.audioRemoveSource }}}</a>
</label> </label>
<# } #> <# } #>
<?php endforeach ?> <?php endforeach ?>
@ -727,19 +730,22 @@ function wp_print_media_templates() {
<script type="text/html" id="tmpl-video-details"> <script type="text/html" id="tmpl-video-details">
<?php $video_types = wp_get_video_extensions(); ?> <?php $video_types = wp_get_video_extensions(); ?>
<div class="media-embed"> <div class="media-embed media-embed-details">
<div class="instructions media-instructions">{{{ wp.media.view.l10n.videoDetailsText }}}</div>
<div class="embed-media-settings embed-video-settings"> <div class="embed-media-settings embed-video-settings">
<div class="wp-video-holder"> <div class="wp-video-holder">
<# <#
var w = ! data.model.width || data.model.width > 640 ? 640 : data.model.width, var isYouTube = ! _.isEmpty( data.model.src ) && data.model.src.match(/youtube|youtu\.be/);
w = ! data.model.width || data.model.width > 640 ? 640 : data.model.width,
h = ! data.model.height ? 360 : data.model.height; h = ! data.model.height ? 360 : data.model.height;
if ( data.model.width && w !== data.model.width ) { if ( data.model.width && w !== data.model.width ) {
h = Math.ceil( ( h * w ) / data.model.width ); h = Math.ceil( ( h * w ) / data.model.width );
} }
#> #>
<video controls <video controls
class="wp-video-shortcode youtube-video" class="wp-video-shortcode{{ isYouTube ? ' youtube-video' : '' }}"
width="{{ w }}" width="{{ w }}"
height="{{ h }}" height="{{ h }}"
<?php <?php
@ -762,7 +768,7 @@ function wp_print_media_templates() {
<?php endforeach ?>#> <?php endforeach ?>#>
> >
<# if ( ! _.isEmpty( data.model.src ) ) { <# if ( ! _.isEmpty( data.model.src ) ) {
if ( data.model.src.match(/youtube|youtu\.be/) ) { #> if ( isYouTube ) { #>
<source src="{{ data.model.src }}" type="video/youtube" /> <source src="{{ data.model.src }}" type="video/youtube" />
<# } else { #> <# } else { #>
<source src="{{ data.model.src }}" type="{{ wp.media.view.settings.embedMimes[ data.model.src.split('.').pop() ] }}" /> <source src="{{ data.model.src }}" type="{{ wp.media.view.settings.embedMimes[ data.model.src.split('.').pop() ] }}" />
@ -779,6 +785,7 @@ function wp_print_media_templates() {
<label class="setting"> <label class="setting">
<span>SRC</span> <span>SRC</span>
<input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" /> <input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" />
<a class="remove-setting">{{{ wp.media.view.l10n.videoRemoveSource }}}</a>
</label> </label>
<# } #> <# } #>
<?php foreach ( $video_types as $type ): <?php foreach ( $video_types as $type ):
@ -786,14 +793,18 @@ function wp_print_media_templates() {
<label class="setting"> <label class="setting">
<span><?php echo strtoupper( $type ) ?></span> <span><?php echo strtoupper( $type ) ?></span>
<input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" /> <input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" />
<a class="remove-setting">{{{ wp.media.view.l10n.videoRemoveSource }}}</a>
</label> </label>
<# } #> <# } #>
<?php endforeach ?> <?php endforeach ?>
</div> </div>
<# if ( ! _.isEmpty( data.model.poster ) ) { #>
<label class="setting"> <label class="setting">
<span><?php _e( 'Poster Image' ); ?></span> <span><?php _e( 'Poster Image' ); ?></span>
<input type="text" data-setting="poster" value="{{ data.model.poster }}" /> <input type="text" disabled="disabled" data-setting="poster" value="{{ data.model.poster }}" />
<a class="remove-setting">{{{ wp.media.view.l10n.videoRemovePoster }}}</a>
</label> </label>
<# } #>
<div class="setting preload"> <div class="setting preload">
<span><?php _e( 'Preload' ); ?></span> <span><?php _e( 'Preload' ); ?></span>
<div class="button-group button-large" data-setting="preload"> <div class="button-group button-large" data-setting="preload">

View File

@ -2443,15 +2443,25 @@ function wp_enqueue_media( $args = array() ) {
'imageDetailsCancel' => __( 'Cancel Edit' ), 'imageDetailsCancel' => __( 'Cancel Edit' ),
'editImage' => __( 'Edit Image' ), 'editImage' => __( 'Edit Image' ),
// Edit Image // Edit Audio
'audioDetailsTitle' => __( 'Audio Details' ), 'audioDetailsTitle' => __( 'Audio Details' ),
'audioReplaceTitle' => __( 'Replace Audio' ), 'audioReplaceTitle' => __( 'Replace Audio' ),
'audioAddSourceTitle' => __( 'Add Audio Source' ),
'audioDetailsCancel' => __( 'Cancel Edit' ), 'audioDetailsCancel' => __( 'Cancel Edit' ),
'audioDetailsText' => __( '"Replace Audio" will remove all associated source files when you update. ' .
'"Add Audio Source" allows you to specify alternate sources for maximum native HTML5 audio playback.' ),
'audioRemoveSource' => __( 'Remove Audio Source' ),
// Edit Image // Edit Video
'videoDetailsTitle' => __( 'Video Details' ), 'videoDetailsTitle' => __( 'Video Details' ),
'videoReplaceTitle' => __( 'Replace Video' ), 'videoReplaceTitle' => __( 'Replace Video' ),
'videoAddSourceTitle' => __( 'Add Video Source' ),
'videoDetailsCancel' => __( 'Cancel Edit' ), 'videoDetailsCancel' => __( 'Cancel Edit' ),
'videoDetailsText' => __( '"Replace Video" will remove all associated source files when you update. ' .
'"Add Video Source" allows you to specify alternate sources for maximum native HTML5 video playback.' ),
'videoRemoveSource' => __( 'Remove Video Source' ),
'videoSelectPosterImageTitle' => _( 'Select Poster Image' ),
'videoRemovePoster' => __( 'Remove Poster Image' ),
// Playlist // Playlist
'playlistDragInfo' => __( 'Drag and drop to reorder tracks.' ), 'playlistDragInfo' => __( 'Drag and drop to reorder tracks.' ),