WordPress.org

Make WordPress Core

Ticket #26631: 26631.5.diff

File 26631.5.diff, 53.3 KB (added by wonderboymusic, 8 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/class-wp-editor.php

     
    232232                                                'wordpress',
    233233                                                'wpeditimage',
    234234                                                'wpgallery',
     235                                                'wpplaylist',
    235236                                                'wplink',
    236237                                                'wpdialogs',
    237238                                        ) ) );
  • src/wp-includes/css/editor.css

     
    472472}
    473473
    474474#wp_editbtns,
    475 #wp_gallerybtns {
     475#wp_gallerybtns,
     476#wp_playlistbtns {
    476477        padding: 2px;
    477478        position: absolute;
    478479        display: none;
     
    482483#wp_editimgbtn,
    483484#wp_delimgbtn,
    484485#wp_editgallery,
    485 #wp_delgallery {
     486#wp_delgallery,
     487#wp_editplaylist,
     488#wp_delplaylist {
    486489        border-color: #999;
    487490        background-color: #eee;
    488491        margin: 2px;
     
    496499#wp_editimgbtn:hover,
    497500#wp_delimgbtn:hover,
    498501#wp_editgallery:hover,
    499 #wp_delgallery:hover {
     502#wp_delgallery:hover,
     503#wp_editplaylist:hover,
     504#wp_delplaylist:hover {
    500505        border-color: #555;
    501506        background-color: #ccc;
    502507}
  • src/wp-includes/css/media-views.css

     
    589589        box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
    590590}
    591591
    592 .media-frame .media-toolbar .add-to-gallery {
     592.media-frame .media-toolbar .add-to-gallery, .media-frame .media-toolbar .add-to-playlist {
    593593        display: none;
    594594}
    595595
     
    14101410        margin: 1.4em 0 0.4em;
    14111411}
    14121412
    1413 .gallery-settings {
     1413.gallery-settings, .playlist-settings {
    14141414        overflow: hidden;
    14151415}
    14161416
     
    16861686        .media-modal-close {
    16871687                right: 10px;
    16881688        }
    1689        
     1689
    16901690        /* Text inputs need to be 16px, or they force zooming on iOS */
    16911691        .media-frame input[type="text"],
    16921692        .media-frame input[type="password"],
     
    17341734        .media-frame-title {
    17351735                display: none;
    17361736        }
    1737        
     1737
    17381738        .media-frame-toolbar {
    17391739                position: absolute;
    17401740                bottom: 0px;
     
    17591759        .attachment-details h3 {
    17601760                margin-top: 45px;
    17611761        }
    1762                
     1762
    17631763        /* Shorten right-side links so they don't overlap the close button */
    17641764        .media-menu a:nth-child(2),
    17651765        .media-menu a:last-child {
     
    17851785                top: 84px;
    17861786                left: 0;
    17871787        }
    1788        
     1788
    17891789        .media-frame-content {
    17901790                left: 0;
    17911791                top: 118px;
     
    17941794        .media-frame .attachments-browser {
    17951795                padding-bottom: 300px;
    17961796        }
    1797        
     1797
    17981798        .media-sidebar {
    17991799                border-bottom: 1px solid #dddddd;
    18001800        }
    1801        
     1801
    18021802        .media-modal {
    18031803                width: auto;
    18041804        }
     
    18671867                border-top: none;
    18681868        }
    18691869
    1870         .gallery-settings h3 {
     1870        .gallery-settings h3, .playlist-settings h3 {
    18711871                margin-top: 45px;
    18721872        }
    18731873
     
    19041904        .media-frame-content {
    19051905                top: 78px;
    19061906        }
    1907        
     1907
    19081908        .attachments-browser .attachments {
    19091909                top: 2px;
    19101910        }
  • 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

     
    460460                                return;
    461461                        }
    462462
     463                        console.log( this );
     464
    463465                        view.set( this.id, menuItem );
    464466                }
    465467        });
     
    752754        });
    753755
    754756        /**
    755          * wp.media.controller.GalleryEdit
    756          *
    757          * @constructor
    758          * @augments wp.media.controller.Library
    759          * @augments wp.media.controller.State
    760          * @augments Backbone.Model
    761          */
    762         media.controller.GalleryEdit = media.controller.Library.extend({
    763                 defaults: {
    764                         id:         'gallery-edit',
    765                         multiple:   false,
    766                         describe:   true,
    767                         edge:       199,
    768                         editing:    false,
    769                         sortable:   true,
    770                         searchable: false,
    771                         toolbar:    'gallery-edit',
    772                         content:    'browse',
    773                         title:      l10n.editGalleryTitle,
    774                         priority:   60,
    775                         dragInfo:   true,
    776 
    777                         // Don't sync the selection, as the Edit Gallery library
    778                         // *is* the selection.
    779                         syncSelection: false
    780                 },
    781 
    782                 initialize: function() {
    783                         // If we haven't been provided a `library`, create a `Selection`.
    784                         if ( ! this.get('library') ) {
    785                                 this.set( 'library', new media.model.Selection() );
    786                         }
    787 
    788                         // The single `Attachment` view to be used in the `Attachments` view.
    789                         if ( ! this.get('AttachmentView') ) {
    790                                 this.set( 'AttachmentView', media.view.Attachment.EditLibrary );
    791                         }
    792                         /**
    793                          * call 'initialize' directly on the parent class
    794                          */
    795                         media.controller.Library.prototype.initialize.apply( this, arguments );
    796                 },
    797 
    798                 activate: function() {
    799                         var library = this.get('library');
    800 
    801                         // Limit the library to images only.
    802                         library.props.set( 'type', 'image' );
    803 
    804                         // Watch for uploaded attachments.
    805                         this.get('library').observe( wp.Uploader.queue );
    806 
    807                         this.frame.on( 'content:render:browse', this.gallerySettings, this );
    808                         /**
    809                          * call 'activate' directly on the parent class
    810                          */
    811                         media.controller.Library.prototype.activate.apply( this, arguments );
    812                 },
    813 
    814                 deactivate: function() {
    815                         // Stop watching for uploaded attachments.
    816                         this.get('library').unobserve( wp.Uploader.queue );
    817 
    818                         this.frame.off( 'content:render:browse', this.gallerySettings, this );
    819                         /**
    820                          * call 'deactivate' directly on the parent class
    821                          */
    822                         media.controller.Library.prototype.deactivate.apply( this, arguments );
    823                 },
    824 
    825                 /**
    826                  * @param {Object} browser
    827                  */
    828                 gallerySettings: function( browser ) {
    829                         var library = this.get('library');
    830 
    831                         if ( ! library || ! browser ) {
    832                                 return;
    833                         }
    834 
    835                         library.gallery = library.gallery || new Backbone.Model();
    836 
    837                         browser.sidebar.set({
    838                                 gallery: new media.view.Settings.Gallery({
    839                                         controller: this,
    840                                         model:      library.gallery,
    841                                         priority:   40
    842                                 })
    843                         });
    844 
    845                         browser.toolbar.set( 'reverse', {
    846                                 text:     l10n.reverseOrder,
    847                                 priority: 80,
    848 
    849                                 click: function() {
    850                                         library.reset( library.toArray().reverse() );
    851                                 }
    852                         });
    853                 }
    854         });
    855 
    856         /**
    857          * wp.media.controller.GalleryAdd
    858          *
    859          * @constructor
    860          * @augments wp.media.controller.Library
    861          * @augments wp.media.controller.State
    862          * @augments Backbone.Model
    863          */
    864         media.controller.GalleryAdd = media.controller.Library.extend({
    865                 defaults: _.defaults({
    866                         id:           'gallery-library',
    867                         filterable:   'uploaded',
    868                         multiple:     'add',
    869                         menu:         'gallery',
    870                         toolbar:      'gallery-add',
    871                         title:        l10n.addToGalleryTitle,
    872                         priority:     100,
    873 
    874                         // Don't sync the selection, as the Edit Gallery library
    875                         // *is* the selection.
    876                         syncSelection: false
    877                 }, media.controller.Library.prototype.defaults ),
    878 
    879                 /**
    880                  * If we haven't been provided a `library`, create a `Selection`.
    881                  */
    882                 initialize: function() {
    883                         if ( ! this.get('library') ) {
    884                                 this.set( 'library', media.query({ type: 'image' }) );
    885                         }
    886                         /**
    887                          * call 'initialize' directly on the parent class
    888                          */
    889                         media.controller.Library.prototype.initialize.apply( this, arguments );
    890                 },
    891 
    892                 activate: function() {
    893                         var library = this.get('library'),
    894                                 edit    = this.frame.state('gallery-edit').get('library');
    895 
    896                         if ( this.editLibrary && this.editLibrary !== edit )
    897                                 library.unobserve( this.editLibrary );
    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 )
    903                                         && ! edit.get( attachment.cid )
    904                                         /**
    905                                          * call 'validator' directly on wp.media.model.Selection
    906                                          */
    907                                         && media.model.Selection.prototype.validator.apply( this, arguments );
    908                         };
    909 
    910                         // Reset the library to ensure that all attachments are re-added
    911                         // to the collection. Do so silently, as calling `observe` will
    912                         // trigger the `reset` event.
    913                         library.reset( library.mirroring.models, { silent: true });
    914                         library.observe( edit );
    915                         this.editLibrary = edit;
    916                         /**
    917                          * call 'activate' directly on the parent class
    918                          */
    919                         media.controller.Library.prototype.activate.apply( this, arguments );
    920                 }
    921         });
    922 
    923         /**
    924757         * wp.media.controller.FeaturedImage
    925758         *
    926759         * @constructor
     
    1005838                }
    1006839        });
    1007840
     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
    10081038        /**
    10091039         * wp.media.controller.ReplaceImage
    10101040         *
     
    17581788                                        menu:    'gallery'
    17591789                                }),
    17601790
    1761                                 new media.controller.GalleryAdd()
     1791                                new media.controller.GalleryAdd(),
     1792
     1793                                new media.controller.Library({
     1794                                        id:         'playlist',
     1795                                        title:      l10n.createPlaylistTitle,
     1796                                        priority:   60,
     1797                                        toolbar:    'main-playlist',
     1798                                        filterable: 'uploaded',
     1799                                        multiple:   'add',
     1800                                        editable:   false,
     1801
     1802                                        library:  media.query( _.defaults({
     1803                                                type: 'audio'
     1804                                        }, options.library ) )
     1805                                }),
     1806
     1807                                // Playlist states.
     1808                                new media.controller.PlaylistEdit({
     1809                                        library: options.selection,
     1810                                        editing: options.editing,
     1811                                        menu:    'playlist'
     1812                                }),
     1813
     1814                                new media.controller.PlaylistAdd(),
     1815
     1816                                new media.controller.Library({
     1817                                        id:         'video-playlist',
     1818                                        title:      l10n.createVideoPlaylistTitle,
     1819                                        priority:   60,
     1820                                        toolbar:    'main-video-playlist',
     1821                                        filterable: 'uploaded',
     1822                                        multiple:   'add',
     1823                                        editable:   false,
     1824
     1825                                        library:  media.query( _.defaults({
     1826                                                type: 'video'
     1827                                        }, options.library ) )
     1828                                }),
     1829
     1830                                // Video Playlist states.
     1831                                new media.controller.VideoPlaylistEdit({
     1832                                        library: options.selection,
     1833                                        editing: options.editing,
     1834                                        menu:    'video-playlist'
     1835                                }),
     1836
     1837                                new media.controller.VideoPlaylistAdd()
    17621838                        ]);
    17631839
    17641840
     
    17731849                         */
    17741850                        media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
    17751851                        this.on( 'menu:create:gallery', this.createMenu, this );
     1852                        this.on( 'menu:create:playlist', this.createMenu, this );
     1853                        this.on( 'menu:create:video-playlist', this.createMenu, this );
    17761854                        this.on( 'toolbar:create:main-insert', this.createToolbar, this );
    1777                         this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1855                        this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
     1856                        this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
     1857                        this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
    17781858                        this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
    17791859                        this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
    17801860
     
    17811861                        var handlers = {
    17821862                                menu: {
    17831863                                        'default': 'mainMenu',
    1784                                         'gallery': 'galleryMenu'
     1864                                        'gallery': 'galleryMenu',
     1865                                        'playlist': 'playlistMenu',
     1866                                        'video-playlist': 'videoPlaylistMenu'
    17851867                                },
    17861868
    17871869                                content: {
     
    17931875                                        'main-insert':      'mainInsertToolbar',
    17941876                                        'main-gallery':     'mainGalleryToolbar',
    17951877                                        'gallery-edit':     'galleryEditToolbar',
    1796                                         'gallery-add':      'galleryAddToolbar'
     1878                                        'gallery-add':      'galleryAddToolbar',
     1879                                        'main-playlist':        'mainPlaylistToolbar',
     1880                                        'playlist-edit':        'playlistEditToolbar',
     1881                                        'playlist-add':         'playlistAddToolbar',
     1882                                        'main-video-playlist': 'mainVideoPlaylistToolbar',
     1883                                        'video-playlist-edit': 'videoPlaylistEditToolbar',
     1884                                        'video-playlist-add': 'videoPlaylistAddToolbar'
    17971885                                }
    17981886                        };
    17991887
     
    18421930                        });
    18431931                },
    18441932
     1933                playlistMenu: function( view ) {
     1934                        var lastState = this.lastState(),
     1935                                previous = lastState && lastState.id,
     1936                                frame = this;
     1937
     1938                        view.set({
     1939                                cancel: {
     1940                                        text:     l10n.cancelPlaylistTitle,
     1941                                        priority: 20,
     1942                                        click:    function() {
     1943                                                if ( previous )
     1944                                                        frame.setState( previous );
     1945                                                else
     1946                                                        frame.close();
     1947                                        }
     1948                                },
     1949                                separateCancel: new media.View({
     1950                                        className: 'separator',
     1951                                        priority: 60
     1952                                })
     1953                        });
     1954                },
     1955
     1956                videoPlaylistMenu: function( view ) {
     1957                        var lastState = this.lastState(),
     1958                                previous = lastState && lastState.id,
     1959                                frame = this;
     1960
     1961                        view.set({
     1962                                cancel: {
     1963                                        text:     l10n.cancelVideoPlaylistTitle,
     1964                                        priority: 20,
     1965                                        click:    function() {
     1966                                                if ( previous )
     1967                                                        frame.setState( previous );
     1968                                                else
     1969                                                        frame.close();
     1970                                        }
     1971                                },
     1972                                separateCancel: new media.View({
     1973                                        className: 'separator',
     1974                                        priority: 80
     1975                                })
     1976                        });
     1977                },
     1978
    18451979                // Content
    18461980                embedContent: function() {
    18471981                        var view = new media.view.Embed({
     
    19602094                        });
    19612095                },
    19622096
     2097                mainPlaylistToolbar: function( view ) {
     2098                        var controller = this;
     2099
     2100                        this.selectionStatusToolbar( view );
     2101
     2102                        view.set( 'playlist', {
     2103                                style:    'primary',
     2104                                text:     l10n.createNewPlaylist,
     2105                                priority: 100,
     2106                                requires: { selection: true },
     2107
     2108                                click: function() {
     2109                                        var selection = controller.state().get('selection'),
     2110                                                edit = controller.state('playlist-edit'),
     2111                                                models = selection.where({ type: 'audio' });
     2112
     2113                                        edit.set( 'library', new media.model.Selection( models, {
     2114                                                props:    selection.props.toJSON(),
     2115                                                multiple: true
     2116                                        }) );
     2117
     2118                                        this.controller.setState('playlist-edit');
     2119                                }
     2120                        });
     2121                },
     2122
     2123                mainVideoPlaylistToolbar: function( view ) {
     2124                        var controller = this;
     2125
     2126                        this.selectionStatusToolbar( view );
     2127
     2128                        view.set( 'video-playlist', {
     2129                                style:    'primary',
     2130                                text:     l10n.createNewVideoPlaylist,
     2131                                priority: 100,
     2132                                requires: { selection: true },
     2133
     2134                                click: function() {
     2135                                        var selection = controller.state().get('selection'),
     2136                                                edit = controller.state('video-playlist-edit'),
     2137                                                models = selection.where({ type: 'video' });
     2138
     2139                                        edit.set( 'library', new media.model.Selection( models, {
     2140                                                props:    selection.props.toJSON(),
     2141                                                multiple: true
     2142                                        }) );
     2143
     2144                                        this.controller.setState('video-playlist-edit');
     2145                                }
     2146                        });
     2147                },
     2148
    19632149                featuredImageToolbar: function( toolbar ) {
    19642150                        this.createSelectToolbar( toolbar, {
    19652151                                text:  l10n.setFeaturedImage,
     
    20282214                                        }
    20292215                                }
    20302216                        }) );
     2217                },
     2218
     2219                playlistEditToolbar: function() {
     2220                        var editing = this.state().get('editing');
     2221                        this.toolbar.set( new media.view.Toolbar({
     2222                                controller: this,
     2223                                items: {
     2224                                        insert: {
     2225                                                style:    'primary',
     2226                                                text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
     2227                                                priority: 80,
     2228                                                requires: { library: true },
     2229
     2230                                                /**
     2231                                                 * @fires wp.media.controller.State#update
     2232                                                 */
     2233                                                click: function() {
     2234                                                        var controller = this.controller,
     2235                                                                state = controller.state();
     2236
     2237                                                        controller.close();
     2238                                                        state.trigger( 'update', state.get('library') );
     2239
     2240                                                        // Restore and reset the default state.
     2241                                                        controller.setState( controller.options.state );
     2242                                                        controller.reset();
     2243                                                }
     2244                                        }
     2245                                }
     2246                        }) );
     2247                },
     2248
     2249                playlistAddToolbar: function() {
     2250                        this.toolbar.set( new media.view.Toolbar({
     2251                                controller: this,
     2252                                items: {
     2253                                        insert: {
     2254                                                style:    'primary',
     2255                                                text:     l10n.addToPlaylist,
     2256                                                priority: 80,
     2257                                                requires: { selection: true },
     2258
     2259                                                /**
     2260                                                 * @fires wp.media.controller.State#reset
     2261                                                 */
     2262                                                click: function() {
     2263                                                        var controller = this.controller,
     2264                                                                state = controller.state(),
     2265                                                                edit = controller.state('playlist-edit');
     2266
     2267                                                        edit.get('library').add( state.get('selection').models );
     2268                                                        state.trigger('reset');
     2269                                                        controller.setState('playlist-edit');
     2270                                                }
     2271                                        }
     2272                                }
     2273                        }) );
     2274                },
     2275
     2276                videoPlaylistEditToolbar: function() {
     2277                        var editing = this.state().get('editing');
     2278                        this.toolbar.set( new media.view.Toolbar({
     2279                                controller: this,
     2280                                items: {
     2281                                        insert: {
     2282                                                style:    'primary',
     2283                                                text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
     2284                                                priority: 140,
     2285                                                requires: { library: true },
     2286
     2287                                                click: function() {
     2288                                                        var controller = this.controller,
     2289                                                                state = controller.state();
     2290
     2291                                                        controller.close();
     2292                                                        state.trigger( 'update', state.get('library') );
     2293
     2294                                                        // Restore and reset the default state.
     2295                                                        controller.setState( controller.options.state );
     2296                                                        controller.reset();
     2297                                                }
     2298                                        }
     2299                                }
     2300                        }) );
     2301                },
     2302
     2303                videoPlaylistAddToolbar: function() {
     2304                        this.toolbar.set( new media.view.Toolbar({
     2305                                controller: this,
     2306                                items: {
     2307                                        insert: {
     2308                                                style:    'primary',
     2309                                                text:     l10n.addToVideoPlaylist,
     2310                                                priority: 140,
     2311                                                requires: { selection: true },
     2312
     2313                                                click: function() {
     2314                                                        var controller = this.controller,
     2315                                                                state = controller.state(),
     2316                                                                edit = controller.state('video-playlist-edit');
     2317
     2318                                                        edit.get('library').add( state.get('selection').models );
     2319                                                        state.trigger('reset');
     2320                                                        controller.setState('video-playlist-edit');
     2321                                                }
     2322                                        }
     2323                                }
     2324                        }) );
    20312325                }
    2032 
    20332326        });
    20342327
    20352328        media.view.MediaFrame.ImageDetails = media.view.MediaFrame.Select.extend({
     
    48495142        });
    48505143
    48515144        /**
     5145         * wp.media.view.Settings.Gallery
     5146         */
     5147        media.view.Settings.Playlist = media.view.Settings.extend({
     5148                className: 'playlist-settings',
     5149                template:  media.template('playlist-settings')
     5150        });
     5151
     5152        /**
    48525153         * wp.media.view.Attachment.Details
    48535154         *
    48545155         * @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/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/skins/wordpress/wp-content.css

     
    108108    border-top: 3px dotted #bbb;
    109109}
    110110
    111 .mce-content-body img.wp-gallery {
     111.mce-content-body img.wp-gallery,
     112.mce-content-body img.wp-playlist,
     113.mce-content-body img.wp-video-playlist{
    112114        border: 1px dashed #888;
    113115        background: #f2f2f2 url("images/gallery.png") no-repeat scroll center center;
    114116        width: 99%;
     
    117119        cursor: pointer;
    118120}
    119121
    120 .mce-content-body img.wp-gallery:hover {
     122.mce-content-body img.wp-playlist {
     123        background: #f2f2f2;
     124}
     125
     126.mce-content-body img.wp-video-playlist {
     127        background-image: url("images/embedded.png");
     128}
     129
     130.mce-content-body img.wp-gallery:hover, .mce-content-body img.wp-playlist:hover {
    121131        background-color: #ededed;
    122132        border-style: solid;
    123133}
    124134
    125 .mce-content-body img.wp-gallery.wp-gallery-selected {
     135.mce-content-body img.wp-gallery.wp-gallery-selected, .mce-content-body img.wp-playlist.wp-playlist-selected {
    126136        background-color: #d8d8d8;
    127137        border-style: solid;
    128138}
  • 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

     
    892892}
    893893
    894894/**
     895 * The Playlist shortcode.
     896 *
     897 * This implements the functionality of the Playlist Shortcode for displaying
     898 * WordPress audio or video files on a post.
     899 *
     900 * @since 3.9.0
     901 *
     902 * @param array $attr Attributes of the shortcode.
     903 * @return string $type Type of playlist. Defaults to audio, video is also supported
     904 */
     905function wp_get_playlist( $attr, $type ) {
     906        $post = get_post();
     907
     908        if ( ! in_array( $type, array( 'audio', 'video' ) ) ) {
     909                return '';
     910        }
     911
     912        static $instance = 0;
     913        $instance++;
     914
     915        if ( ! empty( $attr['ids'] ) ) {
     916                // 'ids' is explicitly ordered, unless you specify otherwise.
     917                if ( empty( $attr['orderby'] ) )
     918                        $attr['orderby'] = 'post__in';
     919                $attr['include'] = $attr['ids'];
     920        }
     921
     922        // Allow plugins/themes to override the default gallery template.
     923        $output = apply_filters( 'post_playlist', '', $attr, $type );
     924        if ( $output != '' )
     925                return $output;
     926
     927        // We're trusting author input, so let's at least make sure it looks like a valid orderby statement
     928        if ( isset( $attr['orderby'] ) ) {
     929                $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
     930                if ( ! $attr['orderby'] )
     931                        unset( $attr['orderby'] );
     932        }
     933
     934        extract( shortcode_atts( array(
     935                'order'         => 'ASC',
     936                'orderby'       => 'menu_order ID',
     937                'id'            => $post ? $post->ID : 0,
     938                'include'       => '',
     939                'exclude'   => '',
     940                'style'         => 'light',
     941                'tracklist' => 'audio' === $type,
     942                'tracknums' => 'audio' === $type,
     943                'images'        => true
     944        ), $attr, 'playlist' ) );
     945
     946        $id = intval( $id );
     947        if ( 'RAND' == $order )
     948                $orderby = 'none';
     949
     950        if ( ! empty( $include ) ) {
     951                $_attachments = get_posts( array( 'include' => $include, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby ) );
     952
     953                $attachments = array();
     954                foreach ( $_attachments as $key => $val ) {
     955                        $attachments[$val->ID] = $_attachments[$key];
     956                }
     957        } elseif ( ! empty( $exclude ) ) {
     958                $attachments = get_children( array('post_parent' => $id, 'exclude' => $exclude, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby ) );
     959        } else {
     960                $attachments = get_children( array('post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => $type, 'order' => $order, 'orderby' => $orderby) );
     961        }
     962
     963        if ( empty( $attachments ) )
     964                return '';
     965
     966        if ( is_feed() ) {
     967                $output = "\n";
     968                foreach ( $attachments as $att_id => $attachment )
     969                        $output .= wp_get_attachment_link( $att_id, $size, true ) . "\n";
     970
     971                return $output;
     972        }
     973
     974        $supports_thumbs = ( current_theme_supports( 'post-thumbnails', "attachment:$type" ) && post_type_supports( "attachment:$type", 'thumbnail' ) ) || $images;
     975
     976        $data = compact( 'type', 'tracklist', 'tracknums', 'images', 'style' );
     977        $tracks = array();
     978        foreach ( $attachments as $attachment ) {
     979                $url = wp_get_attachment_url( $attachment->ID );
     980                $ftype = wp_check_filetype( $url, wp_get_mime_types() );
     981                $track = array(
     982                        'type' => $type,
     983                        'src' => $url,
     984                        'type' => $ftype['ext'],
     985                        'title' => get_the_title( $attachment->ID ),
     986                        'caption' => wptexturize( $attachment->post_excerpt ),
     987                        'description' => wptexturize( $attachment->post_content )
     988                );
     989
     990                $meta = wp_get_attachment_metadata( $attachment->ID );
     991                if ( ! empty( $meta ) ) {
     992                        $track['meta'] = array();
     993
     994                        $keys = array( 'title', 'artist', 'band', 'album', 'genre', 'year', 'length', 'length_formatted' );
     995                        foreach ( $keys as $key ) {
     996                                if ( ! empty( $meta[ $key ] ) ) {
     997                                        $track['meta'][ $key ] = $meta[ $key ];
     998                                }
     999                        }
     1000                }
     1001
     1002                if ( $supports_thumbs ) {
     1003                        $id = get_post_thumbnail_id( $attachment->ID );
     1004                        if ( ! empty( $id ) ) {
     1005                                list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
     1006                                $track['image'] = compact( 'src', 'width', 'height' );
     1007                        }
     1008                }
     1009
     1010                $tracks[] = $track;
     1011        }
     1012        $data['tracks'] = $tracks;
     1013
     1014        ob_start();
     1015
     1016        if ( 1 === $instance ):
     1017                wp_enqueue_style( 'wp-mediaelement' );
     1018                wp_enqueue_script( 'wp-playlist' );
     1019?>
     1020<script type="text/html" id="tmpl-wp-playlist-current-item">
     1021        <# if ( data.image ) { #>
     1022        <img src="{{{ data.image.src }}}"/>
     1023        <# } #>
     1024        <# if ( data.meta.title ) { #>
     1025        <div class="wp-playlist-caption">
     1026                <span class="wp-caption-meta wp-caption-title">&#8220;{{{ data.meta.title }}}&#8221;</span>
     1027                <span class="wp-caption-meta wp-caption-album">{{{ data.meta.album }}}</span>
     1028                <span class="wp-caption-meta wp-caption-artist">{{{ data.meta.artist }}}</span>
     1029        </div>
     1030        <# } else { #>
     1031        <div class="wp-playlist-caption">{{{ data.caption }}}</div>
     1032        <# } #>
     1033</script>
     1034<script type="text/html" id="tmpl-wp-playlist-item">
     1035        <div class="wp-playlist-item">
     1036                <# if ( data.meta.title ) { #>
     1037                <div class="wp-playlist-caption">
     1038                        {{{ data.index }}}.&nbsp;
     1039                        <span class="wp-caption-title">&#8220;{{{ data.meta.title }}}&#8221;</span>
     1040                        <span class="wp-caption-by"><?php _e( 'by' ) ?></span>
     1041                        <span class="wp-caption-artist">{{{ data.meta.artist }}}</span>
     1042                </div>
     1043                <# } else { #>
     1044                <div class="wp-playlist-caption">{{{ data.index }}}. {{{ data.caption }}}</div>
     1045                <# } #>
     1046                <# if ( data.meta.length_formatted ) { #>
     1047                <div class="wp-playlist-item-length">{{{ data.meta.length_formatted }}}</div>
     1048                <# } #>
     1049        </div>
     1050</script>
     1051        <?php endif ?>
     1052<div class="wp-playlist wp-<?php echo $type ?>-playlist wp-playlist-<?php echo $style ?>">
     1053        <div class="wp-playlist-current-item"></div>
     1054        <<?php echo $type ?> controls="controls" preload="metadata"></<?php echo $type ?>>
     1055        <div class="wp-playlist-next"></div>
     1056        <div class="wp-playlist-prev"></div>
     1057        <noscript>
     1058
     1059        </noscript>
     1060        <script type="application/json"><?php echo json_encode( $data ) ?></script>
     1061</div>
     1062        <?php
     1063        $output = ob_get_clean();
     1064
     1065        return $output;
     1066}
     1067
     1068function wp_playlist_shortcode( $attr ) {
     1069        return wp_get_playlist( $attr, 'audio' );
     1070}
     1071add_shortcode( 'playlist', 'wp_playlist_shortcode' );
     1072
     1073function wp_video_playlist_shortcode( $attr ) {
     1074        return wp_get_playlist( $attr, 'video' );
     1075}
     1076add_shortcode( 'video-playlist', 'wp_video_playlist_shortcode' );
     1077
     1078/**
    8951079 * Provide a No-JS Flash fallback as a last resort for audio / video
    8961080 *
    8971081 * @since 3.6.0
     
    19842168                'mediaLibraryTitle'  => __( 'Media Library' ),
    19852169                'insertMediaTitle'   => __( 'Insert Media' ),
    19862170                'createNewGallery'   => __( 'Create a new gallery' ),
     2171                'createNewPlaylist'   => __( 'Create a new playlist' ),
     2172                'createNewVideoPlaylist'   => __( 'Create a new video playlist' ),
    19872173                'returnToLibrary'    => __( '&#8592; Return to library' ),
    19882174                'allMediaItems'      => __( 'All media items' ),
    19892175                'noItemsFound'       => __( 'No items found.' ),
     
    20112197                // Edit Image
    20122198                'imageDetailsTitle'     => __( 'Image Details' ),
    20132199                'imageReplaceTitle'     => __( 'Replace Image' ),
    2014                 'imageDetailsCancel'     => __( 'Cancel Edit' )
     2200                'imageDetailsCancel'    => __( 'Cancel Edit' ),
     2201
     2202                // Playlist
     2203                'playlistDragInfo'    => __( 'Drag and drop to reorder tracks.' ),
     2204                'createPlaylistTitle' => __( 'Create Playlist' ),
     2205                'editPlaylistTitle'   => __( 'Edit Playlist' ),
     2206                'cancelPlaylistTitle' => __( '&#8592; Cancel Playlist' ),
     2207                'insertPlaylist'      => __( 'Insert playlist' ),
     2208                'updatePlaylist'      => __( 'Update playlist' ),
     2209                'addToPlaylist'       => __( 'Add to playlist' ),
     2210                'addToPlaylistTitle'  => __( 'Add to Playlist' ),
     2211
     2212                // Video Playlist
     2213                'videoPlaylistDragInfo'    => __( 'Drag and drop to reorder videos.' ),
     2214                'createVideoPlaylistTitle' => __( 'Create Video Playlist' ),
     2215                'editVideoPlaylistTitle'   => __( 'Edit Video Playlist' ),
     2216                'cancelVideoPlaylistTitle' => __( '&#8592; Cancel Video Playlist' ),
     2217                'insertVideoPlaylist'      => __( 'Insert Video playlist' ),
     2218                'updateVideoPlaylist'      => __( 'Update Video playlist' ),
     2219                'addToVideoPlaylist'       => __( 'Add to Video playlist' ),
     2220                'addToVideoPlaylistTitle'  => __( 'Add to Video Playlist' ),
    20152221        );
    20162222
    20172223        $settings = apply_filters( 'media_view_settings', $settings, $post );
  • src/wp-includes/script-loader.php

     
    311311                'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ),
    312312        ) );
    313313
     314        $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
     315
    314316        $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' );
    315317        did_action( 'init' ) && $scripts->localize( 'zxcvbn-async', '_zxcvbnSettings', array(
    316318                'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',