]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/mce-view.js
WordPress 4.0
[autoinstalls/wordpress.git] / wp-includes / js / mce-view.js
index ec167ca47c586226a96af37a57cd66d904fd1312..85484ef42b9eb3307b17556a88be1b6691e497fd 100644 (file)
@@ -1,4 +1,4 @@
-/* global tinymce, MediaElementPlayer, WPPlaylistView */
+/* global tinymce */
 /**
  * Note: this API is "experimental" meaning that it will probably change
  * in the next few releases based on feedback from 3.9.0.
@@ -8,7 +8,9 @@
 // Ensure the global `wp` object exists.
 window.wp = window.wp || {};
 
-(function($){
+( function( $ ) {
+       'use strict';
+
        var views = {},
                instances = {},
                media = wp.media,
@@ -22,34 +24,237 @@ window.wp = window.wp || {};
         *
         * A Backbone-like View constructor intended for use when rendering a TinyMCE View. The main difference is
         * that the TinyMCE View is not tied to a particular DOM node.
+        *
+        * @param {Object} [options={}]
         */
        wp.mce.View = function( options ) {
-               options || (options = {});
-               _.extend(this, _.pick(options, viewOptions));
-               this.initialize.apply(this, arguments);
+               options = options || {};
+               this.type = options.type;
+               _.extend( this, _.pick( options, viewOptions ) );
+               this.initialize.apply( this, arguments );
        };
 
        _.extend( wp.mce.View.prototype, {
                initialize: function() {},
-               getHtml: function() {},
-               render: function() {
-                       var html = this.getHtml();
-                       // Search all tinymce editor instances and update the placeholders
+               getHtml: function() {
+                       return '';
+               },
+               loadingPlaceholder: function() {
+                       return '' +
+                               '<div class="loading-placeholder">' +
+                                       '<div class="dashicons dashicons-admin-media"></div>' +
+                                       '<div class="wpview-loading"><ins></ins></div>' +
+                               '</div>';
+               },
+               render: function( force ) {
+                       if ( force || ! this.rendered() ) {
+                               this.unbind();
+
+                               this.setContent(
+                                       '<p class="wpview-selection-before">\u00a0</p>' +
+                                       '<div class="wpview-body" contenteditable="false">' +
+                                               '<div class="toolbar">' +
+                                                       ( _.isFunction( views[ this.type ].edit ) ? '<div class="dashicons dashicons-edit edit"></div>' : '' ) +
+                                                       '<div class="dashicons dashicons-no-alt remove"></div>' +
+                                               '</div>' +
+                                               '<div class="wpview-content wpview-type-' + this.type + '">' +
+                                                       ( this.getHtml() || this.loadingPlaceholder() ) +
+                                               '</div>' +
+                                               ( this.overlay ? '<div class="wpview-overlay"></div>' : '' ) +
+                                       '</div>' +
+                                       '<p class="wpview-selection-after">\u00a0</p>',
+                                       'wrap'
+                               );
+
+                               $( this ).trigger( 'ready' );
+
+                               this.rendered( true );
+                       }
+               },
+               unbind: function() {},
+               getEditors: function( callback ) {
+                       var editors = [];
+
                        _.each( tinymce.editors, function( editor ) {
-                               var doc, self = this;
                                if ( editor.plugins.wpview ) {
-                                       doc = editor.getDoc();
-                                       $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
-                                               var node = $( elem );
-                                               // The <ins> is used to mark the end of the wrapper div. Needed when comparing
-                                               // the content as string for preventing extra undo levels.
-                                               node.html( html ).append( '<ins data-wpview-end="1"></ins>' );
-                                               $( self ).trigger( 'ready', elem );
-                                       });
+                                       if ( callback ) {
+                                               callback( editor );
+                                       }
+
+                                       editors.push( editor );
                                }
                        }, this );
+
+                       return editors;
                },
-               unbind: function() {}
+               getNodes: function( callback ) {
+                       var nodes = [],
+                               self = this;
+
+                       this.getEditors( function( editor ) {
+                               $( editor.getBody() )
+                               .find( '[data-wpview-text="' + self.encodedText + '"]' )
+                               .each( function ( i, node ) {
+                                       if ( callback ) {
+                                               callback( editor, node, $( node ).find( '.wpview-content' ).get( 0 ) );
+                                       }
+
+                                       nodes.push( node );
+                               } );
+                       } );
+
+                       return nodes;
+               },
+               setContent: function( html, option ) {
+                       this.getNodes( function ( editor, node, content ) {
+                               var el = ( option === 'wrap' || option === 'replace' ) ? node : content,
+                                       insert = html;
+
+                               if ( _.isString( insert ) ) {
+                                       insert = editor.dom.createFragment( insert );
+                               }
+
+                               if ( option === 'replace' ) {
+                                       editor.dom.replace( insert, el );
+                               } else {
+                                       el.innerHTML = '';
+                                       el.appendChild( insert );
+                               }
+                       } );
+               },
+               /* jshint scripturl: true */
+               setIframes: function ( head, body ) {
+                       var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
+                               importStyles = this.type === 'video' || this.type === 'audio' || this.type === 'playlist';
+
+                       if ( head || body.indexOf( '<script' ) !== -1 ) {
+                               this.getNodes( function ( editor, node, content ) {
+                                       var dom = editor.dom,
+                                               styles = '',
+                                               bodyClasses = editor.getBody().className || '',
+                                               iframe, iframeDoc, i, resize;
+
+                                       content.innerHTML = '';
+                                       head = head || '';
+
+                                       if ( importStyles ) {
+                                               if ( ! wp.mce.views.sandboxStyles ) {
+                                                       tinymce.each( dom.$( 'link[rel="stylesheet"]', editor.getDoc().head ), function( link ) {
+                                                               if ( link.href && link.href.indexOf( 'skins/lightgray/content.min.css' ) === -1 &&
+                                                                       link.href.indexOf( 'skins/wordpress/wp-content.css' ) === -1 ) {
+
+                                                                       styles += dom.getOuterHTML( link ) + '\n';
+                                                               }
+                                                       });
+
+                                                       wp.mce.views.sandboxStyles = styles;
+                                               } else {
+                                                       styles = wp.mce.views.sandboxStyles;
+                                               }
+                                       }
+
+                                       // Seems Firefox needs a bit of time to insert/set the view nodes, or the iframe will fail
+                                       // especially when switching Text => Visual.
+                                       setTimeout( function() {
+                                               iframe = dom.add( content, 'iframe', {
+                                                       src: tinymce.Env.ie ? 'javascript:""' : '',
+                                                       frameBorder: '0',
+                                                       allowTransparency: 'true',
+                                                       scrolling: 'no',
+                                                       'class': 'wpview-sandbox',
+                                                       style: {
+                                                               width: '100%',
+                                                               display: 'block'
+                                                       }
+                                               } );
+
+                                               iframeDoc = iframe.contentWindow.document;
+
+                                               iframeDoc.open();
+                                               iframeDoc.write(
+                                                       '<!DOCTYPE html>' +
+                                                       '<html>' +
+                                                               '<head>' +
+                                                                       '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
+                                                                       head +
+                                                                       styles +
+                                                                       '<style>' +
+                                                                               'html {' +
+                                                                                       'background: transparent;' +
+                                                                                       'padding: 0;' +
+                                                                                       'margin: 0;' +
+                                                                               '}' +
+                                                                               'body#wpview-iframe-sandbox {' +
+                                                                                       'background: transparent;' +
+                                                                                       'padding: 1px 0 !important;' +
+                                                                                       'margin: -1px 0 0 !important;' +
+                                                                               '}' +
+                                                                               'body#wpview-iframe-sandbox:before,' +
+                                                                               'body#wpview-iframe-sandbox:after {' +
+                                                                                       'display: none;' +
+                                                                                       'content: "";' +
+                                                                               '}' +
+                                                                       '</style>' +
+                                                               '</head>' +
+                                                               '<body id="wpview-iframe-sandbox" class="' + bodyClasses + '">' +
+                                                                       body +
+                                                               '</body>' +
+                                                       '</html>'
+                                               );
+                                               iframeDoc.close();
+
+                                               resize = function() {
+                                                       // Make sure the iframe still exists.
+                                                       iframe.contentWindow && $( iframe ).height( $( iframeDoc.body ).height() );
+                                               };
+
+                                               if ( MutationObserver ) {
+                                                       new MutationObserver( _.debounce( function() {
+                                                               resize();
+                                                       }, 100 ) )
+                                                       .observe( iframeDoc.body, {
+                                                               attributes: true,
+                                                               childList: true,
+                                                               subtree: true
+                                                       } );
+                                               } else {
+                                                       for ( i = 1; i < 6; i++ ) {
+                                                               setTimeout( resize, i * 700 );
+                                                       }
+                                               }
+
+                                               if ( importStyles ) {
+                                                       editor.on( 'wp-body-class-change', function() {
+                                                               iframeDoc.body.className = editor.getBody().className;
+                                                       });
+                                               }
+                                       }, 50 );
+                               });
+                       } else {
+                               this.setContent( body );
+                       }
+               },
+               setError: function( message, dashicon ) {
+                       this.setContent(
+                               '<div class="wpview-error">' +
+                                       '<div class="dashicons dashicons-' + ( dashicon ? dashicon : 'no' ) + '"></div>' +
+                                       '<p>' + message + '</p>' +
+                               '</div>'
+                       );
+               },
+               rendered: function( value ) {
+                       var notRendered;
+
+                       this.getNodes( function( editor, node ) {
+                               if ( value != null ) {
+                                       $( node ).data( 'rendered', value === true );
+                               } else {
+                                       notRendered = notRendered || ! $( node ).data( 'rendered' );
+                               }
+                       } );
+
+                       return ! notRendered;
+               }
        } );
 
        // take advantage of the Backbone extend method
@@ -74,6 +279,29 @@ window.wp = window.wp || {};
                 *
                 */
                register: function( type, constructor ) {
+                       var defaultConstructor = {
+                                       type: type,
+                                       View: {},
+                                       toView: function( content ) {
+                                               var match = wp.shortcode.next( this.type, content );
+
+                                               if ( ! match ) {
+                                                       return;
+                                               }
+
+                                               return {
+                                                       index: match.index,
+                                                       content: match.content,
+                                                       options: {
+                                                               shortcode: match.shortcode
+                                                       }
+                                               };
+                                       }
+                               };
+
+                       constructor = _.defaults( constructor, defaultConstructor );
+                       constructor.View = wp.mce.View.extend( constructor.View );
+
                        views[ type ] = constructor;
                },
 
@@ -81,6 +309,8 @@ window.wp = window.wp || {};
                 * wp.mce.views.get( id )
                 *
                 * Returns a TinyMCE view constructor.
+                *
+                * @param type
                 */
                get: function( type ) {
                        return views[ type ];
@@ -90,6 +320,8 @@ window.wp = window.wp || {};
                 * wp.mce.views.unregister( type )
                 *
                 * Unregisters a TinyMCE view.
+                *
+                * @param type
                 */
                unregister: function( type ) {
                        delete views[ type ];
@@ -112,6 +344,7 @@ window.wp = window.wp || {};
                 * matches with wrapper elements, and creates a new instance for
                 * every match, which triggers the related data to be fetched.
                 *
+                * @param content
                 */
                toViews: function( content ) {
                        var pieces = [ { content: content } ],
@@ -180,6 +413,7 @@ window.wp = window.wp || {};
 
                        if ( ! wp.mce.views.getInstance( encodedText ) ) {
                                viewOptions = options;
+                               viewOptions.type = viewType;
                                viewOptions.encodedText = encodedText;
                                instance = new view.View( viewOptions );
                                instances[ encodedText ] = instance;
@@ -189,10 +423,9 @@ window.wp = window.wp || {};
                                tag: 'div',
 
                                attrs: {
-                                       'class': 'wpview-wrap wpview-type-' + viewType,
+                                       'class': 'wpview-wrap',
                                        'data-wpview-text': encodedText,
-                                       'data-wpview-type': viewType,
-                                       'contenteditable': 'false'
+                                       'data-wpview-type': viewType
                                },
 
                                content: '\u00a0'
@@ -215,12 +448,13 @@ window.wp = window.wp || {};
                        if ( ! instance ) {
                                result = view.toView( text );
                                viewOptions = result.options;
+                               viewOptions.type = view.type;
                                viewOptions.encodedText = encodedText;
                                instance = new view.View( viewOptions );
                                instances[ encodedText ] = instance;
                        }
 
-                       wp.mce.views.render();
+                       instance.render();
                },
 
                getInstance: function( encodedText ) {
@@ -236,9 +470,9 @@ window.wp = window.wp || {};
                 * To generate wrapper elements, pass your content through
                 * `wp.mce.view.toViews( content )`.
                 */
-               render: function() {
+               render: function( force ) {
                        _.each( instances, function( instance ) {
-                               instance.render();
+                               instance.render( force );
                        } );
                },
 
@@ -252,26 +486,9 @@ window.wp = window.wp || {};
                }
        };
 
-       wp.mce.gallery = {
-               shortcode: 'gallery',
-               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
-                               }
-                       };
-               },
-               View: wp.mce.View.extend({
-                       className: 'editor-gallery',
-                       template:  media.template('editor-gallery'),
+       wp.mce.views.register( 'gallery', {
+               View: {
+                       template: media.template( 'editor-gallery' ),
 
                        // The fallback post ID to use as a parent for galleries that don't
                        // specify the `ids` or `include` parameters.
@@ -285,8 +502,12 @@ window.wp = window.wp || {};
                        },
 
                        fetch: function() {
+                               var self = this;
+
                                this.attachments = wp.media.gallery.attachments( this.shortcode, this.postID );
-                               this.dfd = this.attachments.more().done( _.bind( this.render, this ) );
+                               this.dfd = this.attachments.more().done( function() {
+                                       self.render( true );
+                               } );
                        },
 
                        getHtml: function() {
@@ -296,7 +517,7 @@ window.wp = window.wp || {};
 
                                // Don't render errors while still fetching attachments
                                if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) {
-                                       return;
+                                       return '';
                                }
 
                                if ( this.attachments.length ) {
@@ -315,13 +536,12 @@ window.wp = window.wp || {};
 
                                options = {
                                        attachments: attachments,
-                                       columns: attrs.columns ? parseInt( attrs.columns, 10 ) : 3
+                                       columns: attrs.columns ? parseInt( attrs.columns, 10 ) : wp.media.galleryDefaults.columns
                                };
 
                                return this.template( options );
-
                        }
-               }),
+               },
 
                edit: function( node ) {
                        var gallery = wp.media.gallery,
@@ -335,45 +555,122 @@ window.wp = window.wp || {};
                                var shortcode = gallery.shortcode( selection ).string();
                                $( node ).attr( 'data-wpview-text', window.encodeURIComponent( shortcode ) );
                                wp.mce.views.refreshView( self, shortcode );
+                       });
+
+                       frame.on( 'close', function() {
                                frame.detach();
                        });
                }
-
-       };
-       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
+        * These are base methods that are shared by the audio and video shortcode's MCE controller.
         *
         * @mixin
         */
-       wp.mce.media = {
-               loaded: false,
-               /**
-                * @global wp.shortcode
-                *
-                * @param {string} content
-                * @returns {Object}
-                */
-               toView:  function( content ) {
-                       var match = wp.shortcode.next( this.shortcode, content );
+       wp.mce.av = {
+               View: {
+                       overlay: true,
 
-                       if ( ! match ) {
-                               return;
-                       }
+                       action: 'parse-media-shortcode',
 
-                       return {
-                               index:   match.index,
-                               content: match.content,
-                               options: {
-                                       shortcode: match.shortcode
+                       initialize: function( options ) {
+                               var self = this;
+
+                               this.shortcode = options.shortcode;
+
+                               _.bindAll( this, 'setIframes', 'setNodes', 'fetch', 'stopPlayers' );
+                               $( this ).on( 'ready', this.setNodes );
+
+                               $( document ).on( 'media:edit', this.stopPlayers );
+
+                               this.fetch();
+
+                               this.getEditors( function( editor ) {
+                                       editor.on( 'hide', self.stopPlayers );
+                               });
+                       },
+
+                       setNodes: function () {
+                               if ( this.parsed ) {
+                                       this.setIframes( this.parsed.head, this.parsed.body );
+                               } else {
+                                       this.fail();
                                }
-                       };
+                       },
+
+                       fetch: function () {
+                               var self = this;
+
+                               wp.ajax.send( this.action, {
+                                       data: {
+                                               post_ID: $( '#post_ID' ).val() || 0,
+                                               type: this.shortcode.tag,
+                                               shortcode: this.shortcode.string()
+                                       }
+                               } )
+                               .done( function( response ) {
+                                       if ( response ) {
+                                               self.parsed = response;
+                                               self.setIframes( response.head, response.body );
+                                       } else {
+                                               self.fail( true );
+                                       }
+                               } )
+                               .fail( function( response ) {
+                                       self.fail( response || true );
+                               } );
+                       },
+
+                       fail: function( error ) {
+                               if ( ! this.error ) {
+                                       if ( error ) {
+                                               this.error = error;
+                                       } else {
+                                               return;
+                                       }
+                               }
+
+                               if ( this.error.message ) {
+                                       if ( ( this.error.type === 'not-embeddable' && this.type === 'embed' ) || this.error.type === 'not-ssl' ||
+                                               this.error.type === 'no-items' ) {
+
+                                               this.setError( this.error.message, 'admin-media' );
+                                       } else {
+                                               this.setContent( '<p>' + this.original + '</p>', 'replace' );
+                                       }
+                               } else if ( this.error.statusText ) {
+                                       this.setError( this.error.statusText, 'admin-media' );
+                               } else if ( this.original ) {
+                                       this.setContent( '<p>' + this.original + '</p>', 'replace' );
+                               }
+                       },
+
+                       stopPlayers: function( remove ) {
+                               var rem = remove === 'remove';
+
+                               this.getNodes( function( editor, node, content ) {
+                                       var p, win,
+                                               iframe = $( 'iframe.wpview-sandbox', content ).get(0);
+
+                                       if ( iframe && ( win = iframe.contentWindow ) && win.mejs ) {
+                                               // Sometimes ME.js may show a "Download File" placeholder and player.remove() doesn't exist there.
+                                               try {
+                                                       for ( p in win.mejs.players ) {
+                                                               win.mejs.players[p].pause();
+
+                                                               if ( rem ) {
+                                                                       win.mejs.players[p].remove();
+                                                               }
+                                                       }
+                                               } catch( er ) {}
+                                       }
+                               });
+                       },
+
+                       unbind: function() {
+                               this.stopPlayers( 'remove' );
+                       }
                },
 
                /**
@@ -387,11 +684,11 @@ window.wp = window.wp || {};
                 * @param {HTMLElement} node
                 */
                edit: function( node ) {
-                       var media = wp.media[ this.shortcode ],
+                       var media = wp.media[ this.type ],
                                self = this,
                                frame, data, callback;
 
-                       wp.media.mixin.pauseAllPlayers();
+                       $( document ).trigger( 'media:edit' );
 
                        data = window.decodeURIComponent( $( node ).attr('data-wpview-text') );
                        frame = media.edit( data );
@@ -400,7 +697,7 @@ window.wp = window.wp || {};
                        } );
 
                        callback = function( selection ) {
-                               var shortcode = wp.media[ self.shortcode ].shortcode( selection ).string();
+                               var shortcode = wp.media[ self.type ].shortcode( selection ).string();
                                $( node ).attr( 'data-wpview-text', window.encodeURIComponent( shortcode ) );
                                wp.mce.views.refreshView( self, shortcode );
                                frame.detach();
@@ -416,278 +713,113 @@ window.wp = window.wp || {};
                }
        };
 
-       /**
-        * 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.players = [];
-                       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';
-
-                       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( '<p>' + media.find( 'source' ).eq(0).prop( 'src' ) + '</p>' );
-                               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) );
-
-                       setTimeout( function() {
-                               wp.mce.media.loaded = true;
-                               self.players.push( new MediaElementPlayer( media, self.mejsSettings ) );
-                       }, wp.mce.media.loaded ? 10 : 500 );
-               },
-
-               /**
-                * Pass data to the View's Underscore template and return the compiled output
-                *
-                * @returns {string}
-                */
-               getHtml: function() {
-                       var attrs = this.shortcode.attrs.named;
-                       attrs.content = this.shortcode.content;
-
-                       return this.template({ model: _.defaults(
-                               attrs,
-                               wp.media[ this.shortcode.tag ].defaults )
-                       });
-               },
-
-               unbind: function() {
-                       this.unsetPlayers();
-               }
-       });
-       _.extend( wp.mce.media.View.prototype, wp.media.mixin );
-
        /**
         * TinyMCE handler for the video shortcode
         *
-        * @mixes wp.mce.media
+        * @mixes wp.mce.av
         */
-       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 );
+       wp.mce.views.register( 'video', _.extend( {}, wp.mce.av, {
+               state: 'video-details'
+       } ) );
 
        /**
         * TinyMCE handler for the audio shortcode
         *
-        * @mixes wp.mce.media
+        * @mixes wp.mce.av
         */
-       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 );
+       wp.mce.views.register( 'audio', _.extend( {}, wp.mce.av, {
+               state: 'audio-details'
+       } ) );
 
        /**
-        * Base View class for playlist shortcodes
+        * TinyMCE handler for the playlist shortcode
         *
-        * @constructor
-        * @augments wp.mce.View
-        * @mixes wp.media.mixin
+        * @mixes wp.mce.av
         */
-       wp.mce.media.PlaylistView = wp.mce.View.extend({
-               className: 'editor-playlist',
-               template:  media.template('editor-playlist'),
-
-               initialize: function( options ) {
-                       this.players = [];
-                       this.data = {};
-                       this.attachments = [];
-                       this.shortcode = options.shortcode;
-                       this.fetch();
-               },
-
-               /**
-                * Asynchronously fetch the shortcode's attachments
-                */
-               fetch: function() {
-                       this.attachments = wp.media.playlist.attachments( this.shortcode );
-                       this.dfd = this.attachments.more().done( _.bind( this.render, this ) );
-               },
-
-               /**
-                * 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
-                */
-               render: function() {
-                       var html = this.getHtml(), self = this;
-
-                       _.each( tinymce.editors, function( editor ) {
-                               var doc;
-                               if ( editor.plugins.wpview ) {
-                                       doc = editor.getDoc();
-                                       $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
-                                               var node = $( elem );
-
-                                               // The <ins> is used to mark the end of the wrapper div. Needed when comparing
-                                               // the content as string for preventing extra undo levels.
-                                               node.html( html ).append( '<ins data-wpview-end="1"></ins>' );
+       wp.mce.views.register( 'playlist', _.extend( {}, wp.mce.av, {
+               state: [ 'playlist-edit', 'video-playlist-edit' ]
+       } ) );
 
-                                               if ( ! self.data.tracks ) {
-                                                       return;
-                                               }
+       /**
+        * TinyMCE handler for the embed shortcode
+        */
+       wp.mce.embedMixin = {
+               View: _.extend( {}, wp.mce.av.View, {
+                       overlay: true,
+                       action: 'parse-embed',
+                       initialize: function( options ) {
+                               this.content = options.content;
+                               this.original = options.url || options.shortcode.string();
 
-                                               self.players.push( new WPPlaylistView({
-                                                       el: $( elem ).find( '.wp-playlist' ).get(0),
-                                                       metadata: self.data
-                                               }).player );
-                                       });
+                               if ( options.url ) {
+                                       this.shortcode = media.embed.shortcode( {
+                                               url: options.url
+                                       } );
+                               } else {
+                                       this.shortcode = options.shortcode;
                                }
-                       }, this );
-               },
-
-               /**
-                * 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.playlist,
-                               options,
-                               attachments,
-                               tracks = [];
-
-                       // Don't render errors while still fetching attachments
-                       if ( this.dfd && 'pending' === this.dfd.state() && ! this.attachments.length ) {
-                               return;
-                       }
-
-                       _.each( model.defaults, function( value, key ) {
-                               data[ key ] = model.coerce( data, key );
-                       });
 
-                       options = {
-                               type: data.type,
-                               style: data.style,
-                               tracklist: data.tracklist,
-                               tracknumbers: data.tracknumbers,
-                               images: data.images,
-                               artists: data.artists
-                       };
+                               _.bindAll( this, 'setIframes', 'setNodes', 'fetch' );
+                               $( this ).on( 'ready', this.setNodes );
 
-                       if ( ! this.attachments.length ) {
-                               return this.template( options );
+                               this.fetch();
                        }
+               } ),
+               edit: function( node ) {
+                       var embed = media.embed,
+                               self = this,
+                               frame,
+                               data,
+                               isURL = 'embedURL' === this.type;
 
-                       attachments = this.attachments.toJSON();
+                       $( document ).trigger( 'media:edit' );
 
-                       _.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
-                               };
+                       data = window.decodeURIComponent( $( node ).attr('data-wpview-text') );
+                       frame = embed.edit( data, isURL );
+                       frame.on( 'close', function() {
+                               frame.detach();
+                       } );
+                       frame.state( 'embed' ).props.on( 'change:url', function (model, url) {
+                               if ( ! url ) {
+                                       return;
+                               }
+                               frame.state( 'embed' ).metadata = model.toJSON();
+                       } );
+                       frame.state( 'embed' ).on( 'select', function() {
+                               var shortcode;
 
-                               if ( 'video' === data.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
-                                       };
+                               if ( isURL ) {
+                                       shortcode = frame.state( 'embed' ).metadata.url;
                                } else {
-                                       options.width = 400;
+                                       shortcode = embed.shortcode( frame.state( 'embed' ).metadata ).string();
                                }
-
-                               track.image = attachment.image;
-                               track.thumb = attachment.thumb;
-
-                               tracks.push( track );
+                               $( node ).attr( 'data-wpview-text', window.encodeURIComponent( shortcode ) );
+                               wp.mce.views.refreshView( self, shortcode );
+                               frame.detach();
                        } );
+                       frame.open();
+               }
+       };
 
-                       options.tracks = tracks;
-                       this.data = options;
+       wp.mce.views.register( 'embed', _.extend( {}, wp.mce.embedMixin ) );
 
-                       return this.template( options );
-               },
+       wp.mce.views.register( 'embedURL', _.extend( {}, wp.mce.embedMixin, {
+               toView: function( content ) {
+                       var re = /(?:^|<p>)(https?:\/\/[^\s"]+?)(?:<\/p>\s*|$)/gi,
+                               match = re.exec( tinymce.trim( content ) );
 
-               unbind: function() {
-                       this.unsetPlayers();
+                       if ( ! match ) {
+                               return;
+                       }
+
+                       return {
+                               index: match.index,
+                               content: match[0],
+                               options: {
+                                       url: match[1]
+                               }
+                       };
                }
-       });
-       _.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', 'video-playlist-edit'],
-               View: wp.mce.media.PlaylistView
-       } );
-       wp.mce.views.register( 'playlist', wp.mce.playlist );
 }(jQuery));