diff --git a/src/wp-includes/js/mce-view.js b/src/wp-includes/js/mce-view.js index 73e33c9cfb..34dc0d2a61 100644 --- a/src/wp-includes/js/mce-view.js +++ b/src/wp-includes/js/mce-view.js @@ -317,4 +317,362 @@ window.wp = window.wp || {}; }; wp.mce.views.register( 'gallery', wp.mce.gallery ); + + /** + * Tiny MCE Views for Audio / Video + * + */ + + /** + * These are base methods that are shared by each shortcode's MCE controller + * + * @mixin + */ + wp.mce.media = { + /** + * @global wp.shortcode + * + * @param {string} content + * @returns {Object} + */ + toView: function( content ) { + var match = wp.shortcode.next( this.shortcode, content ); + + if ( ! match ) { + return; + } + + return { + index: match.index, + content: match.content, + options: { + shortcode: match.shortcode + } + }; + }, + + /** + * Called when a TinyMCE view is clicked for editing. + * - Parses the shortcode out of the element's data attribute + * - Calls the `edit` method on the shortcode model + * - Launches the model window + * - Bind's an `update` callback which updates the element's data attribute + * re-renders the view + * + * @param {HTMLElement} node + */ + edit: function( node ) { + var media = wp.media[ this.shortcode ], + self = this, + frame, data; + + wp.media.mixin.pauseAllPlayers(); + + data = window.decodeURIComponent( $( node ).attr('data-wpview-text') ); + frame = media.edit( data ); + frame.on( 'close', function() { + frame.detach(); + } ); + frame.state( self.state ).on( 'update', function( selection ) { + var shortcode = wp.media[ self.shortcode ].shortcode( selection ).string(); + $( node ).attr( 'data-wpview-text', window.encodeURIComponent( shortcode ) ); + wp.mce.views.refreshView( self, shortcode ); + frame.detach(); + } ); + frame.open(); + } + }; + + /** + * Base View class for audio and video shortcodes + * + * @constructor + * @augments wp.mce.View + * @mixes wp.media.mixin + */ + wp.mce.media.View = wp.mce.View.extend({ + initialize: function( options ) { + this.shortcode = options.shortcode; + _.bindAll( this, 'setPlayer' ); + $(this).on( 'ready', this.setPlayer ); + }, + + /** + * Creates the player instance for the current node + * + * @global MediaElementPlayer + * @global _wpmejsSettings + * + * @param {Event} e + * @param {HTMLElement} node + */ + setPlayer: function(e, node) { + // if the ready event fires on an empty node + if ( ! node ) { + return; + } + + var self = this, + media, + firefox = this.ua.is( 'ff' ), + className = '.wp-' + this.shortcode.tag + '-shortcode'; + + if ( this.player ) { + this.unsetPlayer(); + } + + media = $( node ).find( className ); + + if ( ! this.isCompatible( media ) ) { + media.closest( '.wpview-wrap' ).addClass( 'wont-play' ); + if ( ! media.parent().hasClass( 'wpview-wrap' ) ) { + media.parent().replaceWith( media ); + } + media.replaceWith( '

' + media.find( 'source' ).eq(0).prop( 'src' ) + '

' ); + return; + } else { + media.closest( '.wpview-wrap' ).removeClass( 'wont-play' ); + if ( firefox ) { + media.prop( 'preload', 'metadata' ); + } else { + media.prop( 'preload', 'none' ); + } + } + + media = wp.media.view.MediaDetails.prepareSrc( media.get(0) ); + + // Thanks, Firefox! + if ( firefox ) { + setTimeout( function() { + self.player = new MediaElementPlayer( media, this.mejsSettings ); + }, 50 ); + } else { + this.player = new MediaElementPlayer( media, this.mejsSettings ); + } + }, + + /** + * Pass data to the View's Underscore template and return the compiled output + * + * @returns {string} + */ + getHtml: function() { + var attrs = _.defaults( + this.shortcode.attrs.named, + wp.media[ this.shortcode.tag ].defaults + ); + return this.template({ model: attrs }); + } + }); + _.extend( wp.mce.media.View.prototype, wp.media.mixin ); + + /** + * TinyMCE handler for the video shortcode + * + * @mixes wp.mce.media + */ + wp.mce.video = _.extend( {}, wp.mce.media, { + shortcode: 'video', + state: 'video-details', + View: wp.mce.media.View.extend({ + className: 'editor-video', + template: media.template('editor-video') + }) + } ); + wp.mce.views.register( 'video', wp.mce.video ); + + /** + * TinyMCE handler for the audio shortcode + * + * @mixes wp.mce.media + */ + wp.mce.audio = _.extend( {}, wp.mce.media, { + shortcode: 'audio', + state: 'audio-details', + View: wp.mce.media.View.extend({ + className: 'editor-audio', + template: media.template('editor-audio') + }) + } ); + wp.mce.views.register( 'audio', wp.mce.audio ); + + /** + * Base View class for playlist shortcodes + * + * @constructor + * @augments wp.mce.View + * @mixes wp.media.mixin + */ + wp.mce.media.PlaylistView = wp.mce.View.extend({ + className: 'editor-playlist', + template: media.template('editor-playlist'), + + initialize: function( options ) { + this.data = {}; + this.attachments = []; + this.shortcode = options.shortcode; + _.bindAll( this, 'setPlayer' ); + $(this).on('ready', this.setNode); + }, + + /** + * Set the element context for the view, and then fetch the playlist's + * associated attachments. + * + * @param {Event} e + * @param {HTMLElement} node + */ + setNode: function(e, node) { + this.node = node; + this.fetch(); + }, + + /** + * Asynchronously fetch the shortcode's attachments + */ + fetch: function() { + this.attachments = wp.media[ this.shortcode.tag ].attachments( this.shortcode ); + this.attachments.more().done( this.setPlayer ); + }, + + /** + * Get the HTML for the view (which also set's the data), replace the + * current HTML, and then invoke the WPPlaylistView instance to render + * the playlist in the editor + * + * @global WPPlaylistView + * @global tinymce.editors + */ + setPlayer: function() { + var p, + html = this.getHtml(), + t = this.encodedText, + self = this; + + this.unsetPlayer(); + + _.each( tinymce.editors, function( editor ) { + var doc; + if ( editor.plugins.wpview ) { + doc = editor.getDoc(); + $( doc ).find( '[data-wpview-text="' + t + '"]' ).each(function(i, elem) { + var node = $( elem ); + node.html( html ); + self.node = elem; + }); + } + }, this ); + + p = new WPPlaylistView({ + el: $( self.node ).find( '.wp-playlist' ).get(0), + metadata: this.data + }); + + this.player = p._player; + }, + + /** + * Set the data that will be used to compile the Underscore template, + * compile the template, and then return it. + * + * @returns {string} + */ + getHtml: function() { + var data = this.shortcode.attrs.named, + model = wp.media[ this.shortcode.tag ], + type = 'playlist' === this.shortcode.tag ? 'audio' : 'video', + options, + attachments, + tracks = []; + + if ( ! this.attachments.length ) { + return; + } + + _.each( model.defaults, function( value, key ) { + data[ key ] = model.coerce( data, key ); + }); + + attachments = this.attachments.toJSON(); + + options = { + type: type, + style: data.style, + tracklist: data.tracklist, + tracknumbers: data.tracknumbers, + images: data.images, + artists: data.artists + }; + + _.each( attachments, function( attachment ) { + var size = {}, resize = {}, track = { + src : attachment.url, + type : attachment.mime, + title : attachment.title, + caption : attachment.caption, + description : attachment.description, + meta : attachment.meta + }; + + if ( 'video' === type ) { + size.width = attachment.width; + size.height = attachment.height; + if ( media.view.settings.contentWidth ) { + resize.width = media.view.settings.contentWidth - 22; + resize.height = Math.ceil( ( size.height * resize.width ) / size.width ); + if ( ! options.width ) { + options.width = resize.width; + options.height = resize.height; + } + } else { + if ( ! options.width ) { + options.width = attachment.width; + options.height = attachment.height; + } + } + track.dimensions = { + original : size, + resized : _.isEmpty( resize ) ? size : resize + }; + } else { + options.width = 400; + } + + track.image = attachment.image; + track.thumb = attachment.thumb; + + tracks.push( track ); + } ); + + options.tracks = tracks; + this.data = options; + + return this.template( options ); + } + }); + _.extend( wp.mce.media.PlaylistView.prototype, wp.media.mixin ); + + /** + * TinyMCE handler for the playlist shortcode + * + * @mixes wp.mce.media + */ + wp.mce.playlist = _.extend( {}, wp.mce.media, { + shortcode: 'playlist', + state: 'playlist-edit', + View: wp.mce.media.PlaylistView + } ); + wp.mce.views.register( 'playlist', wp.mce.playlist ); + + /** + * TinyMCE handler for the video-playlist shortcode + * + * @mixes wp.mce.media + */ + wp.mce['video-playlist'] = _.extend( {}, wp.mce.media, { + shortcode: 'video-playlist', + state: 'video-playlist-edit', + View: wp.mce.media.PlaylistView + } ); + wp.mce.views.register( 'video-playlist', wp.mce['video-playlist'] ); }(jQuery)); diff --git a/src/wp-includes/js/media-audiovideo.js b/src/wp-includes/js/media-audiovideo.js index 0249851099..c07ffd78bf 100644 --- a/src/wp-includes/js/media-audiovideo.js +++ b/src/wp-includes/js/media-audiovideo.js @@ -13,7 +13,7 @@ * @mixin */ wp.media.mixin = { - + mejsSettings: baseSettings, /** * Pauses every instance of MediaElementPlayer */ @@ -217,7 +217,8 @@ loop : false, autoplay : false, preload : 'none', - caption : '' + caption : '', + width : 400 }, edit : function( data ) { @@ -1044,364 +1045,6 @@ } } ); - /** - * Tiny MCE Views - * - */ - - /** - * These are base methods that are shared by each shortcode's MCE controller - * - * @mixin - */ - wp.mce.media = { - /** - * @global wp.shortcode - * - * @param {string} content - * @returns {Object} - */ - toView: function( content ) { - var match = wp.shortcode.next( this.shortcode, content ); - - if ( ! match ) { - return; - } - - return { - index: match.index, - content: match.content, - options: { - shortcode: match.shortcode - } - }; - }, - - /** - * Called when a TinyMCE view is clicked for editing. - * - Parses the shortcode out of the element's data attribute - * - Calls the `edit` method on the shortcode model - * - Launches the model window - * - Bind's an `update` callback which updates the element's data attribute - * re-renders the view - * - * @param {HTMLElement} node - */ - edit: function( node ) { - var media = wp.media[ this.shortcode ], - self = this, - frame, data; - - wp.media.mixin.pauseAllPlayers(); - - data = window.decodeURIComponent( $( node ).attr('data-wpview-text') ); - frame = media.edit( data ); - frame.on( 'close', function() { - frame.detach(); - } ); - frame.state( self.state ).on( 'update', function( selection ) { - var shortcode = wp.media[ self.shortcode ].shortcode( selection ).string(); - $( node ).attr( 'data-wpview-text', window.encodeURIComponent( shortcode ) ); - wp.mce.views.refreshView( self, shortcode ); - frame.detach(); - } ); - frame.open(); - } - }; - - /** - * Base View class for audio and video shortcodes - * - * @constructor - * @augments wp.mce.View - * @mixes wp.media.mixin - */ - wp.mce.media.View = wp.mce.View.extend({ - initialize: function( options ) { - this.shortcode = options.shortcode; - _.bindAll( this, 'setPlayer' ); - $(this).on( 'ready', this.setPlayer ); - }, - - /** - * Creates the player instance for the current node - * - * @global MediaElementPlayer - * @global _wpmejsSettings - * - * @param {Event} e - * @param {HTMLElement} node - */ - setPlayer: function(e, node) { - // if the ready event fires on an empty node - if ( ! node ) { - return; - } - - var self = this, - media, - firefox = this.ua.is( 'ff' ), - className = '.wp-' + this.shortcode.tag + '-shortcode'; - - if ( this.player ) { - this.unsetPlayer(); - } - - media = $( node ).find( className ); - - if ( ! this.isCompatible( media ) ) { - media.closest( '.wpview-wrap' ).addClass( 'wont-play' ); - if ( ! media.parent().hasClass( 'wpview-wrap' ) ) { - media.parent().replaceWith( media ); - } - media.replaceWith( '

' + media.find( 'source' ).eq(0).prop( 'src' ) + '

' ); - return; - } else { - media.closest( '.wpview-wrap' ).removeClass( 'wont-play' ); - if ( firefox ) { - media.prop( 'preload', 'metadata' ); - } else { - media.prop( 'preload', 'none' ); - } - } - - media = wp.media.view.MediaDetails.prepareSrc( media.get(0) ); - - // Thanks, Firefox! - if ( firefox ) { - setTimeout( function() { - self.player = new MediaElementPlayer( media, baseSettings ); - }, 50 ); - } else { - this.player = new MediaElementPlayer( media, baseSettings ); - } - }, - - /** - * Pass data to the View's Underscore template and return the compiled output - * - * @returns {string} - */ - getHtml: function() { - var attrs = _.defaults( - this.shortcode.attrs.named, - wp.media[ this.shortcode.tag ].defaults - ); - return this.template({ model: attrs }); - } - }); - _.extend( wp.mce.media.View.prototype, wp.media.mixin ); - - /** - * TinyMCE handler for the video shortcode - * - * @mixes wp.mce.media - */ - wp.mce.video = _.extend( {}, wp.mce.media, { - shortcode: 'video', - state: 'video-details', - View: wp.mce.media.View.extend({ - className: 'editor-video', - template: media.template('editor-video') - }) - } ); - wp.mce.views.register( 'video', wp.mce.video ); - - /** - * TinyMCE handler for the audio shortcode - * - * @mixes wp.mce.media - */ - wp.mce.audio = _.extend( {}, wp.mce.media, { - shortcode: 'audio', - state: 'audio-details', - View: wp.mce.media.View.extend({ - className: 'editor-audio', - template: media.template('editor-audio') - }) - } ); - wp.mce.views.register( 'audio', wp.mce.audio ); - - /** - * Base View class for playlist shortcodes - * - * @constructor - * @augments wp.mce.View - * @mixes wp.media.mixin - */ - wp.mce.media.PlaylistView = wp.mce.View.extend({ - className: 'editor-playlist', - template: media.template('editor-playlist'), - - initialize: function( options ) { - this.data = {}; - this.attachments = []; - this.shortcode = options.shortcode; - _.bindAll( this, 'setPlayer' ); - $(this).on('ready', this.setNode); - }, - - /** - * Set the element context for the view, and then fetch the playlist's - * associated attachments. - * - * @param {Event} e - * @param {HTMLElement} node - */ - setNode: function(e, node) { - this.node = node; - this.fetch(); - }, - - /** - * Asynchronously fetch the shortcode's attachments - */ - fetch: function() { - this.attachments = wp.media[ this.shortcode.tag ].attachments( this.shortcode ); - this.attachments.more().done( this.setPlayer ); - }, - - /** - * Get the HTML for the view (which also set's the data), replace the - * current HTML, and then invoke the WPPlaylistView instance to render - * the playlist in the editor - * - * @global WPPlaylistView - * @global tinymce.editors - */ - setPlayer: function() { - var p, - html = this.getHtml(), - t = this.encodedText, - self = this; - - this.unsetPlayer(); - - _.each( tinymce.editors, function( editor ) { - var doc; - if ( editor.plugins.wpview ) { - doc = editor.getDoc(); - $( doc ).find( '[data-wpview-text="' + t + '"]' ).each(function(i, elem) { - var node = $( elem ); - node.html( html ); - self.node = elem; - }); - } - }, this ); - - p = new WPPlaylistView({ - el: $( self.node ).find( '.wp-playlist' ).get(0), - metadata: this.data - }); - - this.player = p._player; - }, - - /** - * Set the data that will be used to compile the Underscore template, - * compile the template, and then return it. - * - * @returns {string} - */ - getHtml: function() { - var data = this.shortcode.attrs.named, - model = wp.media[ this.shortcode.tag ], - type = 'playlist' === this.shortcode.tag ? 'audio' : 'video', - options, - attachments, - tracks = []; - - if ( ! this.attachments.length ) { - return; - } - - _.each( model.defaults, function( value, key ) { - data[ key ] = model.coerce( data, key ); - }); - - attachments = this.attachments.toJSON(); - - options = { - type: type, - style: data.style, - tracklist: data.tracklist, - tracknumbers: data.tracknumbers, - images: data.images, - artists: data.artists - }; - - _.each( attachments, function( attachment ) { - var size = {}, resize = {}, track = { - src : attachment.url, - type : attachment.mime, - title : attachment.title, - caption : attachment.caption, - description : attachment.description, - meta : attachment.meta - }; - - if ( 'video' === type ) { - size.width = attachment.width; - size.height = attachment.height; - if ( media.view.settings.contentWidth ) { - resize.width = media.view.settings.contentWidth - 22; - resize.height = Math.ceil( ( size.height * resize.width ) / size.width ); - if ( ! options.width ) { - options.width = resize.width; - options.height = resize.height; - } - } else { - if ( ! options.width ) { - options.width = attachment.width; - options.height = attachment.height; - } - } - track.dimensions = { - original : size, - resized : _.isEmpty( resize ) ? size : resize - }; - } else { - options.width = 400; - } - - track.image = attachment.image; - track.thumb = attachment.thumb; - - tracks.push( track ); - } ); - - options.tracks = tracks; - this.data = options; - - return this.template( options ); - } - }); - _.extend( wp.mce.media.PlaylistView.prototype, wp.media.mixin ); - - /** - * TinyMCE handler for the playlist shortcode - * - * @mixes wp.mce.media - */ - wp.mce.playlist = _.extend( {}, wp.mce.media, { - shortcode: 'playlist', - state: 'playlist-edit', - View: wp.mce.media.PlaylistView - } ); - wp.mce.views.register( 'playlist', wp.mce.playlist ); - - /** - * TinyMCE handler for the video-playlist shortcode - * - * @mixes wp.mce.media - */ - wp.mce['video-playlist'] = _.extend( {}, wp.mce.media, { - shortcode: 'video-playlist', - state: 'video-playlist-edit', - View: wp.mce.media.PlaylistView - } ); - wp.mce.views.register( 'video-playlist', wp.mce['video-playlist'] ); - /** * Event binding */ diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php index 4a8851918b..49cbe9083e 100644 --- a/src/wp-includes/media-template.php +++ b/src/wp-includes/media-template.php @@ -18,6 +18,7 @@ function wp_underscore_audio_template() { ?>