WordPress.org

Make WordPress Core

Changeset 27239


Ignore:
Timestamp:
02/24/2014 06:07:51 PM (6 years ago)
Author:
wonderboymusic
Message:

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.

Location:
trunk/src/wp-includes
Files:
3 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/css/media-views.css

    r27236 r27239  
    594594    -webkit-box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    595595    box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    596 }
    597 
    598 .media-frame .media-toolbar .add-to-gallery {
    599     display: none;
    600596}
    601597
     
    14281424}
    14291425
    1430 .gallery-settings {
     1426.collection-settings {
    14311427    overflow: hidden;
    14321428}
     
    18871883    }
    18881884
    1889     .gallery-settings h3 {
     1885    .collection-settings h3 {
    18901886        margin-top: 45px;
    18911887    }
  • trunk/src/wp-includes/js/media-editor.js

    r27238 r27239  
    320320
    321321                if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) {
    322                     _.each(['tracknumbers', 'tracklist', 'images'], function (setting) {
     322                    _.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) {
    323323                        if ( 'undefined' === typeof attrs[setting] ) {
    324324                            attrs['_' + setting] = wp.media[ prop ].defaults[ setting ];
     
    396396
    397397            if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) {
    398                 _.each(['tracknumbers', 'tracklist', 'images'], function (setting) {
     398                _.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) {
    399399                    if ( attrs['_' + setting] ) {
    400400                        attrs[setting] = true;
     
    559559    }());
    560560
     561    wp.media.playlist = (function() {
     562        var playlist = {
     563            defaults : {
     564                id: wp.media.view.settings.post.id,
     565                style: 'light',
     566                tracklist: true,
     567                tracknumbers: true,
     568                images: true,
     569                artists: true
     570            }
     571        };
     572
     573        return _.extend(playlist, wp.media.collection.instance( 'playlist', {
     574            type : 'audio',
     575            title : wp.media.view.l10n.editPlaylistTitle
     576        }));
     577    }());
     578
     579    wp.media['video-playlist'] = (function() {
     580        var playlist = {
     581            defaults : {
     582                id: wp.media.view.settings.post.id,
     583                style: 'light',
     584                tracklist: false,
     585                tracknumbers: false,
     586                images: false
     587            }
     588        };
     589
     590        return _.extend(playlist, wp.media.collection.instance( 'video-playlist', {
     591            type : 'video',
     592            title : wp.media.view.l10n.editVideoPlaylistTitle
     593        }));
     594    }());
     595
    561596    /**
    562597     * wp.media.featuredImage
     
    775810                 */
    776811                this.insert( wp.media.gallery.shortcode( selection ).string() );
     812            }, this );
     813
     814            workflow.state('playlist-edit').on( 'update', function( selection ) {
     815                this.insert( wp.media.playlist.shortcode( selection ).string() );
     816            }, this );
     817
     818            workflow.state('video-playlist-edit').on( 'update', function( selection ) {
     819                this.insert( wp.media['video-playlist'].shortcode( selection ).string() );
    777820            }, this );
    778821
     
    10161059                    options.state = 'gallery';
    10171060                    options.title = wp.media.view.l10n.createGalleryTitle;
     1061                } else if ( elem.hasClass( 'playlist' ) ) {
     1062                    options.state = 'playlist';
     1063                    options.title = wp.media.view.l10n.createPlaylistTitle;
     1064                } else if ( elem.hasClass( 'video-playlist' ) ) {
     1065                    options.state = 'video-playlist';
     1066                    options.title = wp.media.view.l10n.createVideoPlaylistTitle;
    10181067                }
    10191068
  • trunk/src/wp-includes/js/media-views.js

    r27215 r27239  
    942942        }
    943943    });
     944
     945    // wp.media.controller.PlaylistEdit
     946    // -------------------------------
     947    media.controller.PlaylistEdit = media.controller.CollectionEdit( 'playlist', {
     948        type: 'audio',
     949        settings: 'Playlist',
     950        dragInfoText: l10n.playlistDragInfo,
     951        defaults: {
     952            title: l10n.editPlaylistTitle,
     953            dragInfo : false
     954        }
     955    });
     956
     957    // wp.media.controller.PlaylistAdd
     958    // ---------------------------------
     959    media.controller.PlaylistAdd = media.controller.CollectionAdd( 'playlist', {
     960        type: 'audio',
     961        defaults: {
     962            title: l10n.addToPlaylistTitle
     963        }
     964    });
     965
     966    // wp.media.controller.VideoPlaylistEdit
     967    // -------------------------------
     968    media.controller.VideoPlaylistEdit = media.controller.CollectionEdit( 'video-playlist', {
     969        type: 'video',
     970        settings: 'Playlist',
     971        dragInfoText: l10n.videoPlaylistDragInfo,
     972        defaults: {
     973            title: l10n.editVideoPlaylistTitle,
     974            dragInfo : false
     975        }
     976    });
     977
     978    // wp.media.controller.VideoPlaylistAdd
     979    // ---------------------------------
     980    media.controller.VideoPlaylistAdd = media.controller.CollectionAdd( 'video-playlist', {
     981        type: 'video',
     982        defaults: {
     983            title: l10n.addToVideoPlaylistTitle
     984        }
     985    });
     986
    944987    /**
    945988     * wp.media.controller.FeaturedImage
     
    17681811                }),
    17691812
    1770                 new media.controller.GalleryAdd()
     1813                new media.controller.GalleryAdd(),
     1814
     1815                new media.controller.Library({
     1816                    id:         'playlist',
     1817                    title:      l10n.createPlaylistTitle,
     1818                    priority:   60,
     1819                    toolbar:    'main-playlist',
     1820                    filterable: 'uploaded',
     1821                    multiple:   'add',
     1822                    editable:   false,
     1823
     1824                    library:  media.query( _.defaults({
     1825                        type: 'audio'
     1826                    }, options.library ) )
     1827                }),
     1828
     1829                // Playlist states.
     1830                new media.controller.PlaylistEdit({
     1831                    library: options.selection,
     1832                    editing: options.editing,
     1833                    menu:    'playlist'
     1834                }),
     1835
     1836                new media.controller.PlaylistAdd(),
     1837
     1838                new media.controller.Library({
     1839                    id:         'video-playlist',
     1840                    title:      l10n.createVideoPlaylistTitle,
     1841                    priority:   60,
     1842                    toolbar:    'main-video-playlist',
     1843                    filterable: 'uploaded',
     1844                    multiple:   'add',
     1845                    editable:   false,
     1846
     1847                    library:  media.query( _.defaults({
     1848                        type: 'video'
     1849                    }, options.library ) )
     1850                }),
     1851
     1852                // Video Playlist states.
     1853                new media.controller.VideoPlaylistEdit({
     1854                    library: options.selection,
     1855                    editing: options.editing,
     1856                    menu:    'video-playlist'
     1857                }),
     1858
     1859                new media.controller.VideoPlaylistAdd()
    17711860            ]);
    17721861
     
    17831872            media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
    17841873            this.on( 'menu:create:gallery', this.createMenu, this );
     1874            this.on( 'menu:create:playlist', this.createMenu, this );
     1875            this.on( 'menu:create:video-playlist', this.createMenu, this );
    17851876            this.on( 'toolbar:create:main-insert', this.createToolbar, this );
    1786             this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1877            this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1878            this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
     1879            this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
    17871880            this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
    17881881            this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
     
    17911884                menu: {
    17921885                    'default': 'mainMenu',
    1793                     'gallery': 'galleryMenu'
     1886                    'gallery': 'galleryMenu',
     1887                    'playlist': 'playlistMenu',
     1888                    'video-playlist': 'videoPlaylistMenu'
    17941889                },
    17951890
     
    18031898                    'main-gallery':     'mainGalleryToolbar',
    18041899                    'gallery-edit':     'galleryEditToolbar',
    1805                     'gallery-add':      'galleryAddToolbar'
     1900                    'gallery-add':      'galleryAddToolbar',
     1901                    'main-playlist':    'mainPlaylistToolbar',
     1902                    'playlist-edit':    'playlistEditToolbar',
     1903                    'playlist-add':     'playlistAddToolbar',
     1904                    'main-video-playlist': 'mainVideoPlaylistToolbar',
     1905                    'video-playlist-edit': 'videoPlaylistEditToolbar',
     1906                    'video-playlist-add': 'videoPlaylistAddToolbar'
    18061907                }
    18071908            };
     
    18531954        },
    18541955
     1956        playlistMenu: function( view ) {
     1957            var lastState = this.lastState(),
     1958                previous = lastState && lastState.id,
     1959                frame = this;
     1960
     1961            view.set({
     1962                cancel: {
     1963                    text:     l10n.cancelPlaylistTitle,
     1964                    priority: 20,
     1965                    click:    function() {
     1966                        if ( previous )
     1967                            frame.setState( previous );
     1968                        else
     1969                            frame.close();
     1970                    }
     1971                },
     1972                separateCancel: new media.View({
     1973                    className: 'separator',
     1974                    priority: 60
     1975                })
     1976            });
     1977        },
     1978
     1979        videoPlaylistMenu: function( view ) {
     1980            var lastState = this.lastState(),
     1981                previous = lastState && lastState.id,
     1982                frame = this;
     1983
     1984            view.set({
     1985                cancel: {
     1986                    text:     l10n.cancelVideoPlaylistTitle,
     1987                    priority: 20,
     1988                    click:    function() {
     1989                        if ( previous )
     1990                            frame.setState( previous );
     1991                        else
     1992                            frame.close();
     1993                    }
     1994                },
     1995                separateCancel: new media.View({
     1996                    className: 'separator',
     1997                    priority: 80
     1998                })
     1999            });
     2000        },
     2001
    18552002        // Content
    18562003        embedContent: function() {
     
    19672114
    19682115                    this.controller.setState('gallery-edit');
     2116                }
     2117            });
     2118        },
     2119
     2120        mainPlaylistToolbar: function( view ) {
     2121            var controller = this;
     2122
     2123            this.selectionStatusToolbar( view );
     2124
     2125            view.set( 'playlist', {
     2126                style:    'primary',
     2127                text:     l10n.createNewPlaylist,
     2128                priority: 100,
     2129                requires: { selection: true },
     2130
     2131                click: function() {
     2132                    var selection = controller.state().get('selection'),
     2133                        edit = controller.state('playlist-edit'),
     2134                        models = selection.where({ type: 'audio' });
     2135
     2136                    edit.set( 'library', new media.model.Selection( models, {
     2137                        props:    selection.props.toJSON(),
     2138                        multiple: true
     2139                    }) );
     2140
     2141                    this.controller.setState('playlist-edit');
     2142                }
     2143            });
     2144        },
     2145
     2146        mainVideoPlaylistToolbar: function( view ) {
     2147            var controller = this;
     2148
     2149            this.selectionStatusToolbar( view );
     2150
     2151            view.set( 'video-playlist', {
     2152                style:    'primary',
     2153                text:     l10n.createNewVideoPlaylist,
     2154                priority: 100,
     2155                requires: { selection: true },
     2156
     2157                click: function() {
     2158                    var selection = controller.state().get('selection'),
     2159                        edit = controller.state('video-playlist-edit'),
     2160                        models = selection.where({ type: 'video' });
     2161
     2162                    edit.set( 'library', new media.model.Selection( models, {
     2163                        props:    selection.props.toJSON(),
     2164                        multiple: true
     2165                    }) );
     2166
     2167                    this.controller.setState('video-playlist-edit');
    19692168                }
    19702169            });
     
    20392238                }
    20402239            }) );
    2041         }
    2042 
     2240        },
     2241
     2242        playlistEditToolbar: function() {
     2243            var editing = this.state().get('editing');
     2244            this.toolbar.set( new media.view.Toolbar({
     2245                controller: this,
     2246                items: {
     2247                    insert: {
     2248                        style:    'primary',
     2249                        text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
     2250                        priority: 80,
     2251                        requires: { library: true },
     2252
     2253                        /**
     2254                         * @fires wp.media.controller.State#update
     2255                         */
     2256                        click: function() {
     2257                            var controller = this.controller,
     2258                                state = controller.state();
     2259
     2260                            controller.close();
     2261                            state.trigger( 'update', state.get('library') );
     2262
     2263                            // Restore and reset the default state.
     2264                            controller.setState( controller.options.state );
     2265                            controller.reset();
     2266                        }
     2267                    }
     2268                }
     2269            }) );
     2270        },
     2271
     2272        playlistAddToolbar: function() {
     2273            this.toolbar.set( new media.view.Toolbar({
     2274                controller: this,
     2275                items: {
     2276                    insert: {
     2277                        style:    'primary',
     2278                        text:     l10n.addToPlaylist,
     2279                        priority: 80,
     2280                        requires: { selection: true },
     2281
     2282                        /**
     2283                         * @fires wp.media.controller.State#reset
     2284                         */
     2285                        click: function() {
     2286                            var controller = this.controller,
     2287                                state = controller.state(),
     2288                                edit = controller.state('playlist-edit');
     2289
     2290                            edit.get('library').add( state.get('selection').models );
     2291                            state.trigger('reset');
     2292                            controller.setState('playlist-edit');
     2293                        }
     2294                    }
     2295                }
     2296            }) );
     2297        },
     2298
     2299        videoPlaylistEditToolbar: function() {
     2300            var editing = this.state().get('editing');
     2301            this.toolbar.set( new media.view.Toolbar({
     2302                controller: this,
     2303                items: {
     2304                    insert: {
     2305                        style:    'primary',
     2306                        text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
     2307                        priority: 140,
     2308                        requires: { library: true },
     2309
     2310                        click: function() {
     2311                            var controller = this.controller,
     2312                                state = controller.state();
     2313
     2314                            controller.close();
     2315                            state.trigger( 'update', state.get('library') );
     2316
     2317                            // Restore and reset the default state.
     2318                            controller.setState( controller.options.state );
     2319                            controller.reset();
     2320                        }
     2321                    }
     2322                }
     2323            }) );
     2324        },
     2325
     2326        videoPlaylistAddToolbar: function() {
     2327            this.toolbar.set( new media.view.Toolbar({
     2328                controller: this,
     2329                items: {
     2330                    insert: {
     2331                        style:    'primary',
     2332                        text:     l10n.addToVideoPlaylist,
     2333                        priority: 140,
     2334                        requires: { selection: true },
     2335
     2336                        click: function() {
     2337                            var controller = this.controller,
     2338                                state = controller.state(),
     2339                                edit = controller.state('video-playlist-edit');
     2340
     2341                            edit.get('library').add( state.get('selection').models );
     2342                            state.trigger('reset');
     2343                            controller.setState('video-playlist-edit');
     2344                        }
     2345                    }
     2346                }
     2347            }) );
     2348        }
    20432349    });
    20442350
     
    48655171     */
    48665172    media.view.Settings.Gallery = media.view.Settings.extend({
    4867         className: 'gallery-settings',
     5173        className: 'collection-settings gallery-settings',
    48685174        template:  media.template('gallery-settings')
     5175    });
     5176
     5177    /**
     5178     * wp.media.view.Settings.Playlist
     5179     *
     5180     * @constructor
     5181     * @augments wp.media.view.Settings
     5182     * @augments wp.media.View
     5183     * @augments wp.Backbone.View
     5184     * @augments Backbone.View
     5185     */
     5186    media.view.Settings.Playlist = media.view.Settings.extend({
     5187        className: 'collection-settings playlist-settings',
     5188        template:  media.template('playlist-settings')
    48695189    });
    48705190
  • trunk/src/wp-includes/js/mediaelement/wp-mediaelement.css

    r24948 r27239  
    1414    width: auto !important;
    1515}
     16
     17.wp-playlist {
     18    border: 1px solid #ccc;
     19    padding: 10px;
     20    margin: 12px 0 18px;
     21    font-size: 85%;
     22    line-height: 160%;
     23}
     24
     25.wp-playlist audio,
     26.wp-playlist video {
     27    display: inline-block;
     28    max-width: 100%;
     29}
     30
     31.wp-playlist .mejs-container {
     32    margin: 0;
     33    width: 100%;
     34}
     35
     36.wp-playlist .mejs-controls .mejs-button button {
     37    outline: 0;
     38}
     39
     40.wp-playlist-light {
     41    background: #fff;
     42}
     43
     44.wp-playlist-dark {
     45    color: #fff;
     46    background: #000;
     47}
     48
     49.wp-playlist-caption {
     50    max-width: 85%;
     51    overflow: hidden;
     52    text-overflow: ellipsis;
     53    white-space: nowrap;
     54}
     55
     56.wp-caption-meta {
     57    display: block;
     58}
     59
     60.wp-caption-title {
     61    font-size: 100%;
     62}
     63
     64.wp-caption-album {
     65    font-style: italic;
     66    overflow: hidden;
     67    text-overflow: ellipsis;
     68    white-space: nowrap;
     69}
     70
     71.wp-caption-artist {
     72    font-size: 85%;
     73    text-transform: uppercase;
     74}
     75
     76.wp-caption-by {
     77    font-size: 65%;
     78    font-weight: bold;
     79}
     80
     81.wp-playlist-item-length {
     82    position: absolute;
     83    right: 0;
     84    top: 0;
     85}
     86
     87.wp-playlist-tracks {
     88    margin-top: 10px;
     89    border-top: 1px solid #ccc;
     90}
     91
     92.wp-playlist-item {
     93    position: relative;
     94    cursor: pointer;
     95    border-bottom: 1px solid #ccc;
     96}
     97
     98.wp-playlist-current-item {
     99    overflow: hidden;
     100    margin-bottom: 10px;
     101    height: 60px;
     102}
     103
     104.wp-playlist-current-item img {
     105    float: left;
     106    max-width: 60px;
     107    height: auto;
     108    margin-right: 10px;
     109}
     110
     111.wp-playlist-current-item .wp-caption-title,
     112.wp-playlist-current-item .wp-caption-artist {
     113    overflow: hidden;
     114    text-overflow: ellipsis;
     115    white-space: nowrap;
     116}
  • trunk/src/wp-includes/js/plupload/handlers.js

    r26205 r27239  
    2323
    2424    // Disable submit
    25     jQuery('#insert-gallery').prop('disabled', true);
     25    jQuery('#insert-gallery, #insert-playlist').prop('disabled', true);
    2626}
    2727
     
    6565    if ( items.length == 1 ) {
    6666        items.addClass('open').find('.slidetoggle').show();
    67         jQuery('.insert-gallery').hide();
     67        jQuery('.insert-gallery, .insert-playlist').hide();
    6868    } else if ( items.length > 1 ) {
    6969        items.removeClass('open');
    70         // Only show Gallery button when there are at least two files.
    71         jQuery('.insert-gallery').show();
     70        // Only show Gallery/Playlist buttons when there are at least two files.
     71        jQuery('.insert-gallery, .insert-playlist').show();
    7272    }
    7373
     
    258258
    259259function uploadComplete() {
    260     jQuery('#insert-gallery').prop('disabled', false);
     260    jQuery('#insert-gallery, #insert-playlist').prop('disabled', false);
    261261}
    262262
  • trunk/src/wp-includes/js/swfupload/handlers.js

    r24472 r27239  
    213213        jQuery('.savebutton').hide();
    214214
    215     // Only show Gallery button when there are at least two files.
    216     if ( items.length > 1 )
    217         jQuery('.insert-gallery').show();
    218     else
    219         jQuery('.insert-gallery').hide();
     215    // Only show Gallery/Playlist buttons when there are at least two files.
     216    if ( items.length > 1 ) {
     217        jQuery('.insert-gallery, .insert-playlist').show();
     218    } else {
     219        jQuery('.insert-gallery, .insert-playlist').hide();
     220    }
    220221}
    221222
     
    239240    if ( swfu.getStats().files_queued == 0 ) {
    240241        jQuery('#cancel-upload').prop('disabled', true);
    241         jQuery('#insert-gallery').prop('disabled', false);
     242        jQuery('#insert-gallery, #insert-playlist').prop('disabled', false);
    242243    }
    243244}
  • trunk/src/wp-includes/js/tinymce/langs/wp-langs-en.js

    r26876 r27239  
    479479        editgallery: "Edit Gallery",
    480480        delgallery: "Delete Gallery",
     481        editplaylist: "Edit Playlist",
     482        delplaylist: "Delete Playlist",
    481483        wp_fullscreen_desc: "Distraction Free Writing mode (Alt + Shift + W)"
    482484    });
  • trunk/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js

    r27177 r27239  
    2626
    2727    function replaceAVShortcodes( content ) {
    28         var testRegex = /\[(audio|video)[^\]]*\]/,
    29             replaceRegex = /\[(audio|video)[^\]]*\]([\s\S]*?\[\/\1\])?/;
     28        var testRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]/,
     29            replaceRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]([\s\S]*?\[\/\1\])?/;
    3030
    3131        while ( testRegex.test( content ) ) {
     
    6161
    6262        // Check if the `wp.media.gallery` API exists.
    63         if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.gallery ) {
     63        if ( typeof wp === 'undefined' || ! wp.media ) {
    6464            return;
    6565        }
    6666
    6767        // Make sure we've selected a gallery node.
    68         if ( editor.dom.hasClass( node, 'wp-gallery' ) ) {
     68        if ( editor.dom.hasClass( node, 'wp-gallery' ) && wp.media.gallery ) {
    6969            gallery = wp.media.gallery;
    7070            data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
     
    7373            frame.state('gallery-edit').on( 'update', function( selection ) {
    7474                var shortcode = gallery.shortcode( selection ).string();
     75                editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
     76            });
     77        } else if ( editor.dom.hasClass( node, 'wp-playlist' ) && wp.media.playlist ) {
     78            data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
     79            frame = wp.media.playlist.edit( data );
     80
     81            frame.state('playlist-edit').on( 'update', function( selection ) {
     82                var shortcode = wp.media.playlist.shortcode( selection ).string();
     83                editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
     84            });
     85        } else if ( editor.dom.hasClass( node, 'wp-video-playlist' ) && wp.media['video-playlist'] ) {
     86            data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
     87            frame = wp.media['video-playlist'].edit( data );
     88
     89            frame.state('video-playlist-edit').on( 'update', function( selection ) {
     90                var shortcode = wp.media['video-playlist'].shortcode( selection ).string();
    7591                editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
    7692            });
     
    139155            } else if ( dom.hasClass( node, 'wp-audio' ) ) {
    140156                event.name = 'audio';
     157            } else if ( dom.hasClass( node, 'wp-playlist' ) ) {
     158                event.name = 'playlist';
     159            } else if ( dom.hasClass( node, 'wp-video-playlist' ) ) {
     160                event.name = 'video-playlist';
    141161            }
    142162        }
  • trunk/src/wp-includes/js/tinymce/skins/wordpress/wp-content.css

    r27177 r27239  
    144144}
    145145
     146.mce-content-body img.wp-media.wp-playlist {
     147    background-image: url("images/playlist-audio.png");
     148}
     149
     150.mce-content-body img.wp-media.wp-video-playlist {
     151    background-image: url("images/playlist-video.png");
     152}
     153
    146154#wp-image-toolbar {
    147155    position: absolute;
  • trunk/src/wp-includes/media-template.php

    r27143 r27239  
    412412    </script>
    413413
     414    <script type="text/html" id="tmpl-playlist-settings">
     415        <h3><?php _e('Playlist Settings'); ?></h3>
     416
     417        <label class="setting">
     418            <span><?php _e( 'Random Order' ); ?></span>
     419            <input type="checkbox" data-setting="_orderbyRandom" />
     420        </label>
     421
     422        <label class="setting">
     423            <span><?php _e('Style'); ?></span>
     424            <select class="style" data-setting="style">
     425                <option value="light">
     426                    <?php esc_attr_e('Light'); ?>
     427                </option>
     428                <option value="dark">
     429                    <?php esc_attr_e('Dark'); ?>
     430                </option>
     431            </select>
     432        </label>
     433
     434        <label class="setting">
     435            <span><?php _e( 'Show Tracklist' ); ?></span>
     436            <input type="checkbox" data-setting="_tracklist" />
     437        </label>
     438
     439        <label class="setting">
     440            <span><?php _e( 'Show Track Numbers' ); ?></span>
     441            <input type="checkbox" data-setting="_tracknumbers" />
     442        </label>
     443
     444        <label class="setting">
     445            <span><?php _e( 'Show Artist Name in Tracklist' ); ?></span>
     446            <input type="checkbox" data-setting="_artists" />
     447        </label>
     448
     449        <label class="setting">
     450            <span><?php _e( 'Show Images' ); ?></span>
     451            <input type="checkbox" data-setting="_images" />
     452        </label>
     453    </script>
     454
    414455    <script type="text/html" id="tmpl-embed-link-settings">
    415456        <label class="setting">
  • trunk/src/wp-includes/media.php

    r27209 r27239  
    933933    return $output;
    934934}
     935
     936/**
     937 * The Playlist shortcode.
     938 *
     939 * This implements the functionality of the Playlist Shortcode for displaying
     940 * a collection of WordPress audio or video files in a post.
     941 *
     942 * @since 3.9.0
     943 *
     944 * @param array $attr Attributes of the shortcode.
     945 * @return string $type Type of playlist. Defaults to audio, video is also supported
     946 */
     947function wp_get_playlist( $attr, $type ) {
     948    global $content_width;
     949    $post = get_post();
     950
     951    if ( ! in_array( $type, array( 'audio', 'video' ) ) ) {
     952        return '';
     953    }
     954
     955    static $instance = 0;
     956    $instance++;
     957
     958    if ( ! empty( $attr['ids'] ) ) {
     959        // 'ids' is explicitly ordered, unless you specify otherwise.
     960        if ( empty( $attr['orderby'] ) ) {
     961            $attr['orderby'] = 'post__in';
     962        }
     963        $attr['include'] = $attr['ids'];
     964    }
     965
     966    // Allow plugins/themes to override the default gallery template.
     967    $output = apply_filters( 'post_playlist', '', $attr, $type );
     968    if ( $output != '' ) {
     969        return $output;
     970    }
     971
     972    // We're trusting author input, so let's at least make sure it looks like a valid orderby statement
     973    if ( isset( $attr['orderby'] ) ) {
     974        $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
     975        if ( ! $attr['orderby'] )
     976            unset( $attr['orderby'] );
     977    }
     978
     979    extract( shortcode_atts( array(
     980        'order'     => 'ASC',
     981        'orderby'   => 'menu_order ID',
     982        'id'        => $post ? $post->ID : 0,
     983        'include'   => '',
     984        'exclude'   => '',
     985        'style'     => 'light',
     986        'tracklist' => 'audio' === $type,
     987        'tracknumbers' => 'audio' === $type,
     988        'images'    => true,
     989        'artists'   => true
     990    ), $attr, 'playlist' ) );
     991
     992    $id = intval( $id );
     993    if ( 'RAND' == $order ) {
     994        $orderby = 'none';
     995    }
     996
     997    $args = array(
     998        'post_status' => 'inherit',
     999        'post_type' => 'attachment',
     1000        'post_mime_type' => $type,
     1001        'order' => $order,
     1002        'orderby' => $orderby
     1003    );
     1004
     1005    if ( ! empty( $include ) ) {
     1006        $args['include'] = $include;
     1007        $_attachments = get_posts( $args );
     1008
     1009        $attachments = array();
     1010        foreach ( $_attachments as $key => $val ) {
     1011            $attachments[$val->ID] = $_attachments[$key];
     1012        }
     1013    } elseif ( ! empty( $exclude ) ) {
     1014        $args['post_parent'] = $id;
     1015        $args['exclude'] = $exclude;
     1016        $attachments = get_children( $args );
     1017    } else {
     1018        $args['post_parent'] = $id;
     1019        $attachments = get_children( $args );
     1020    }
     1021
     1022    if ( empty( $attachments ) ) {
     1023        return '';
     1024    }
     1025
     1026    if ( is_feed() ) {
     1027        $output = "\n";
     1028        foreach ( $attachments as $att_id => $attachment ) {
     1029            $output .= wp_get_attachment_link( $att_id ) . "\n";
     1030        }
     1031        return $output;
     1032    }
     1033
     1034    $supports_thumbs = ( current_theme_supports( 'post-thumbnails', "attachment:$type" ) && post_type_supports( "attachment:$type", 'thumbnail' ) )
     1035        || $images;
     1036
     1037    $outer = 22; // default padding and border of wrapper
     1038    $theme_width = $content_width - $outer;
     1039    $data = compact( 'type', 'style' );
     1040
     1041    // don't pass strings to JSON, will be truthy in JS
     1042    foreach ( array( 'tracklist', 'tracknumbers', 'images', 'artists' ) as $key ) {
     1043        $data[$key] = filter_var( $$key, FILTER_VALIDATE_BOOLEAN );
     1044    }
     1045
     1046    $tracks = array();
     1047    foreach ( $attachments as $attachment ) {
     1048        $url = wp_get_attachment_url( $attachment->ID );
     1049        $ftype = wp_check_filetype( $url, wp_get_mime_types() );
     1050        $track = array(
     1051            'type' => $type,
     1052            'src' => $url,
     1053            'type' => $ftype['ext'],
     1054            'title' => get_the_title( $attachment->ID ),
     1055            'caption' => wptexturize( $attachment->post_excerpt ),
     1056            'description' => wptexturize( $attachment->post_content )
     1057        );
     1058
     1059        $meta = wp_get_attachment_metadata( $attachment->ID );
     1060        if ( ! empty( $meta ) ) {
     1061            $track['meta'] = array();
     1062
     1063            $keys = array( 'title', 'artist', 'band', 'album', 'genre', 'year', 'length', 'length_formatted' );
     1064            foreach ( $keys as $key ) {
     1065                if ( ! empty( $meta[ $key ] ) ) {
     1066                    $track['meta'][ $key ] = $meta[ $key ];
     1067                }
     1068            }
     1069
     1070            if ( 'video' === $type ) {
     1071                $width = empty( $meta['width'] ) ? 640 : $meta['width'];
     1072                $height = empty( $meta['height'] ) ? 360 : $meta['height'];
     1073                $theme_height = round( ( $height * $theme_width ) / $width );
     1074                $track['dimensions'] = array(
     1075                    'original' => compact( 'width', 'height' ),
     1076                    'resized' => array(
     1077                        'width' => $theme_width,
     1078                        'height' => $theme_height
     1079                    )
     1080                );
     1081            }
     1082        }
     1083
     1084        if ( $supports_thumbs ) {
     1085            $id = get_post_thumbnail_id( $attachment->ID );
     1086            if ( ! empty( $id ) ) {
     1087                list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
     1088                $track['image'] = compact( 'src', 'width', 'height' );
     1089                list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumb' );
     1090                $track['thumb'] = compact( 'src', 'width', 'height' );
     1091            }
     1092        }
     1093
     1094        $tracks[] = $track;
     1095    }
     1096    $data['tracks'] = $tracks;
     1097
     1098    ob_start();
     1099
     1100    if ( 1 === $instance ):
     1101        wp_enqueue_style( 'wp-mediaelement' );
     1102        wp_enqueue_script( 'wp-playlist' );
     1103?>
     1104<!--[if lt IE 9]><script>document.createElement('<?php echo $type ?>');</script><![endif]-->
     1105<script type="text/html" id="tmpl-wp-playlist-current-item">
     1106    <# if ( data.image ) { #>
     1107    <img src="{{{ data.thumb.src }}}"/>
     1108    <# } #>
     1109    <# if ( data.meta.title ) { #>
     1110    <div class="wp-playlist-caption">
     1111        <span class="wp-caption-meta wp-caption-title">&#8220;{{{ data.meta.title }}}&#8221;</span>
     1112        <span class="wp-caption-meta wp-caption-album">{{{ data.meta.album }}}</span>
     1113        <span class="wp-caption-meta wp-caption-artist">{{{ data.meta.artist }}}</span>
     1114    </div>
     1115    <# } else { #>
     1116    <div class="wp-playlist-caption">{{{ data.caption }}}</div>
     1117    <# } #>
     1118</script>
     1119<script type="text/html" id="tmpl-wp-playlist-item">
     1120    <div class="wp-playlist-item">
     1121        <# if ( ( data.title || data.meta.title ) && ( ! data.artists || data.meta.artist ) ) { #>
     1122        <div class="wp-playlist-caption">
     1123            {{{ data.index ? ( data.index + '.&nbsp;' ) : '' }}}
     1124            <span class="wp-caption-title">&#8220;{{{ data.title ? data.title : data.meta.title }}}&#8221;</span>
     1125            <# if ( data.artists ) { #>
     1126            <span class="wp-caption-by"><?php _e( 'by' ) ?></span>
     1127            <span class="wp-caption-artist">{{{ data.meta.artist }}}</span>
     1128            <# } #>
     1129        </div>
     1130        <# } else { #>
     1131        <div class="wp-playlist-caption">{{{ data.index ? ( data.index + '.' ) : '' }}} {{{ data.caption ? data.caption : data.title }}}</div>
     1132        <# } #>
     1133        <# if ( data.meta.length_formatted ) { #>
     1134        <div class="wp-playlist-item-length">{{{ data.meta.length_formatted }}}</div>
     1135        <# } #>
     1136    </div>
     1137</script>
     1138    <?php endif ?>
     1139<div class="wp-playlist wp-<?php echo $type ?>-playlist wp-playlist-<?php echo $style ?>">
     1140    <?php if ( 'audio' === $type ): ?>
     1141    <div class="wp-playlist-current-item"></div>
     1142    <?php endif ?>
     1143    <<?php echo $type ?> controls="controls" preload="metadata" width="<?php echo $content_width - $outer ?>"></<?php echo $type ?>>
     1144    <div class="wp-playlist-next"></div>
     1145    <div class="wp-playlist-prev"></div>
     1146    <noscript>
     1147    <?php
     1148    $output = "\n";
     1149    foreach ( $attachments as $att_id => $attachment ) {
     1150        $output .= wp_get_attachment_link( $att_id ) . "\n";
     1151    }
     1152
     1153    echo $output;
     1154    ?>
     1155    </noscript>
     1156    <script type="application/json"><?php echo json_encode( $data ) ?></script>
     1157</div>
     1158    <?php
     1159    return ob_get_clean();
     1160}
     1161
     1162/**
     1163 * Playlist shortcode handler
     1164 *
     1165 * @since 3.9.0
     1166 *
     1167 * @param array $attr Parsed shortcode attributes.
     1168 * @return string The resolved playlist shortcode markup.
     1169 */
     1170function wp_playlist_shortcode( $attr ) {
     1171    return wp_get_playlist( $attr, 'audio' );
     1172}
     1173add_shortcode( 'playlist', 'wp_playlist_shortcode' );
     1174
     1175/**
     1176 * Video playlist shortcode handler
     1177 *
     1178 * @since 3.9.0
     1179 *
     1180 * @param array $attr Parsed shortcode attributes.
     1181 * @return string The resolved video playlist shortcode markup.
     1182 */
     1183function wp_video_playlist_shortcode( $attr ) {
     1184    return wp_get_playlist( $attr, 'video' );
     1185}
     1186add_shortcode( 'video-playlist', 'wp_video_playlist_shortcode' );
    9351187
    9361188/**
     
    20452297        'insertMediaTitle'   => __( 'Insert Media' ),
    20462298        'createNewGallery'   => __( 'Create a new gallery' ),
     2299        'createNewPlaylist'   => __( 'Create a new playlist' ),
     2300        'createNewVideoPlaylist'   => __( 'Create a new video playlist' ),
    20472301        'returnToLibrary'    => __( '&#8592; Return to library' ),
    20482302        'allMediaItems'      => __( 'All media items' ),
     
    20722326        'imageDetailsTitle'     => __( 'Image Details' ),
    20732327        'imageReplaceTitle'     => __( 'Replace Image' ),
    2074         'imageDetailsCancel'     => __( 'Cancel Edit' )
     2328        'imageDetailsCancel'    => __( 'Cancel Edit' ),
     2329
     2330        // Playlist
     2331        'playlistDragInfo'    => __( 'Drag and drop to reorder tracks.' ),
     2332        'createPlaylistTitle' => __( 'Create Playlist' ),
     2333        'editPlaylistTitle'   => __( 'Edit Playlist' ),
     2334        'cancelPlaylistTitle' => __( '&#8592; Cancel Playlist' ),
     2335        'insertPlaylist'      => __( 'Insert playlist' ),
     2336        'updatePlaylist'      => __( 'Update playlist' ),
     2337        'addToPlaylist'       => __( 'Add to playlist' ),
     2338        'addToPlaylistTitle'  => __( 'Add to Playlist' ),
     2339
     2340        // Video Playlist
     2341        'videoPlaylistDragInfo'    => __( 'Drag and drop to reorder videos.' ),
     2342        'createVideoPlaylistTitle' => __( 'Create Video Playlist' ),
     2343        'editVideoPlaylistTitle'   => __( 'Edit Video Playlist' ),
     2344        'cancelVideoPlaylistTitle' => __( '&#8592; Cancel Video Playlist' ),
     2345        'insertVideoPlaylist'      => __( 'Insert video playlist' ),
     2346        'updateVideoPlaylist'      => __( 'Update video playlist' ),
     2347        'addToVideoPlaylist'       => __( 'Add to video playlist' ),
     2348        'addToVideoPlaylistTitle'  => __( 'Add to Video Playlist' ),
    20752349    );
    20762350
  • trunk/src/wp-includes/script-loader.php

    r27233 r27239  
    315315        'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ),
    316316    ) );
     317
     318    $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
    317319
    318320    $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' );
Note: See TracChangeset for help on using the changeset viewer.