diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index 1e8c3fd2d7..9a4478ab2e 100644 --- a/src/wp-includes/css/media-views.css +++ b/src/wp-includes/css/media-views.css @@ -1460,7 +1460,7 @@ } .embed-link-settings, -.embed-image-settings { +.embed-media-settings { position: absolute; top: 60px; left: 0; @@ -1470,7 +1470,7 @@ overflow: auto; } -.image-details .embed-image-settings { +.image-details .embed-media-settings { top: 0; } @@ -1523,7 +1523,7 @@ margin: 2px 0; } -.media-embed .setting input, +.media-embed .setting input[type="text"], .media-embed .setting textarea { display: block; width: 100%; @@ -1872,7 +1872,7 @@ /* Image From Link */ .embed-link-settings, - .embed-image-settings { + .embed-media-settings { padding-bottom: 52px; } diff --git a/src/wp-includes/js/media-editor.js b/src/wp-includes/js/media-editor.js index 76befb34b4..7fabf4cc9d 100644 --- a/src/wp-includes/js/media-editor.js +++ b/src/wp-includes/js/media-editor.js @@ -274,29 +274,32 @@ } }; + wp.media.mixin = { + /** + * A helper function to avoid truthy and falsey values being + * passed as an input that expects booleans. If key is undefined in the map, + * but has a default value, set it. + * + * @param {object} attrs Map of props from a shortcode or settings. + * @param {string} key The key within the passed map to check for a value. + * @returns {mixed|undefined} The original or coerced value of key within attrs + */ + coerce: function ( attrs, key ) { + if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) { + attrs[ key ] = this.defaults[ key ]; + } else if ( 'true' === attrs[ key ] ) { + attrs[ key ] = true; + } else if ( 'false' === attrs[ key ] ) { + attrs[ key ] = false; + } + return attrs[ key ]; + } + }; + wp.media.collection = function(attributes) { var collections = {}; - return _.extend( attributes, { - /** - * A helper function to avoid truthy and falsey values being - * passed as an input that expects booleans. If key is undefined in the map, - * but has a default value, set it. - * - * @param {object} attrs Map of props from a shortcode or settings. - * @param {string} key The key within the passed map to check for a value. - * @returns {mixed|undefined} The original or coerced value of key within attrs - */ - coerce: function ( attrs, key ) { - if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) { - attrs[ key ] = this.defaults[ key ]; - } else if ( 'true' === attrs[ key ] ) { - attrs[ key ] = true; - } else if ( 'false' === attrs[ key ] ) { - attrs[ key ] = false; - } - return attrs[ key ]; - }, + return _.extend( attributes, wp.media.mixin, { /** * Retrieve attachments based on the properties of the passed shortcode * @@ -536,7 +539,7 @@ } }); - wp.media['video-playlist'] = new wp.media.collection( { + wp.media['video-playlist'] = new wp.media.collection({ tag: 'video-playlist', type : 'video', editTitle : wp.media.view.l10n.editVideoPlaylistTitle, @@ -547,7 +550,89 @@ tracknumbers: false, images: true } - } ); + }); + + wp.media.audio = _.extend({ + defaults : { + id : wp.media.view.settings.post.id, + src : '', + loop : false, + autoplay : false, + preload : 'none' + }, + + edit : function (data) { + var frame, shortcode = wp.shortcode.next( 'audio', data ).shortcode; + frame = wp.media({ + frame: 'audio', + state: 'audio-details', + metadata: _.defaults( + shortcode.attrs.named, + wp.media.audio.defaults + ) + }); + + return frame; + }, + + shortcode : function (shortcode) { + var self = this; + + _.each( wp.media.audio.defaults, function( value, key ) { + shortcode[ key ] = self.coerce( shortcode, key ); + + if ( value === shortcode[ key ] ) { + delete shortcode[ key ]; + } + }); + + return wp.shortcode.string({ + tag: 'audio', + attrs: shortcode + }); + } + }, wp.media.mixin); + + wp.media.video = _.extend({ + defaults : { + id : wp.media.view.settings.post.id, + src : '', + poster : '', + loop : false, + autoplay : false, + preload : 'metadata' + }, + + edit : function (data) { + var frame, shortcode = wp.shortcode.next( 'video', data ).shortcode; + frame = wp.media({ + frame: 'video', + state: 'video-details', + metadata: _.defaults( + shortcode.attrs.named, + wp.media.video.defaults + ) + }); + + return frame; + }, + + shortcode : function (shortcode) { + var self = this; + _.each( wp.media.video.defaults, function( value, key ) { + shortcode[ key ] = self.coerce( shortcode, key ); + + if ( value === shortcode[ key ] ) { + delete shortcode[ key ]; + } + }); + + return wp.shortcode.string({ + tag: 'video', + attrs: shortcode + }); + } + }, wp.media.mixin); /** * wp.media.featuredImage diff --git a/src/wp-includes/js/media-models.js b/src/wp-includes/js/media-models.js index 5363ffe737..bfafbd1580 100644 --- a/src/wp-includes/js/media-models.js +++ b/src/wp-includes/js/media-models.js @@ -32,6 +32,10 @@ window.wp = window.wp || {}; frame = new MediaFrame.Post( attributes ); } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) { frame = new MediaFrame.ImageDetails( attributes ); + } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) { + frame = new MediaFrame.AudioDetails( attributes ); + } else if ( 'video' === attributes.frame && MediaFrame.VideoDetails ) { + frame = new MediaFrame.VideoDetails( attributes ); } delete attributes.frame; @@ -447,6 +451,64 @@ window.wp = window.wp || {}; } }); + /** + * wp.media.model.PostAudio + * + * @constructor + * @augments Backbone.Model + **/ + PostAudio = media.model.PostAudio = Backbone.Model.extend({ + initialize: function() { + this.attachment = false; + }, + + changeAttachment: function( attachment, props ) { + 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.set( this.extension, '' ); + } + + _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) { + self.set( ext, '' ); + } ); + } + }); + + /** + * wp.media.model.PostVideo + * + * @constructor + * @augments Backbone.Model + **/ + PostVideo = media.model.PostVideo = Backbone.Model.extend({ + initialize: function() { + this.attachment = false; + }, + + changeAttachment: function( attachment, props ) { + 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.set( this.extension, '' ); + } + + _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) { + self.set( ext, '' ); + } ); + } + }); + /** * wp.media.model.Attachments * diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js index 6c0d4b6f2a..54a6c05ad3 100644 --- a/src/wp-includes/js/media-views.js +++ b/src/wp-includes/js/media-views.js @@ -1,5 +1,5 @@ /* global _wpMediaViewsL10n, confirm, getUserSetting, setUserSetting */ -(function($){ +(function($, _){ var media = wp.media, l10n; // Link any localized strings. @@ -759,6 +759,58 @@ } }); + /** + * wp.media.controller.AudioDetails + * + * @constructor + * @augments wp.media.controller.State + * @augments Backbone.Model + */ + media.controller.AudioDetails = media.controller.State.extend({ + defaults: _.defaults({ + id: 'audio-details', + toolbar: 'audio-details', + title: l10n.audioDetailsTitle, + content: 'audio-details', + menu: 'audio-details', + router: false, + attachment: false, + priority: 60, + editing: false + }, media.controller.Library.prototype.defaults ), + + initialize: function( options ) { + this.audio = options.audio; + media.controller.State.prototype.initialize.apply( this, arguments ); + } + }); + + /** + * wp.media.controller.VideoDetails + * + * @constructor + * @augments wp.media.controller.State + * @augments Backbone.Model + */ + media.controller.VideoDetails = media.controller.State.extend({ + defaults: _.defaults({ + id: 'video-details', + toolbar: 'video-details', + title: l10n.videoDetailsTitle, + content: 'video-details', + menu: 'video-details', + router: false, + attachment: false, + priority: 60, + editing: false + }, media.controller.Library.prototype.defaults ), + + initialize: function( options ) { + this.video = options.video; + media.controller.State.prototype.initialize.apply( this, arguments ); + } + }); + /** * wp.media.controller.CollectionEdit * @@ -1067,6 +1119,142 @@ } }); + /** + * wp.media.controller.ReplaceVideo + * + * Replace a selected single video + * + * @constructor + * @augments wp.media.controller.Library + * @augments wp.media.controller.State + * @augments Backbone.Model + */ + media.controller.ReplaceVideo = media.controller.Library.extend({ + defaults: _.defaults({ + id: 'replace-video', + filterable: 'uploaded', + multiple: false, + toolbar: 'replace', + title: l10n.replaceVideoTitle, + priority: 60, + syncSelection: false + }, media.controller.Library.prototype.defaults ), + + initialize: function( options ) { + var library, comparator; + + this.video = options.video; + // If we haven't been provided a `library`, create a `Selection`. + if ( ! this.get('library') ) { + this.set( 'library', media.query({ type: 'video' }) ); + } + + 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.video.attachment; + + selection.reset( attachment ? [ 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 ] : [] ); + } + }); + /** * wp.media.controller.Embed * @@ -2461,6 +2649,289 @@ }); + media.view.MediaFrame.AudioDetails = media.view.MediaFrame.Select.extend({ + defaults: { + id: 'audio', + url: '', + menu: 'audio-details', + content: 'audio-details', + toolbar: 'audio-details', + type: 'link', + title: l10n.audioDetailsTitle, + priority: 120 + }, + + initialize: function( options ) { + this.audio = new media.model.PostAudio( options.metadata ); + this.options.selection = new media.model.Selection( this.audio.attachment, { multiple: false } ); + media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments ); + }, + + bindHandlers: function() { + media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments ); + this.on( 'menu:create:audio-details', this.createMenu, this ); + this.on( 'content:render:audio-details', this.renderAudioDetailsContent, this ); + this.on( 'menu:render:audio-details', this.renderMenu, this ); + this.on( 'toolbar:render:audio-details', this.renderAudioDetailsToolbar, this ); + // override the select toolbar + this.on( 'toolbar:render:replace', this.renderReplaceAudioToolbar, this ); + }, + + createStates: function() { + this.states.add([ + new media.controller.AudioDetails({ + audio: this.audio, + editable: false, + menu: 'audio-details' + }), + new media.controller.ReplaceAudio({ + id: 'replace-audio', + library: media.query( { type: 'audio' } ), + audio: this.audio, + multiple: false, + title: l10n.audioReplaceTitle, + menu: 'audio-details', + toolbar: 'replace', + priority: 80, + displaySettings: true + }) + ]); + }, + + renderAudioDetailsContent: function() { + var view = new media.view.AudioDetails({ + controller: this, + model: this.state().audio, + attachment: this.state().audio.attachment + }).render(); + + this.content.set( view ); + }, + + renderMenu: function( view ) { + var lastState = this.lastState(), + previous = lastState && lastState.id, + frame = this; + + view.set({ + cancel: { + text: l10n.audioDetailsCancel, + priority: 20, + click: function() { + if ( previous ) { + frame.setState( previous ); + } else { + frame.close(); + } + } + }, + separateCancel: new media.View({ + className: 'separator', + priority: 40 + }) + }); + + }, + + renderAudioDetailsToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + select: { + style: 'primary', + text: l10n.update, + priority: 80, + + click: function() { + var controller = this.controller, + state = controller.state(); + + controller.close(); + + // 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 + state.trigger( 'update', controller.audio.toJSON() ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + }, + + renderReplaceAudioToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + replace: { + style: 'primary', + text: l10n.replace, + priority: 80, + + click: function() { + var controller = this.controller, + state = controller.state(), + selection = state.get( 'selection' ), + attachment = selection.single(); + + controller.audio.changeAttachment( attachment, state.display( attachment ) ); + + // 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 + state.trigger( 'replace', controller.audio.toJSON() ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + } + }); + + media.view.MediaFrame.VideoDetails = media.view.MediaFrame.Select.extend({ + defaults: { + id: 'video', + url: '', + menu: 'video-details', + content: 'video-details', + toolbar: 'video-details', + type: 'link', + title: l10n.videoDetailsTitle, + priority: 120 + }, + + initialize: function( options ) { + this.video = new media.model.PostVideo( options.metadata ); + this.options.selection = new media.model.Selection( this.video.attachment, { multiple: false } ); + media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments ); + }, + + bindHandlers: function() { + media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments ); + this.on( 'menu:create:video-details', this.createMenu, this ); + this.on( 'content:render:video-details', this.renderVideoDetailsContent, this ); + this.on( 'menu:render:video-details', this.renderMenu, this ); + this.on( 'toolbar:render:video-details', this.renderVideoDetailsToolbar, this ); + // override the select toolbar + this.on( 'toolbar:render:replace', this.renderReplaceVideoToolbar, this ); + }, + + createStates: function() { + this.states.add([ + new media.controller.VideoDetails({ + video: this.video, + editable: false, + menu: 'video-details' + }), + new media.controller.ReplaceVideo({ + id: 'replace-video', + library: media.query( { type: 'video' } ), + video: this.video, + multiple: false, + title: l10n.videoReplaceTitle, + menu: 'video-details', + toolbar: 'replace', + priority: 80, + displaySettings: true + }) + ]); + }, + + renderVideoDetailsContent: function() { + var view = new media.view.VideoDetails({ + controller: this, + model: this.state().video, + attachment: this.state().video.attachment + }).render(); + + this.content.set( view ); + }, + + renderMenu: function( view ) { + var lastState = this.lastState(), + previous = lastState && lastState.id, + frame = this; + + view.set({ + cancel: { + text: l10n.videoDetailsCancel, + priority: 20, + click: function() { + if ( previous ) { + frame.setState( previous ); + } else { + frame.close(); + } + } + }, + separateCancel: new media.View({ + className: 'separator', + priority: 40 + }) + }); + + }, + + renderVideoDetailsToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + select: { + style: 'primary', + text: l10n.update, + priority: 80, + + click: function() { + var controller = this.controller, + state = controller.state(); + + controller.close(); + + // 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 + state.trigger( 'update', controller.video.toJSON() ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + }, + + renderReplaceVideoToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + replace: { + style: 'primary', + text: l10n.replace, + priority: 80, + + click: function() { + var controller = this.controller, + state = controller.state(), + selection = state.get( 'selection' ), + attachment = selection.single(); + + controller.video.changeAttachment( attachment, state.display( attachment ) ); + + state.trigger( 'replace', controller.video.toJSON() ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + } + }); /** * wp.media.view.Modal @@ -5605,7 +6076,7 @@ * @augments Backbone.View */ media.view.EmbedImage = media.view.Settings.AttachmentDisplay.extend({ - className: 'embed-image-settings', + className: 'embed-media-settings', template: media.template('embed-image-settings'), initialize: function() { @@ -5666,4 +6137,131 @@ this.$( '.embed-image-settings' ).scrollTop( 0 ); } }); -}(jQuery)); + + media.view.AudioDetails = media.view.Settings.AttachmentDisplay.extend({ + className: 'audio-details', + template: media.template('audio-details'), + + initialize: function() { + _.bindAll(this, 'player', 'close'); + + this.listenTo( this.controller, 'close', this.close ); + + // used in AttachmentDisplay.prototype.updateLinkTo + this.options.attachment = this.model.attachment; + media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments ); + }, + + prepare: function() { + var attachment = false; + + if ( this.model.attachment ) { + attachment = this.model.attachment.toJSON(); + } + return _.defaults({ + model: this.model.toJSON(), + attachment: attachment + }, this.options ); + }, + + close : function() { + this.mejs.pause(); + this.remove(); + delete this.mejs; + delete this.mejsInstance; + }, + + player : function (mejs) { + this.mejs = mejs; + }, + + render: function() { + var self = this, settings = { + success : this.player + }; + + if ( ! _.isUndefined( window._wpmejsSettings ) ) { + settings.pluginPath = _wpmejsSettings.pluginPath; + } + + media.view.Settings.AttachmentDisplay.prototype.render.apply( this, arguments ); + setTimeout( function() { self.resetFocus(); }, 10 ); + + this.mejsInstance = new MediaElementPlayer( this.$('audio').get(0), settings ); + + return this; + }, + + resetFocus: function() { + this.$( '.embed-media-settings' ).scrollTop( 0 ); + } + }); + + media.view.VideoDetails = media.view.Settings.AttachmentDisplay.extend({ + className: 'video-details', + template: media.template('video-details'), + + initialize: function() { + _.bindAll(this, 'player', 'played'); + + this.removable = false; + this.listenTo( this.controller, 'close', this.close ); + + // used in AttachmentDisplay.prototype.updateLinkTo + this.options.attachment = this.model.attachment; + media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments ); + }, + + prepare: function() { + var attachment = false; + + if ( this.model.attachment ) { + attachment = this.model.attachment.toJSON(); + } + return _.defaults({ + model: this.model.toJSON(), + attachment: attachment + }, this.options ); + }, + + close : function() { + if ( this.removable ) { + this.mejs.pause(); + } + this.remove(); + this.mejs = this.mejsInstance = null; + }, + + played : function () { + this.removable = true; + }, + + player : function (mejs) { + this.mejs = mejs; + this.mejs.addEventListener( 'play', this.played ); + }, + + render: function() { + var self = this, settings = { + success : this.player + }; + + if ( ! _.isUndefined( window._wpmejsSettings ) ) { + settings.pluginPath = _wpmejsSettings.pluginPath; + } + + media.view.Settings.AttachmentDisplay.prototype.render.apply( this, arguments ); + setTimeout( function() { self.resetFocus(); }, 10 ); + + if ( ! this.mejsInstance ) { + this.mejsInstance = new MediaElementPlayer( this.$('video').get(0), settings ); + } + + return this; + }, + + resetFocus: function() { + this.$( '.embed-media-settings' ).scrollTop( 0 ); + } + }); +}(jQuery, _)); diff --git a/src/wp-includes/js/mediaelement/wp-mediaelement.css b/src/wp-includes/js/mediaelement/wp-mediaelement.css index b598d26db2..623ddb01da 100644 --- a/src/wp-includes/js/mediaelement/wp-mediaelement.css +++ b/src/wp-includes/js/mediaelement/wp-mediaelement.css @@ -14,6 +14,27 @@ width: auto !important; } +.audio-details .wp-audio-shortcode { + display: inline-block; + max-width: 400px; +} + +.video-details .embed-video-settings, +.audio-details .embed-audio-settings { + top: 10px; +} + +.video-details .embed-video-settings .checkbox-setting, +.audio-details .embed-audio-settings .checkbox-setting { + width: 100px; + clear: none; +} + +.video-details .wp-video-holder { + width: 100%; + max-width: 640px; +} + .wp-playlist { border: 1px solid #ccc; padding: 10px; diff --git a/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js b/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js index 6e645709f3..75ef53d6e0 100644 --- a/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js @@ -64,10 +64,11 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { return; } + data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ); + // Make sure we've selected a gallery node. if ( editor.dom.hasClass( node, 'wp-gallery' ) && wp.media.gallery ) { gallery = wp.media.gallery; - data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ); frame = gallery.edit( data ); frame.state('gallery-edit').on( 'update', function( selection ) { @@ -76,7 +77,6 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { frame.detach(); }); } else if ( editor.dom.hasClass( node, 'wp-playlist' ) && wp.media.playlist ) { - data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ); frame = wp.media.playlist.edit( data ); frame.state('playlist-edit').on( 'update', function( selection ) { @@ -85,7 +85,6 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { frame.detach(); }); } else if ( editor.dom.hasClass( node, 'wp-video-playlist' ) && wp.media['video-playlist'] ) { - data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ); frame = wp.media['video-playlist'].edit( data ); frame.state('video-playlist-edit').on( 'update', function( selection ) { @@ -93,9 +92,23 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); frame.detach(); }); + } else if ( editor.dom.hasClass( node, 'wp-video' ) ) { + frame = wp.media.video.edit( data ); + frame.state( 'video-details' ).on( 'update replace', function ( selection ) { + var shortcode = wp.media.video.shortcode( selection ); + editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); + } ); + frame.open(); + } else if ( editor.dom.hasClass( node, 'wp-audio' ) ) { + frame = wp.media.audio.edit( data ); + frame.state( 'audio-details' ).on( 'update replace', function ( selection ) { + var shortcode = wp.media.audio.shortcode( selection ); + editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); + } ); + frame.open(); } else { // temp - window.console && window.console.log( 'Edit AV shortcode ' + window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ) ); + window.console && window.console.log( 'Edit AV shortcode ' + data ); } } diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php index 97430d0f18..8200b313f7 100644 --- a/src/wp-includes/media-template.php +++ b/src/wp-includes/media-template.php @@ -555,7 +555,7 @@ function wp_print_media_templates() { + + + + __( 'Replace Image' ), 'imageDetailsCancel' => __( 'Cancel Edit' ), + // Edit Image + 'audioDetailsTitle' => __( 'Audio Details' ), + 'audioReplaceTitle' => __( 'Replace Audio' ), + 'audioDetailsCancel' => __( 'Cancel Edit' ), + + // Edit Image + 'videoDetailsTitle' => __( 'Video Details' ), + 'videoReplaceTitle' => __( 'Replace Video' ), + 'videoDetailsCancel' => __( 'Cancel Edit' ), + // Playlist 'playlistDragInfo' => __( 'Drag and drop to reorder tracks.' ), 'createPlaylistTitle' => __( 'Create Playlist' ), diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 6b928f7a65..70b9735afd 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -389,7 +389,7 @@ function wp_default_scripts( &$scripts ) { // To enqueue media-views or media-editor, call wp_enqueue_media(). // Both rely on numerous settings, styles, and templates to operate correctly. - $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable' ), false, 1 ); + $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement' ), false, 1 ); $scripts->add( 'media-editor', "/wp-includes/js/media-editor$suffix.js", array( 'shortcode', 'media-views' ), false, 1 ); $scripts->add( 'mce-view', "/wp-includes/js/mce-view$suffix.js", array( 'shortcode', 'media-models' ), false, 1 ); @@ -614,7 +614,7 @@ function wp_default_styles( &$styles ) { $styles->add( 'admin-bar', "/wp-includes/css/admin-bar$suffix.css", array( 'open-sans', 'dashicons' ) ); $styles->add( 'wp-auth-check', "/wp-includes/css/wp-auth-check$suffix.css", array( 'dashicons' ) ); $styles->add( 'editor-buttons', "/wp-includes/css/editor$suffix.css", array( 'dashicons' ) ); - $styles->add( 'media-views', "/wp-includes/css/media-views$suffix.css", array( 'buttons', 'dashicons' ) ); + $styles->add( 'media-views', "/wp-includes/css/media-views$suffix.css", array( 'buttons', 'dashicons', 'wp-mediaelement' ) ); $styles->add( 'wp-pointer', "/wp-includes/css/wp-pointer$suffix.css", array( 'dashicons' ) ); // External libraries and friends