Ticket #26631: 26631.4.diff
File 26631.4.diff, 58.9 KB (added by , 11 years ago) |
---|
-
src/wp-admin/js/media-upload.js
28 28 } else if ( h.indexOf('[gallery') !== -1 ) { 29 29 if ( ed.plugins.wpgallery ) 30 30 h = ed.plugins.wpgallery._do_gallery(h); 31 } else if ( h.indexOf('[playlist') !== -1 ) { 32 if ( ed.plugins.wpplaylist ) 33 h = ed.plugins.wpplaylist._do_playlist(h); 31 34 } else if ( h.indexOf('[embed') === 0 ) { 32 35 if ( ed.plugins.wordpress ) 33 36 h = ed.plugins.wordpress._setEmbed(h); -
src/wp-content/themes/twentyfourteen/functions.php
111 111 'max_posts' => 6, 112 112 ) ); 113 113 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 114 118 // This theme uses its own gallery styles. 115 119 add_filter( 'use_default_gallery_style', '__return_false' ); 116 120 } -
src/wp-includes/class-wp-editor.php
233 233 'wordpress', 234 234 'wpeditimage', 235 235 'wpgallery', 236 'wpplaylist', 236 237 'wplink', 237 238 'wpdialogs', 238 239 ) ) ); -
src/wp-includes/css/editor.css
463 463 } 464 464 465 465 #wp_editbtns, 466 #wp_gallerybtns { 466 #wp_gallerybtns, 467 #wp_playlistbtns { 467 468 padding: 2px; 468 469 position: absolute; 469 470 display: none; … … 473 474 #wp_editimgbtn, 474 475 #wp_delimgbtn, 475 476 #wp_editgallery, 476 #wp_delgallery { 477 #wp_delgallery, 478 #wp_editplaylist, 479 #wp_delplaylist { 477 480 border-color: #999; 478 481 background-color: #eee; 479 482 margin: 2px; … … 487 490 #wp_editimgbtn:hover, 488 491 #wp_delimgbtn:hover, 489 492 #wp_editgallery:hover, 490 #wp_delgallery:hover { 493 #wp_delgallery:hover, 494 #wp_editplaylist:hover, 495 #wp_delplaylist:hover { 491 496 border-color: #555; 492 497 background-color: #ccc; 493 498 } -
src/wp-includes/css/media-views.css
585 585 box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); 586 586 } 587 587 588 .media-frame .media-toolbar .add-to-gallery {588 .media-frame .media-toolbar .add-to-gallery, .media-frame .media-toolbar .add-to-playlist { 589 589 display: none; 590 590 } 591 591 … … 1406 1406 margin: 1.4em 0 0.4em; 1407 1407 } 1408 1408 1409 .gallery-settings {1409 .gallery-settings, .playlist-settings { 1410 1410 overflow: hidden; 1411 1411 } 1412 1412 … … 1674 1674 .media-modal-close { 1675 1675 right: 10px; 1676 1676 } 1677 1677 1678 1678 /* Text inputs need to be 16px, or they force zooming on iOS */ 1679 1679 .media-frame input[type="text"], 1680 1680 .media-frame input[type="password"], … … 1722 1722 .media-frame-title { 1723 1723 display: none; 1724 1724 } 1725 1725 1726 1726 .media-frame-toolbar { 1727 1727 position: absolute; 1728 1728 bottom: 0px; … … 1747 1747 .attachment-details h3 { 1748 1748 margin-top: 45px; 1749 1749 } 1750 1750 1751 1751 /* Shorten right-side links so they don't overlap the close button */ 1752 1752 .media-menu a:nth-child(2), 1753 1753 .media-menu a:last-child { … … 1773 1773 top: 84px; 1774 1774 left: 0; 1775 1775 } 1776 1776 1777 1777 .media-frame-content { 1778 1778 left: 0; 1779 1779 top: 118px; … … 1782 1782 .media-frame .attachments-browser { 1783 1783 padding-bottom: 300px; 1784 1784 } 1785 1785 1786 1786 .media-sidebar { 1787 1787 border-bottom: 1px solid #dddddd; 1788 1788 } 1789 1789 1790 1790 .media-modal { 1791 1791 width: auto; 1792 1792 } … … 1855 1855 border-top: none; 1856 1856 } 1857 1857 1858 .gallery-settings h3 {1858 .gallery-settings h3, .playlist-settings h3 { 1859 1859 margin-top: 45px; 1860 1860 } 1861 1861 … … 1892 1892 .media-frame-content { 1893 1893 top: 78px; 1894 1894 } 1895 1895 1896 1896 .attachments-browser .attachments { 1897 1897 top: 2px; 1898 1898 } -
src/wp-includes/js/media-editor.js
4 4 // ----------------------------- 5 5 (function($){ 6 6 // Stores the editors' `wp.media.controller.Frame` instances. 7 var workflows = {} ;7 var workflows = {}, cache = {}; 8 8 9 9 wp.media.string = { 10 10 // Joins the `props` and `attachment` objects, … … 204 204 } 205 205 }; 206 206 207 wp.media.gallery = (function() { 208 var galleries = {}; 209 210 return { 211 defaults: { 212 order: 'ASC', 213 id: wp.media.view.settings.post.id, 214 itemtag: 'dl', 215 icontag: 'dt', 216 captiontag: 'dd', 217 columns: '3', 218 link: 'post', 219 size: 'thumbnail', 220 orderby: 'menu_order ID' 221 }, 222 223 attachments: function( shortcode ) { 207 wp.media.collection = { 208 attachments : function ( prop, type ) { 209 return function( shortcode ) { 224 210 var shortcodeString = shortcode.string(), 225 result = galleries[ shortcodeString ],211 result = cache[ shortcodeString ], 226 212 attrs, args, query, others; 227 213 228 delete galleries[ shortcodeString ];214 delete cache[ shortcodeString ]; 229 215 230 216 if ( result ) 231 217 return result; 232 218 233 219 // Fill the default shortcode attributes. 234 attrs = _.defaults( shortcode.attrs.named, wp.media.gallery.defaults );220 attrs = _.defaults( shortcode.attrs.named, this.defaults ); 235 221 args = _.pick( attrs, 'orderby', 'order' ); 236 222 237 args.type = 'image';223 args.type = type; 238 224 args.perPage = -1; 239 225 240 226 // Mark the `orderby` override attribute. 241 if ( undefined !== attrs.orderby )227 if ( undefined !== attrs.orderby ) 242 228 attrs._orderByField = attrs.orderby; 243 229 244 230 if ( 'rand' === attrs.orderby ) … … 266 252 others = _.omit( attrs, 'id', 'ids', 'include', 'exclude', 'orderby', 'order' ); 267 253 268 254 query = wp.media.query( args ); 269 query.gallery = new Backbone.Model( others ); 255 query.type = type; 256 query[ prop ] = new Backbone.Model( others ); 270 257 return query; 271 }, 258 }; 259 }, 272 260 273 shortcode: function( attachments ) { 274 var props = attachments.props.toJSON(), 275 attrs = _.pick( props, 'orderby', 'order' ), 276 shortcode, clone; 261 shortcodeAttrs : function ( prop, attachments ) { 262 var props = attachments.props.toJSON(), 263 attrs = _.pick( props, 'orderby', 'order', 'style' ); 277 264 278 if ( attachments.gallery)279 _.extend( attrs, attachments.gallery.toJSON() );265 if ( attachments[ prop ] ) 266 _.extend( attrs, attachments[ prop ].toJSON() ); 280 267 281 // Convert all galleryshortcodes to use the `ids` property.282 283 284 268 // Convert all collection shortcodes to use the `ids` property. 269 // Ignore `post__in` and `post__not_in`; the attachments in 270 // the collection will already reflect those properties. 271 attrs.ids = attachments.pluck('id'); 285 272 286 287 288 273 // Copy the `uploadedTo` post ID. 274 if ( props.uploadedTo ) 275 attrs.id = props.uploadedTo; 289 276 290 // Check if the gallery is randomly ordered. 277 // Check if the collection is randomly ordered. 278 delete attrs.orderby; 279 280 if ( attrs._orderbyRandom ) 281 attrs.orderby = 'rand'; 282 else if ( attrs._orderByField && attrs._orderByField != 'rand' ) 283 attrs.orderby = attrs._orderByField; 284 285 delete attrs._orderbyRandom; 286 delete attrs._orderByField; 287 288 // If the `ids` attribute is set and `orderby` attribute 289 // is the default value, clear it for cleaner output. 290 if ( attrs.ids && 'post__in' === attrs.orderby ) 291 291 delete attrs.orderby; 292 292 293 if ( attrs._orderbyRandom ) 294 attrs.orderby = 'rand'; 295 else if ( attrs._orderByField && attrs._orderByField != 'rand' ) 296 attrs.orderby = attrs._orderByField; 293 // Remove default attributes from the shortcode. 294 _.each( this.defaults, function( value, key ) { 295 if ( value === attrs[ key ] ) 296 delete attrs[ key ]; 297 }); 297 298 298 delete attrs._orderbyRandom;299 delete attrs._orderByField;299 return attrs; 300 }, 300 301 301 // If the `ids` attribute is set and `orderby` attribute 302 // is the default value, clear it for cleaner output. 303 if ( attrs.ids && 'post__in' === attrs.orderby ) 304 delete attrs.orderby; 302 editSelection : function ( prop, shortcode ) { 303 var defaultPostId = wp.media[ prop ].defaults.id, 304 attachments, selection; 305 305 306 // Remove default attributes from the shortcode. 307 _.each( wp.media.gallery.defaults, function( value, key ) { 308 if ( value === attrs[ key ] ) 309 delete attrs[ key ]; 310 }); 306 // Ignore the rest of the match object. 307 shortcode = shortcode.shortcode; 311 308 312 shortcode = new wp.shortcode({ 313 tag: 'gallery', 314 attrs: attrs, 315 type: 'single' 316 }); 309 if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) 310 shortcode.set( 'id', defaultPostId ); 317 311 318 // Use a cloned version of the gallery. 319 clone = new wp.media.model.Attachments( attachments.models, { 320 props: props 321 }); 322 clone.gallery = attachments.gallery; 323 galleries[ shortcode.string() ] = clone; 312 attachments = wp.media[ prop ].attachments( shortcode ); 324 313 325 return shortcode; 326 }, 314 selection = new wp.media.model.Selection( attachments.models, { 315 props: attachments.props.toJSON(), 316 multiple: true 317 }); 327 318 328 edit: function( content ) { 329 var shortcode = wp.shortcode.next( 'gallery', content ), 330 defaultPostId = wp.media.gallery.defaults.id, 331 attachments, selection; 319 selection[ prop ] = attachments[ prop ]; 332 320 333 // Bail if we didn't match the shortcode or all of the content. 334 if ( ! shortcode || shortcode.content !== content ) 335 return; 321 // Fetch the query's attachments, and then break ties from the 322 // query to allow for sorting. 323 selection.more().done( function() { 324 // Break ties with the query. 325 selection.props.set({ query: false }); 326 selection.unmirror(); 327 selection.props.unset('orderby'); 328 }); 336 329 337 // Ignore the rest of the match object.338 shortcode = shortcode.shortcode;330 return selection; 331 }, 339 332 340 if ( _.isUndefined( shortcode.get('id') ) && ! _.isUndefined( defaultPostId ) ) 341 shortcode.set( 'id', defaultPostId ); 333 cacheShortcode : function ( prop, attachments, shortcode ) { 334 // Use a cloned version of the playlist. 335 var clone = new wp.media.model.Attachments( attachments.models, { 336 props: attachments.props.toJSON() 337 }); 338 clone[ prop ] = attachments[ prop ]; 339 cache[ shortcode.string() ] = clone; 342 340 343 attachments = wp.media.gallery.attachments( shortcode ); 341 return shortcode; 342 }, 344 343 345 selection = new wp.media.model.Selection( attachments.models,{346 props: attachments.props.toJSON(),347 multiple: true348 });344 getEditFrame : function ( args ) { 345 // Destroy the previous gallery frame. 346 if ( this.frame ) 347 this.frame.dispose(); 349 348 350 selection.gallery = attachments.gallery; 349 // Store the current gallery frame. 350 this.frame = wp.media( _.extend( { 351 frame: 'post', 352 editing: true, 353 multiple: true, 354 }, args ) ).open(); 351 355 352 // Fetch the query's attachments, and then break ties from the 353 // query to allow for sorting. 354 selection.more().done( function() { 355 // Break ties with the query. 356 selection.props.set({ query: false }); 357 selection.unmirror(); 358 selection.props.unset('orderby'); 359 }); 356 return this.frame; 357 }, 360 358 361 // Destroy the previous gallery frame. 362 if ( this.frame ) 363 this.frame.dispose(); 359 instance : function ( prop, args ) { 360 return { 361 attachments: this.attachments( prop, args.type ), 362 shortcode: function( attachments ) { 363 var shortcode = new wp.shortcode({ 364 tag: prop, 365 attrs: wp.media.collection.shortcodeAttrs( prop, attachments ), 366 type: 'single' 367 }); 364 368 365 // Store the current gallery frame. 366 this.frame = wp.media({ 367 frame: 'post', 368 state: 'gallery-edit', 369 title: wp.media.view.l10n.editGalleryTitle, 370 editing: true, 371 multiple: true, 372 selection: selection 373 }).open(); 369 return wp.media.collection.cacheShortcode( prop, attachments, shortcode ); 370 }, 371 edit: function( content ) { 372 var shortcode = wp.shortcode.next( prop, content ); 374 373 375 return this.frame; 374 // Bail if we didn't match the shortcode or all of the content. 375 if ( ! shortcode || shortcode.content !== content ) 376 return; 377 378 return wp.media.collection.getEditFrame( { 379 title: args.title, 380 state: prop + '-edit', 381 selection: wp.media.collection.editSelection( prop, shortcode ) 382 } ); 383 } 384 }; 385 } 386 }; 387 388 wp.media.gallery = (function() { 389 var gallery = { 390 defaults : { 391 itemtag: 'dl', 392 icontag: 'dt', 393 captiontag: 'dd', 394 columns: '3', 395 link: 'post', 396 size: 'thumbnail', 397 order: 'ASC', 398 id: wp.media.view.settings.post.id, 399 orderby : 'menu_order ID' 376 400 } 377 401 }; 402 403 return _.extend(gallery, wp.media.collection.instance( 'gallery', { 404 type : 'image', 405 title : wp.media.view.l10n.editGalleryTitle 406 })); 378 407 }()); 379 408 409 wp.media.playlist = (function() { 410 var playlist = { 411 defaults : { 412 id: wp.media.view.settings.post.id, 413 style: 'light', 414 tracklist: true, 415 tracknumbers: true 416 } 417 }; 418 419 return _.extend(playlist, wp.media.collection.instance( 'playlist', { 420 type : 'audio', 421 title : wp.media.view.l10n.editPlaylistTitle 422 })); 423 }()); 424 425 wp.media['video-playlist'] = (function() { 426 var playlist = { 427 defaults : { 428 id: wp.media.view.settings.post.id, 429 style: 'dark', 430 tracklist: false, 431 tracknumbers: false 432 } 433 }; 434 435 return _.extend(playlist, wp.media.collection.instance( 'video-playlist', { 436 type : 'video', 437 title : wp.media.view.l10n.editVideoPlaylistTitle 438 })); 439 }()); 440 380 441 wp.media.featuredImage = { 381 442 get: function() { 382 443 return wp.media.view.settings.post.featuredImageId; … … 480 541 } else if ( h.indexOf('[gallery') !== -1 ) { 481 542 if ( ed.plugins.wpgallery ) 482 543 h = ed.plugins.wpgallery._do_gallery(h); 544 } else if ( h.indexOf('[playlist') !== -1 ) { 545 if ( ed.plugins.wpplaylist ) 546 h = ed.plugins.wpplaylist._do_playlist(h); 483 547 } else if ( h.indexOf('[embed') === 0 ) { 484 548 if ( ed.plugins.wordpress ) 485 549 h = ed.plugins.wordpress._setEmbed(h); … … 531 595 this.insert( wp.media.gallery.shortcode( selection ).string() ); 532 596 }, this ); 533 597 598 workflow.state('playlist-edit').on( 'update', function( selection ) { 599 this.insert( wp.media.playlist.shortcode( selection ).string() ); 600 }, this ); 601 602 workflow.state('video-playlist-edit').on( 'update', function( selection ) { 603 this.insert( wp.media['video-playlist'].shortcode( selection ).string() ); 604 }, this ); 605 534 606 workflow.state('embed').on( 'select', function() { 535 607 var state = workflow.state(), 536 608 type = state.get('type'), … … 704 776 if ( $this.hasClass( 'gallery' ) ) { 705 777 options.state = 'gallery'; 706 778 options.title = wp.media.view.l10n.createGalleryTitle; 779 } else if ( $this.hasClass( 'playlist' ) ) { 780 options.state = 'playlist'; 781 options.title = wp.media.view.l10n.createPlaylistTitle; 782 } else if ( $this.hasClass( 'video-playlist' ) ) { 783 options.state = 'video-playlist'; 784 options.title = wp.media.view.l10n.createVideoPlaylistTitle; 707 785 } 708 786 709 787 wp.media.editor.open( editor, options ); -
src/wp-includes/js/media-views.js
556 556 } 557 557 }); 558 558 559 // wp.media.controller.GalleryEdit 560 // ------------------------------- 561 media.controller.GalleryEdit = media.controller.Library.extend({ 562 defaults: { 563 id: 'gallery-edit', 564 multiple: false, 565 describe: true, 566 edge: 199, 567 editing: false, 568 sortable: true, 569 searchable: false, 570 toolbar: 'gallery-edit', 571 content: 'browse', 572 title: l10n.editGalleryTitle, 573 priority: 60, 574 dragInfo: true, 559 media.controller.CollectionEdit = function ( prop, args ) { 560 return media.controller.Library.extend({ 561 defaults : _.defaults(args.defaults || {}, { 562 id: prop + '-edit', 563 toolbar: prop + '-edit', 564 multiple: false, 565 describe: true, 566 edge: 199, 567 editing: false, 568 sortable: true, 569 searchable: false, 570 content: 'browse', 571 priority: 60, 572 dragInfo: true, 575 573 576 // Don't sync the selection, as the Edit Gallery library577 // *is* the selection.578 syncSelection: false579 },574 // Don't sync the selection, as the Edit Gallery library 575 // *is* the selection. 576 syncSelection: false 577 }), 580 578 581 initialize: function() {582 // If we haven't been provided a `library`, create a `Selection`.583 if ( ! this.get('library') )584 this.set( 'library', new media.model.Selection() );579 initialize: function() { 580 // If we haven't been provided a `library`, create a `Selection`. 581 if ( ! this.get('library') ) 582 this.set( 'library', new media.model.Selection() ); 585 583 586 // The single `Attachment` view to be used in the `Attachments` view.587 if ( ! this.get('AttachmentView') )588 this.set( 'AttachmentView', media.view.Attachment.EditLibrary );589 media.controller.Library.prototype.initialize.apply( this, arguments );590 },584 // The single `Attachment` view to be used in the `Attachments` view. 585 if ( ! this.get('AttachmentView') ) 586 this.set( 'AttachmentView', media.view.Attachment.EditLibrary ); 587 media.controller.Library.prototype.initialize.apply( this, arguments ); 588 }, 591 589 592 activate: function() {593 var library = this.get('library');590 activate: function() { 591 var library = this.get('library'); 594 592 595 // Limit the library to images only.596 library.props.set( 'type', 'image');593 // Limit the library to images only. 594 library.props.set( 'type', args.type ); 597 595 598 // Watch for uploaded attachments.599 this.get('library').observe( wp.Uploader.queue );596 // Watch for uploaded attachments. 597 this.get('library').observe( wp.Uploader.queue ); 600 598 601 this.frame.on( 'content:render:browse', this.gallerySettings, this );599 this.frame.on( 'content:render:browse', this.settings, this ); 602 600 603 media.controller.Library.prototype.activate.apply( this, arguments );604 },601 media.controller.Library.prototype.activate.apply( this, arguments ); 602 }, 605 603 606 deactivate: function() {607 // Stop watching for uploaded attachments.608 this.get('library').unobserve( wp.Uploader.queue );604 deactivate: function() { 605 // Stop watching for uploaded attachments. 606 this.get('library').unobserve( wp.Uploader.queue ); 609 607 610 this.frame.off( 'content:render:browse', this.gallerySettings, this );608 this.frame.off( 'content:render:browse', this.settings, this ); 611 609 612 media.controller.Library.prototype.deactivate.apply( this, arguments );613 },610 media.controller.Library.prototype.deactivate.apply( this, arguments ); 611 }, 614 612 615 gallerySettings: function( browser ) {616 var library = this.get('library');613 settings: function( browser ) { 614 var library = this.get('library'), obj = {}; 617 615 618 if ( ! library || ! browser )619 return;616 if ( ! library || ! browser ) 617 return; 620 618 621 library.gallery = library.gallery|| new Backbone.Model();619 library[ prop ] = library[ prop ] || new Backbone.Model(); 622 620 623 browser.sidebar.set({ 624 gallery: new media.view.Settings.Gallery({ 621 obj[ prop ] = new media.view.Settings[ args.settings ]({ 625 622 controller: this, 626 model: library .gallery,623 model: library[ prop ], 627 624 priority: 40 628 }) 629 }); 625 }); 630 626 631 browser.toolbar.set( 'reverse', { 632 text: l10n.reverseOrder, 633 priority: 80, 627 browser.sidebar.set( obj ); 634 628 635 click: function() { 636 library.reset( library.toArray().reverse() ); 629 if ( args.dragInfoText ) { 630 browser.toolbar.set( 'dragInfo', new media.View({ 631 el: $( '<div class="instructions">' + args.dragInfoText + '</div>' )[0], 632 priority: -40 633 }) ); 637 634 } 638 });639 }640 });641 635 642 // wp.media.controller.GalleryAdd 643 // --------------------------------- 644 media.controller.GalleryAdd = media.controller.Library.extend({ 645 defaults: _.defaults({ 646 id: 'gallery-library', 647 filterable: 'uploaded', 648 multiple: 'add', 649 menu: 'gallery', 650 toolbar: 'gallery-add', 651 title: l10n.addToGalleryTitle, 652 priority: 100, 636 browser.toolbar.set( 'reverse', { 637 text: l10n.reverseOrder, 638 priority: 80, 653 639 654 // Don't sync the selection, as the Edit Gallery library 655 // *is* the selection. 656 syncSelection: false 657 }, media.controller.Library.prototype.defaults ), 640 click: function() { 641 library.reset( library.toArray().reverse() ); 642 } 643 }); 644 } 645 }); 646 }; 658 647 659 initialize: function() { 660 // If we haven't been provided a `library`, create a `Selection`. 661 if ( ! this.get('library') ) 662 this.set( 'library', media.query({ type: 'image' }) ); 648 media.controller.CollectionAdd = function ( prop, args ) { 649 return media.controller.Library.extend({ 650 defaults: _.defaults({ 651 id: prop + '-library', 652 filterable: 'uploaded', 653 multiple: 'add', 654 menu: prop, 655 toolbar: prop + '-add', 656 priority: 100, 663 657 664 media.controller.Library.prototype.initialize.apply( this, arguments ); 665 }, 658 // Don't sync the selection, as the Edit Gallery library 659 // *is* the selection. 660 syncSelection: false 661 }, args.defaults || {}, media.controller.Library.prototype.defaults ), 662 initialize: function() { 663 // If we haven't been provided a `library`, create a `Selection`. 664 if ( ! this.get('library') ) 665 this.set( 'library', media.query({ type: args.type }) ); 666 666 667 activate: function() { 668 var library = this.get('library'), 669 edit = this.frame.state('gallery-edit').get('library'); 667 media.controller.Library.prototype.initialize.apply( this, arguments ); 668 }, 670 669 671 if ( this.editLibrary && this.editLibrary !== edit ) 672 library.unobserve( this.editLibrary ); 670 activate: function() { 671 var library = this.get('library'), 672 edit = this.frame.state(prop + '-edit').get('library'); 673 673 674 // Accepts attachments that exist in the original library and 675 // that do not exist in gallery's library. 676 library.validator = function( attachment ) { 677 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && media.model.Selection.prototype.validator.apply( this, arguments ); 678 }; 674 if ( this.editLibrary && this.editLibrary !== edit ) 675 library.unobserve( this.editLibrary ); 679 676 680 // Reset the library to ensure that all attachments are re-added 681 // to the collection. Do so silently, as calling `observe` will 682 // trigger the `reset` event. 683 library.reset( library.mirroring.models, { silent: true }); 684 library.observe( edit ); 685 this.editLibrary = edit; 677 // Accepts attachments that exist in the original library and 678 // that do not exist in gallery's library. 679 library.validator = function( attachment ) { 680 return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && media.model.Selection.prototype.validator.apply( this, arguments ); 681 }; 686 682 687 media.controller.Library.prototype.activate.apply( this, arguments ); 683 // Reset the library to ensure that all attachments are re-added 684 // to the collection. Do so silently, as calling `observe` will 685 // trigger the `reset` event. 686 library.reset( library.mirroring.models, { silent: true }); 687 library.observe( edit ); 688 this.editLibrary = edit; 689 690 media.controller.Library.prototype.activate.apply( this, arguments ); 691 } 692 }); 693 }; 694 695 // wp.media.controller.GalleryEdit 696 // ------------------------------- 697 media.controller.GalleryEdit = media.controller.CollectionEdit( 'gallery', { 698 type: 'image', 699 settings: 'Gallery', 700 defaults: { 701 title: l10n.editGalleryTitle 688 702 } 689 703 }); 690 704 705 // wp.media.controller.GalleryAdd 706 // --------------------------------- 707 media.controller.GalleryAdd = media.controller.CollectionAdd( 'gallery', { 708 type: 'image', 709 defaults: { 710 title: l10n.addToGalleryTitle 711 } 712 }); 713 714 // wp.media.controller.PlaylistEdit 715 // ------------------------------- 716 media.controller.PlaylistEdit = media.controller.CollectionEdit( 'playlist', { 717 type: 'audio', 718 settings: 'Playlist', 719 dragInfoText: l10n.playlistDragInfo, 720 defaults: { 721 title: l10n.editPlaylistTitle, 722 dragInfo : false 723 } 724 }); 725 726 // wp.media.controller.PlaylistAdd 727 // --------------------------------- 728 media.controller.PlaylistAdd = media.controller.CollectionAdd( 'playlist', { 729 type: 'audio', 730 defaults: { 731 title: l10n.addToPlaylistTitle 732 } 733 }); 734 735 // wp.media.controller.PlaylistEdit 736 // ------------------------------- 737 media.controller.VideoPlaylistEdit = media.controller.CollectionEdit( 'video-playlist', { 738 type: 'video', 739 settings: 'Playlist', 740 dragInfoText: l10n.videoPlaylistDragInfo, 741 defaults: { 742 title: l10n.editVideoPlaylistTitle, 743 dragInfo : false 744 } 745 }); 746 747 // wp.media.controller.PlaylistAdd 748 // --------------------------------- 749 media.controller.VideoPlaylistAdd = media.controller.CollectionAdd( 'video-playlist', { 750 type: 'video', 751 defaults: { 752 title: l10n.addToVideoPlaylistTitle 753 } 754 }); 755 691 756 // wp.media.controller.FeaturedImage 692 757 // --------------------------------- 693 758 media.controller.FeaturedImage = media.controller.Library.extend({ … … 1308 1373 menu: 'gallery' 1309 1374 }), 1310 1375 1311 new media.controller.GalleryAdd() 1376 new media.controller.GalleryAdd(), 1377 1378 new media.controller.Library({ 1379 id: 'playlist', 1380 title: l10n.createPlaylistTitle, 1381 priority: 60, 1382 toolbar: 'main-playlist', 1383 filterable: 'uploaded', 1384 multiple: 'add', 1385 editable: false, 1386 1387 library: media.query( _.defaults({ 1388 type: 'audio', 1389 }, options.library ) ) 1390 }), 1391 1392 // Gallery states. 1393 new media.controller.PlaylistEdit({ 1394 library: options.selection, 1395 editing: options.editing, 1396 menu: 'playlist' 1397 }), 1398 1399 new media.controller.PlaylistAdd(), 1400 1401 new media.controller.Library({ 1402 id: 'video-playlist', 1403 title: l10n.createVideoPlaylistTitle, 1404 priority: 60, 1405 toolbar: 'main-video-playlist', 1406 filterable: 'uploaded', 1407 multiple: 'add', 1408 editable: false, 1409 1410 library: media.query( _.defaults({ 1411 type: 'video' 1412 }, options.library ) ) 1413 }), 1414 1415 // Gallery states. 1416 new media.controller.VideoPlaylistEdit({ 1417 library: options.selection, 1418 editing: options.editing, 1419 menu: 'video-playlist' 1420 }), 1421 1422 new media.controller.VideoPlaylistAdd() 1312 1423 ]); 1313 1424 1314 1425 … … 1320 1431 bindHandlers: function() { 1321 1432 media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments ); 1322 1433 this.on( 'menu:create:gallery', this.createMenu, this ); 1434 this.on( 'menu:create:playlist', this.createMenu, this ); 1435 this.on( 'menu:create:video-playlist', this.createMenu, this ); 1323 1436 this.on( 'toolbar:create:main-insert', this.createToolbar, this ); 1324 1437 this.on( 'toolbar:create:main-gallery', this.createToolbar, this ); 1438 this.on( 'toolbar:create:main-playlist', this.createToolbar, this ); 1439 this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this ); 1325 1440 this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this ); 1326 1441 this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this ); 1327 1442 1328 1443 var handlers = { 1329 1444 menu: { 1330 1445 'default': 'mainMenu', 1331 'gallery': 'galleryMenu' 1446 'gallery': 'galleryMenu', 1447 'playlist': 'playlistMenu', 1448 'video-playlist': 'playlistMenu' 1332 1449 }, 1333 1450 1334 1451 content: { … … 1337 1454 }, 1338 1455 1339 1456 toolbar: { 1340 'main-insert': 'mainInsertToolbar', 1341 'main-gallery': 'mainGalleryToolbar', 1342 'gallery-edit': 'galleryEditToolbar', 1343 'gallery-add': 'galleryAddToolbar' 1457 'main-insert': 'mainInsertToolbar', 1458 'main-gallery': 'mainGalleryToolbar', 1459 'gallery-edit': 'galleryEditToolbar', 1460 'gallery-add': 'galleryAddToolbar', 1461 'main-playlist': 'mainPlaylistToolbar', 1462 'playlist-edit': 'playlistEditToolbar', 1463 'playlist-add': 'playlistAddToolbar', 1464 'main-video-playlist': 'mainVideoPlaylistToolbar', 1465 'video-playlist-edit': 'videoPlaylistEditToolbar', 1466 'video-playlist-add': 'videoPlaylistAddToolbar' 1344 1467 } 1345 1468 }; 1346 1469 … … 1384 1507 }); 1385 1508 }, 1386 1509 1510 playlistMenu: function( view ) { 1511 var lastState = this.lastState(), 1512 previous = lastState && lastState.id, 1513 frame = this; 1514 1515 view.set({ 1516 cancel: { 1517 text: l10n.cancelPlaylistTitle, 1518 priority: 20, 1519 click: function() { 1520 if ( previous ) 1521 frame.setState( previous ); 1522 else 1523 frame.close(); 1524 } 1525 }, 1526 separateCancel: new media.View({ 1527 className: 'separator', 1528 priority: 60 1529 }) 1530 }); 1531 }, 1532 1533 videoPlaylistMenu: function( view ) { 1534 var lastState = this.lastState(), 1535 previous = lastState && lastState.id, 1536 frame = this; 1537 1538 view.set({ 1539 cancel: { 1540 text: l10n.cancelVideoPlaylistTitle, 1541 priority: 20, 1542 click: function() { 1543 if ( previous ) 1544 frame.setState( previous ); 1545 else 1546 frame.close(); 1547 } 1548 }, 1549 separateCancel: new media.View({ 1550 className: 'separator', 1551 priority: 80 1552 }) 1553 }); 1554 }, 1555 1387 1556 // Content 1388 1557 embedContent: function() { 1389 1558 var view = new media.view.Embed({ … … 1489 1658 }); 1490 1659 }, 1491 1660 1661 mainPlaylistToolbar: function( view ) { 1662 var controller = this; 1663 1664 this.selectionStatusToolbar( view ); 1665 1666 view.set( 'playlist', { 1667 style: 'primary', 1668 text: l10n.createNewPlaylist, 1669 priority: 100, 1670 requires: { selection: true }, 1671 1672 click: function() { 1673 var selection = controller.state().get('selection'), 1674 edit = controller.state('playlist-edit'), 1675 models = selection.where({ type: 'audio' }); 1676 1677 edit.set( 'library', new media.model.Selection( models, { 1678 props: selection.props.toJSON(), 1679 multiple: true 1680 }) ); 1681 1682 this.controller.setState('playlist-edit'); 1683 } 1684 }); 1685 }, 1686 1687 mainVideoPlaylistToolbar: function( view ) { 1688 var controller = this; 1689 1690 this.selectionStatusToolbar( view ); 1691 1692 view.set( 'video-playlist', { 1693 style: 'primary', 1694 text: l10n.createNewVideoPlaylist, 1695 priority: 100, 1696 requires: { selection: true }, 1697 1698 click: function() { 1699 var selection = controller.state().get('selection'), 1700 edit = controller.state('video-playlist-edit'), 1701 models = selection.where({ type: 'video' }); 1702 1703 edit.set( 'library', new media.model.Selection( models, { 1704 props: selection.props.toJSON(), 1705 multiple: true 1706 }) ); 1707 1708 this.controller.setState('video-playlist-edit'); 1709 } 1710 }); 1711 }, 1712 1492 1713 featuredImageToolbar: function( toolbar ) { 1493 1714 this.createSelectToolbar( toolbar, { 1494 1715 text: l10n.setFeaturedImage, … … 1551 1772 } 1552 1773 } 1553 1774 }) ); 1775 }, 1776 1777 playlistEditToolbar: function() { 1778 var editing = this.state().get('editing'); 1779 this.toolbar.set( new media.view.Toolbar({ 1780 controller: this, 1781 items: { 1782 insert: { 1783 style: 'primary', 1784 text: editing ? l10n.updatePlaylist : l10n.insertPlaylist, 1785 priority: 120, 1786 requires: { library: true }, 1787 1788 click: function() { 1789 var controller = this.controller, 1790 state = controller.state(); 1791 1792 controller.close(); 1793 state.trigger( 'update', state.get('library') ); 1794 1795 // Restore and reset the default state. 1796 controller.setState( controller.options.state ); 1797 controller.reset(); 1798 } 1799 } 1800 } 1801 }) ); 1802 }, 1803 1804 playlistAddToolbar: function() { 1805 this.toolbar.set( new media.view.Toolbar({ 1806 controller: this, 1807 items: { 1808 insert: { 1809 style: 'primary', 1810 text: l10n.addToPlaylist, 1811 priority: 120, 1812 requires: { selection: true }, 1813 1814 click: function() { 1815 var controller = this.controller, 1816 state = controller.state(), 1817 edit = controller.state('playlist-edit'); 1818 1819 edit.get('library').add( state.get('selection').models ); 1820 state.trigger('reset'); 1821 controller.setState('playlist-edit'); 1822 } 1823 } 1824 } 1825 }) ); 1826 }, 1827 1828 videoPlaylistEditToolbar: function() { 1829 var editing = this.state().get('editing'); 1830 this.toolbar.set( new media.view.Toolbar({ 1831 controller: this, 1832 items: { 1833 insert: { 1834 style: 'primary', 1835 text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist, 1836 priority: 140, 1837 requires: { library: true }, 1838 1839 click: function() { 1840 var controller = this.controller, 1841 state = controller.state(); 1842 1843 controller.close(); 1844 state.trigger( 'update', state.get('library') ); 1845 1846 // Restore and reset the default state. 1847 controller.setState( controller.options.state ); 1848 controller.reset(); 1849 } 1850 } 1851 } 1852 }) ); 1853 }, 1854 1855 videoPlaylistAddToolbar: function() { 1856 this.toolbar.set( new media.view.Toolbar({ 1857 controller: this, 1858 items: { 1859 insert: { 1860 style: 'primary', 1861 text: l10n.addToVideoPlaylist, 1862 priority: 140, 1863 requires: { selection: true }, 1864 1865 click: function() { 1866 var controller = this.controller, 1867 state = controller.state(), 1868 edit = controller.state('video-playlist-edit'); 1869 1870 edit.get('library').add( state.get('selection').models ); 1871 state.trigger('reset'); 1872 controller.setState('video-playlist-edit'); 1873 } 1874 } 1875 } 1876 }) ); 1554 1877 } 1555 1878 }); 1556 1879 … … 3713 4036 }); 3714 4037 3715 4038 /** 4039 * wp.media.view.Settings.Gallery 4040 */ 4041 media.view.Settings.Playlist = media.view.Settings.extend({ 4042 className: 'playlist-settings', 4043 template: media.template('playlist-settings') 4044 }); 4045 4046 /** 3716 4047 * wp.media.view.Attachment.Details 3717 4048 */ 3718 4049 media.view.Attachment.Details = media.view.Attachment.extend({ -
src/wp-includes/js/mediaelement/wp-mediaelement.css
13 13 .me-cannotplay { 14 14 width: auto !important; 15 15 } 16 17 .wp-playlist { 18 border: 1px solid #ccc; 19 padding: 10px; 20 margin: 12px 0 18px; 21 font-size: 85%; 22 } 23 24 .wp-playlist .mejs-container { 25 margin: 0; 26 } 27 28 .wp-playlist .mejs-controls .mejs-button button { 29 outline: 0; 30 } 31 32 .wp-playlist-audio { 33 34 } 35 36 .wp-playlist-video { 37 38 } 39 40 .wp-playlist-light { 41 background: #fff; 42 } 43 44 .wp-playlist-dark { 45 color: #fff; 46 background: #000; 47 } 48 49 .wp-playlist-current-item { 50 overflow: hidden; 51 margin-bottom: 10px; 52 } 53 54 .wp-playlist-current-item img { 55 float: left; 56 max-width: 60px; 57 height: auto; 58 margin-right: 10px; 59 } 60 61 .wp-playlist-caption { 62 } 63 64 .wp-caption-meta { 65 display: block; 66 } 67 68 .wp-caption-title { 69 font-size: 100%; 70 } 71 72 .wp-caption-album { 73 font-style: italic; 74 } 75 76 .wp-caption-artist { 77 font-size: 80%; 78 text-transform: uppercase; 79 } 80 81 .wp-caption-by { 82 font-size: 65%; 83 font-weight: bold; 84 } 85 86 .wp-playlist-item-length { 87 position: absolute; 88 right: 0; 89 top: 0; 90 } 91 92 .wp-playlist-tracks { 93 margin-top: 10px; 94 border-top: 1px solid #ccc; 95 } 96 97 .wp-playlist-item { 98 position: relative; 99 cursor: pointer; 100 border-bottom: 1px solid #ccc; 101 } 102 No newline at end of file -
src/wp-includes/js/mediaelement/wp-playlist.js
1 "use strict"; 2 3 /*globals window, document, $, jQuery */ 4 5 (function ($, _, Backbone) { 6 7 var WPPlaylistView = Backbone.View.extend({ 8 index : 0, 9 10 itemTemplate : wp.template('wp-playlist-item'), 11 12 currentTemplate : wp.template('wp-playlist-current-item'), 13 14 initialize : function () { 15 this.data = $.parseJSON( this.$('script').html() ); 16 this.playerNode = this.$( this.data.type ); 17 18 this.tracks = new Backbone.Collection( this.data.tracks ); 19 this.current = this.tracks.first(); 20 this.currentNode = this.$( '.wp-playlist-current-item' ); 21 this.renderCurrent(); 22 23 if ( this.data.tracklist ) { 24 this.renderTracks(); 25 } 26 27 this.playerNode.attr( 'src', this.current.get('src') ); 28 29 _.bindAll( this, 'bindPlayer', 'ended', 'clickTrack' ); 30 31 new MediaElementPlayer( this.playerNode.get(0), { 32 pluginPath : '/wp-includes/js/mediaelement/', 33 success: this.bindPlayer 34 }); 35 }, 36 37 renderCurrent : function () { 38 this.currentNode.html( this.currentTemplate( this.current.toJSON() ) ); 39 }, 40 41 renderTracks : function () { 42 var that = this, i = 1, tracklist = $( '<div class="wp-playlist-tracks"></div>' ); 43 this.tracks.each(function (model) { 44 model.set( 'index', i ); 45 tracklist.append( that.itemTemplate( model.toJSON() ) ); 46 i += 1; 47 }); 48 this.$el.append( tracklist ); 49 }, 50 51 events : { 52 'click .wp-playlist-item' : 'clickTrack', 53 'click .wp-playlist-next' : 'next', 54 'click .wp-playlist-prev' : 'prev', 55 }, 56 57 bindPlayer : function (mejs) { 58 this.player = mejs; 59 this.player.addEventListener( 'ended', this.ended ); 60 }, 61 62 clickTrack : function (e) { 63 this.index = this.$( '.wp-playlist-item' ).index( e.currentTarget ); 64 this.setCurrent(); 65 }, 66 67 ended : function () { 68 if ( this.index + 1 < this.tracks.length ) { 69 this.next(); 70 } else { 71 this.player.pause(); 72 this.index = 0; 73 this.current = this.tracks.at( this.index ); 74 this.playerNode.attr( 'src', this.current.get( 'src' ) ); 75 this.player.load(); 76 } 77 }, 78 79 next : function () { 80 this.index = this.index + 1 >= this.tracks.length ? 0 : this.index + 1; 81 this.setCurrent(); 82 }, 83 84 prev : function () { 85 this.index = this.index - 1 < 0 ? this.tracks.length - 1 : this.index - 1; 86 this.setCurrent(); 87 }, 88 89 setCurrent : function () { 90 this.current = this.tracks.at( this.index ); 91 this.renderCurrent(); 92 93 this.player.pause(); 94 this.playerNode.attr( 'src', this.current.get( 'src' ) ); 95 this.player.load(); 96 this.player.play(); 97 } 98 }); 99 100 $(document).ready(function () { 101 $('.wp-playlist').each(function () { 102 return new WPPlaylistView({ el: this }); 103 }); 104 }); 105 106 }(jQuery, _, Backbone)); 107 No newline at end of file -
src/wp-includes/js/plupload/handlers.js
22 22 .appendTo( jQuery('#media-items' ) ); 23 23 24 24 // Disable submit 25 jQuery('#insert-gallery ').prop('disabled', true);25 jQuery('#insert-gallery, #insert-playlist').prop('disabled', true); 26 26 } 27 27 28 28 function uploadStart() { … … 47 47 48 48 if ( max > hundredmb && file.size > hundredmb ) { 49 49 setTimeout(function(){ 50 50 51 51 if ( file.status < 3 && file.loaded === 0 ) { // not uploading 52 52 wpFileError(file, pluploadL10n.big_upload_failed.replace('%1$s', '<a class="uploader-html" href="#">').replace('%2$s', '</a>')); 53 53 up.stop(); // stops the whole queue … … 64 64 // Just one file, no need for collapsible part 65 65 if ( items.length == 1 ) { 66 66 items.addClass('open').find('.slidetoggle').show(); 67 jQuery('.insert-gallery ').hide();67 jQuery('.insert-gallery, .insert-playlist').hide(); 68 68 } else if ( items.length > 1 ) { 69 69 items.removeClass('open'); 70 // Only show Gallery buttonwhen 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(); 72 72 } 73 73 74 74 // Only show Save buttons when there is at least one file. … … 171 171 success: function( ){ 172 172 var type, 173 173 item = jQuery('#media-item-' + fileObj.id); 174 174 175 175 if ( type = jQuery('#type-of-' + fileObj.id).val() ) 176 176 jQuery('#' + type + '-counter').text(jQuery('#' + type + '-counter').text()-0+1); 177 177 … … 257 257 } 258 258 259 259 function uploadComplete() { 260 jQuery('#insert-gallery ').prop('disabled', false);260 jQuery('#insert-gallery, #insert-playlist').prop('disabled', false); 261 261 } 262 262 263 263 function switchUploader(s) { -
src/wp-includes/js/swfupload/handlers.js
25 25 jQuery('.progress', '#media-item-' + fileObj.id).show(); 26 26 27 27 // Disable submit and enable cancel 28 jQuery('#insert-gallery ').prop('disabled', true);28 jQuery('#insert-gallery, #insert-playlist').prop('disabled', true); 29 29 jQuery('#cancel-upload').prop('disabled', false); 30 30 } 31 31 32 32 function uploadStart(fileObj) { 33 33 try { 34 34 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); 36 36 } catch(e){} 37 37 38 38 return true; … … 212 212 else 213 213 jQuery('.savebutton').hide(); 214 214 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 } 220 221 } 221 222 222 223 function uploadSuccess(fileObj, serverData) { … … 238 239 // If no more uploads queued, enable the submit button 239 240 if ( swfu.getStats().files_queued == 0 ) { 240 241 jQuery('#cancel-upload').prop('disabled', true); 241 jQuery('#insert-gallery ').prop('disabled', false);242 jQuery('#insert-gallery, #insert-playlist').prop('disabled', false); 242 243 } 243 244 } 244 245 -
src/wp-includes/js/tinymce/langs/wp-langs-en.js
478 478 add_audio: "Add Audio", 479 479 editgallery: "Edit Gallery", 480 480 delgallery: "Delete Gallery", 481 editplaylist: "Edit Playlist", 482 delplaylist: "Delete Playlist", 481 483 wp_fullscreen_desc: "Distraction Free Writing mode (Alt + Shift + W)" 482 484 }); 483 485 -
src/wp-includes/js/tinymce/plugins/wpplaylist/plugin.js
1 /* global tinymce */ 2 tinymce.PluginManager.add('wpplaylist', function( editor ) { 3 4 function parsePlaylist( content ) { 5 return content 6 .replace( /\[video-playlist([^\]]*)\]/g, function( match, attr ) { 7 var data = tinymce.DOM.encode( attr ); 8 9 return '<span class="post-format-video"></span><img src="' + tinymce.Env.transparentSrc + '" class="wp-video-playlist mceItem" ' + 10 'title="video-playlist' + data + '" data-mce-resize="false" data-mce-placeholder="1" />'; 11 }) 12 .replace( /\[playlist([^\]]*)\]/g, function( match, attr ) { 13 var data = tinymce.DOM.encode( attr ); 14 15 return '<span class="post-format-audio"></span><img src="' + tinymce.Env.transparentSrc + '" class="wp-playlist mceItem" ' + 16 'title="playlist' + data + '" data-mce-resize="false" data-mce-placeholder="1" />'; 17 }); 18 } 19 20 function getPlaylist( content ) { 21 function getAttr( str, name ) { 22 name = new RegExp( name + '=\"([^\"]+)\"', 'g' ).exec( str ); 23 return name ? tinymce.DOM.decode( name[1] ) : ''; 24 } 25 26 return content.replace( /(?:<p[^>]*>)*(<img[^>]+>)(?:<\/p>)*/g, function( match, image ) { 27 var cls = getAttr( image, 'class' ); 28 29 if ( cls.indexOf('wp-playlist') !== -1 ) { 30 return '<p>['+ tinymce.trim( getAttr( image, 'title' ) ) +']</p>'; 31 } else if ( cls.indexOf('wp-video-playlist') !== -1 ) { 32 return '<p>['+ tinymce.trim( getAttr( image, 'title' ) ) +']</p>'; 33 } 34 35 return match; 36 }); 37 } 38 39 // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('...'); 40 editor.addCommand( 'WP_Playlist', function() { 41 var frame, node; 42 43 // Check if the `wp.media.playlist` API exists. 44 if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.playlist || ! wp.media['video-playlist'] ) { 45 return; 46 } 47 48 node = editor.selection.getNode(); 49 50 // Make sure we've selected a playlist node. 51 if ( node.nodeName === 'IMG' ) { 52 if ( editor.dom.hasClass( node, 'wp-playlist' ) ) { 53 frame = wp.media.playlist.edit( '[' + editor.dom.getAttrib( node, 'title' ) + ']' ); 54 frame.state('playlist-edit').on( 'update', function( selection ) { 55 var shortcode = wp.media.playlist.shortcode( selection ).string().slice( 1, -1 ); 56 editor.dom.setAttrib( node, 'title', shortcode ); 57 }); 58 } else if ( editor.dom.hasClass( node, 'wp-video-playlist' ) ) { 59 frame = wp.media['video-playlist'].edit( '[' + editor.dom.getAttrib( node, 'title' ) + ']' ); 60 frame.state('video-playlist-edit').on( 'update', function( selection ) { 61 var shortcode = wp.media['video-playlist'].shortcode( selection ).string().slice( 1, -1 ); 62 editor.dom.setAttrib( node, 'title', shortcode ); 63 }); 64 } 65 } 66 }); 67 /* 68 editor.on( 'init', function( e ) { 69 // _createButtons() 70 71 // iOS6 doesn't show the buttons properly on click, show them on 'touchstart' 72 if ( 'ontouchstart' in window ) { 73 editor.dom.events.bind( editor.getBody(), 'touchstart', function( e ) { 74 var target = e.target; 75 76 if ( target.nodeName == 'IMG' && editor.dom.hasClass( target, 'wp-playlist' ) ) { 77 editor.selection.select( target ); 78 editor.dom.events.cancel( e ); 79 editor.plugins.wordpress._hideButtons(); 80 editor.plugins.wordpress._showButtons( target, 'wp_playlistbtns' ); 81 } 82 }); 83 } 84 }); 85 */ 86 editor.on( 'mouseup', function( e ) { 87 if ( e.target.nodeName === 'IMG' && ( editor.dom.hasClass( e.target, 'wp-playlist' ) || editor.dom.hasClass( e.target, 'wp-video-playlist' ) ) ) { 88 // Don't trigger on right-click 89 if ( e.button !== 2 ) { 90 if ( editor.dom.hasClass( e.target, 'wp-playlist-selected' ) ) { 91 editor.execCommand('WP_Playlist'); 92 editor.dom.removeClass( e.target, 'wp-playlist-selected' ); 93 } else { 94 editor.dom.addClass( e.target, 'wp-playlist-selected' ); 95 } 96 } 97 } else { 98 editor.dom.removeClass( editor.dom.select( 'img.wp-playlist-selected' ), 'wp-playlist-selected' ); 99 } 100 }); 101 102 // Display 'playlist' instead of img in element path 103 editor.on( 'ResolveName', function( e ) { 104 var dom = editor.dom, 105 target = e.target; 106 107 if ( target.nodeName === 'IMG' && dom.hasClass( target, 'wp-playlist' ) ) { 108 e.name = 'playlist'; 109 } else if ( target.nodeName === 'IMG' && dom.hasClass( target, 'wp-video-playlist' ) ) { 110 e.name = 'video-playlist'; 111 } 112 }); 113 114 editor.on( 'BeforeSetContent', function( e ) { 115 e.content = parsePlaylist( e.content ); 116 }); 117 118 editor.on( 'PostProcess', function( e ) { 119 if ( e.get ) { 120 e.content = getPlaylist( e.content ); 121 } 122 }); 123 124 return { 125 _do_playlist: parsePlaylist, 126 _get_playlist: getPlaylist 127 }; 128 }); 129 No newline at end of file -
src/wp-includes/js/tinymce/skins/wordpress/wp-content.css
108 108 border-top: 3px dotted #bbb; 109 109 } 110 110 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{ 112 114 border: 1px dashed #888; 113 115 background: #f2f2f2 url("images/gallery.png") no-repeat scroll center center; 114 116 width: 99%; … … 117 119 cursor: pointer; 118 120 } 119 121 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 { 121 131 background-color: #ededed; 122 132 border-style: solid; 123 133 } 124 134 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 { 126 136 background-color: #d8d8d8; 127 137 border-style: solid; 128 138 } -
src/wp-includes/media-template.php
411 411 </label> 412 412 </script> 413 413 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 414 450 <script type="text/html" id="tmpl-embed-link-settings"> 415 451 <label class="setting"> 416 452 <span><?php _e('Title'); ?></span> -
src/wp-includes/media.php
892 892 } 893 893 894 894 /** 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 */ 905 function 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">“{{{ data.meta.title }}}”</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 }}}. 1039 <span class="wp-caption-title">“{{{ data.meta.title }}}”</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 1068 function wp_playlist_shortcode( $attr ) { 1069 return wp_get_playlist( $attr, 'audio' ); 1070 } 1071 add_shortcode( 'playlist', 'wp_playlist_shortcode' ); 1072 1073 function wp_video_playlist_shortcode( $attr ) { 1074 return wp_get_playlist( $attr, 'video' ); 1075 } 1076 add_shortcode( 'video-playlist', 'wp_video_playlist_shortcode' ); 1077 1078 /** 895 1079 * Provide a No-JS Flash fallback as a last resort for audio / video 896 1080 * 897 1081 * @since 3.6.0 … … 1982 2166 'mediaLibraryTitle' => __( 'Media Library' ), 1983 2167 'insertMediaTitle' => __( 'Insert Media' ), 1984 2168 'createNewGallery' => __( 'Create a new gallery' ), 2169 'createNewPlaylist' => __( 'Create a new playlist' ), 2170 'createNewVideoPlaylist' => __( 'Create a new video playlist' ), 1985 2171 'returnToLibrary' => __( '← Return to library' ), 1986 2172 'allMediaItems' => __( 'All media items' ), 1987 2173 'noItemsFound' => __( 'No items found.' ), … … 2005 2191 'addToGallery' => __( 'Add to gallery' ), 2006 2192 'addToGalleryTitle' => __( 'Add to Gallery' ), 2007 2193 'reverseOrder' => __( 'Reverse order' ), 2194 2195 // Playlist 2196 'playlistDragInfo' => __( 'Drag and drop to reorder tracks.' ), 2197 'createPlaylistTitle' => __( 'Create Playlist' ), 2198 'editPlaylistTitle' => __( 'Edit Playlist' ), 2199 'cancelPlaylistTitle' => __( '← Cancel Playlist' ), 2200 'insertPlaylist' => __( 'Insert playlist' ), 2201 'updatePlaylist' => __( 'Update playlist' ), 2202 'addToPlaylist' => __( 'Add to playlist' ), 2203 'addToPlaylistTitle' => __( 'Add to Playlist' ), 2204 2205 // Video Playlist 2206 'videoPlaylistDragInfo' => __( 'Drag and drop to reorder videos.' ), 2207 'createVideoPlaylistTitle' => __( 'Create Video Playlist' ), 2208 'editVideoPlaylistTitle' => __( 'Edit Video Playlist' ), 2209 'cancelVideoPlaylistTitle' => __( '← Cancel Video Playlist' ), 2210 'insertVideoPlaylist' => __( 'Insert Video playlist' ), 2211 'updateVideoPlaylist' => __( 'Update Video playlist' ), 2212 'addToVideoPlaylist' => __( 'Add to Video playlist' ), 2213 'addToVideoPlaylistTitle' => __( 'Add to Video Playlist' ), 2008 2214 ); 2009 2215 2010 2216 $settings = apply_filters( 'media_view_settings', $settings, $post ); -
src/wp-includes/script-loader.php
311 311 'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ), 312 312 ) ); 313 313 314 $scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 ); 315 314 316 $scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' ); 315 317 did_action( 'init' ) && $scripts->localize( 'zxcvbn-async', '_zxcvbnSettings', array( 316 318 'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',