WordPress.org

Make WordPress Core

Ticket #26631: 26631.6.diff

File 26631.6.diff, 57.3 KB (added by wonderboymusic, 7 years ago)
  • src/wp-content/themes/twentyfourteen/functions.php

     
    111111                'max_posts' => 6,
    112112        ) );
    113113
     114        add_post_type_support( 'attachment:audio', 'thumbnail' );
     115        add_post_type_support( 'attachment:video', 'thumbnail' );
     116        add_theme_support( 'post-thumbnails', array( 'post', 'attachment:audio', 'attachment:video' ) );
     117
    114118        // This theme uses its own gallery styles.
    115119        add_filter( 'use_default_gallery_style', '__return_false' );
    116120}
  • src/wp-includes/css/editor.css

     
    833833}
    834834
    835835#wp_editbtns,
    836 #wp_gallerybtns {
     836#wp_gallerybtns,
     837#wp_playlistbtns {
    837838        padding: 2px;
    838839        position: absolute;
    839840        display: none;
     
    843844#wp_editimgbtn,
    844845#wp_delimgbtn,
    845846#wp_editgallery,
    846 #wp_delgallery {
     847#wp_delgallery,
     848#wp_editplaylist,
     849#wp_delplaylist {
    847850        border-color: #999;
    848851        background-color: #eee;
    849852        margin: 2px;
     
    857860#wp_editimgbtn:hover,
    858861#wp_delimgbtn:hover,
    859862#wp_editgallery:hover,
    860 #wp_delgallery:hover {
     863#wp_delgallery:hover,
     864#wp_editplaylist:hover,
     865#wp_delplaylist:hover {
    861866        border-color: #555;
    862867        background-color: #ccc;
    863868}
  • src/wp-includes/css/media-views.css

     
    592592        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    593593}
    594594
    595 .media-frame .media-toolbar .add-to-gallery {
     595.media-frame .media-toolbar .add-to-gallery,
     596.media-frame .media-toolbar .add-to-playlist {
    596597        display: none;
    597598}
    598599
     
    14241425        margin: 1.4em 0 0.4em;
    14251426}
    14261427
    1427 .gallery-settings {
     1428.gallery-settings, .playlist-settings {
    14281429        overflow: hidden;
    14291430}
    14301431
     
    18831884                border-top: none;
    18841885        }
    18851886
    1886         .gallery-settings h3 {
     1887        .gallery-settings h3, .playlist-settings h3 {
    18871888                margin-top: 45px;
    18881889        }
    18891890
  • src/wp-includes/js/media-editor.js

     
    88         *
    99         * @static
    1010         */
    11         var workflows = {};
     11        var workflows = {}, cache = {};
    1212
    1313        /**
    1414         * wp.media.string
     
    273273                        return html;
    274274                }
    275275        };
    276 
    277276        /**
    278          * wp.media.gallery
     277         * wp.media.collection
    279278         * @namespace
    280279         */
    281         wp.media.gallery = (function() {
    282                 /**
    283                  *
    284                  * @static
    285                  * @type object
    286                  */
    287                 var galleries = {};
     280        wp.media.collection = {
     281                attachments : function ( prop, type ) {
     282                        return function( shortcode ) {
     283                                var shortcodeString = shortcode.string(),
     284                                        result = cache[ shortcodeString ],
     285                                        attrs, args, query, others;
    288286
    289                 return {
    290                         /**
    291                          * Default gallery properties
    292                          *
    293                          * @global wp.media.view.settings
    294                          * @readonly
    295                          */
    296                         defaults: {
    297                                 order:      'ASC',
    298                                 id:         wp.media.view.settings.post.id,
    299                                 itemtag:    'dl',
    300                                 icontag:    'dt',
    301                                 captiontag: 'dd',
    302                                 columns:    '3',
    303                                 link:       'post',
    304                                 size:       'thumbnail',
    305                                 orderby:    'menu_order ID'
    306                         },
    307                         /**
    308                          * Retrieve attachments based on the properties of the passed shortcode
    309                          *
    310                          * @global wp.media.query
    311                          *
    312                          * @param {wp.shortcode} shortcode An instance of wp.shortcode().
    313                          * @returns {wp.media.model.Attachments} A Backbone.Collection containing
    314                          *      the images belonging to a gallery. The 'gallery' prop is a Backbone.Model
    315                          *      containing the 'props' for the gallery.
    316                          */
    317                         attachments: function( shortcode ) {
    318                                 var shortcodeString = shortcode.string(),
    319                                         result = galleries[ shortcodeString ],
    320                                         attrs, args, query, others;
     287                                delete cache[ shortcodeString ];
    321288
    322                                 delete galleries[ shortcodeString ];
    323 
    324                                 if ( result ) {
    325                                         return result;
     289                                if ( result ) {
     290                                        return result;
    326291                                }
    327292
    328                                 // Fill the default shortcode attributes.
    329                                 attrs = _.defaults( shortcode.attrs.named, wp.media.gallery.defaults );
    330                                 args  = _.pick( attrs, 'orderby', 'order' );
     293                                // Fill the default shortcode attributes.
     294                                attrs = _.defaults( shortcode.attrs.named, this.defaults );
     295                                args  = _.pick( attrs, 'orderby', 'order' );
    331296
    332                                 args.type    = 'image';
    333                                 args.perPage = -1;
     297                                args.type = type;
     298                                args.perPage = -1;
    334299
    335                                 // Mark the `orderby` override attribute.
    336                                 if( undefined !== attrs.orderby ) {
    337                                         attrs._orderByField = attrs.orderby;
     300                                // Mark the `orderby` override attribute.
     301                                if ( undefined !== attrs.orderby ) {
     302                                        attrs._orderByField = attrs.orderby;
    338303                                }
     304
    339305                                if ( 'rand' === attrs.orderby ) {
    340306                                        attrs._orderbyRandom = true;
    341307                                }
     
    365331                                others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' );
    366332
    367333                                query = wp.media.query( args );
    368                                 query.gallery = new Backbone.Model( others );
     334                                query[ prop ] = new Backbone.Model( others );
    369335                                return query;
    370                         },
    371                         /**
    372                          * Triggered when clicking 'Insert Gallery' or 'Update Gallery'
    373                          *
    374                          * @global wp.shortcode
    375                          * @global wp.media.model.Attachments
    376                          *
    377                          * @param {wp.media.model.Attachments} attachments A Backbone.Collection containing
    378                          *      the images belonging to a gallery. The 'gallery' prop is a Backbone.Model
    379                          *      containing the 'props' for the gallery.
    380                          * @returns {wp.shortcode}
    381                          */
    382                         shortcode: function( attachments ) {
    383                                 var props = attachments.props.toJSON(),
    384                                         attrs = _.pick( props, 'orderby', 'order' ),
    385                                         shortcode, clone;
     336                        };
     337                },
    386338
    387                                 if ( attachments.gallery ) {
    388                                         _.extend( attrs, attachments.gallery.toJSON() );
    389                                 }
     339                shortcodeAttrs : function ( prop, attachments ) {
     340                        var props = attachments.props.toJSON(),
     341                                attrs = _.pick( props, 'orderby', 'order', 'style' );
    390342
    391                                 // Convert all gallery shortcodes to use the `ids` property.
    392                                 // Ignore `post__in` and `post__not_in`; the attachments in
    393                                 // the collection will already reflect those properties.
    394                                 attrs.ids = attachments.pluck('id');
     343                        if ( attachments[ prop ] ) {
     344                                _.extend( attrs, attachments[ prop ].toJSON() );
     345                        }
    395346
    396                                 // Copy the `uploadedTo` post ID.
    397                                 if ( props.uploadedTo ) {
    398                                         attrs.id = props.uploadedTo;
    399                                 }
     347                        // Convert all collection shortcodes to use the `ids` property.
     348                        // Ignore `post__in` and `post__not_in`; the attachments in
     349                        // the collection will already reflect those properties.
     350                        attrs.ids = attachments.pluck('id');
    400351
    401                                 // Check if the gallery is randomly ordered.
    402                                 delete attrs.orderby;
     352                        // Copy the `uploadedTo` post ID.
     353                        if ( props.uploadedTo ) {
     354                                attrs.id = props.uploadedTo;
     355                        }
    403356
    404                                 if ( attrs._orderbyRandom ) {
    405                                         attrs.orderby = 'rand';
    406                                 } else if ( attrs._orderByField && attrs._orderByField != 'rand' ) {
    407                                         attrs.orderby = attrs._orderByField;
    408                                 }
     357                        // Check if the collection is randomly ordered.
     358                        delete attrs.orderby;
    409359
    410                                 delete attrs._orderbyRandom;
    411                                 delete attrs._orderByField;
     360                        if ( attrs._orderbyRandom ) {
     361                                attrs.orderby = 'rand';
     362                        } else if ( attrs._orderByField && attrs._orderByField != 'rand' ) {
     363                                attrs.orderby = attrs._orderByField;
     364                        }
    412365
    413                                 // If the `ids` attribute is set and `orderby` attribute
    414                                 // is the default value, clear it for cleaner output.
    415                                 if ( attrs.ids && 'post__in' === attrs.orderby ) {
    416                                         delete attrs.orderby;
    417                                 }
     366                        delete attrs._orderbyRandom;
     367                        delete attrs._orderByField;
    418368
    419                                 // Remove default attributes from the shortcode.
    420                                 _.each( wp.media.gallery.defaults, function( value, key ) {
    421                                         if ( value === attrs[ key ] )
    422                                                 delete attrs[ key ];
    423                                 });
     369                        // If the `ids` attribute is set and `orderby` attribute
     370                        // is the default value, clear it for cleaner output.
     371                        if ( attrs.ids && 'post__in' === attrs.orderby ) {
     372                                delete attrs.orderby;
     373                        }
    424374
    425                                 shortcode = new wp.shortcode({
    426                                         tag:    'gallery',
    427                                         attrs:  attrs,
    428                                         type:   'single'
    429                                 });
     375                        // Remove default attributes from the shortcode.
     376                        _.each( this.defaults, function( value, key ) {
     377                                if ( value === attrs[ key ] ) {
     378                                        delete attrs[ key ];
     379                                }
     380                        });
    430381
    431                                 // Use a cloned version of the gallery.
    432                                 clone = new wp.media.model.Attachments( attachments.models, {
    433                                         props: props
    434                                 });
    435                                 clone.gallery = attachments.gallery;
    436                                 galleries[ shortcode.string() ] = clone;
     382                        return attrs;
     383                },
    437384
    438                                 return shortcode;
    439                         },
    440                         /**
    441                          * Triggered when double-clicking a Gallery shortcode placeholder
    442                          *   in the editor
    443                          *
    444                          * @global wp.shortcode
    445                          * @global wp.media.model.Selection
    446                          * @global wp.media.view.l10n
    447                          *
    448                          * @param {string} content Content that is searched for possible
    449                          *    shortcode markup matching the passed tag name,
    450                          *
    451                          * @this wp.media.gallery
    452                          *
    453                          * @returns {wp.media.view.MediaFrame.Select} A media workflow.
    454                          */
    455                         edit: function( content ) {
    456                                 var shortcode = wp.shortcode.next( 'gallery', content ),
    457                                         defaultPostId = wp.media.gallery.defaults.id,
    458                                         attachments, selection;
     385                editSelection : function ( prop, shortcode ) {
     386                        var defaultPostId = wp.media[ prop ].defaults.id,
     387                                attachments, selection;
    459388
    460                                 // Bail if we didn't match the shortcode or all of the content.
    461                                 if ( ! shortcode || shortcode.content !== content ) {
    462                                         return;
    463                                 }
     389                        // Ignore the rest of the match object.
     390                        shortcode = shortcode.shortcode;
    464391
    465                                 // Ignore the rest of the match object.
    466                                 shortcode = shortcode.shortcode;
     392                        if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) {
     393                                shortcode.set( 'id', defaultPostId );
     394                        }
    467395
    468                                 if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) {
    469                                         shortcode.set( 'id', defaultPostId );
    470                                 }
     396                        attachments = wp.media[ prop ].attachments( shortcode );
    471397
    472                                 attachments = wp.media.gallery.attachments( shortcode );
     398                        selection = new wp.media.model.Selection( attachments.models, {
     399                                props:    attachments.props.toJSON(),
     400                                multiple: true
     401                        });
    473402
    474                                 selection = new wp.media.model.Selection( attachments.models, {
    475                                         props:    attachments.props.toJSON(),
    476                                         multiple: true
    477                                 });
     403                        selection[ prop ] = attachments[ prop ];
    478404
    479                                 selection.gallery = attachments.gallery;
     405                        // Fetch the query's attachments, and then break ties from the
     406                        // query to allow for sorting.
     407                        selection.more().done( function() {
     408                                // Break ties with the query.
     409                                selection.props.set({ query: false });
     410                                selection.unmirror();
     411                                selection.props.unset('orderby');
     412                        });
    480413
    481                                 // Fetch the query's attachments, and then break ties from the
    482                                 // query to allow for sorting.
    483                                 selection.more().done( function() {
    484                                         // Break ties with the query.
    485                                         selection.props.set({ query: false });
    486                                         selection.unmirror();
    487                                         selection.props.unset('orderby');
    488                                 });
     414                        return selection;
     415                },
    489416
    490                                 // Destroy the previous gallery frame.
    491                                 if ( this.frame ) {
    492                                         this.frame.dispose();
    493                                 }
     417                cacheShortcode : function ( prop, attachments, shortcode ) {
     418                        // Use a cloned version of the playlist.
     419                        var clone = new wp.media.model.Attachments( attachments.models, {
     420                                props: attachments.props.toJSON()
     421                        });
     422                        clone[ prop ] = attachments[ prop ];
     423                        cache[ shortcode.string() ] = clone;
    494424
    495                                 // Store the current gallery frame.
    496                                 this.frame = wp.media({
    497                                         frame:     'post',
    498                                         state:     'gallery-edit',
    499                                         title:     wp.media.view.l10n.editGalleryTitle,
    500                                         editing:   true,
    501                                         multiple:  true,
    502                                         selection: selection
    503                                 }).open();
     425                        return shortcode;
     426                },
    504427
    505                                 return this.frame;
     428                getEditFrame : function ( args ) {
     429                        // Destroy the previous gallery frame.
     430                        if ( this.frame ) {
     431                                this.frame.dispose();
    506432                        }
    507                 };
    508         }());
    509433
     434                        // Store the current gallery frame.
     435                        this.frame = wp.media( _.extend( {
     436                                frame:     'post',
     437                                editing:   true,
     438                                multiple:  true,
     439                        }, args ) ).open();
     440
     441                        return this.frame;
     442                },
     443
     444                instance : function ( prop, args ) {
     445                        return {
     446                                attachments: this.attachments( prop, args.type ),
     447                                shortcode: function( attachments ) {
     448                                        var shortcode = new wp.shortcode({
     449                                                tag:    prop,
     450                                                attrs:  wp.media.collection.shortcodeAttrs( prop, attachments ),
     451                                                type:   'single'
     452                                        });
     453
     454                                        return wp.media.collection.cacheShortcode( prop, attachments, shortcode );
     455                                },
     456                                edit: function( content ) {
     457                                        var shortcode = wp.shortcode.next( prop, content );
     458
     459                                        // Bail if we didn't match the shortcode or all of the content.
     460                                        if ( ! shortcode || shortcode.content !== content ) {
     461                                                return;
     462                                        }
     463
     464                                        return wp.media.collection.getEditFrame( {
     465                                                title:          args.title,
     466                                                state:          prop + '-edit',
     467                                                selection:      wp.media.collection.editSelection( prop, shortcode )
     468                                        } );
     469                                }
     470                        };
     471                }
     472        };
     473
     474        wp.media.gallery = (function() {
     475                var gallery = {
     476                        defaults : {
     477                                itemtag:    'dl',
     478                                icontag:    'dt',
     479                                captiontag: 'dd',
     480                                columns:    '3',
     481                                link:       'post',
     482                                size:       'thumbnail',
     483                                order: 'ASC',
     484                                id: wp.media.view.settings.post.id,
     485                                orderby : 'menu_order ID'
     486                        }
     487                };
     488
     489                return _.extend(gallery, wp.media.collection.instance( 'gallery', {
     490                        type : 'image',
     491                        title : wp.media.view.l10n.editGalleryTitle
     492                }));
     493        }());
     494
     495        wp.media.playlist = (function() {
     496                var playlist = {
     497                        defaults : {
     498                                id:         wp.media.view.settings.post.id,
     499                                style:          'light',
     500                                tracklist:      true,
     501                                tracknumbers: true
     502                        }
     503                };
     504
     505                return _.extend(playlist, wp.media.collection.instance( 'playlist', {
     506                        type : 'audio',
     507                        title : wp.media.view.l10n.editPlaylistTitle
     508                }));
     509        }());
     510
     511        wp.media['video-playlist'] = (function() {
     512                var playlist = {
     513                        defaults : {
     514                                id:         wp.media.view.settings.post.id,
     515                                style:          'dark',
     516                                tracklist:      false,
     517                                tracknumbers: false
     518                        }
     519                };
     520
     521                return _.extend(playlist, wp.media.collection.instance( 'video-playlist', {
     522                        type : 'video',
     523                        title : wp.media.view.l10n.editVideoPlaylistTitle
     524                }));
     525        }());
     526
    510527        /**
    511528         * wp.media.featuredImage
    512529         * @namespace
     
    725742                                this.insert( wp.media.gallery.shortcode( selection ).string() );
    726743                        }, this );
    727744
     745                        workflow.state('playlist-edit').on( 'update', function( selection ) {
     746                                this.insert( wp.media.playlist.shortcode( selection ).string() );
     747                        }, this );
     748
     749                        workflow.state('video-playlist-edit').on( 'update', function( selection ) {
     750                                this.insert( wp.media['video-playlist'].shortcode( selection ).string() );
     751                        }, this );
     752
    728753                        workflow.state('embed').on( 'select', function() {
    729754                                /**
    730755                                 * @this wp.media.editor
     
    964989                                if ( elem.hasClass( 'gallery' ) ) {
    965990                                        options.state = 'gallery';
    966991                                        options.title = wp.media.view.l10n.createGalleryTitle;
     992                                } else if ( elem.hasClass( 'playlist' ) ) {
     993                                        options.state = 'playlist';
     994                                        options.title = wp.media.view.l10n.createPlaylistTitle;
     995                                } else if ( elem.hasClass( 'video-playlist' ) ) {
     996                                        options.state = 'video-playlist';
     997                                        options.title = wp.media.view.l10n.createVideoPlaylistTitle;
    967998                                }
    968999
    9691000                                wp.media.editor.open( editor, options );
  • src/wp-includes/js/media-views.js

     
    760760        });
    761761
    762762        /**
    763          * wp.media.controller.GalleryEdit
    764          *
    765          * @constructor
    766          * @augments wp.media.controller.Library
    767          * @augments wp.media.controller.State
    768          * @augments Backbone.Model
    769          */
    770         media.controller.GalleryEdit = media.controller.Library.extend({
    771                 defaults: {
    772                         id:         'gallery-edit',
    773                         multiple:   false,
    774                         describe:   true,
    775                         edge:       199,
    776                         editing:    false,
    777                         sortable:   true,
    778                         searchable: false,
    779                         toolbar:    'gallery-edit',
    780                         content:    'browse',
    781                         title:      l10n.editGalleryTitle,
    782                         priority:   60,
    783                         dragInfo:   true,
    784 
    785                         // Don't sync the selection, as the Edit Gallery library
    786                         // *is* the selection.
    787                         syncSelection: false
    788                 },
    789 
    790                 initialize: function() {
    791                         // If we haven't been provided a `library`, create a `Selection`.
    792                         if ( ! this.get('library') ) {
    793                                 this.set( 'library', new media.model.Selection() );
    794                         }
    795 
    796                         // The single `Attachment` view to be used in the `Attachments` view.
    797                         if ( ! this.get('AttachmentView') ) {
    798                                 this.set( 'AttachmentView', media.view.Attachment.EditLibrary );
    799                         }
    800 
    801                         media.controller.Library.prototype.initialize.apply( this, arguments );
    802                 },
    803 
    804                 activate: function() {
    805                         var library = this.get('library');
    806 
    807                         // Limit the library to images only.
    808                         library.props.set( 'type', 'image' );
    809 
    810                         // Watch for uploaded attachments.
    811                         this.get('library').observe( wp.Uploader.queue );
    812 
    813                         this.frame.on( 'content:render:browse', this.gallerySettings, this );
    814 
    815                         media.controller.Library.prototype.activate.apply( this, arguments );
    816                 },
    817 
    818                 deactivate: function() {
    819                         // Stop watching for uploaded attachments.
    820                         this.get('library').unobserve( wp.Uploader.queue );
    821 
    822                         this.frame.off( 'content:render:browse', this.gallerySettings, this );
    823 
    824                         media.controller.Library.prototype.deactivate.apply( this, arguments );
    825                 },
    826 
    827                 /**
    828                  * @param {Object} browser
    829                  */
    830                 gallerySettings: function( browser ) {
    831                         var library = this.get('library');
    832 
    833                         if ( ! library || ! browser ) {
    834                                 return;
    835                         }
    836 
    837                         library.gallery = library.gallery || new Backbone.Model();
    838 
    839                         browser.sidebar.set({
    840                                 gallery: new media.view.Settings.Gallery({
    841                                         controller: this,
    842                                         model:      library.gallery,
    843                                         priority:   40
    844                                 })
    845                         });
    846 
    847                         browser.toolbar.set( 'reverse', {
    848                                 text:     l10n.reverseOrder,
    849                                 priority: 80,
    850 
    851                                 click: function() {
    852                                         library.reset( library.toArray().reverse() );
    853                                 }
    854                         });
    855                 }
    856         });
    857 
    858         /**
    859          * wp.media.controller.GalleryAdd
    860          *
    861          * @constructor
    862          * @augments wp.media.controller.Library
    863          * @augments wp.media.controller.State
    864          * @augments Backbone.Model
    865          */
    866         media.controller.GalleryAdd = media.controller.Library.extend({
    867                 defaults: _.defaults({
    868                         id:           'gallery-library',
    869                         filterable:   'uploaded',
    870                         multiple:     'add',
    871                         menu:         'gallery',
    872                         toolbar:      'gallery-add',
    873                         title:        l10n.addToGalleryTitle,
    874                         priority:     100,
    875 
    876                         // Don't sync the selection, as the Edit Gallery library
    877                         // *is* the selection.
    878                         syncSelection: false
    879                 }, media.controller.Library.prototype.defaults ),
    880 
    881                 /**
    882                  * If we haven't been provided a `library`, create a `Selection`.
    883                  */
    884                 initialize: function() {
    885                         if ( ! this.get('library') ) {
    886                                 this.set( 'library', media.query({ type: 'image' }) );
    887                         }
    888                         media.controller.Library.prototype.initialize.apply( this, arguments );
    889                 },
    890 
    891                 activate: function() {
    892                         var library = this.get('library'),
    893                                 edit    = this.frame.state('gallery-edit').get('library');
    894 
    895                         if ( this.editLibrary && this.editLibrary !== edit ) {
    896                                 library.unobserve( this.editLibrary );
    897                         }
    898 
    899                         // Accepts attachments that exist in the original library and
    900                         // that do not exist in gallery's library.
    901                         library.validator = function( attachment ) {
    902                                 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) &&
    903                                         media.model.Selection.prototype.validator.apply( this, arguments );
    904                         };
    905 
    906                         // Reset the library to ensure that all attachments are re-added
    907                         // to the collection. Do so silently, as calling `observe` will
    908                         // trigger the `reset` event.
    909                         library.reset( library.mirroring.models, { silent: true });
    910                         library.observe( edit );
    911                         this.editLibrary = edit;
    912 
    913                         media.controller.Library.prototype.activate.apply( this, arguments );
    914                 }
    915         });
    916 
    917         /**
    918763         * wp.media.controller.FeaturedImage
    919764         *
    920765         * @constructor
     
    993838                }
    994839        });
    995840
     841        media.controller.CollectionEdit = function ( prop, args ) {
     842                return media.controller.Library.extend({
     843                        defaults : _.defaults(args.defaults || {}, {
     844                                id:         prop + '-edit',
     845                                toolbar:    prop + '-edit',
     846                                multiple:   false,
     847                                describe:   true,
     848                                edge:       199,
     849                                editing:    false,
     850                                sortable:   true,
     851                                searchable: false,
     852                                content:    'browse',
     853                                priority:   60,
     854                                dragInfo:   true,
     855
     856                                // Don't sync the selection, as the Edit {Collection} library
     857                                // *is* the selection.
     858                                syncSelection: false
     859                        }),
     860
     861                        initialize: function() {
     862                                // If we haven't been provided a `library`, create a `Selection`.
     863                                if ( ! this.get('library') ) {
     864                                        this.set( 'library', new media.model.Selection() );
     865                                }
     866                                // The single `Attachment` view to be used in the `Attachments` view.
     867                                if ( ! this.get('AttachmentView') ) {
     868                                        this.set( 'AttachmentView', media.view.Attachment.EditLibrary );
     869                                }
     870                                media.controller.Library.prototype.initialize.apply( this, arguments );
     871                        },
     872
     873                        activate: function() {
     874                                var library = this.get('library');
     875
     876                                // Limit the library to images only.
     877                                library.props.set( 'type', args.type );
     878
     879                                // Watch for uploaded attachments.
     880                                this.get('library').observe( wp.Uploader.queue );
     881
     882                                this.frame.on( 'content:render:browse', this.settings, this );
     883
     884                                media.controller.Library.prototype.activate.apply( this, arguments );
     885                        },
     886
     887                        deactivate: function() {
     888                                // Stop watching for uploaded attachments.
     889                                this.get('library').unobserve( wp.Uploader.queue );
     890
     891                                this.frame.off( 'content:render:browse', this.settings, this );
     892
     893                                media.controller.Library.prototype.deactivate.apply( this, arguments );
     894                        },
     895
     896                        settings: function( browser ) {
     897                                var library = this.get('library'), obj = {};
     898
     899                                if ( ! library || ! browser ) {
     900                                        return;
     901                                }
     902
     903                                library[ prop ] = library[ prop ] || new Backbone.Model();
     904
     905                                obj[ prop ] = new media.view.Settings[ args.settings ]({
     906                                        controller: this,
     907                                        model:      library[ prop ],
     908                                        priority:   40
     909                                });
     910
     911                                browser.sidebar.set( obj );
     912
     913                                if ( args.dragInfoText ) {
     914                                        browser.toolbar.set( 'dragInfo', new media.View({
     915                                                el: $( '<div class="instructions">' + args.dragInfoText + '</div>' )[0],
     916                                                priority: -40
     917                                        }) );
     918                                }
     919
     920                                browser.toolbar.set( 'reverse', {
     921                                        text:     l10n.reverseOrder,
     922                                        priority: 80,
     923
     924                                        click: function() {
     925                                                library.reset( library.toArray().reverse() );
     926                                        }
     927                                });
     928                        }
     929                });
     930        };
     931
     932        media.controller.CollectionAdd = function ( prop, args ) {
     933                return media.controller.Library.extend({
     934                        defaults: _.defaults({
     935                                id:           prop + '-library',
     936                                filterable:   'uploaded',
     937                                multiple:     'add',
     938                                menu:         prop,
     939                                toolbar:      prop + '-add',
     940                                priority:     100,
     941                                syncSelection: false
     942                        }, args.defaults || {}, media.controller.Library.prototype.defaults ),
     943                        initialize: function() {
     944                                // If we haven't been provided a `library`, create a `Selection`.
     945                                if ( ! this.get('library') ) {
     946                                        this.set( 'library', media.query({ type: args.type }) );
     947                                }
     948                                media.controller.Library.prototype.initialize.apply( this, arguments );
     949                        },
     950
     951                        activate: function() {
     952                                var library = this.get('library'),
     953                                        edit    = this.frame.state(prop + '-edit').get('library');
     954
     955                                if ( this.editLibrary && this.editLibrary !== edit ) {
     956                                        library.unobserve( this.editLibrary );
     957                                }
     958
     959                                // Accepts attachments that exist in the original library and
     960                                // that do not exist in gallery's library.
     961                                library.validator = function( attachment ) {
     962                                        return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && media.model.Selection.prototype.validator.apply( this, arguments );
     963                                };
     964
     965                                // Reset the library to ensure that all attachments are re-added
     966                                // to the collection. Do so silently, as calling `observe` will
     967                                // trigger the `reset` event.
     968                                library.reset( library.mirroring.models, { silent: true });
     969                                library.observe( edit );
     970                                this.editLibrary = edit;
     971
     972                                media.controller.Library.prototype.activate.apply( this, arguments );
     973                        }
     974                });
     975        };
     976
     977        // wp.media.controller.GalleryEdit
     978        // -------------------------------
     979        media.controller.GalleryEdit = media.controller.CollectionEdit( 'gallery', {
     980                type: 'image',
     981                settings: 'Gallery',
     982                defaults: {
     983                        title: l10n.editGalleryTitle
     984                }
     985        });
     986
     987        // wp.media.controller.GalleryAdd
     988        // ---------------------------------
     989        media.controller.GalleryAdd = media.controller.CollectionAdd( 'gallery', {
     990                type: 'image',
     991                defaults: {
     992                        title: l10n.addToGalleryTitle
     993                }
     994        });
     995
     996        // wp.media.controller.PlaylistEdit
     997        // -------------------------------
     998        media.controller.PlaylistEdit = media.controller.CollectionEdit( 'playlist', {
     999                type: 'audio',
     1000                settings: 'Playlist',
     1001                dragInfoText: l10n.playlistDragInfo,
     1002                defaults: {
     1003                        title: l10n.editPlaylistTitle,
     1004                        dragInfo : false
     1005                }
     1006        });
     1007
     1008        // wp.media.controller.PlaylistAdd
     1009        // ---------------------------------
     1010        media.controller.PlaylistAdd = media.controller.CollectionAdd( 'playlist', {
     1011                type: 'audio',
     1012                defaults: {
     1013                        title: l10n.addToPlaylistTitle
     1014                }
     1015        });
     1016
     1017        // wp.media.controller.VideoPlaylistEdit
     1018        // -------------------------------
     1019        media.controller.VideoPlaylistEdit = media.controller.CollectionEdit( 'video-playlist', {
     1020                type: 'video',
     1021                settings: 'Playlist',
     1022                dragInfoText: l10n.videoPlaylistDragInfo,
     1023                defaults: {
     1024                        title: l10n.editVideoPlaylistTitle,
     1025                        dragInfo : false
     1026                }
     1027        });
     1028
     1029        // wp.media.controller.VideoPlaylistAdd
     1030        // ---------------------------------
     1031        media.controller.VideoPlaylistAdd = media.controller.CollectionAdd( 'video-playlist', {
     1032                type: 'video',
     1033                defaults: {
     1034                        title: l10n.addToVideoPlaylistTitle
     1035                }
     1036        });
     1037
    9961038        /**
    9971039         * wp.media.controller.ReplaceImage
    9981040         *
     
    17401782                                        menu:    'gallery'
    17411783                                }),
    17421784
    1743                                 new media.controller.GalleryAdd()
     1785                                new media.controller.GalleryAdd(),
     1786
     1787                                new media.controller.Library({
     1788                                        id:         'playlist',
     1789                                        title:      l10n.createPlaylistTitle,
     1790                                        priority:   60,
     1791                                        toolbar:    'main-playlist',
     1792                                        filterable: 'uploaded',
     1793                                        multiple:   'add',
     1794                                        editable:   false,
     1795
     1796                                        library:  media.query( _.defaults({
     1797                                                type: 'audio'
     1798                                        }, options.library ) )
     1799                                }),
     1800
     1801                                // Playlist states.
     1802                                new media.controller.PlaylistEdit({
     1803                                        library: options.selection,
     1804                                        editing: options.editing,
     1805                                        menu:    'playlist'
     1806                                }),
     1807
     1808                                new media.controller.PlaylistAdd(),
     1809
     1810                                new media.controller.Library({
     1811                                        id:         'video-playlist',
     1812                                        title:      l10n.createVideoPlaylistTitle,
     1813                                        priority:   60,
     1814                                        toolbar:    'main-video-playlist',
     1815                                        filterable: 'uploaded',
     1816                                        multiple:   'add',
     1817                                        editable:   false,
     1818
     1819                                        library:  media.query( _.defaults({
     1820                                                type: 'video'
     1821                                        }, options.library ) )
     1822                                }),
     1823
     1824                                // Video Playlist states.
     1825                                new media.controller.VideoPlaylistEdit({
     1826                                        library: options.selection,
     1827                                        editing: options.editing,
     1828                                        menu:    'video-playlist'
     1829                                }),
     1830
     1831                                new media.controller.VideoPlaylistAdd()
    17441832                        ]);
    17451833
    17461834
     
    17551843                         */
    17561844                        media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
    17571845                        this.on( 'menu:create:gallery', this.createMenu, this );
     1846                        this.on( 'menu:create:playlist', this.createMenu, this );
     1847                        this.on( 'menu:create:video-playlist', this.createMenu, this );
    17581848                        this.on( 'toolbar:create:main-insert', this.createToolbar, this );
    1759                         this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1849                        this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1850                        this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
     1851                        this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
    17601852                        this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
    17611853                        this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
    17621854
    17631855                        var handlers = {
    17641856                                menu: {
    17651857                                        'default': 'mainMenu',
    1766                                         'gallery': 'galleryMenu'
     1858                                        'gallery': 'galleryMenu',
     1859                                        'playlist': 'playlistMenu',
     1860                                        'video-playlist': 'videoPlaylistMenu'
    17671861                                },
    17681862
    17691863                                content: {
     
    17751869                                        'main-insert':      'mainInsertToolbar',
    17761870                                        'main-gallery':     'mainGalleryToolbar',
    17771871                                        'gallery-edit':     'galleryEditToolbar',
    1778                                         'gallery-add':      'galleryAddToolbar'
     1872                                        'gallery-add':      'galleryAddToolbar',
     1873                                        'main-playlist':        'mainPlaylistToolbar',
     1874                                        'playlist-edit':        'playlistEditToolbar',
     1875                                        'playlist-add':         'playlistAddToolbar',
     1876                                        'main-video-playlist': 'mainVideoPlaylistToolbar',
     1877                                        'video-playlist-edit': 'videoPlaylistEditToolbar',
     1878                                        'video-playlist-add': 'videoPlaylistAddToolbar'
    17791879                                }
    17801880                        };
    17811881
     
    18251925                        });
    18261926                },
    18271927
     1928                playlistMenu: function( view ) {
     1929                        var lastState = this.lastState(),
     1930                                previous = lastState && lastState.id,
     1931                                frame = this;
     1932
     1933                        view.set({
     1934                                cancel: {
     1935                                        text:     l10n.cancelPlaylistTitle,
     1936                                        priority: 20,
     1937                                        click:    function() {
     1938                                                if ( previous )
     1939                                                        frame.setState( previous );
     1940                                                else
     1941                                                        frame.close();
     1942                                        }
     1943                                },
     1944                                separateCancel: new media.View({
     1945                                        className: 'separator',
     1946                                        priority: 60
     1947                                })
     1948                        });
     1949                },
     1950
     1951                videoPlaylistMenu: function( view ) {
     1952                        var lastState = this.lastState(),
     1953                                previous = lastState && lastState.id,
     1954                                frame = this;
     1955
     1956                        view.set({
     1957                                cancel: {
     1958                                        text:     l10n.cancelVideoPlaylistTitle,
     1959                                        priority: 20,
     1960                                        click:    function() {
     1961                                                if ( previous )
     1962                                                        frame.setState( previous );
     1963                                                else
     1964                                                        frame.close();
     1965                                        }
     1966                                },
     1967                                separateCancel: new media.View({
     1968                                        className: 'separator',
     1969                                        priority: 80
     1970                                })
     1971                        });
     1972                },
     1973
    18281974                // Content
    18291975                embedContent: function() {
    18301976                        var view = new media.view.Embed({
     
    19432089                        });
    19442090                },
    19452091
     2092                mainPlaylistToolbar: function( view ) {
     2093                        var controller = this;
     2094
     2095                        this.selectionStatusToolbar( view );
     2096
     2097                        view.set( 'playlist', {
     2098                                style:    'primary',
     2099                                text:     l10n.createNewPlaylist,
     2100                                priority: 100,
     2101                                requires: { selection: true },
     2102
     2103                                click: function() {
     2104                                        var selection = controller.state().get('selection'),
     2105                                                edit = controller.state('playlist-edit'),
     2106                                                models = selection.where({ type: 'audio' });
     2107
     2108                                        edit.set( 'library', new media.model.Selection( models, {
     2109                                                props:    selection.props.toJSON(),
     2110                                                multiple: true
     2111                                        }) );
     2112
     2113                                        this.controller.setState('playlist-edit');
     2114                                }
     2115                        });
     2116                },
     2117
     2118                mainVideoPlaylistToolbar: function( view ) {
     2119                        var controller = this;
     2120
     2121                        this.selectionStatusToolbar( view );
     2122
     2123                        view.set( 'video-playlist', {
     2124                                style:    'primary',
     2125                                text:     l10n.createNewVideoPlaylist,
     2126                                priority: 100,
     2127                                requires: { selection: true },
     2128
     2129                                click: function() {
     2130                                        var selection = controller.state().get('selection'),
     2131                                                edit = controller.state('video-playlist-edit'),
     2132                                                models = selection.where({ type: 'video' });
     2133
     2134                                        edit.set( 'library', new media.model.Selection( models, {
     2135                                                props:    selection.props.toJSON(),
     2136                                                multiple: true
     2137                                        }) );
     2138
     2139                                        this.controller.setState('video-playlist-edit');
     2140                                }
     2141                        });
     2142                },
     2143
    19462144                featuredImageToolbar: function( toolbar ) {
    19472145                        this.createSelectToolbar( toolbar, {
    19482146                                text:  l10n.setFeaturedImage,
     
    20112209                                        }
    20122210                                }
    20132211                        }) );
     2212                },
     2213
     2214                playlistEditToolbar: function() {
     2215                        var editing = this.state().get('editing');
     2216                        this.toolbar.set( new media.view.Toolbar({
     2217                                controller: this,
     2218                                items: {
     2219                                        insert: {
     2220                                                style:    'primary',
     2221                                                text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
     2222                                                priority: 80,
     2223                                                requires: { library: true },
     2224
     2225                                                /**
     2226                                                 * @fires wp.media.controller.State#update
     2227                                                 */
     2228                                                click: function() {
     2229                                                        var controller = this.controller,
     2230                                                                state = controller.state();
     2231
     2232                                                        controller.close();
     2233                                                        state.trigger( 'update', state.get('library') );
     2234
     2235                                                        // Restore and reset the default state.
     2236                                                        controller.setState( controller.options.state );
     2237                                                        controller.reset();
     2238                                                }
     2239                                        }
     2240                                }
     2241                        }) );
     2242                },
     2243
     2244                playlistAddToolbar: function() {
     2245                        this.toolbar.set( new media.view.Toolbar({
     2246                                controller: this,
     2247                                items: {
     2248                                        insert: {
     2249                                                style:    'primary',
     2250                                                text:     l10n.addToPlaylist,
     2251                                                priority: 80,
     2252                                                requires: { selection: true },
     2253
     2254                                                /**
     2255                                                 * @fires wp.media.controller.State#reset
     2256                                                 */
     2257                                                click: function() {
     2258                                                        var controller = this.controller,
     2259                                                                state = controller.state(),
     2260                                                                edit = controller.state('playlist-edit');
     2261
     2262                                                        edit.get('library').add( state.get('selection').models );
     2263                                                        state.trigger('reset');
     2264                                                        controller.setState('playlist-edit');
     2265                                                }
     2266                                        }
     2267                                }
     2268                        }) );
     2269                },
     2270
     2271                videoPlaylistEditToolbar: function() {
     2272                        var editing = this.state().get('editing');
     2273                        this.toolbar.set( new media.view.Toolbar({
     2274                                controller: this,
     2275                                items: {
     2276                                        insert: {
     2277                                                style:    'primary',
     2278                                                text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
     2279                                                priority: 140,
     2280                                                requires: { library: true },
     2281
     2282                                                click: function() {
     2283                                                        var controller = this.controller,
     2284                                                                state = controller.state();
     2285
     2286                                                        controller.close();
     2287                                                        state.trigger( 'update', state.get('library') );
     2288
     2289                                                        // Restore and reset the default state.
     2290                                                        controller.setState( controller.options.state );
     2291                                                        controller.reset();
     2292                                                }
     2293                                        }
     2294                                }
     2295                        }) );
     2296                },
     2297
     2298                videoPlaylistAddToolbar: function() {
     2299                        this.toolbar.set( new media.view.Toolbar({
     2300                                controller: this,
     2301                                items: {
     2302                                        insert: {
     2303                                                style:    'primary',
     2304                                                text:     l10n.addToVideoPlaylist,
     2305                                                priority: 140,
     2306                                                requires: { selection: true },
     2307
     2308                                                click: function() {
     2309                                                        var controller = this.controller,
     2310                                                                state = controller.state(),
     2311                                                                edit = controller.state('video-playlist-edit');
     2312
     2313                                                        edit.get('library').add( state.get('selection').models );
     2314                                                        state.trigger('reset');
     2315                                                        controller.setState('video-playlist-edit');
     2316                                                }
     2317                                        }
     2318                                }
     2319                        }) );
    20142320                }
    2015 
    20162321        });
    20172322
    20182323        media.view.MediaFrame.ImageDetails = media.view.MediaFrame.Select.extend({
     
    48425147        });
    48435148
    48445149        /**
     5150         * wp.media.view.Settings.Gallery
     5151         */
     5152        media.view.Settings.Playlist = media.view.Settings.extend({
     5153                className: 'playlist-settings',
     5154                template:  media.template('playlist-settings')
     5155        });
     5156
     5157        /**
    48455158         * wp.media.view.Attachment.Details
    48465159         *
    48475160         * @constructor
  • src/wp-includes/js/mediaelement/wp-mediaelement.css

     
    1313.me-cannotplay {
    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}
     23
     24.wp-playlist .mejs-container {
     25        margin: 0;
     26}
     27
     28.wp-playlist .mejs-controls .mejs-button button {
     29        outline: 0;
     30}
     31
     32.wp-playlist-audio {
     33
     34}
     35
     36.wp-playlist-video {
     37
     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-current-item {
     50        overflow: hidden;
     51        margin-bottom: 10px;
     52}
     53
     54.wp-playlist-current-item img {
     55        float: left;
     56        max-width: 60px;
     57        height: auto;
     58        margin-right: 10px;
     59}
     60
     61.wp-playlist-caption {
     62}
     63
     64.wp-caption-meta {
     65        display: block;
     66}
     67
     68.wp-caption-title {
     69        font-size: 100%;
     70}
     71
     72.wp-caption-album {
     73        font-style: italic;
     74}
     75
     76.wp-caption-artist {
     77        font-size: 80%;
     78        text-transform: uppercase;
     79}
     80
     81.wp-caption-by {
     82        font-size: 65%;
     83        font-weight: bold;
     84}
     85
     86.wp-playlist-item-length {
     87        position: absolute;
     88        right: 0;
     89        top: 0;
     90}
     91
     92.wp-playlist-tracks {
     93        margin-top: 10px;
     94        border-top: 1px solid #ccc;
     95}
     96
     97.wp-playlist-item {
     98        position: relative;
     99        cursor: pointer;
     100        border-bottom: 1px solid #ccc;
     101}
     102 No newline at end of file
  • src/wp-includes/js/mediaelement/wp-playlist.js

     
     1/*globals window, document, $, jQuery */
     2
     3(function ($, _, Backbone) {
     4        "use strict";
     5
     6        var WPPlaylistView = Backbone.View.extend({
     7                index : 0,
     8
     9                itemTemplate : wp.template('wp-playlist-item'),
     10
     11                currentTemplate : wp.template('wp-playlist-current-item'),
     12
     13                initialize : function () {
     14                        var settings = {};
     15
     16                        this.data = $.parseJSON( this.$('script').html() );
     17                        this.playerNode = this.$( this.data.type );
     18
     19                        this.tracks = new Backbone.Collection( this.data.tracks );
     20                        this.current = this.tracks.first();
     21                        this.currentNode = this.$( '.wp-playlist-current-item' );
     22                        this.renderCurrent();
     23
     24                        if ( this.data.tracklist ) {
     25                                this.renderTracks();
     26                        }
     27
     28                        this.playerNode.attr( 'src', this.current.get('src') );
     29
     30                        _.bindAll( this, 'bindPlayer', 'ended', 'clickTrack' );
     31
     32                        if ( typeof _wpmejsSettings !== 'undefined' ) {
     33                                settings.pluginPath = _wpmejsSettings.pluginPath;
     34                        }
     35                        settings.success = this.bindPlayer;
     36
     37                        new MediaElementPlayer( this.playerNode.get(0), settings );
     38                },
     39
     40                renderCurrent : function () {
     41                        this.currentNode.html( this.currentTemplate( this.current.toJSON() ) );
     42                },
     43
     44                renderTracks : function () {
     45                        var that = this, i = 1, tracklist = $( '<div class="wp-playlist-tracks"></div>' );
     46                        this.tracks.each(function (model) {
     47                                model.set( 'index', that.data.tracknums ? i : false );
     48                                tracklist.append( that.itemTemplate( model.toJSON() ) );
     49                                i += 1;
     50                        });
     51                        this.$el.append( tracklist );
     52                },
     53
     54                events : {
     55                        'click .wp-playlist-item' : 'clickTrack',
     56                        'click .wp-playlist-next' : 'next',
     57                        'click .wp-playlist-prev' : 'prev'
     58                },
     59
     60                bindPlayer : function (mejs) {
     61                        this.player = mejs;
     62                        this.player.addEventListener( 'ended', this.ended );
     63                },
     64
     65                clickTrack : function (e) {
     66                        this.index = this.$( '.wp-playlist-item' ).index( e.currentTarget );
     67                        this.setCurrent();
     68                },
     69
     70                ended : function () {
     71                        if ( this.index + 1 < this.tracks.length ) {
     72                                this.next();
     73                        } else {
     74                                this.player.pause();
     75                                this.index = 0;
     76                                this.current = this.tracks.at( this.index );
     77                                this.playerNode.attr( 'src', this.current.get( 'src' ) );
     78                                this.player.load();
     79                        }
     80                },
     81
     82                next : function () {
     83                        this.index = this.index + 1 >= this.tracks.length ? 0 : this.index + 1;
     84                        this.setCurrent();
     85                },
     86
     87                prev : function () {
     88                        this.index = this.index - 1 < 0 ? this.tracks.length - 1 : this.index - 1;
     89                        this.setCurrent();
     90                },
     91
     92                setCurrent : function () {
     93                        this.current = this.tracks.at( this.index );
     94                        this.renderCurrent();
     95
     96                        this.player.pause();
     97                        this.playerNode.attr( 'src', this.current.get( 'src' ) );
     98                        this.player.load();
     99                        this.player.play();
     100                }
     101        });
     102
     103    $(document).ready(function () {
     104                $('.wp-playlist').each(function () {
     105                        return new WPPlaylistView({ el: this });
     106                });
     107    });
     108
     109}(jQuery, _, Backbone));
     110 No newline at end of file
  • src/wp-includes/js/plupload/handlers.js

     
    2222                .appendTo( jQuery('#media-items' ) );
    2323
    2424        // Disable submit
    25         jQuery('#insert-gallery').prop('disabled', true);
     25        jQuery('#insert-gallery, #insert-playlist').prop('disabled', true);
    2626}
    2727
    2828function uploadStart() {
     
    4747
    4848        if ( max > hundredmb && file.size > hundredmb ) {
    4949                setTimeout(function(){
    50                        
     50
    5151                        if ( file.status < 3 && file.loaded === 0 ) { // not uploading
    5252                                wpFileError(file, pluploadL10n.big_upload_failed.replace('%1$s', '<a class="uploader-html" href="#">').replace('%2$s', '</a>'));
    5353                                up.stop(); // stops the whole queue
     
    6464        // Just one file, no need for collapsible part
    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
    7474        // Only show Save buttons when there is at least one file.
     
    171171                        success: function( ){
    172172                                var type,
    173173                                        item = jQuery('#media-item-' + fileObj.id);
    174                                
     174
    175175                                if ( type = jQuery('#type-of-' + fileObj.id).val() )
    176176                                        jQuery('#' + type + '-counter').text(jQuery('#' + type + '-counter').text()-0+1);
    177177
     
    257257}
    258258
    259259function uploadComplete() {
    260         jQuery('#insert-gallery').prop('disabled', false);
     260        jQuery('#insert-gallery, #insert-playlist').prop('disabled', false);
    261261}
    262262
    263263function switchUploader(s) {
  • src/wp-includes/js/swfupload/handlers.js

     
    2525        jQuery('.progress', '#media-item-' + fileObj.id).show();
    2626
    2727        // Disable submit and enable cancel
    28         jQuery('#insert-gallery').prop('disabled', true);
     28        jQuery('#insert-gallery, #insert-playlist').prop('disabled', true);
    2929        jQuery('#cancel-upload').prop('disabled', false);
    3030}
    3131
    3232function uploadStart(fileObj) {
    3333        try {
    3434                if ( typeof topWin.tb_remove != 'undefined' )
    35                         topWin.jQuery('#TB_overlay').unbind('click', topWin.tb_remove); 
     35                        topWin.jQuery('#TB_overlay').unbind('click', topWin.tb_remove);
    3636        } catch(e){}
    3737
    3838        return true;
     
    212212        else
    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
    222223function uploadSuccess(fileObj, serverData) {
     
    238239        // If no more uploads queued, enable the submit button
    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}
    244245
  • src/wp-includes/js/tinymce/langs/wp-langs-en.js

     
    478478                add_audio: "Add Audio",
    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        });
    483485
  • src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js

     
    2525        }
    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 ) ) {
    3232                        content = content.replace( replaceRegex, replaceCallback );
     
    6060                }
    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' ) );
    7171                        frame = gallery.edit( data );
     
    7474                                var shortcode = gallery.shortcode( selection ).string();
    7575                                editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
    7676                        });
     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 = gallery.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();
     91                                editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
     92                        });
    7793                } else {
    7894                        // temp
    7995                        window.console && window.console.log( 'Edit AV shortcode ' + window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ) );
     
    138154                                event.name = 'video';
    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                }
    143163        });
  • src/wp-includes/js/tinymce/skins/wordpress/wp-content.css

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

     
    411411                </label>
    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="tracknums" />
     442                </label>
     443
     444                <label class="setting">
     445                        <span><?php _e( 'Show Images' ); ?></span>
     446                        <input type="checkbox" data-setting="images" />
     447                </label>
     448        </script>
     449
    414450        <script type="text/html" id="tmpl-embed-link-settings">
    415451                <label class="setting">
    416452                        <span><?php _e('Title'); ?></span>
  • src/wp-includes/media.php

     
    934934}
    935935
    936936/**
     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        $post = get_post();
     949
     950        if ( ! in_array( $type, array( 'audio', 'video' ) ) ) {
     951                return '';
     952        }
     953
     954        static $instance = 0;
     955        $instance++;
     956
     957        if ( ! empty( $attr['ids'] ) ) {
     958                // 'ids' is explicitly ordered, unless you specify otherwise.
     959                if ( empty( $attr['orderby'] ) ) {
     960                        $attr['orderby'] = 'post__in';
     961                }
     962                $attr['include'] = $attr['ids'];
     963        }
     964
     965        // Allow plugins/themes to override the default gallery template.
     966        $output = apply_filters( 'post_playlist', '', $attr, $type );
     967        if ( $output != '' ) {
     968                return $output;
     969        }
     970
     971        // We're trusting author input, so let's at least make sure it looks like a valid orderby statement
     972        if ( isset( $attr['orderby'] ) ) {
     973                $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
     974                if ( ! $attr['orderby'] )
     975                        unset( $attr['orderby'] );
     976        }
     977
     978        extract( shortcode_atts( array(
     979                'order'         => 'ASC',
     980                'orderby'       => 'menu_order ID',
     981                'id'            => $post ? $post->ID : 0,
     982                'include'       => '',
     983                'exclude'   => '',
     984                'style'         => 'light',
     985                'tracklist' => 'audio' === $type,
     986                'tracknums' => 'audio' === $type,
     987                'images'        => true
     988        ), $attr, 'playlist' ) );
     989
     990        $id = intval( $id );
     991        if ( 'RAND' == $order ) {
     992                $orderby = 'none';
     993        }
     994
     995        $args = array(
     996                'post_status' => 'inherit',
     997                'post_type' => 'attachment',
     998                'post_mime_type' => $type,
     999                'order' => $order,
     1000                'orderby' => $orderby
     1001        );
     1002
     1003        if ( ! empty( $include ) ) {
     1004                $args['include'] = $include;
     1005                $_attachments = get_posts( $args );
     1006
     1007                $attachments = array();
     1008                foreach ( $_attachments as $key => $val ) {
     1009                        $attachments[$val->ID] = $_attachments[$key];
     1010                }
     1011        } elseif ( ! empty( $exclude ) ) {
     1012                $args['post_parent'] = $id;
     1013                $args['exclude'] = $exclude;
     1014                $attachments = get_children( $args );
     1015        } else {
     1016                $args['post_parent'] = $id;
     1017                $attachments = get_children( $args );
     1018        }
     1019
     1020        if ( empty( $attachments ) ) {
     1021                return '';
     1022        }
     1023
     1024        if ( is_feed() ) {
     1025                $output = "\n";
     1026                foreach ( $attachments as $att_id => $attachment ) {
     1027                        $output .= wp_get_attachment_link( $att_id, $size, true ) . "\n";
     1028                }
     1029                return $output;
     1030        }
     1031
     1032        $supports_thumbs = ( current_theme_supports( 'post-thumbnails', "attachment:$type" ) && post_type_supports( "attachment:$type", 'thumbnail' ) )
     1033                || $images;
     1034
     1035        $data = compact( 'type', 'style' );
     1036        // don't pass strings to JSON, will be truthy in JS
     1037        $data['tracklist'] = filter_var( $tracklist, FILTER_VALIDATE_BOOLEAN );
     1038        $data['tracknums'] = filter_var( $tracknums, FILTER_VALIDATE_BOOLEAN );
     1039        $data['images'] = filter_var( $images, FILTER_VALIDATE_BOOLEAN );
     1040
     1041        $tracks = array();
     1042        foreach ( $attachments as $attachment ) {
     1043                $url = wp_get_attachment_url( $attachment->ID );
     1044                $ftype = wp_check_filetype( $url, wp_get_mime_types() );
     1045                $track = array(
     1046                        'type' => $type,
     1047                        'src' => $url,
     1048                        'type' => $ftype['ext'],
     1049                        'title' => get_the_title( $attachment->ID ),
     1050                        'caption' => wptexturize( $attachment->post_excerpt ),
     1051                        'description' => wptexturize( $attachment->post_content )
     1052                );
     1053
     1054                $meta = wp_get_attachment_metadata( $attachment->ID );
     1055                if ( ! empty( $meta ) ) {
     1056                        $track['meta'] = array();
     1057
     1058                        $keys = array( 'title', 'artist', 'band', 'album', 'genre', 'year', 'length', 'length_formatted' );
     1059                        foreach ( $keys as $key ) {
     1060                                if ( ! empty( $meta[ $key ] ) ) {
     1061                                        $track['meta'][ $key ] = $meta[ $key ];
     1062                                }
     1063                        }
     1064                }
     1065
     1066                if ( $supports_thumbs ) {
     1067                        $id = get_post_thumbnail_id( $attachment->ID );
     1068                        if ( ! empty( $id ) ) {
     1069                                list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
     1070                                $track['image'] = compact( 'src', 'width', 'height' );
     1071                                list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumb' );
     1072                                $track['thumb'] = compact( 'src', 'width', 'height' );
     1073                        }
     1074                }
     1075
     1076                $tracks[] = $track;
     1077        }
     1078        $data['tracks'] = $tracks;
     1079
     1080        ob_start();
     1081
     1082        if ( 1 === $instance ):
     1083                wp_enqueue_style( 'wp-mediaelement' );
     1084                wp_enqueue_script( 'wp-playlist' );
     1085?>
     1086<script type="text/html" id="tmpl-wp-playlist-current-item">
     1087        <# if ( data.image ) { #>
     1088        <img src="{{{ data.image.src }}}"/>
     1089        <# } #>
     1090        <# if ( data.meta.title ) { #>
     1091        <div class="wp-playlist-caption">
     1092                <span class="wp-caption-meta wp-caption-title">&#8220;{{{ data.meta.title }}}&#8221;</span>
     1093                <span class="wp-caption-meta wp-caption-album">{{{ data.meta.album }}}</span>
     1094                <span class="wp-caption-meta wp-caption-artist">{{{ data.meta.artist }}}</span>
     1095        </div>
     1096        <# } else { #>
     1097        <div class="wp-playlist-caption">{{{ data.caption }}}</div>
     1098        <# } #>
     1099</script>
     1100<script type="text/html" id="tmpl-wp-playlist-item">
     1101        <div class="wp-playlist-item">
     1102                <# if ( ( data.title || data.meta.title ) && data.meta.artist ) { #>
     1103                <div class="wp-playlist-caption">
     1104                        {{{ data.index ? ( data.index + '.&nbsp;' ) : '' }}}
     1105                        <span class="wp-caption-title">&#8220;{{{ data.title ? data.title : data.meta.title }}}&#8221;</span>
     1106                        <span class="wp-caption-by"><?php _e( 'by' ) ?></span>
     1107                        <span class="wp-caption-artist">{{{ data.meta.artist }}}</span>
     1108                </div>
     1109                <# } else { #>
     1110                <div class="wp-playlist-caption">{{{ data.index ? ( data.index + '.' ) : '' }}} {{{ data.caption ? data.caption : data.title }}}</div>
     1111                <# } #>
     1112                <# if ( data.meta.length_formatted ) { #>
     1113                <div class="wp-playlist-item-length">{{{ data.meta.length_formatted }}}</div>
     1114                <# } #>
     1115        </div>
     1116</script>
     1117        <?php endif ?>
     1118<div class="wp-playlist wp-<?php echo $type ?>-playlist wp-playlist-<?php echo $style ?>">
     1119        <div class="wp-playlist-current-item"></div>
     1120        <<?php echo $type ?> controls="controls" preload="metadata"></<?php echo $type ?>>
     1121        <div class="wp-playlist-next"></div>
     1122        <div class="wp-playlist-prev"></div>
     1123        <noscript>
     1124        <?php
     1125        $output = "\n";
     1126        foreach ( $attachments as $att_id => $attachment ) {
     1127                $output .= wp_get_attachment_link( $att_id, $size, true ) . "\n";
     1128        }
     1129
     1130        echo $output;
     1131        ?>
     1132        </noscript>
     1133        <script type="application/json"><?php echo json_encode( $data ) ?></script>
     1134</div>
     1135        <?php
     1136        return ob_get_clean();
     1137}
     1138
     1139function wp_playlist_shortcode( $attr ) {
     1140        return wp_get_playlist( $attr, 'audio' );
     1141}
     1142add_shortcode( 'playlist', 'wp_playlist_shortcode' );
     1143
     1144function wp_video_playlist_shortcode( $attr ) {
     1145        return wp_get_playlist( $attr, 'video' );
     1146}
     1147add_shortcode( 'video-playlist', 'wp_video_playlist_shortcode' );
     1148
     1149/**
    9371150 * Provide a No-JS Flash fallback as a last resort for audio / video
    9381151 *
    9391152 * @since 3.6.0
     
    20442257                'mediaLibraryTitle'  => __( 'Media Library' ),
    20452258                'insertMediaTitle'   => __( 'Insert Media' ),
    20462259                'createNewGallery'   => __( 'Create a new gallery' ),
     2260                'createNewPlaylist'   => __( 'Create a new playlist' ),
     2261                'createNewVideoPlaylist'   => __( 'Create a new video playlist' ),
    20472262                'returnToLibrary'    => __( '&#8592; Return to library' ),
    20482263                'allMediaItems'      => __( 'All media items' ),
    20492264                'noItemsFound'       => __( 'No items found.' ),
     
    20712286                // Edit Image
    20722287                'imageDetailsTitle'     => __( 'Image Details' ),
    20732288                'imageReplaceTitle'     => __( 'Replace Image' ),
    2074                 'imageDetailsCancel'     => __( 'Cancel Edit' )
     2289                'imageDetailsCancel'    => __( 'Cancel Edit' ),
     2290
     2291                // Playlist
     2292                'playlistDragInfo'    => __( 'Drag and drop to reorder tracks.' ),
     2293                'createPlaylistTitle' => __( 'Create Playlist' ),
     2294                'editPlaylistTitle'   => __( 'Edit Playlist' ),
     2295                'cancelPlaylistTitle' => __( '&#8592; Cancel Playlist' ),
     2296                'insertPlaylist'      => __( 'Insert playlist' ),
     2297                'updatePlaylist'      => __( 'Update playlist' ),
     2298                'addToPlaylist'       => __( 'Add to playlist' ),
     2299                'addToPlaylistTitle'  => __( 'Add to Playlist' ),
     2300
     2301                // Video Playlist
     2302                'videoPlaylistDragInfo'    => __( 'Drag and drop to reorder videos.' ),
     2303                'createVideoPlaylistTitle' => __( 'Create Video Playlist' ),
     2304                'editVideoPlaylistTitle'   => __( 'Edit Video Playlist' ),
     2305                'cancelVideoPlaylistTitle' => __( '&#8592; Cancel Video Playlist' ),
     2306                'insertVideoPlaylist'      => __( 'Insert Video playlist' ),
     2307                'updateVideoPlaylist'      => __( 'Update Video playlist' ),
     2308                'addToVideoPlaylist'       => __( 'Add to Video playlist' ),
     2309                'addToVideoPlaylistTitle'  => __( 'Add to Video Playlist' ),
    20752310        );
    20762311
    20772312        $settings = apply_filters( 'media_view_settings', $settings, $post );
  • src/wp-includes/script-loader.php

     
    315315                'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ),
    316316        ) );
    317317
     318        $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
     319
    318320        $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' );
    319321        did_action( 'init' ) && $scripts->localize( 'zxcvbn-async', '_zxcvbnSettings', array(
    320322                'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',