From 9ff3e7c214de38369654fd174fffee615de1e671 Mon Sep 17 00:00:00 2001 From: Scott Taylor Date: Mon, 24 Feb 2014 18:07:51 +0000 Subject: [PATCH] Add core support for Playlists and Video Playlists. * Playlists operate like galleries in the admin. * Provide default UI and JS support in themes using MediaElement and Backbone. * The shortcodes are clickable, editable, and configurable using the media modal. * Playlists support images for each item, whether or not the current theme supports images for `attachment:audio` and `attachment:video` * Playlists respond to `$content_width` and resize videos accordingly. * All playlist data is included inline, using a script tag with `type="application/json"`, allowing anyone to unenqueue the WP playlist JS and roll their own. * Playlist styles are minimal and work out of the box in the last 5 default themes. They inherit and adapt to the current theme's font styles, and their rules are easily overrideable. See #26631. git-svn-id: https://develop.svn.wordpress.org/trunk@27239 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/css/media-views.css | 8 +- src/wp-includes/js/media-editor.js | 53 ++- src/wp-includes/js/media-views.js | 332 +++++++++++++++++- .../js/mediaelement/wp-mediaelement.css | 101 ++++++ .../js/mediaelement/wp-playlist.js | 130 +++++++ src/wp-includes/js/plupload/handlers.js | 10 +- src/wp-includes/js/swfupload/handlers.js | 13 +- .../js/tinymce/langs/wp-langs-en.js | 2 + .../js/tinymce/plugins/wpgallery/plugin.js | 28 +- .../skins/wordpress/images/playlist-audio.png | Bin 0 -> 1292 bytes .../skins/wordpress/images/playlist-video.png | Bin 0 -> 1162 bytes .../js/tinymce/skins/wordpress/wp-content.css | 8 + src/wp-includes/media-template.php | 41 +++ src/wp-includes/media.php | 276 ++++++++++++++- src/wp-includes/script-loader.php | 2 + 15 files changed, 974 insertions(+), 30 deletions(-) create mode 100644 src/wp-includes/js/mediaelement/wp-playlist.js create mode 100644 src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png create mode 100644 src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index cd4510572f..6d9d9b0a40 100644 --- a/src/wp-includes/css/media-views.css +++ b/src/wp-includes/css/media-views.css @@ -595,10 +595,6 @@ box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); } -.media-frame .media-toolbar .add-to-gallery { - display: none; -} - .media-frame-title h1 { padding: 0 16px; font-size: 22px; @@ -1427,7 +1423,7 @@ margin: 1.4em 0 0.4em; } -.gallery-settings { +.collection-settings { overflow: hidden; } @@ -1886,7 +1882,7 @@ border-top: none; } - .gallery-settings h3 { + .collection-settings h3 { margin-top: 45px; } diff --git a/src/wp-includes/js/media-editor.js b/src/wp-includes/js/media-editor.js index 39d8774d5b..fdf27bb319 100644 --- a/src/wp-includes/js/media-editor.js +++ b/src/wp-includes/js/media-editor.js @@ -319,7 +319,7 @@ } if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) { - _.each(['tracknumbers', 'tracklist', 'images'], function (setting) { + _.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) { if ( 'undefined' === typeof attrs[setting] ) { attrs['_' + setting] = wp.media[ prop ].defaults[ setting ]; } else if ( 'true' === attrs[setting] || true === attrs[setting] ) { @@ -395,7 +395,7 @@ } if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) { - _.each(['tracknumbers', 'tracklist', 'images'], function (setting) { + _.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) { if ( attrs['_' + setting] ) { attrs[setting] = true; } else { @@ -558,6 +558,41 @@ })); }()); + wp.media.playlist = (function() { + var playlist = { + defaults : { + id: wp.media.view.settings.post.id, + style: 'light', + tracklist: true, + tracknumbers: true, + images: true, + artists: true + } + }; + + return _.extend(playlist, wp.media.collection.instance( 'playlist', { + type : 'audio', + title : wp.media.view.l10n.editPlaylistTitle + })); + }()); + + wp.media['video-playlist'] = (function() { + var playlist = { + defaults : { + id: wp.media.view.settings.post.id, + style: 'light', + tracklist: false, + tracknumbers: false, + images: false + } + }; + + return _.extend(playlist, wp.media.collection.instance( 'video-playlist', { + type : 'video', + title : wp.media.view.l10n.editVideoPlaylistTitle + })); + }()); + /** * wp.media.featuredImage * @namespace @@ -776,6 +811,14 @@ this.insert( wp.media.gallery.shortcode( selection ).string() ); }, this ); + workflow.state('playlist-edit').on( 'update', function( selection ) { + this.insert( wp.media.playlist.shortcode( selection ).string() ); + }, this ); + + workflow.state('video-playlist-edit').on( 'update', function( selection ) { + this.insert( wp.media['video-playlist'].shortcode( selection ).string() ); + }, this ); + workflow.state('embed').on( 'select', function() { /** * @this wp.media.editor @@ -1015,6 +1058,12 @@ if ( elem.hasClass( 'gallery' ) ) { options.state = 'gallery'; options.title = wp.media.view.l10n.createGalleryTitle; + } else if ( elem.hasClass( 'playlist' ) ) { + options.state = 'playlist'; + options.title = wp.media.view.l10n.createPlaylistTitle; + } else if ( elem.hasClass( 'video-playlist' ) ) { + options.state = 'video-playlist'; + options.title = wp.media.view.l10n.createVideoPlaylistTitle; } wp.media.editor.open( editor, options ); diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js index 1904a0fd6c..f4924e1a5e 100644 --- a/src/wp-includes/js/media-views.js +++ b/src/wp-includes/js/media-views.js @@ -941,6 +941,49 @@ title: l10n.addToGalleryTitle } }); + + // wp.media.controller.PlaylistEdit + // ------------------------------- + media.controller.PlaylistEdit = media.controller.CollectionEdit( 'playlist', { + type: 'audio', + settings: 'Playlist', + dragInfoText: l10n.playlistDragInfo, + defaults: { + title: l10n.editPlaylistTitle, + dragInfo : false + } + }); + + // wp.media.controller.PlaylistAdd + // --------------------------------- + media.controller.PlaylistAdd = media.controller.CollectionAdd( 'playlist', { + type: 'audio', + defaults: { + title: l10n.addToPlaylistTitle + } + }); + + // wp.media.controller.VideoPlaylistEdit + // ------------------------------- + media.controller.VideoPlaylistEdit = media.controller.CollectionEdit( 'video-playlist', { + type: 'video', + settings: 'Playlist', + dragInfoText: l10n.videoPlaylistDragInfo, + defaults: { + title: l10n.editVideoPlaylistTitle, + dragInfo : false + } + }); + + // wp.media.controller.VideoPlaylistAdd + // --------------------------------- + media.controller.VideoPlaylistAdd = media.controller.CollectionAdd( 'video-playlist', { + type: 'video', + defaults: { + title: l10n.addToVideoPlaylistTitle + } + }); + /** * wp.media.controller.FeaturedImage * @@ -1767,7 +1810,53 @@ menu: 'gallery' }), - new media.controller.GalleryAdd() + new media.controller.GalleryAdd(), + + new media.controller.Library({ + id: 'playlist', + title: l10n.createPlaylistTitle, + priority: 60, + toolbar: 'main-playlist', + filterable: 'uploaded', + multiple: 'add', + editable: false, + + library: media.query( _.defaults({ + type: 'audio' + }, options.library ) ) + }), + + // Playlist states. + new media.controller.PlaylistEdit({ + library: options.selection, + editing: options.editing, + menu: 'playlist' + }), + + new media.controller.PlaylistAdd(), + + new media.controller.Library({ + id: 'video-playlist', + title: l10n.createVideoPlaylistTitle, + priority: 60, + toolbar: 'main-video-playlist', + filterable: 'uploaded', + multiple: 'add', + editable: false, + + library: media.query( _.defaults({ + type: 'video' + }, options.library ) ) + }), + + // Video Playlist states. + new media.controller.VideoPlaylistEdit({ + library: options.selection, + editing: options.editing, + menu: 'video-playlist' + }), + + new media.controller.VideoPlaylistAdd() ]); @@ -1782,15 +1871,21 @@ */ media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments ); this.on( 'menu:create:gallery', this.createMenu, this ); + this.on( 'menu:create:playlist', this.createMenu, this ); + this.on( 'menu:create:video-playlist', this.createMenu, this ); this.on( 'toolbar:create:main-insert', this.createToolbar, this ); - this.on( 'toolbar:create:main-gallery', this.createToolbar, this ); + this.on( 'toolbar:create:main-gallery', this.createToolbar, this ); + this.on( 'toolbar:create:main-playlist', this.createToolbar, this ); + this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this ); this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this ); this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this ); var handlers = { menu: { 'default': 'mainMenu', - 'gallery': 'galleryMenu' + 'gallery': 'galleryMenu', + 'playlist': 'playlistMenu', + 'video-playlist': 'videoPlaylistMenu' }, content: { @@ -1802,7 +1897,13 @@ 'main-insert': 'mainInsertToolbar', 'main-gallery': 'mainGalleryToolbar', 'gallery-edit': 'galleryEditToolbar', - 'gallery-add': 'galleryAddToolbar' + 'gallery-add': 'galleryAddToolbar', + 'main-playlist': 'mainPlaylistToolbar', + 'playlist-edit': 'playlistEditToolbar', + 'playlist-add': 'playlistAddToolbar', + 'main-video-playlist': 'mainVideoPlaylistToolbar', + 'video-playlist-edit': 'videoPlaylistEditToolbar', + 'video-playlist-add': 'videoPlaylistAddToolbar' } }; @@ -1852,6 +1953,52 @@ }); }, + playlistMenu: function( view ) { + var lastState = this.lastState(), + previous = lastState && lastState.id, + frame = this; + + view.set({ + cancel: { + text: l10n.cancelPlaylistTitle, + priority: 20, + click: function() { + if ( previous ) + frame.setState( previous ); + else + frame.close(); + } + }, + separateCancel: new media.View({ + className: 'separator', + priority: 60 + }) + }); + }, + + videoPlaylistMenu: function( view ) { + var lastState = this.lastState(), + previous = lastState && lastState.id, + frame = this; + + view.set({ + cancel: { + text: l10n.cancelVideoPlaylistTitle, + priority: 20, + click: function() { + if ( previous ) + frame.setState( previous ); + else + frame.close(); + } + }, + separateCancel: new media.View({ + className: 'separator', + priority: 80 + }) + }); + }, + // Content embedContent: function() { var view = new media.view.Embed({ @@ -1970,6 +2117,58 @@ }); }, + mainPlaylistToolbar: function( view ) { + var controller = this; + + this.selectionStatusToolbar( view ); + + view.set( 'playlist', { + style: 'primary', + text: l10n.createNewPlaylist, + priority: 100, + requires: { selection: true }, + + click: function() { + var selection = controller.state().get('selection'), + edit = controller.state('playlist-edit'), + models = selection.where({ type: 'audio' }); + + edit.set( 'library', new media.model.Selection( models, { + props: selection.props.toJSON(), + multiple: true + }) ); + + this.controller.setState('playlist-edit'); + } + }); + }, + + mainVideoPlaylistToolbar: function( view ) { + var controller = this; + + this.selectionStatusToolbar( view ); + + view.set( 'video-playlist', { + style: 'primary', + text: l10n.createNewVideoPlaylist, + priority: 100, + requires: { selection: true }, + + click: function() { + var selection = controller.state().get('selection'), + edit = controller.state('video-playlist-edit'), + models = selection.where({ type: 'video' }); + + edit.set( 'library', new media.model.Selection( models, { + props: selection.props.toJSON(), + multiple: true + }) ); + + this.controller.setState('video-playlist-edit'); + } + }); + }, + featuredImageToolbar: function( toolbar ) { this.createSelectToolbar( toolbar, { text: l10n.setFeaturedImage, @@ -2038,8 +2237,115 @@ } } }) ); - } + }, + playlistEditToolbar: function() { + var editing = this.state().get('editing'); + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + insert: { + style: 'primary', + text: editing ? l10n.updatePlaylist : l10n.insertPlaylist, + priority: 80, + requires: { library: true }, + + /** + * @fires wp.media.controller.State#update + */ + click: function() { + var controller = this.controller, + state = controller.state(); + + controller.close(); + state.trigger( 'update', state.get('library') ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + }, + + playlistAddToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + insert: { + style: 'primary', + text: l10n.addToPlaylist, + priority: 80, + requires: { selection: true }, + + /** + * @fires wp.media.controller.State#reset + */ + click: function() { + var controller = this.controller, + state = controller.state(), + edit = controller.state('playlist-edit'); + + edit.get('library').add( state.get('selection').models ); + state.trigger('reset'); + controller.setState('playlist-edit'); + } + } + } + }) ); + }, + + videoPlaylistEditToolbar: function() { + var editing = this.state().get('editing'); + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + insert: { + style: 'primary', + text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist, + priority: 140, + requires: { library: true }, + + click: function() { + var controller = this.controller, + state = controller.state(); + + controller.close(); + state.trigger( 'update', state.get('library') ); + + // Restore and reset the default state. + controller.setState( controller.options.state ); + controller.reset(); + } + } + } + }) ); + }, + + videoPlaylistAddToolbar: function() { + this.toolbar.set( new media.view.Toolbar({ + controller: this, + items: { + insert: { + style: 'primary', + text: l10n.addToVideoPlaylist, + priority: 140, + requires: { selection: true }, + + click: function() { + var controller = this.controller, + state = controller.state(), + edit = controller.state('video-playlist-edit'); + + edit.get('library').add( state.get('selection').models ); + state.trigger('reset'); + controller.setState('video-playlist-edit'); + } + } + } + }) ); + } }); media.view.MediaFrame.ImageDetails = media.view.MediaFrame.Select.extend({ @@ -4864,10 +5170,24 @@ * @augments Backbone.View */ media.view.Settings.Gallery = media.view.Settings.extend({ - className: 'gallery-settings', + className: 'collection-settings gallery-settings', template: media.template('gallery-settings') }); + /** + * wp.media.view.Settings.Playlist + * + * @constructor + * @augments wp.media.view.Settings + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ + media.view.Settings.Playlist = media.view.Settings.extend({ + className: 'collection-settings playlist-settings', + template: media.template('playlist-settings') + }); + /** * wp.media.view.Attachment.Details * diff --git a/src/wp-includes/js/mediaelement/wp-mediaelement.css b/src/wp-includes/js/mediaelement/wp-mediaelement.css index 18ad94855b..b598d26db2 100644 --- a/src/wp-includes/js/mediaelement/wp-mediaelement.css +++ b/src/wp-includes/js/mediaelement/wp-mediaelement.css @@ -13,3 +13,104 @@ .me-cannotplay { width: auto !important; } + +.wp-playlist { + border: 1px solid #ccc; + padding: 10px; + margin: 12px 0 18px; + font-size: 85%; + line-height: 160%; +} + +.wp-playlist audio, +.wp-playlist video { + display: inline-block; + max-width: 100%; +} + +.wp-playlist .mejs-container { + margin: 0; + width: 100%; +} + +.wp-playlist .mejs-controls .mejs-button button { + outline: 0; +} + +.wp-playlist-light { + background: #fff; +} + +.wp-playlist-dark { + color: #fff; + background: #000; +} + +.wp-playlist-caption { + max-width: 85%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.wp-caption-meta { + display: block; +} + +.wp-caption-title { + font-size: 100%; +} + +.wp-caption-album { + font-style: italic; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.wp-caption-artist { + font-size: 85%; + text-transform: uppercase; +} + +.wp-caption-by { + font-size: 65%; + font-weight: bold; +} + +.wp-playlist-item-length { + position: absolute; + right: 0; + top: 0; +} + +.wp-playlist-tracks { + margin-top: 10px; + border-top: 1px solid #ccc; +} + +.wp-playlist-item { + position: relative; + cursor: pointer; + border-bottom: 1px solid #ccc; +} + +.wp-playlist-current-item { + overflow: hidden; + margin-bottom: 10px; + height: 60px; +} + +.wp-playlist-current-item img { + float: left; + max-width: 60px; + height: auto; + margin-right: 10px; +} + +.wp-playlist-current-item .wp-caption-title, +.wp-playlist-current-item .wp-caption-artist { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/wp-includes/js/mediaelement/wp-playlist.js b/src/wp-includes/js/mediaelement/wp-playlist.js new file mode 100644 index 0000000000..9fd7184139 --- /dev/null +++ b/src/wp-includes/js/mediaelement/wp-playlist.js @@ -0,0 +1,130 @@ +/*globals window, document, $, jQuery */ + +(function ($, _, Backbone) { + "use strict"; + + var WPPlaylistView = Backbone.View.extend({ + index : 0, + + itemTemplate : wp.template('wp-playlist-item'), + + initialize : function () { + var settings = {}; + + this.data = $.parseJSON( this.$('script').html() ); + this.playerNode = this.$( this.data.type ); + + this.tracks = new Backbone.Collection( this.data.tracks ); + this.current = this.tracks.first(); + + if ( 'audio' === this.data.type ) { + this.currentTemplate = wp.template('wp-playlist-current-item'); + this.currentNode = this.$( '.wp-playlist-current-item' ); + } + + this.renderCurrent(); + + if ( this.data.tracklist ) { + this.renderTracks(); + } + + this.playerNode.attr( 'src', this.current.get('src') ); + + _.bindAll( this, 'bindPlayer', 'ended', 'clickTrack' ); + + if ( typeof _wpmejsSettings !== 'undefined' ) { + settings.pluginPath = _wpmejsSettings.pluginPath; + } + settings.success = this.bindPlayer; + + new MediaElementPlayer( this.playerNode.get(0), settings ); + }, + + renderCurrent : function () { + var dimensions; + if ( 'video' === this.data.type ) { + if ( this.data.images && this.current.get( 'image' ) ) { + this.playerNode.attr( 'poster', this.current.get( 'image' ).src ); + } + dimensions = this.current.get( 'dimensions' ).resized; + this.playerNode.attr( 'width', dimensions.width ); + this.playerNode.attr( 'height', dimensions.height ); + } else { + if ( ! this.data.images ) { + this.current.set( 'image', false ); + } + this.currentNode.html( this.currentTemplate( this.current.toJSON() ) ); + } + }, + + renderTracks : function () { + var that = this, i = 1, tracklist = $( '
' ); + this.tracks.each(function (model) { + if ( ! that.data.images ) { + model.set( 'image', false ); + } + model.set( 'artists', that.data.artists ); + model.set( 'index', that.data.tracknumbers ? i : false ); + tracklist.append( that.itemTemplate( model.toJSON() ) ); + i += 1; + }); + this.$el.append( tracklist ); + }, + + events : { + 'click .wp-playlist-item' : 'clickTrack', + 'click .wp-playlist-next' : 'next', + 'click .wp-playlist-prev' : 'prev' + }, + + bindPlayer : function (mejs) { + this.player = mejs; + this.player.addEventListener( 'ended', this.ended ); + }, + + clickTrack : function (e) { + this.index = this.$( '.wp-playlist-item' ).index( e.currentTarget ); + this.setCurrent(); + }, + + ended : function () { + if ( this.index + 1 < this.tracks.length ) { + this.next(); + } else { + this.index = 0; + this.current = this.tracks.at( this.index ); + this.loadCurrent(); + } + }, + + next : function () { + this.index = this.index + 1 >= this.tracks.length ? 0 : this.index + 1; + this.setCurrent(); + }, + + prev : function () { + this.index = this.index - 1 < 0 ? this.tracks.length - 1 : this.index - 1; + this.setCurrent(); + }, + + loadCurrent : function () { + this.player.pause(); + this.playerNode.attr( 'src', this.current.get( 'src' ) ); + this.renderCurrent(); + this.player.load(); + }, + + setCurrent : function () { + this.current = this.tracks.at( this.index ); + this.loadCurrent(); + this.player.play(); + } + }); + + $(document).ready(function () { + $('.wp-playlist').each(function () { + return new WPPlaylistView({ el: this }); + }); + }); + +}(jQuery, _, Backbone)); \ No newline at end of file diff --git a/src/wp-includes/js/plupload/handlers.js b/src/wp-includes/js/plupload/handlers.js index 7b1a7b0953..4378848a14 100644 --- a/src/wp-includes/js/plupload/handlers.js +++ b/src/wp-includes/js/plupload/handlers.js @@ -22,7 +22,7 @@ function fileQueued(fileObj) { .appendTo( jQuery('#media-items' ) ); // Disable submit - jQuery('#insert-gallery').prop('disabled', true); + jQuery('#insert-gallery, #insert-playlist').prop('disabled', true); } function uploadStart() { @@ -64,11 +64,11 @@ function updateMediaForm() { // Just one file, no need for collapsible part if ( items.length == 1 ) { items.addClass('open').find('.slidetoggle').show(); - jQuery('.insert-gallery').hide(); + jQuery('.insert-gallery, .insert-playlist').hide(); } else if ( items.length > 1 ) { items.removeClass('open'); - // Only show Gallery button when there are at least two files. - jQuery('.insert-gallery').show(); + // Only show Gallery/Playlist buttons when there are at least two files. + jQuery('.insert-gallery, .insert-playlist').show(); } // Only show Save buttons when there is at least one file. @@ -257,7 +257,7 @@ function deleteError() { } function uploadComplete() { - jQuery('#insert-gallery').prop('disabled', false); + jQuery('#insert-gallery, #insert-playlist').prop('disabled', false); } function switchUploader(s) { diff --git a/src/wp-includes/js/swfupload/handlers.js b/src/wp-includes/js/swfupload/handlers.js index f38172c38d..83b7a557fa 100644 --- a/src/wp-includes/js/swfupload/handlers.js +++ b/src/wp-includes/js/swfupload/handlers.js @@ -212,11 +212,12 @@ function updateMediaForm() { else jQuery('.savebutton').hide(); - // Only show Gallery button when there are at least two files. - if ( items.length > 1 ) - jQuery('.insert-gallery').show(); - else - jQuery('.insert-gallery').hide(); + // Only show Gallery/Playlist buttons when there are at least two files. + if ( items.length > 1 ) { + jQuery('.insert-gallery, .insert-playlist').show(); + } else { + jQuery('.insert-gallery, .insert-playlist').hide(); + } } function uploadSuccess(fileObj, serverData) { @@ -238,7 +239,7 @@ function uploadComplete(fileObj) { // If no more uploads queued, enable the submit button if ( swfu.getStats().files_queued == 0 ) { jQuery('#cancel-upload').prop('disabled', true); - jQuery('#insert-gallery').prop('disabled', false); + jQuery('#insert-gallery, #insert-playlist').prop('disabled', false); } } diff --git a/src/wp-includes/js/tinymce/langs/wp-langs-en.js b/src/wp-includes/js/tinymce/langs/wp-langs-en.js index f31606d681..496afb6326 100644 --- a/src/wp-includes/js/tinymce/langs/wp-langs-en.js +++ b/src/wp-includes/js/tinymce/langs/wp-langs-en.js @@ -478,6 +478,8 @@ add_audio: "Add Audio", editgallery: "Edit Gallery", delgallery: "Delete Gallery", + editplaylist: "Edit Playlist", + delplaylist: "Delete Playlist", wp_fullscreen_desc: "Distraction Free Writing mode (Alt + Shift + W)" }); diff --git a/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js b/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js index 31705c2412..0233133289 100644 --- a/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js @@ -25,8 +25,8 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { } function replaceAVShortcodes( content ) { - var testRegex = /\[(audio|video)[^\]]*\]/, - replaceRegex = /\[(audio|video)[^\]]*\]([\s\S]*?\[\/\1\])?/; + var testRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]/, + replaceRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]([\s\S]*?\[\/\1\])?/; while ( testRegex.test( content ) ) { content = content.replace( replaceRegex, replaceCallback ); @@ -60,12 +60,12 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { } // Check if the `wp.media.gallery` API exists. - if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.gallery ) { + if ( typeof wp === 'undefined' || ! wp.media ) { return; } // Make sure we've selected a gallery node. - if ( editor.dom.hasClass( node, 'wp-gallery' ) ) { + 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 ); @@ -74,6 +74,22 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { var shortcode = gallery.shortcode( selection ).string(); editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); }); + } 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 ) { + var shortcode = wp.media.playlist.shortcode( selection ).string(); + editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); + }); + } 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 ) { + var shortcode = wp.media['video-playlist'].shortcode( selection ).string(); + editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); + }); } else { // temp window.console && window.console.log( 'Edit AV shortcode ' + window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ) ); @@ -138,6 +154,10 @@ tinymce.PluginManager.add('wpgallery', function( editor ) { event.name = 'video'; } else if ( dom.hasClass( node, 'wp-audio' ) ) { event.name = 'audio'; + } else if ( dom.hasClass( node, 'wp-playlist' ) ) { + event.name = 'playlist'; + } else if ( dom.hasClass( node, 'wp-video-playlist' ) ) { + event.name = 'video-playlist'; } } }); diff --git a/src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png b/src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png new file mode 100644 index 0000000000000000000000000000000000000000..ef87164edab3b77de17dee2048ff9a6a31ac222c GIT binary patch literal 1292 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xd_B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxR5#hc$WX!DQqR!T z#M01EN5ROz&{*HlK;Otx*U-?)#N5izOaTg%fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7KMf`)Hma%LWg zuL;)R>ucqiS6q^qmz?V9Vygr+LN7Bj#mdFS(9qn##o5xx(#+7%)!4$t)ZEg|$-vam z*}~Go#093;B|o_|H#M&WrZ)wl*9E6uP+G_>0NU)5T9jFqn&MWJpQ`}&vsET;w>aW7 z52`l>w_BWW>eUB2MjsTjNHGl)0wy026P|8?9C*r4%>yR)B4E1qUT~<9fq}8!)5S5Q z;#Sh1|Nrd|C$ROL;7dtKkrC;0DijuD=L?x&)Mr(2rO{b+a*tKP4eJ6E@qVjBx0mLBnAIKi}0PT)1uM#d?%tR076ymjzXxW<-LyFxi3f+P9?qoJ^uIm?m8L>1R5 z2?7GFk60aQWIO&(SS5ed*Q6+cQT5lvyGL4-yEG0e9B@o2`oVo9!k`C<*m8a(}UI4VU15ym3URUki6Eiv*(XkOqg+u!6@N)@UcXcfY_?eWHVZPaNH9#FGc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxR5#hc$WX!DQqR!T z#M01EN5ROz&{*HlK;Otx*U-?)#N5izOaTg%fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7KMf`)Hma%LWg zuL;)R>ucqiS6q^qmz?V9Vygr+LN7Bj#mdFh$kg1)!raox(#+7%)!4$t)ZEg|$-vam z*}~Go#093;B|o_|H#M&WrZ)wl*8-0NU)5T9jFqn&MWJpQ`}&vsET;x0vEI z52`l>w_D6`>eUB2MjsTjNHGl)0wy026P|8?9C*r4%>yR)B4E0{{3PuFFi-sTba4!+ zxb{r)hhO z*U;g6_Tnyah5*(nv$vR){P&JI_{L|}DQ8Ak-%pvk8Em2(9!OoWT9F;_dV$}^{me-& zW@byIlWoGE#dbe + + + + +
+ +
+ + < controls="controls" preload="metadata" width="">> +
+
+ + +
+ __( 'Media Library' ), 'insertMediaTitle' => __( 'Insert Media' ), 'createNewGallery' => __( 'Create a new gallery' ), + 'createNewPlaylist' => __( 'Create a new playlist' ), + 'createNewVideoPlaylist' => __( 'Create a new video playlist' ), 'returnToLibrary' => __( '← Return to library' ), 'allMediaItems' => __( 'All media items' ), 'noItemsFound' => __( 'No items found.' ), @@ -2071,7 +2325,27 @@ function wp_enqueue_media( $args = array() ) { // Edit Image 'imageDetailsTitle' => __( 'Image Details' ), 'imageReplaceTitle' => __( 'Replace Image' ), - 'imageDetailsCancel' => __( 'Cancel Edit' ) + 'imageDetailsCancel' => __( 'Cancel Edit' ), + + // Playlist + 'playlistDragInfo' => __( 'Drag and drop to reorder tracks.' ), + 'createPlaylistTitle' => __( 'Create Playlist' ), + 'editPlaylistTitle' => __( 'Edit Playlist' ), + 'cancelPlaylistTitle' => __( '← Cancel Playlist' ), + 'insertPlaylist' => __( 'Insert playlist' ), + 'updatePlaylist' => __( 'Update playlist' ), + 'addToPlaylist' => __( 'Add to playlist' ), + 'addToPlaylistTitle' => __( 'Add to Playlist' ), + + // Video Playlist + 'videoPlaylistDragInfo' => __( 'Drag and drop to reorder videos.' ), + 'createVideoPlaylistTitle' => __( 'Create Video Playlist' ), + 'editVideoPlaylistTitle' => __( 'Edit Video Playlist' ), + 'cancelVideoPlaylistTitle' => __( '← Cancel Video Playlist' ), + 'insertVideoPlaylist' => __( 'Insert video playlist' ), + 'updateVideoPlaylist' => __( 'Update video playlist' ), + 'addToVideoPlaylist' => __( 'Add to video playlist' ), + 'addToVideoPlaylistTitle' => __( 'Add to Video Playlist' ), ); $settings = apply_filters( 'media_view_settings', $settings, $post ); diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index caeb56b77f..64147605b2 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -315,6 +315,8 @@ function wp_default_scripts( &$scripts ) { 'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ), ) ); + $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 ); + $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' ); did_action( 'init' ) && $scripts->localize( 'zxcvbn-async', '_zxcvbnSettings', array( 'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',