Ticket #27437: 27437.2.diff
File 27437.2.diff, 58.2 KB (added by , 11 years ago) |
---|
-
src/wp-includes/js/media-audiovideo.js
1 /* global _wpMediaViewsL10n */ 2 3 (function ($, _, Backbone) { 4 var media = wp.media, l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n; 5 6 /** 7 * @mixin 8 */ 9 wp.media.mixin = { 10 11 /** 12 * Pauses every instance of MediaElementPlayer 13 */ 14 pauseAllPlayers: function () { 15 var p; 16 if ( window.mejs && window.mejs.players ) { 17 for ( p in window.mejs.players ) { 18 window.mejs.players[p].pause(); 19 } 20 } 21 }, 22 23 /** 24 * Utility to identify the user's browser 25 */ 26 ua: { 27 is : function (browser) { 28 var passes = false, ua = window.navigator.userAgent; 29 30 switch ( browser ) { 31 case 'oldie': 32 passes = ua.match(/MSIE [6-8]/gi) !== null; 33 break; 34 case 'ie': 35 passes = ua.match(/MSIE/gi) !== null; 36 break; 37 case 'ff': 38 passes = ua.match(/firefox/gi) !== null; 39 break; 40 case 'opera': 41 passes = ua.match(/OPR/) !== null; 42 break; 43 case 'safari': 44 passes = ua.match(/safari/gi) !== null && ua.match(/chrome/gi) === null; 45 break; 46 case 'chrome': 47 passes = ua.match(/safari/gi) && ua.match(/chrome/gi) !== null; 48 break; 49 } 50 51 return passes; 52 } 53 }, 54 55 /** 56 * Specify compatibility for native playback by browser 57 */ 58 compat :{ 59 'opera' : { 60 audio: ['ogg', 'wav'], 61 video: ['ogg', 'webm'] 62 }, 63 'chrome' : { 64 audio: ['ogg', 'mpeg', 'x-ms-wma'], 65 video: ['ogg', 'webm', 'mp4', 'm4v', 'mpeg'] 66 }, 67 'ff' : { 68 audio: ['ogg', 'mpeg'], 69 video: ['ogg', 'webm'] 70 }, 71 'safari' : { 72 audio: ['mpeg', 'wav'], 73 video: ['mp4', 'm4v', 'mpeg', 'x-ms-wmv', 'quicktime'] 74 }, 75 'ie' : { 76 audio: ['mpeg'], 77 video: ['mp4', 'm4v', 'mpeg'] 78 } 79 }, 80 81 /** 82 * Determine if the passed media contains a <source> that provides 83 * native playback in the user's browser 84 * 85 * @param {jQuery} media 86 * @returns {Boolean} 87 */ 88 isCompatible: function ( media ) { 89 if ( ! media.find( 'source' ).length ) { 90 return false; 91 } 92 93 var ua = this.ua, test = false, found = false, sources; 94 95 if ( ua.is( 'oldIE' ) ) { 96 return false; 97 } 98 99 sources = media.find( 'source' ); 100 101 _.find( this.compat, function (supports, browser) { 102 if ( ua.is( browser ) ) { 103 found = true; 104 _.each( sources, function (elem) { 105 var audio = new RegExp( 'audio\/(' + supports.audio.join('|') + ')', 'gi' ), 106 video = new RegExp( 'video\/(' + supports.video.join('|') + ')', 'gi' ); 107 108 if ( elem.type.match( video ) !== null || elem.type.match( audio ) !== null ) { 109 test = true; 110 } 111 } ); 112 } 113 114 return test || found; 115 } ); 116 117 return test; 118 }, 119 120 /** 121 * Override the MediaElement method for removing a player. 122 * MediaElement tries to pull the audio/video tag out of 123 * its container and re-add it to the DOM. 124 */ 125 removePlayer: function() { 126 var t = this.player, featureIndex, feature; 127 128 // invoke features cleanup 129 for ( featureIndex in t.options.features ) { 130 feature = t.options.features[featureIndex]; 131 if ( t['clean' + feature] ) { 132 try { 133 t['clean' + feature](t); 134 } catch (e) {} 135 } 136 } 137 138 if ( ! t.isDynamic ) { 139 t.$node.remove(); 140 } 141 142 if ( 'native' !== t.media.pluginType ) { 143 t.media.remove(); 144 } 145 146 delete window.mejs.players[t.id]; 147 148 t.container.remove(); 149 t.globalUnbind(); 150 delete t.node.player; 151 }, 152 153 /** 154 * Allows any class that has set 'player' to a MediaElementPlayer 155 * instance to remove the player when listening to events. 156 * 157 * Examples: modal closes, shortcode properties are removed, etc. 158 */ 159 unsetPlayer : function() { 160 if ( this.player ) { 161 wp.media.mixin.pauseAllPlayers(); 162 wp.media.mixin.removePlayer.apply( this ); 163 this.player = false; 164 } 165 } 166 }; 167 168 /** 169 * Autowire "collection"-type shortcodes 170 */ 171 wp.media.playlist = new wp.media.collection({ 172 tag: 'playlist', 173 type : 'audio', 174 editTitle : l10n.editPlaylistTitle, 175 defaults : { 176 id: wp.media.view.settings.post.id, 177 style: 'light', 178 tracklist: true, 179 tracknumbers: true, 180 images: true, 181 artists: true 182 } 183 }); 184 185 wp.media['video-playlist'] = new wp.media.collection({ 186 tag: 'video-playlist', 187 type : 'video', 188 editTitle : l10n.editVideoPlaylistTitle, 189 defaults : { 190 id: wp.media.view.settings.post.id, 191 style: 'light', 192 tracklist: false, 193 tracknumbers: false, 194 images: true 195 } 196 }); 197 198 /** 199 * Shortcode modeling for audio 200 * `edit()` prepares the shortcode for the media modal 201 * `shortcode()` builds the new shortcode after update 202 * 203 * @namespace 204 */ 205 wp.media.audio = { 206 coerce : wp.media.coerce, 207 208 defaults : { 209 id : wp.media.view.settings.post.id, 210 src : '', 211 loop : false, 212 autoplay : false, 213 preload : 'none' 214 }, 215 216 edit : function (data) { 217 var frame, shortcode = wp.shortcode.next( 'audio', data ).shortcode; 218 frame = wp.media({ 219 frame: 'audio', 220 state: 'audio-details', 221 metadata: _.defaults( 222 shortcode.attrs.named, 223 wp.media.audio.defaults 224 ) 225 }); 226 227 return frame; 228 }, 229 230 shortcode : function (shortcode) { 231 var self = this; 232 233 _.each( wp.media.audio.defaults, function( value, key ) { 234 shortcode[ key ] = self.coerce( shortcode, key ); 235 236 if ( value === shortcode[ key ] ) { 237 delete shortcode[ key ]; 238 } 239 }); 240 241 return wp.shortcode.string({ 242 tag: 'audio', 243 attrs: shortcode 244 }); 245 } 246 }; 247 248 /** 249 * Shortcode modeling for video 250 * `edit()` prepares the shortcode for the media modal 251 * `shortcode()` builds the new shortcode after update 252 * 253 * @namespace 254 */ 255 wp.media.video = { 256 coerce : wp.media.coerce, 257 258 defaults : { 259 id : wp.media.view.settings.post.id, 260 src : '', 261 poster : '', 262 loop : false, 263 autoplay : false, 264 preload : 'metadata', 265 content : '' 266 }, 267 268 edit : function (data) { 269 var frame, 270 defaults = this.defaults, 271 shortcode = wp.shortcode.next( 'video', data ).shortcode, 272 attrs; 273 274 attrs = shortcode.attrs.named; 275 attrs.content = shortcode.content; 276 277 frame = wp.media({ 278 frame: 'video', 279 state: 'video-details', 280 metadata: _.defaults( attrs, defaults ) 281 }); 282 283 return frame; 284 }, 285 286 shortcode : function (shortcode) { 287 var self = this, content = shortcode.content; 288 delete shortcode.content; 289 290 _.each( this.defaults, function( value, key ) { 291 shortcode[ key ] = self.coerce( shortcode, key ); 292 293 if ( value === shortcode[ key ] ) { 294 delete shortcode[ key ]; 295 } 296 }); 297 298 return wp.shortcode.string({ 299 tag: 'video', 300 attrs: shortcode, 301 content: content 302 }); 303 } 304 }; 305 306 /** 307 * wp.media.model.PostMedia 308 * 309 * @constructor 310 * @augments Backbone.Model 311 **/ 312 media.model.PostMedia = Backbone.Model.extend({ 313 initialize: function() { 314 this.attachment = false; 315 }, 316 317 setSource: function ( attachment ) { 318 this.attachment = attachment; 319 this.extension = attachment.get('filename' ).split('.').pop(); 320 321 if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) { 322 this.unset( 'src' ); 323 } 324 325 if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) { 326 this.set( this.extension, this.attachment.get( 'url' ) ); 327 } else { 328 this.unset( this.extension ); 329 } 330 }, 331 332 changeAttachment: function( attachment ) { 333 var self = this; 334 335 this.setSource( attachment ); 336 337 this.unset( 'src' ); 338 _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) { 339 self.unset( ext ); 340 } ); 341 } 342 }); 343 344 /** 345 * wp.media.controller.AudioDetails 346 * 347 * @constructor 348 * @augments wp.media.controller.State 349 * @augments Backbone.Model 350 */ 351 media.controller.AudioDetails = media.controller.State.extend({ 352 defaults: _.defaults({ 353 id: 'audio-details', 354 toolbar: 'audio-details', 355 title: l10n.audioDetailsTitle, 356 content: 'audio-details', 357 menu: 'audio-details', 358 router: false, 359 attachment: false, 360 priority: 60, 361 editing: false 362 }, media.controller.Library.prototype.defaults ), 363 364 initialize: function( options ) { 365 this.media = options.media; 366 media.controller.State.prototype.initialize.apply( this, arguments ); 367 } 368 }); 369 370 /** 371 * wp.media.controller.VideoDetails 372 * 373 * @constructor 374 * @augments wp.media.controller.State 375 * @augments Backbone.Model 376 */ 377 media.controller.VideoDetails = media.controller.State.extend({ 378 defaults: _.defaults({ 379 id: 'video-details', 380 toolbar: 'video-details', 381 title: l10n.videoDetailsTitle, 382 content: 'video-details', 383 menu: 'video-details', 384 router: false, 385 attachment: false, 386 priority: 60, 387 editing: false 388 }, media.controller.Library.prototype.defaults ), 389 390 initialize: function( options ) { 391 this.media = options.media; 392 media.controller.State.prototype.initialize.apply( this, arguments ); 393 } 394 }); 395 396 /** 397 * wp.media.view.MediaFrame.MediaDetails 398 * 399 * @constructor 400 * @augments wp.media.view.MediaFrame.Select 401 * @augments wp.media.view.MediaFrame 402 * @augments wp.media.view.Frame 403 * @augments wp.media.View 404 * @augments wp.Backbone.View 405 * @augments Backbone.View 406 * @mixes wp.media.controller.StateMachine 407 */ 408 media.view.MediaFrame.MediaDetails = media.view.MediaFrame.Select.extend({ 409 defaults: { 410 id: 'media', 411 url: '', 412 menu: 'media-details', 413 content: 'media-details', 414 toolbar: 'media-details', 415 type: 'link', 416 priority: 120 417 }, 418 419 initialize: function( options ) { 420 this.DetailsView = options.DetailsView; 421 this.cancelText = options.cancelText; 422 this.addText = options.addText; 423 424 this.media = new media.model.PostMedia( options.metadata ); 425 this.options.selection = new media.model.Selection( this.media.attachment, { multiple: false } ); 426 media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments ); 427 }, 428 429 bindHandlers: function() { 430 var menu = this.defaults.menu; 431 432 media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments ); 433 434 this.on( 'menu:create:' + menu, this.createMenu, this ); 435 this.on( 'content:render:' + menu, this.renderDetailsContent, this ); 436 this.on( 'menu:render:' + menu, this.renderMenu, this ); 437 this.on( 'toolbar:render:' + menu, this.renderDetailsToolbar, this ); 438 }, 439 440 renderDetailsContent: function() { 441 var view = new this.DetailsView({ 442 controller: this, 443 model: this.state().media, 444 attachment: this.state().media.attachment 445 }).render(); 446 447 this.content.set( view ); 448 }, 449 450 renderMenu: function( view ) { 451 var lastState = this.lastState(), 452 previous = lastState && lastState.id, 453 frame = this; 454 455 view.set({ 456 cancel: { 457 text: this.cancelText, 458 priority: 20, 459 click: function() { 460 if ( previous ) { 461 frame.setState( previous ); 462 } else { 463 frame.close(); 464 } 465 } 466 }, 467 separateCancel: new media.View({ 468 className: 'separator', 469 priority: 40 470 }) 471 }); 472 473 }, 474 475 renderDetailsToolbar: function() { 476 this.toolbar.set( new media.view.Toolbar({ 477 controller: this, 478 items: { 479 select: { 480 style: 'primary', 481 text: l10n.update, 482 priority: 80, 483 484 click: function() { 485 var controller = this.controller, 486 state = controller.state(); 487 488 controller.close(); 489 490 state.trigger( 'update', controller.media.toJSON() ); 491 492 // Restore and reset the default state. 493 controller.setState( controller.options.state ); 494 controller.reset(); 495 } 496 } 497 } 498 }) ); 499 }, 500 501 renderReplaceToolbar: function() { 502 this.toolbar.set( new media.view.Toolbar({ 503 controller: this, 504 items: { 505 replace: { 506 style: 'primary', 507 text: l10n.replace, 508 priority: 80, 509 510 click: function() { 511 var controller = this.controller, 512 state = controller.state(), 513 selection = state.get( 'selection' ), 514 attachment = selection.single(); 515 516 controller.media.changeAttachment( attachment ); 517 518 state.trigger( 'replace', controller.media.toJSON() ); 519 520 // Restore and reset the default state. 521 controller.setState( controller.options.state ); 522 controller.reset(); 523 } 524 } 525 } 526 }) ); 527 }, 528 529 renderAddSourceToolbar: function() { 530 this.toolbar.set( new media.view.Toolbar({ 531 controller: this, 532 items: { 533 replace: { 534 style: 'primary', 535 text: this.addText, 536 priority: 80, 537 538 click: function() { 539 var controller = this.controller, 540 state = controller.state(), 541 selection = state.get( 'selection' ), 542 attachment = selection.single(); 543 544 controller.media.setSource( attachment ); 545 546 state.trigger( 'add-source', controller.media.toJSON() ); 547 548 // Restore and reset the default state. 549 controller.setState( controller.options.state ); 550 controller.reset(); 551 } 552 } 553 } 554 }) ); 555 } 556 }); 557 558 /** 559 * wp.media.view.MediaFrame.AudioDetails 560 * 561 * @constructor 562 * @augments wp.media.view.MediaFrame.MediaDetails 563 * @augments wp.media.view.MediaFrame.Select 564 * @augments wp.media.view.MediaFrame 565 * @augments wp.media.view.Frame 566 * @augments wp.media.View 567 * @augments wp.Backbone.View 568 * @augments Backbone.View 569 * @mixes wp.media.controller.StateMachine 570 */ 571 media.view.MediaFrame.AudioDetails = media.view.MediaFrame.MediaDetails.extend({ 572 defaults: { 573 id: 'audio', 574 url: '', 575 menu: 'audio-details', 576 content: 'audio-details', 577 toolbar: 'audio-details', 578 type: 'link', 579 title: l10n.audioDetailsTitle, 580 priority: 120 581 }, 582 583 initialize: function( options ) { 584 options.DetailsView = media.view.AudioDetails; 585 options.cancelText = l10n.audioDetailsCancel; 586 options.addText = l10n.audioAddSourceTitle; 587 588 media.view.MediaFrame.MediaDetails.prototype.initialize.call( this, options ); 589 }, 590 591 bindHandlers: function() { 592 media.view.MediaFrame.MediaDetails.prototype.bindHandlers.apply( this, arguments ); 593 594 this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this ); 595 this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this ); 596 }, 597 598 createStates: function() { 599 this.states.add([ 600 new media.controller.AudioDetails( { 601 media: this.media, 602 editable: false, 603 menu: 'audio-details' 604 } ), 605 606 new media.controller.MediaLibrary( { 607 type: 'audio', 608 id: 'replace-audio', 609 title: l10n.audioReplaceTitle, 610 toolbar: 'replace-audio', 611 media: this.media, 612 menu: 'audio-details' 613 } ), 614 615 new media.controller.MediaLibrary( { 616 type: 'audio', 617 id: 'add-audio-source', 618 title: l10n.audioAddSourceTitle, 619 toolbar: 'add-audio-source', 620 media: this.media, 621 menu: false 622 } ) 623 ]); 624 } 625 }); 626 627 /** 628 * wp.media.view.MediaFrame.VideoDetails 629 * 630 * @constructor 631 * @augments wp.media.view.MediaFrame.MediaDetails 632 * @augments wp.media.view.MediaFrame.Select 633 * @augments wp.media.view.MediaFrame 634 * @augments wp.media.view.Frame 635 * @augments wp.media.View 636 * @augments wp.Backbone.View 637 * @augments Backbone.View 638 * @mixes wp.media.controller.StateMachine 639 */ 640 media.view.MediaFrame.VideoDetails = media.view.MediaFrame.MediaDetails.extend({ 641 defaults: { 642 id: 'video', 643 url: '', 644 menu: 'video-details', 645 content: 'video-details', 646 toolbar: 'video-details', 647 type: 'link', 648 title: l10n.videoDetailsTitle, 649 priority: 120 650 }, 651 652 initialize: function( options ) { 653 options.DetailsView = media.view.VideoDetails; 654 options.cancelText = l10n.videoDetailsCancel; 655 options.addText = l10n.videoAddSourceTitle; 656 657 media.view.MediaFrame.MediaDetails.prototype.initialize.call( this, options ); 658 }, 659 660 bindHandlers: function() { 661 media.view.MediaFrame.MediaDetails.prototype.bindHandlers.apply( this, arguments ); 662 663 this.on( 'toolbar:render:replace-video', this.renderReplaceToolbar, this ); 664 this.on( 'toolbar:render:add-video-source', this.renderAddSourceToolbar, this ); 665 this.on( 'toolbar:render:select-poster-image', this.renderSelectPosterImageToolbar, this ); 666 this.on( 'toolbar:render:add-track', this.renderAddTrackToolbar, this ); 667 }, 668 669 createStates: function() { 670 this.states.add([ 671 new media.controller.VideoDetails({ 672 media: this.media, 673 editable: false, 674 menu: 'video-details' 675 }), 676 677 new media.controller.MediaLibrary( { 678 type: 'video', 679 id: 'replace-video', 680 title: l10n.videoReplaceTitle, 681 toolbar: 'replace-video', 682 media: this.media, 683 menu: 'video-details' 684 } ), 685 686 new media.controller.MediaLibrary( { 687 type: 'video', 688 id: 'add-video-source', 689 title: l10n.videoAddSourceTitle, 690 toolbar: 'add-video-source', 691 media: this.media, 692 menu: false 693 } ), 694 695 new media.controller.MediaLibrary( { 696 type: 'image', 697 id: 'select-poster-image', 698 title: l10n.videoSelectPosterImageTitle, 699 toolbar: 'select-poster-image', 700 media: this.media, 701 menu: 'video-details' 702 } ), 703 704 new media.controller.MediaLibrary( { 705 type: 'text', 706 id: 'add-track', 707 title: l10n.videoAddTrackTitle, 708 toolbar: 'add-track', 709 media: this.media, 710 menu: 'video-details' 711 } ) 712 ]); 713 }, 714 715 renderSelectPosterImageToolbar: function() { 716 this.toolbar.set( new media.view.Toolbar({ 717 controller: this, 718 items: { 719 replace: { 720 style: 'primary', 721 text: l10n.videoSelectPosterImageTitle, 722 priority: 80, 723 724 click: function() { 725 var controller = this.controller, 726 state = controller.state(), 727 selection = state.get( 'selection' ), 728 attachment = selection.single(); 729 730 controller.media.set( 'poster', attachment.get( 'url' ) ); 731 732 state.trigger( 'set-poster-image', controller.media.toJSON() ); 733 734 // Restore and reset the default state. 735 controller.setState( controller.options.state ); 736 controller.reset(); 737 } 738 } 739 } 740 }) ); 741 }, 742 743 renderAddTrackToolbar: function() { 744 this.toolbar.set( new media.view.Toolbar({ 745 controller: this, 746 items: { 747 replace: { 748 style: 'primary', 749 text: l10n.videoAddTrackTitle, 750 priority: 80, 751 752 click: function() { 753 var controller = this.controller, 754 state = controller.state(), 755 selection = state.get( 'selection' ), 756 attachment = selection.single(), 757 content = controller.media.get( 'content' ); 758 759 if ( -1 === content.indexOf( attachment.get( 'url' ) ) ) { 760 content += [ 761 '<track srclang="en" label="English"kind="subtitles" src="', 762 attachment.get( 'url' ), 763 '" />' 764 ].join(''); 765 766 controller.media.set( 'content', content ); 767 } 768 769 state.trigger( 'add-track', controller.media.toJSON() ); 770 771 // Restore and reset the default state. 772 controller.setState( controller.options.state ); 773 controller.reset(); 774 } 775 } 776 } 777 }) ); 778 } 779 }); 780 781 /** 782 * wp.media.view.AudioDetails 783 * 784 * @contructor 785 * @augments wp.media.view.MediaDetails 786 * @augments wp.media.view.Settings.AttachmentDisplay 787 * @augments wp.media.view.Settings 788 * @augments wp.media.View 789 * @augments wp.Backbone.View 790 * @augments Backbone.View 791 */ 792 media.view.AudioDetails = media.view.MediaDetails.extend({ 793 className: 'audio-details', 794 template: media.template('audio-details'), 795 796 setMedia: function() { 797 var audio = this.$('.wp-audio-shortcode'); 798 799 if ( audio.find( 'source' ).length ) { 800 if ( audio.is(':hidden') ) { 801 audio.show(); 802 } 803 this.media = media.view.MediaDetails.prepareSrc( audio.get(0) ); 804 } else { 805 audio.hide(); 806 this.media = false; 807 } 808 809 return this; 810 } 811 }); 812 813 /** 814 * wp.media.view.VideoDetails 815 * 816 * @contructor 817 * @augments wp.media.view.MediaDetails 818 * @augments wp.media.view.Settings.AttachmentDisplay 819 * @augments wp.media.view.Settings 820 * @augments wp.media.View 821 * @augments wp.Backbone.View 822 * @augments Backbone.View 823 */ 824 media.view.VideoDetails = media.view.MediaDetails.extend({ 825 className: 'video-details', 826 template: media.template('video-details'), 827 828 setMedia: function() { 829 var video = this.$('.wp-video-shortcode'); 830 831 if ( video.find( 'source' ).length ) { 832 if ( video.is(':hidden') ) { 833 video.show(); 834 } 835 836 if ( ! video.hasClass('youtube-video') ) { 837 this.media = media.view.MediaDetails.prepareSrc( video.get(0) ); 838 } else { 839 this.media = video.get(0); 840 } 841 } else { 842 video.hide(); 843 this.media = false; 844 } 845 846 return this; 847 } 848 }); 849 850 _.extend( wp.media.playlist, { 851 /** 852 * Determine how many audio and video files the user has uploaded 853 */ 854 counts : (function (settings) { 855 var counts = {}; 856 857 return function () { 858 if ( ! _.isEmpty( counts ) ) { 859 return counts; 860 } 861 862 var a = 0, v = 0; 863 _.each( settings.attachmentCounts, function (total, mime) { 864 var type; 865 if ( -1 < mime.indexOf('/') ) { 866 type = mime.split('/')[0]; 867 868 total = parseInt(total, 10); 869 870 switch ( type ) { 871 case 'audio': 872 a += total; 873 break; 874 case 'video': 875 v += total; 876 break; 877 } 878 } 879 } ); 880 881 counts.audio = a; 882 counts.video = v; 883 884 return counts; 885 }; 886 }(media.view.settings)), 887 888 /** 889 * Return the playlist states for MediaFrame.Post 890 * 891 * @param {Object} options 892 * @returns {Array} 893 */ 894 states : function (options) { 895 return [ 896 new media.controller.Library({ 897 id: 'playlist', 898 title: l10n.createPlaylistTitle, 899 priority: 60, 900 toolbar: 'main-playlist', 901 filterable: 'uploaded', 902 multiple: 'add', 903 editable: false, 904 905 library: media.query( _.defaults({ 906 type: 'audio' 907 }, options.library ) ) 908 }), 909 910 // Playlist states. 911 new media.controller.CollectionEdit({ 912 type: 'audio', 913 collectionType: 'playlist', 914 title: l10n.editPlaylistTitle, 915 SettingsView: media.view.Settings.Playlist, 916 library: options.selection, 917 editing: options.editing, 918 menu: 'playlist', 919 dragInfoText: l10n.playlistDragInfo, 920 dragInfo: false 921 }), 922 923 new media.controller.CollectionAdd({ 924 type: 'audio', 925 collectionType: 'playlist', 926 title: l10n.addToPlaylistTitle 927 }) 928 ]; 929 }, 930 931 /** 932 * Return the video-playlist states for MediaFrame.Post 933 * 934 * @param {Object} options 935 * @returns {Array} 936 */ 937 videoStates : function (options) { 938 return [ 939 new media.controller.Library({ 940 id: 'video-playlist', 941 title: l10n.createVideoPlaylistTitle, 942 priority: 60, 943 toolbar: 'main-video-playlist', 944 filterable: 'uploaded', 945 multiple: 'add', 946 editable: false, 947 948 library: media.query( _.defaults({ 949 type: 'video' 950 }, options.library ) ) 951 }), 952 953 // Video Playlist states. 954 new media.controller.CollectionEdit({ 955 type: 'video', 956 collectionType: 'video-playlist', 957 title: l10n.editVideoPlaylistTitle, 958 SettingsView: media.view.Settings.Playlist, 959 library: options.selection, 960 editing: options.editing, 961 menu: 'video-playlist', 962 dragInfoText: l10n.videoPlaylistDragInfo, 963 dragInfo: false 964 }), 965 966 new media.controller.CollectionAdd({ 967 type: 'video', 968 collectionType: 'video-playlist', 969 title: l10n.addToVideoPlaylistTitle 970 }) 971 ]; 972 } 973 } ); 974 975 function init() { 976 $(document.body) 977 .on( 'click', '.wp-switch-editor', wp.media.mixin.pauseAllPlayers ) 978 .on( 'click', '.add-media-source', function () { 979 media.frame.setState('add-' + media.frame.defaults.id + '-source'); 980 } ); 981 } 982 983 $( init ); 984 985 }(jQuery, _, Backbone)); 986 No newline at end of file -
src/wp-includes/js/media-editor.js
11 11 var workflows = {}; 12 12 13 13 /** 14 * A helper mixin function to avoid truthy and falsey values being 15 * passed as an input that expects booleans. If key is undefined in the map, 16 * but has a default value, set it. 17 * 18 * @param {object} attrs Map of props from a shortcode or settings. 19 * @param {string} key The key within the passed map to check for a value. 20 * @returns {mixed|undefined} The original or coerced value of key within attrs 21 */ 22 wp.media.coerce = function ( attrs, key ) { 23 if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) { 24 attrs[ key ] = this.defaults[ key ]; 25 } else if ( 'true' === attrs[ key ] ) { 26 attrs[ key ] = true; 27 } else if ( 'false' === attrs[ key ] ) { 28 attrs[ key ] = false; 29 } 30 return attrs[ key ]; 31 }; 32 33 /** 14 34 * wp.media.string 15 35 * @namespace 16 36 */ … … 274 294 } 275 295 }; 276 296 277 /**278 * @mixin279 */280 wp.media.mixin = {281 /**282 * A helper function to avoid truthy and falsey values being283 * passed as an input that expects booleans. If key is undefined in the map,284 * but has a default value, set it.285 *286 * @param {object} attrs Map of props from a shortcode or settings.287 * @param {string} key The key within the passed map to check for a value.288 * @returns {mixed|undefined} The original or coerced value of key within attrs289 */290 coerce: function ( attrs, key ) {291 if ( _.isUndefined( attrs[ key ] ) && ! _.isUndefined( this.defaults[ key ] ) ) {292 attrs[ key ] = this.defaults[ key ];293 } else if ( 'true' === attrs[ key ] ) {294 attrs[ key ] = true;295 } else if ( 'false' === attrs[ key ] ) {296 attrs[ key ] = false;297 }298 return attrs[ key ];299 },300 301 pauseAllPlayers: function () {302 var p;303 if ( window.mejs && window.mejs.players ) {304 for ( p in window.mejs.players ) {305 window.mejs.players[p].pause();306 }307 }308 },309 310 ua: {311 is : function (browser) {312 var passes = false, ua = window.navigator.userAgent;313 314 switch ( browser ) {315 case 'oldie':316 passes = ua.match(/MSIE [6-8]/gi) !== null;317 break;318 case 'ie':319 passes = ua.match(/MSIE/gi) !== null;320 break;321 case 'ff':322 passes = ua.match(/firefox/gi) !== null;323 break;324 case 'opera':325 passes = ua.match(/OPR/) !== null;326 break;327 case 'safari':328 passes = ua.match(/safari/gi) !== null && ua.match(/chrome/gi) === null;329 break;330 case 'chrome':331 passes = ua.match(/safari/gi) && ua.match(/chrome/gi) !== null;332 break;333 }334 335 return passes;336 }337 },338 339 compat :{340 'opera' : {341 audio: ['ogg', 'wav'],342 video: ['ogg', 'webm']343 },344 'chrome' : {345 audio: ['ogg', 'mpeg', 'x-ms-wma'],346 video: ['ogg', 'webm', 'mp4', 'm4v', 'mpeg']347 },348 'ff' : {349 audio: ['ogg', 'mpeg'],350 video: ['ogg', 'webm']351 },352 'safari' : {353 audio: ['mpeg', 'wav'],354 video: ['mp4', 'm4v', 'mpeg', 'x-ms-wmv', 'quicktime']355 },356 'ie' : {357 audio: ['mpeg'],358 video: ['mp4', 'm4v', 'mpeg']359 }360 },361 362 isCompatible: function ( media ) {363 if ( ! media.find( 'source' ).length ) {364 return false;365 }366 367 var ua = this.ua, test = false, found = false, sources;368 369 if ( ua.is( 'oldIE' ) ) {370 return false;371 }372 373 sources = media.find( 'source' );374 375 _.find( this.compat, function (supports, browser) {376 if ( ua.is( browser ) ) {377 found = true;378 _.each( sources, function (elem) {379 var audio = new RegExp( 'audio\/(' + supports.audio.join('|') + ')', 'gi' ),380 video = new RegExp( 'video\/(' + supports.video.join('|') + ')', 'gi' );381 382 if ( elem.type.match( video ) !== null || elem.type.match( audio ) !== null ) {383 test = true;384 }385 } );386 }387 388 return test || found;389 } );390 391 return test;392 },393 394 /**395 * Override the MediaElement method for removing a player.396 * MediaElement tries to pull the audio/video tag out of397 * its container and re-add it to the DOM.398 */399 removePlayer: function() {400 var t = this.player, featureIndex, feature;401 402 // invoke features cleanup403 for ( featureIndex in t.options.features ) {404 feature = t.options.features[featureIndex];405 if ( t['clean' + feature] ) {406 try {407 t['clean' + feature](t);408 } catch (e) {}409 }410 }411 412 if ( ! t.isDynamic ) {413 t.$node.remove();414 }415 416 if ( 'native' !== t.media.pluginType ) {417 t.media.remove();418 }419 420 delete window.mejs.players[t.id];421 422 t.container.remove();423 t.globalUnbind();424 delete t.node.player;425 },426 427 /**428 * Allows any class that has set 'player' to a MediaElementPlayer429 * instance to remove the player when listening to events.430 *431 * Examples: modal closes, shortcode properties are removed, etc.432 */433 unsetPlayer : function() {434 if ( this.player ) {435 wp.media.mixin.pauseAllPlayers();436 wp.media.mixin.removePlayer.apply( this );437 this.player = false;438 }439 }440 };441 442 297 wp.media.collection = function(attributes) { 443 298 var collections = {}; 444 299 445 300 return _.extend( attributes, { 446 coerce : wp.media. mixin.coerce,301 coerce : wp.media.coerce, 447 302 /** 448 303 * Retrieve attachments based on the properties of the passed shortcode 449 304 * … … 669 524 } 670 525 }); 671 526 672 wp.media.playlist = new wp.media.collection({673 tag: 'playlist',674 type : 'audio',675 editTitle : wp.media.view.l10n.editPlaylistTitle,676 defaults : {677 id: wp.media.view.settings.post.id,678 style: 'light',679 tracklist: true,680 tracknumbers: true,681 images: true,682 artists: true683 }684 });685 686 wp.media['video-playlist'] = new wp.media.collection({687 tag: 'video-playlist',688 type : 'video',689 editTitle : wp.media.view.l10n.editVideoPlaylistTitle,690 defaults : {691 id: wp.media.view.settings.post.id,692 style: 'light',693 tracklist: false,694 tracknumbers: false,695 images: true696 }697 });698 699 527 /** 700 * @namespace701 */702 wp.media.audio = {703 coerce : wp.media.mixin.coerce,704 705 defaults : {706 id : wp.media.view.settings.post.id,707 src : '',708 loop : false,709 autoplay : false,710 preload : 'none'711 },712 713 edit : function (data) {714 var frame, shortcode = wp.shortcode.next( 'audio', data ).shortcode;715 frame = wp.media({716 frame: 'audio',717 state: 'audio-details',718 metadata: _.defaults(719 shortcode.attrs.named,720 wp.media.audio.defaults721 )722 });723 724 return frame;725 },726 727 shortcode : function (shortcode) {728 var self = this;729 730 _.each( wp.media.audio.defaults, function( value, key ) {731 shortcode[ key ] = self.coerce( shortcode, key );732 733 if ( value === shortcode[ key ] ) {734 delete shortcode[ key ];735 }736 });737 738 return wp.shortcode.string({739 tag: 'audio',740 attrs: shortcode741 });742 }743 };744 745 /**746 * @namespace747 */748 wp.media.video = {749 coerce : wp.media.mixin.coerce,750 751 defaults : {752 id : wp.media.view.settings.post.id,753 src : '',754 poster : '',755 loop : false,756 autoplay : false,757 preload : 'metadata',758 content : ''759 },760 761 edit : function (data) {762 var frame,763 defaults = this.defaults,764 shortcode = wp.shortcode.next( 'video', data ).shortcode,765 attrs;766 767 attrs = shortcode.attrs.named;768 attrs.content = shortcode.content;769 770 frame = wp.media({771 frame: 'video',772 state: 'video-details',773 metadata: _.defaults( attrs, defaults )774 });775 776 return frame;777 },778 779 shortcode : function (shortcode) {780 var self = this, content = shortcode.content;781 delete shortcode.content;782 783 _.each( this.defaults, function( value, key ) {784 shortcode[ key ] = self.coerce( shortcode, key );785 786 if ( value === shortcode[ key ] ) {787 delete shortcode[ key ];788 }789 });790 791 return wp.shortcode.string({792 tag: 'video',793 attrs: shortcode,794 content: content795 });796 }797 };798 799 /**800 528 * wp.media.featuredImage 801 529 * @namespace 802 530 */ … … 1279 1007 if ( elem.hasClass( 'gallery' ) ) { 1280 1008 options.state = 'gallery'; 1281 1009 options.title = wp.media.view.l10n.createGalleryTitle; 1282 } else if ( elem.hasClass( 'playlist' ) ) {1283 options.state = 'playlist';1284 options.title = wp.media.view.l10n.createPlaylistTitle;1285 } else if ( elem.hasClass( 'video-playlist' ) ) {1286 options.state = 'video-playlist';1287 options.title = wp.media.view.l10n.createVideoPlaylistTitle;1288 1010 } 1289 1011 1290 1012 wp.media.editor.open( editor, options ); 1291 }) 1292 .on( 'click', '.wp-switch-editor', wp.media.mixin.pauseAllPlayers ); 1013 }); 1293 1014 1294 1015 // Initialize and render the Editor drag-and-drop uploader. 1295 1016 new wp.media.view.EditorUploader().render(); -
src/wp-includes/js/media-models.js
40 40 41 41 delete attributes.frame; 42 42 43 media.frame = frame; 44 43 45 return frame; 44 46 }; 45 47 … … 453 455 }); 454 456 455 457 /** 456 * wp.media.model.PostMedia457 *458 * @constructor459 * @augments Backbone.Model460 **/461 media.model.PostMedia = Backbone.Model.extend({462 initialize: function() {463 this.attachment = false;464 },465 466 setSource: function ( attachment ) {467 this.attachment = attachment;468 this.extension = attachment.get('filename' ).split('.').pop();469 470 if ( this.get( 'src' ) && this.extension === this.get( 'src' ).split('.').pop() ) {471 this.unset( 'src' );472 }473 474 if ( _.contains( wp.media.view.settings.embedExts, this.extension ) ) {475 this.set( this.extension, this.attachment.get( 'url' ) );476 } else {477 this.unset( this.extension );478 }479 },480 481 changeAttachment: function( attachment ) {482 var self = this;483 484 this.setSource( attachment );485 486 this.unset( 'src' );487 _.each( _.without( wp.media.view.settings.embedExts, this.extension ), function (ext) {488 self.unset( ext );489 } );490 }491 });492 493 /**494 458 * wp.media.model.Attachments 495 459 * 496 460 * @constructor -
src/wp-includes/js/media-views.js
766 766 }); 767 767 768 768 /** 769 * wp.media.controller.AudioDetails770 *771 * @constructor772 * @augments wp.media.controller.State773 * @augments Backbone.Model774 */775 media.controller.AudioDetails = media.controller.State.extend({776 defaults: _.defaults({777 id: 'audio-details',778 toolbar: 'audio-details',779 title: l10n.audioDetailsTitle,780 content: 'audio-details',781 menu: 'audio-details',782 router: false,783 attachment: false,784 priority: 60,785 editing: false786 }, media.controller.Library.prototype.defaults ),787 788 initialize: function( options ) {789 this.media = options.media;790 media.controller.State.prototype.initialize.apply( this, arguments );791 }792 });793 794 /**795 * wp.media.controller.VideoDetails796 *797 * @constructor798 * @augments wp.media.controller.State799 * @augments Backbone.Model800 */801 media.controller.VideoDetails = media.controller.State.extend({802 defaults: _.defaults({803 id: 'video-details',804 toolbar: 'video-details',805 title: l10n.videoDetailsTitle,806 content: 'video-details',807 menu: 'video-details',808 router: false,809 attachment: false,810 priority: 60,811 editing: false812 }, media.controller.Library.prototype.defaults ),813 814 initialize: function( options ) {815 this.media = options.media;816 media.controller.State.prototype.initialize.apply( this, arguments );817 }818 });819 820 /**821 769 * wp.media.controller.CollectionEdit 822 770 * 823 771 * @constructor … … 1930 1878 }, 1931 1879 1932 1880 createStates: function() { 1933 var options = this.options ;1881 var options = this.options, counts; 1934 1882 1935 1883 // Add the default states. 1936 1884 this.states.add([ … … 1990 1938 type: 'image', 1991 1939 collectionType: 'gallery', 1992 1940 title: l10n.addToGalleryTitle 1993 }), 1941 }) 1942 ]); 1994 1943 1995 new media.controller.Library({ 1996 id: 'playlist', 1997 title: l10n.createPlaylistTitle, 1998 priority: 60, 1999 toolbar: 'main-playlist', 2000 filterable: 'uploaded', 2001 multiple: 'add', 2002 editable: false, 1944 counts = media.playlist.counts(); 2003 1945 2004 library: media.query( _.defaults({ 2005 type: 'audio' 2006 }, options.library ) ) 2007 }), 1946 if ( counts.audio ) { 1947 this.states.add( media.playlist.states(options) ); 1948 } 2008 1949 2009 // Playlist states. 2010 new media.controller.CollectionEdit({ 2011 type: 'audio', 2012 collectionType: 'playlist', 2013 title: l10n.editPlaylistTitle, 2014 SettingsView: media.view.Settings.Playlist, 2015 library: options.selection, 2016 editing: options.editing, 2017 menu: 'playlist', 2018 dragInfoText: l10n.playlistDragInfo, 2019 dragInfo: false 2020 }), 1950 if ( counts.video ) { 1951 this.states.add( media.playlist.videoStates(options) ); 1952 } 2021 1953 2022 new media.controller.CollectionAdd({2023 type: 'audio',2024 collectionType: 'playlist',2025 title: l10n.addToPlaylistTitle2026 }),2027 2028 new media.controller.Library({2029 id: 'video-playlist',2030 title: l10n.createVideoPlaylistTitle,2031 priority: 60,2032 toolbar: 'main-video-playlist',2033 filterable: 'uploaded',2034 multiple: 'add',2035 editable: false,2036 2037 library: media.query( _.defaults({2038 type: 'video'2039 }, options.library ) )2040 }),2041 2042 // Video Playlist states.2043 new media.controller.CollectionEdit({2044 type: 'video',2045 collectionType: 'video-playlist',2046 title: l10n.editVideoPlaylistTitle,2047 SettingsView: media.view.Settings.Playlist,2048 library: options.selection,2049 editing: options.editing,2050 menu: 'video-playlist',2051 dragInfoText: l10n.videoPlaylistDragInfo,2052 dragInfo: false2053 }),2054 2055 new media.controller.CollectionAdd({2056 type: 'video',2057 collectionType: 'video-playlist',2058 title: l10n.addToVideoPlaylistTitle2059 })2060 ]);2061 2062 2063 1954 if ( media.view.settings.post.featuredImageId ) { 2064 1955 this.states.add( new media.controller.FeaturedImage() ); 2065 1956 } … … 2746 2637 }); 2747 2638 2748 2639 /** 2749 * wp.media.view.MediaFrame.MediaDetails2750 *2751 * @constructor2752 * @augments wp.media.view.MediaFrame.Select2753 * @augments wp.media.view.MediaFrame2754 * @augments wp.media.view.Frame2755 * @augments wp.media.View2756 * @augments wp.Backbone.View2757 * @augments Backbone.View2758 * @mixes wp.media.controller.StateMachine2759 */2760 media.view.MediaFrame.MediaDetails = media.view.MediaFrame.Select.extend({2761 defaults: {2762 id: 'media',2763 url: '',2764 menu: 'media-details',2765 content: 'media-details',2766 toolbar: 'media-details',2767 type: 'link',2768 priority: 1202769 },2770 2771 initialize: function( options ) {2772 this.DetailsView = options.DetailsView;2773 this.cancelText = options.cancelText;2774 this.addText = options.addText;2775 2776 this.media = new media.model.PostMedia( options.metadata );2777 this.options.selection = new media.model.Selection( this.media.attachment, { multiple: false } );2778 media.view.MediaFrame.Select.prototype.initialize.apply( this, arguments );2779 },2780 2781 bindHandlers: function() {2782 var menu = this.defaults.menu;2783 2784 media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );2785 2786 this.on( 'menu:create:' + menu, this.createMenu, this );2787 this.on( 'content:render:' + menu, this.renderDetailsContent, this );2788 this.on( 'menu:render:' + menu, this.renderMenu, this );2789 this.on( 'toolbar:render:' + menu, this.renderDetailsToolbar, this );2790 },2791 2792 renderDetailsContent: function() {2793 var view = new this.DetailsView({2794 controller: this,2795 model: this.state().media,2796 attachment: this.state().media.attachment2797 }).render();2798 2799 this.content.set( view );2800 },2801 2802 renderMenu: function( view ) {2803 var lastState = this.lastState(),2804 previous = lastState && lastState.id,2805 frame = this;2806 2807 view.set({2808 cancel: {2809 text: this.cancelText,2810 priority: 20,2811 click: function() {2812 if ( previous ) {2813 frame.setState( previous );2814 } else {2815 frame.close();2816 }2817 }2818 },2819 separateCancel: new media.View({2820 className: 'separator',2821 priority: 402822 })2823 });2824 2825 },2826 2827 renderDetailsToolbar: function() {2828 this.toolbar.set( new media.view.Toolbar({2829 controller: this,2830 items: {2831 select: {2832 style: 'primary',2833 text: l10n.update,2834 priority: 80,2835 2836 click: function() {2837 var controller = this.controller,2838 state = controller.state();2839 2840 controller.close();2841 2842 state.trigger( 'update', controller.media.toJSON() );2843 2844 // Restore and reset the default state.2845 controller.setState( controller.options.state );2846 controller.reset();2847 }2848 }2849 }2850 }) );2851 },2852 2853 renderReplaceToolbar: function() {2854 this.toolbar.set( new media.view.Toolbar({2855 controller: this,2856 items: {2857 replace: {2858 style: 'primary',2859 text: l10n.replace,2860 priority: 80,2861 2862 click: function() {2863 var controller = this.controller,2864 state = controller.state(),2865 selection = state.get( 'selection' ),2866 attachment = selection.single();2867 2868 controller.media.changeAttachment( attachment );2869 2870 state.trigger( 'replace', controller.media.toJSON() );2871 2872 // Restore and reset the default state.2873 controller.setState( controller.options.state );2874 controller.reset();2875 }2876 }2877 }2878 }) );2879 },2880 2881 renderAddSourceToolbar: function() {2882 this.toolbar.set( new media.view.Toolbar({2883 controller: this,2884 items: {2885 replace: {2886 style: 'primary',2887 text: this.addText,2888 priority: 80,2889 2890 click: function() {2891 var controller = this.controller,2892 state = controller.state(),2893 selection = state.get( 'selection' ),2894 attachment = selection.single();2895 2896 controller.media.setSource( attachment );2897 2898 state.trigger( 'add-source', controller.media.toJSON() );2899 2900 // Restore and reset the default state.2901 controller.setState( controller.options.state );2902 controller.reset();2903 }2904 }2905 }2906 }) );2907 }2908 });2909 2910 /**2911 * wp.media.view.MediaFrame.AudioDetails2912 *2913 * @constructor2914 * @augments wp.media.view.MediaFrame.MediaDetails2915 * @augments wp.media.view.MediaFrame.Select2916 * @augments wp.media.view.MediaFrame2917 * @augments wp.media.view.Frame2918 * @augments wp.media.View2919 * @augments wp.Backbone.View2920 * @augments Backbone.View2921 * @mixes wp.media.controller.StateMachine2922 */2923 media.view.MediaFrame.AudioDetails = media.view.MediaFrame.MediaDetails.extend({2924 defaults: {2925 id: 'audio',2926 url: '',2927 menu: 'audio-details',2928 content: 'audio-details',2929 toolbar: 'audio-details',2930 type: 'link',2931 title: l10n.audioDetailsTitle,2932 priority: 1202933 },2934 2935 initialize: function( options ) {2936 options.DetailsView = media.view.AudioDetails;2937 options.cancelText = l10n.audioDetailsCancel;2938 options.addText = l10n.audioAddSourceTitle;2939 2940 media.view.MediaFrame.MediaDetails.prototype.initialize.call( this, options );2941 },2942 2943 bindHandlers: function() {2944 media.view.MediaFrame.MediaDetails.prototype.bindHandlers.apply( this, arguments );2945 2946 this.on( 'toolbar:render:replace-audio', this.renderReplaceToolbar, this );2947 this.on( 'toolbar:render:add-audio-source', this.renderAddSourceToolbar, this );2948 },2949 2950 createStates: function() {2951 this.states.add([2952 new media.controller.AudioDetails( {2953 media: this.media,2954 editable: false,2955 menu: 'audio-details'2956 } ),2957 2958 new media.controller.MediaLibrary( {2959 type: 'audio',2960 id: 'replace-audio',2961 title: l10n.audioReplaceTitle,2962 toolbar: 'replace-audio',2963 media: this.media,2964 menu: 'audio-details'2965 } ),2966 2967 new media.controller.MediaLibrary( {2968 type: 'audio',2969 id: 'add-audio-source',2970 title: l10n.audioAddSourceTitle,2971 toolbar: 'add-audio-source',2972 media: this.media,2973 menu: 'audio-details'2974 } )2975 ]);2976 }2977 });2978 2979 /**2980 * wp.media.view.MediaFrame.VideoDetails2981 *2982 * @constructor2983 * @augments wp.media.view.MediaFrame.MediaDetails2984 * @augments wp.media.view.MediaFrame.Select2985 * @augments wp.media.view.MediaFrame2986 * @augments wp.media.view.Frame2987 * @augments wp.media.View2988 * @augments wp.Backbone.View2989 * @augments Backbone.View2990 * @mixes wp.media.controller.StateMachine2991 */2992 media.view.MediaFrame.VideoDetails = media.view.MediaFrame.MediaDetails.extend({2993 defaults: {2994 id: 'video',2995 url: '',2996 menu: 'video-details',2997 content: 'video-details',2998 toolbar: 'video-details',2999 type: 'link',3000 title: l10n.videoDetailsTitle,3001 priority: 1203002 },3003 3004 initialize: function( options ) {3005 options.DetailsView = media.view.VideoDetails;3006 options.cancelText = l10n.videoDetailsCancel;3007 options.addText = l10n.videoAddSourceTitle;3008 3009 media.view.MediaFrame.MediaDetails.prototype.initialize.call( this, options );3010 },3011 3012 bindHandlers: function() {3013 media.view.MediaFrame.MediaDetails.prototype.bindHandlers.apply( this, arguments );3014 3015 this.on( 'toolbar:render:replace-video', this.renderReplaceToolbar, this );3016 this.on( 'toolbar:render:add-video-source', this.renderAddSourceToolbar, this );3017 this.on( 'toolbar:render:select-poster-image', this.renderSelectPosterImageToolbar, this );3018 this.on( 'toolbar:render:add-track', this.renderAddTrackToolbar, this );3019 },3020 3021 createStates: function() {3022 this.states.add([3023 new media.controller.VideoDetails({3024 media: this.media,3025 editable: false,3026 menu: 'video-details'3027 }),3028 3029 new media.controller.MediaLibrary( {3030 type: 'video',3031 id: 'replace-video',3032 title: l10n.videoReplaceTitle,3033 toolbar: 'replace-video',3034 media: this.media,3035 menu: 'video-details'3036 } ),3037 3038 new media.controller.MediaLibrary( {3039 type: 'video',3040 id: 'add-video-source',3041 title: l10n.videoAddSourceTitle,3042 toolbar: 'add-video-source',3043 media: this.media,3044 menu: 'video-details'3045 } ),3046 3047 new media.controller.MediaLibrary( {3048 type: 'image',3049 id: 'select-poster-image',3050 title: l10n.videoSelectPosterImageTitle,3051 toolbar: 'select-poster-image',3052 media: this.media,3053 menu: 'video-details'3054 } ),3055 3056 new media.controller.MediaLibrary( {3057 type: 'text',3058 id: 'add-track',3059 title: l10n.videoAddTrackTitle,3060 toolbar: 'add-track',3061 media: this.media,3062 menu: 'video-details'3063 } )3064 ]);3065 },3066 3067 renderSelectPosterImageToolbar: function() {3068 this.toolbar.set( new media.view.Toolbar({3069 controller: this,3070 items: {3071 replace: {3072 style: 'primary',3073 text: l10n.videoSelectPosterImageTitle,3074 priority: 80,3075 3076 click: function() {3077 var controller = this.controller,3078 state = controller.state(),3079 selection = state.get( 'selection' ),3080 attachment = selection.single();3081 3082 controller.media.set( 'poster', attachment.get( 'url' ) );3083 3084 state.trigger( 'set-poster-image', controller.media.toJSON() );3085 3086 // Restore and reset the default state.3087 controller.setState( controller.options.state );3088 controller.reset();3089 }3090 }3091 }3092 }) );3093 },3094 3095 renderAddTrackToolbar: function() {3096 this.toolbar.set( new media.view.Toolbar({3097 controller: this,3098 items: {3099 replace: {3100 style: 'primary',3101 text: l10n.videoAddTrackTitle,3102 priority: 80,3103 3104 click: function() {3105 var controller = this.controller,3106 state = controller.state(),3107 selection = state.get( 'selection' ),3108 attachment = selection.single(),3109 content = controller.media.get( 'content' );3110 3111 if ( -1 === content.indexOf( attachment.get( 'url' ) ) ) {3112 content += [3113 '<track srclang="en" label="English"kind="subtitles" src="',3114 attachment.get( 'url' ),3115 '" />'3116 ].join('');3117 3118 controller.media.set( 'content', content );3119 }3120 3121 state.trigger( 'add-track', controller.media.toJSON() );3122 3123 // Restore and reset the default state.3124 controller.setState( controller.options.state );3125 controller.reset();3126 }3127 }3128 }3129 }) );3130 }3131 });3132 3133 /**3134 2640 * wp.media.view.Modal 3135 2641 * 3136 2642 * @constructor … … 6663 6169 }); 6664 6170 6665 6171 /** 6666 * wp.media.view.AudioDetails6667 *6668 * @contructor6669 * @augments wp.media.view.MediaDetails6670 * @augments wp.media.view.Settings.AttachmentDisplay6671 * @augments wp.media.view.Settings6672 * @augments wp.media.View6673 * @augments wp.Backbone.View6674 * @augments Backbone.View6675 */6676 media.view.AudioDetails = media.view.MediaDetails.extend({6677 className: 'audio-details',6678 template: media.template('audio-details'),6679 6680 setMedia: function() {6681 var audio = this.$('.wp-audio-shortcode');6682 6683 if ( audio.find( 'source' ).length ) {6684 if ( audio.is(':hidden') ) {6685 audio.show();6686 }6687 this.media = media.view.MediaDetails.prepareSrc( audio.get(0) );6688 } else {6689 audio.hide();6690 this.media = false;6691 }6692 6693 return this;6694 }6695 });6696 6697 /**6698 * wp.media.view.VideoDetails6699 *6700 * @contructor6701 * @augments wp.media.view.MediaDetails6702 * @augments wp.media.view.Settings.AttachmentDisplay6703 * @augments wp.media.view.Settings6704 * @augments wp.media.View6705 * @augments wp.Backbone.View6706 * @augments Backbone.View6707 */6708 media.view.VideoDetails = media.view.MediaDetails.extend({6709 className: 'video-details',6710 template: media.template('video-details'),6711 6712 setMedia: function() {6713 var video = this.$('.wp-video-shortcode');6714 6715 if ( video.find( 'source' ).length ) {6716 if ( video.is(':hidden') ) {6717 video.show();6718 }6719 6720 if ( ! video.hasClass('youtube-video') ) {6721 this.media = media.view.MediaDetails.prepareSrc( video.get(0) );6722 } else {6723 this.media = video.get(0);6724 }6725 } else {6726 video.hide();6727 this.media = false;6728 }6729 6730 return this;6731 }6732 });6733 6734 /**6735 6172 * wp.media.view.Spinner 6736 6173 * 6737 6174 * @constructor -
src/wp-includes/js/mediaelement/wp-mediaelement.css
26 26 27 27 .media-embed-details .embed-media-settings { 28 28 padding-top: 0; 29 top: 28px; 29 30 } 30 31 31 32 .media-embed-details .instructions { … … 44 45 color: #f00; 45 46 } 46 47 47 .media-embed-details .embed-media-settings {48 top: 0;49 }50 51 48 .media-embed-details .embed-media-settings .checkbox-setting { 52 49 width: 100px; 53 50 clear: none; -
src/wp-includes/media-template.php
765 765 </script> 766 766 767 767 <script type="text/html" id="tmpl-audio-details"> 768 <# var ext, html5types = { mp3: true, ogg: true }; #> 769 768 770 <?php $audio_types = wp_get_audio_extensions(); ?> 769 771 <div class="media-embed media-embed-details"> 770 772 <div class="embed-media-settings embed-audio-settings"> 771 <div class="instructions media-instructions">{{{ wp.media.view.l10n.audioDetailsText }}}</div>772 773 773 <?php wp_underscore_audio_template() ?> 774 774 775 <# if ( ! _.isEmpty( data.model.src ) ) { #> 775 <# if ( ! _.isEmpty( data.model.src ) ) { 776 ext = data.model.src.split('.').pop(); 777 if ( html5types[ ext ] ) { 778 delete html5types[ ext ]; 779 } 780 #> 776 781 <label class="setting"> 777 782 <span>SRC</span> 778 783 <input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" /> … … 782 787 <?php 783 788 784 789 foreach ( $audio_types as $type ): 785 ?><# if ( ! _.isEmpty( data.model.<?php echo $type ?> ) ) { #> 790 ?><# if ( ! _.isEmpty( data.model.<?php echo $type ?> ) ) { 791 if ( ! _.isUndefined( html5types.<?php echo $type ?> ) ) { 792 delete html5types.<?php echo $type ?>; 793 } 794 #> 786 795 <label class="setting"> 787 796 <span><?php echo strtoupper( $type ) ?></span> 788 797 <input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" /> … … 791 800 <# } #> 792 801 <?php endforeach ?> 793 802 803 <# if ( ! _.isEmpty( html5types ) ) { #> 804 <div class="setting"> 805 <span>{{{ wp.media.view.l10n.mediaHTML5Text }}}</span> 806 <div class="button-large"> 807 <# _.each( html5types, function (value, type) { #> 808 <button class="button add-media-source">{{ type }}</button> 809 <# } ) #> 810 </div> 811 </div> 812 <# } #> 813 794 814 <div class="setting preload"> 795 815 <span><?php _e( 'Preload' ); ?></span> 796 816 <div class="button-group button-large" data-setting="preload"> … … 815 835 </script> 816 836 817 837 <script type="text/html" id="tmpl-video-details"> 838 <# var ext, html5types = { mp4: true, ogv: true, webm: true }; #> 839 818 840 <?php $video_types = wp_get_video_extensions(); ?> 819 841 <div class="media-embed media-embed-details"> 820 842 <div class="embed-media-settings embed-video-settings"> 821 <div class="instructions media-instructions">{{{ wp.media.view.l10n.videoDetailsText }}}</div>822 843 <div class="wp-video-holder"> 823 844 <# 824 845 var isYouTube = ! _.isEmpty( data.model.src ) && data.model.src.match(/youtube|youtu\.be/); … … 832 853 833 854 <?php wp_underscore_video_template() ?> 834 855 835 <# if ( ! _.isEmpty( data.model.src ) ) { #> 856 <# if ( ! _.isEmpty( data.model.src ) ) { 857 ext = data.model.src.split('.').pop(); 858 if ( html5types[ ext ] ) { 859 delete html5types[ ext ]; 860 } 861 #> 836 862 <label class="setting"> 837 863 <span>SRC</span> 838 864 <input type="text" disabled="disabled" data-setting="src" value="{{ data.model.src }}" /> … … 840 866 </label> 841 867 <# } #> 842 868 <?php foreach ( $video_types as $type ): 843 ?><# if ( ! _.isEmpty( data.model.<?php echo $type ?> ) ) { #> 869 ?><# if ( ! _.isEmpty( data.model.<?php echo $type ?> ) ) { 870 if ( ! _.isUndefined( html5types.<?php echo $type ?> ) ) { 871 delete html5types.<?php echo $type ?>; 872 } 873 #> 844 874 <label class="setting"> 845 875 <span><?php echo strtoupper( $type ) ?></span> 846 876 <input type="text" disabled="disabled" data-setting="<?php echo $type ?>" value="{{ data.model.<?php echo $type ?> }}" /> … … 849 879 <# } #> 850 880 <?php endforeach ?> 851 881 </div> 882 883 <# if ( ! _.isEmpty( html5types ) ) { #> 884 <div class="setting"> 885 <span>{{{ wp.media.view.l10n.mediaHTML5Text }}}</span> 886 <div class="button-large"> 887 <# _.each( html5types, function (value, type) { #> 888 <button class="button add-media-source">{{ type }}</button> 889 <# } ) #> 890 </div> 891 </div> 892 <# } #> 893 852 894 <# if ( ! _.isEmpty( data.model.poster ) ) { #> 853 895 <label class="setting"> 854 896 <span><?php _e( 'Poster Image' ); ?></span> -
src/wp-includes/media.php
2400 2400 'id' => 0, 2401 2401 ), 2402 2402 'defaultProps' => $props, 2403 'attachmentCounts' => wp_count_attachments(), 2403 2404 'embedExts' => $exts, 2404 2405 'embedMimes' => $ext_mimes 2405 2406 ); … … 2495 2496 /* translators: suggested height of header image in pixels */ 2496 2497 'suggestedHeight' => __( 'Suggested height is %d pixels.' ), 2497 2498 2499 'mediaHTML5Text' => __( 'Add alternate sources for maximum HTML5 playback:' ), 2500 2498 2501 // Edit Audio 2499 2502 'audioDetailsTitle' => __( 'Audio Details' ), 2500 2503 'audioReplaceTitle' => __( 'Replace Audio' ), 2501 2504 'audioAddSourceTitle' => __( 'Add Audio Source' ), 2502 2505 'audioDetailsCancel' => __( 'Cancel Edit' ), 2503 'audioDetailsText' => __( '"Replace Audio" will remove all associated source files when you update. ' .2504 '"Add Audio Source" allows you to specify alternate sources for maximum native HTML5 audio playback.' ),2505 2506 2506 2507 // Edit Video 2507 2508 'videoDetailsTitle' => __( 'Video Details' ), … … 2508 2509 'videoReplaceTitle' => __( 'Replace Video' ), 2509 2510 'videoAddSourceTitle' => __( 'Add Video Source' ), 2510 2511 'videoDetailsCancel' => __( 'Cancel Edit' ), 2511 'videoDetailsText' => __( '"Replace Video" will remove all associated source files when you update. ' .2512 '"Add Video Source" allows you to specify alternate sources for maximum native HTML5 video playback.' ),2513 2512 'videoSelectPosterImageTitle' => _( 'Select Poster Image' ), 2514 2513 'videoAddTrackTitle' => __( 'Add Subtitles' ), 2515 2514 … … 2542 2541 wp_localize_script( 'media-views', '_wpMediaViewsL10n', $strings ); 2543 2542 2544 2543 wp_enqueue_script( 'media-editor' ); 2544 wp_enqueue_script( 'media-audiovideo' ); 2545 2545 wp_enqueue_style( 'media-views' ); 2546 2546 wp_enqueue_style( 'imgareaselect' ); 2547 2547 wp_plupload_default_settings(); -
src/wp-includes/script-loader.php
395 395 // Both rely on numerous settings, styles, and templates to operate correctly. 396 396 $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement', 'image-edit' ), false, 1 ); 397 397 $scripts->add( 'media-editor', "/wp-includes/js/media-editor$suffix.js", array( 'shortcode', 'media-views' ), false, 1 ); 398 $scripts->add( 'media-audiovideo', "/wp-includes/js/media-audiovideo$suffix.js", array( 'media-editor' ), false, 1 ); 398 399 $scripts->add( 'mce-view', "/wp-includes/js/mce-view$suffix.js", array( 'shortcode', 'media-models' ), false, 1 ); 399 400 400 401 if ( is_admin() ) {