]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/mce-view.js
Wordpress 4.6-scripts
[autoinstalls/wordpress.git] / wp-includes / js / mce-view.js
index eab4560c82784ea1a65961fba1dff945ead68d38..3d2616d7852eb1b9c4661e0a2fb11d4b222f2ae9 100644 (file)
@@ -1,7 +1,5 @@
 /* global tinymce */
 
-window.wp = window.wp || {};
-
 /*
  * The TinyMCE view API.
  *
@@ -24,7 +22,7 @@ window.wp = window.wp || {};
  * |- registered view
  * |  |- ...
  */
-( function( window, wp, $ ) {
+( function( window, wp, shortcode, $ ) {
        'use strict';
 
        var views = {},
@@ -93,8 +91,7 @@ window.wp = window.wp || {};
                setMarkers: function( content ) {
                        var pieces = [ { content: content } ],
                                self = this,
-                               instance,
-                               current;
+                               instance, current;
 
                        _.each( views, function( view, type ) {
                                current = pieces.slice();
@@ -102,7 +99,7 @@ window.wp = window.wp || {};
 
                                _.each( current, function( piece ) {
                                        var remaining = piece.content,
-                                               result;
+                                               result, text;
 
                                        // Ignore processed pieces, but retain their location.
                                        if ( piece.processed ) {
@@ -119,10 +116,11 @@ window.wp = window.wp || {};
                                                }
 
                                                instance = self.createInstance( type, result.content, result.options );
+                                               text = instance.loader ? '.' : instance.text;
 
                                                // Add the processed piece for the match.
                                                pieces.push( {
-                                                       content: '<p data-wpview-marker="' + instance.encodedText + '">' + instance.text + '</p>',
+                                                       content: instance.ignore ? text : '<p data-wpview-marker="' + instance.encodedText + '">' + text + '</p>',
                                                        processed: true
                                                } );
 
@@ -138,31 +136,37 @@ window.wp = window.wp || {};
                                } );
                        } );
 
-                       return _.pluck( pieces, 'content' ).join( '' );
+                       content = _.pluck( pieces, 'content' ).join( '' );
+                       return content.replace( /<p>\s*<p data-wpview-marker=/g, '<p data-wpview-marker=' ).replace( /<\/p>\s*<\/p>/g, '</p>' );
                },
 
                /**
                 * Create a view instance.
                 *
-                * @param {String} type    The view type.
-                * @param {String} text    The textual representation of the view.
-                * @param {Object} options Options.
+                * @param {String}  type    The view type.
+                * @param {String}  text    The textual representation of the view.
+                * @param {Object}  options Options.
+                * @param {Boolean} force   Recreate the instance. Optional.
                 *
                 * @return {wp.mce.View} The view instance.
                 */
-               createInstance: function( type, text, options ) {
+               createInstance: function( type, text, options, force ) {
                        var View = this.get( type ),
                                encodedText,
                                instance;
 
-                       text = tinymce.DOM.decode( text ),
-                       encodedText = encodeURIComponent( text ),
-                       instance = this.getInstance( encodedText );
+                       text = tinymce.DOM.decode( text );
 
-                       if ( instance ) {
-                               return instance;
+                       if ( ! force ) {
+                               instance = this.getInstance( text );
+
+                               if ( instance ) {
+                                       return instance;
+                               }
                        }
 
+                       encodedText = encodeURIComponent( text );
+
                        options = _.extend( options || {}, {
                                text: text,
                                encodedText: encodedText
@@ -214,12 +218,13 @@ window.wp = window.wp || {};
                 * @param {String}         text   The new text.
                 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
                 * @param {HTMLElement}    node   The view node to update.
+                * @param {Boolean}        force  Recreate the instance. Optional.
                 */
-               update: function( text, editor, node ) {
+               update: function( text, editor, node, force ) {
                        var instance = this.getInstance( node );
 
                        if ( instance ) {
-                               instance.update( text, editor, node );
+                               instance.update( text, editor, node, force );
                        }
                },
 
@@ -233,8 +238,8 @@ window.wp = window.wp || {};
                        var instance = this.getInstance( node );
 
                        if ( instance && instance.edit ) {
-                               instance.edit( instance.text, function( text ) {
-                                       instance.update( text, editor, node );
+                               instance.edit( instance.text, function( text, force ) {
+                                       instance.update( text, editor, node, force );
                                } );
                        }
                },
@@ -300,8 +305,8 @@ window.wp = window.wp || {};
                /**
                 * Renders all view nodes tied to this view instance that are not yet rendered.
                 *
-                * @param {String} content The content to render. Optional.
-                * @param {Boolean} force Rerender all view nodes tied to this view instance.
+                * @param {String}  content The content to render. Optional.
+                * @param {Boolean} force   Rerender all view nodes tied to this view instance. Optional.
                 */
                render: function( content, force ) {
                        if ( content != null ) {
@@ -322,9 +327,9 @@ window.wp = window.wp || {};
                        this.replaceMarkers();
 
                        if ( content ) {
-                               this.setContent( content, function( editor, node, contentNode ) {
+                               this.setContent( content, function( editor, node ) {
                                        $( node ).data( 'rendered', true );
-                                       this.bindNode.call( this, editor, node, contentNode );
+                                       this.bindNode.call( this, editor, node );
                                }, force ? null : false );
                        } else {
                                this.setLoader();
@@ -346,9 +351,8 @@ window.wp = window.wp || {};
                 * Runs before their content is removed from the DOM.
                 */
                unbind: function() {
-                       this.getNodes( function( editor, node, contentNode ) {
-                               this.unbindNode.call( this, editor, node, contentNode );
-                               $( node ).trigger( 'wp-mce-view-unbind' );
+                       this.getNodes( function( editor, node ) {
+                               this.unbindNode.call( this, editor, node );
                        }, true );
                },
 
@@ -389,7 +393,7 @@ window.wp = window.wp || {};
                                                return rendered ? data : ! data;
                                        } )
                                        .each( function() {
-                                               callback.call( self, editor, this, $( this ).find( '.wpview-content' ).get( 0 ) );
+                                               callback.call( self, editor, this, this /* back compat */ );
                                        } );
                        } );
                },
@@ -416,23 +420,18 @@ window.wp = window.wp || {};
                 */
                replaceMarkers: function() {
                        this.getMarkers( function( editor, node ) {
-                               if ( $( node ).text() !== this.text ) {
+                               var $viewNode;
+
+                               if ( ! this.loader && $( node ).text() !== this.text ) {
                                        editor.dom.setAttrib( node, 'data-wpview-marker', null );
                                        return;
                                }
 
-                               editor.dom.replace(
-                                       editor.dom.createFragment(
-                                               '<div class="wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '">' +
-                                                       '<p class="wpview-selection-before">\u00a0</p>' +
-                                                       '<div class="wpview-body" contenteditable="false">' +
-                                                               '<div class="wpview-content wpview-type-' + this.type + '"></div>' +
-                                                       '</div>' +
-                                                       '<p class="wpview-selection-after">\u00a0</p>' +
-                                               '</div>'
-                                       ),
-                                       node
+                               $viewNode = editor.$(
+                                       '<div class="wpview wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '" contenteditable="false"></div>'
                                );
+
+                               editor.$( node ).replaceWith( $viewNode );
                        } );
                },
 
@@ -458,17 +457,20 @@ window.wp = window.wp || {};
                        } else if ( _.isString( content ) && content.indexOf( '<script' ) !== -1 ) {
                                this.setIframes( '', content, callback, rendered );
                        } else {
-                               this.getNodes( function( editor, node, contentNode ) {
+                               this.getNodes( function( editor, node ) {
                                        content = content.body || content;
 
                                        if ( content.indexOf( '<iframe' ) !== -1 ) {
-                                               content += '<div class="wpview-overlay"></div>';
+                                               content += '<span class="mce-shim"></span>';
                                        }
 
-                                       contentNode.innerHTML = '';
-                                       contentNode.appendChild( _.isString( content ) ? editor.dom.createFragment( content ) : content );
+                                       editor.undoManager.transact( function() {
+                                               node.innerHTML = '';
+                                               node.appendChild( _.isString( content ) ? editor.dom.createFragment( content ) : content );
+                                               editor.dom.add( node, 'span', { 'class': 'wpview-end' } );
+                                       } );
 
-                                       callback && callback.call( this, editor, node, contentNode );
+                                       callback && callback.call( this, editor, node );
                                }, rendered );
                        }
                },
@@ -482,10 +484,9 @@ window.wp = window.wp || {};
                 * @param {Boolean}  rendered Only set for (un)rendered nodes. Optional.
                 */
                setIframes: function( head, body, callback, rendered ) {
-                       var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
-                               self = this;
+                       var self = this;
 
-                       this.getNodes( function( editor, node, contentNode ) {
+                       this.getNodes( function( editor, node ) {
                                var dom = editor.dom,
                                        styles = '',
                                        bodyClasses = editor.getBody().className || '',
@@ -499,30 +500,53 @@ window.wp = window.wp || {};
                                        }
                                } );
 
+                               if ( self.iframeHeight ) {
+                                       dom.add( node, 'span', {
+                                               'data-mce-bogus': 1,
+                                               style: {
+                                                       display: 'block',
+                                                       width: '100%',
+                                                       height: self.iframeHeight
+                                               }
+                                       }, '\u200B' );
+                               }
+
                                // Seems the browsers need a bit of time to insert/set the view nodes,
                                // or the iframe will fail especially when switching Text => Visual.
                                setTimeout( function() {
-                                       var iframe, iframeDoc, observer, i;
-
-                                       contentNode.innerHTML = '';
+                                       var iframe, iframeWin, iframeDoc, MutationObserver, observer, i, block;
+
+                                       editor.undoManager.transact( function() {
+                                               node.innerHTML = '';
+
+                                               iframe = dom.add( node, 'iframe', {
+                                                       /* jshint scripturl: true */
+                                                       src: tinymce.Env.ie ? 'javascript:""' : '',
+                                                       frameBorder: '0',
+                                                       allowTransparency: 'true',
+                                                       scrolling: 'no',
+                                                       'class': 'wpview-sandbox',
+                                                       style: {
+                                                               width: '100%',
+                                                               display: 'block'
+                                                       },
+                                                       height: self.iframeHeight
+                                               } );
 
-                                       iframe = dom.add( contentNode, 'iframe', {
-                                               /* jshint scripturl: true */
-                                               src: tinymce.Env.ie ? 'javascript:""' : '',
-                                               frameBorder: '0',
-                                               allowTransparency: 'true',
-                                               scrolling: 'no',
-                                               'class': 'wpview-sandbox',
-                                               style: {
-                                                       width: '100%',
-                                                       display: 'block'
-                                               }
+                                               dom.add( node, 'span', { 'class': 'mce-shim' } );
+                                               dom.add( node, 'span', { 'class': 'wpview-end' } );
                                        } );
 
-                                       dom.add( contentNode, 'div', { 'class': 'wpview-overlay' } );
-
-                                       iframeDoc = iframe.contentWindow.document;
+                                       // Bail if the iframe node is not attached to the DOM.
+                                       // Happens when the view is dragged in the editor.
+                                       // There is a browser restriction when iframes are moved in the DOM. They get emptied.
+                                       // The iframe will be rerendered after dropping the view node at the new location.
+                                       if ( ! iframe.contentWindow ) {
+                                               return;
+                                       }
 
+                                       iframeWin = iframe.contentWindow;
+                                       iframeDoc = iframeWin.document;
                                        iframeDoc.open();
 
                                        iframeDoc.write(
@@ -559,21 +583,36 @@ window.wp = window.wp || {};
                                        iframeDoc.close();
 
                                        function resize() {
-                                               var $iframe, iframeDocHeight;
+                                               var $iframe;
+
+                                               if ( block ) {
+                                                       return;
+                                               }
 
                                                // Make sure the iframe still exists.
                                                if ( iframe.contentWindow ) {
                                                        $iframe = $( iframe );
-                                                       iframeDocHeight = $( iframeDoc.body ).height();
+                                                       self.iframeHeight = $( iframeDoc.body ).height();
 
-                                                       if ( $iframe.height() !== iframeDocHeight ) {
-                                                               $iframe.height( iframeDocHeight );
+                                                       if ( $iframe.height() !== self.iframeHeight ) {
+                                                               $iframe.height( self.iframeHeight );
                                                                editor.nodeChanged();
                                                        }
                                                }
                                        }
 
-                                       $( iframe.contentWindow ).on( 'load', resize );
+                                       if ( self.iframeHeight ) {
+                                               block = true;
+
+                                               setTimeout( function() {
+                                                       block = false;
+                                                       resize();
+                                               }, 3000 );
+                                       }
+
+                                       $( iframeWin ).on( 'load', resize );
+
+                                       MutationObserver = iframeWin.MutationObserver || iframeWin.WebKitMutationObserver || iframeWin.MozMutationObserver;
 
                                        if ( MutationObserver ) {
                                                observer = new MutationObserver( _.debounce( resize, 100 ) );
@@ -583,27 +622,13 @@ window.wp = window.wp || {};
                                                        childList: true,
                                                        subtree: true
                                                } );
-
-                                               $( node ).one( 'wp-mce-view-unbind', function() {
-                                                       observer.disconnect();
-                                               } );
                                        } else {
                                                for ( i = 1; i < 6; i++ ) {
                                                        setTimeout( resize, i * 700 );
                                                }
                                        }
 
-                                       function classChange() {
-                                               iframeDoc.body.className = editor.getBody().className;
-                                       }
-
-                                       editor.on( 'wp-body-class-change', classChange );
-
-                                       $( node ).one( 'wp-mce-view-unbind', function() {
-                                               editor.off( 'wp-body-class-change', classChange );
-                                       } );
-
-                                       callback && callback.call( self, editor, node, contentNode );
+                                       callback && callback.call( self, editor, node );
                                }, 50 );
                        }, rendered );
                },
@@ -624,7 +649,7 @@ window.wp = window.wp || {};
                 * Sets an error for all view nodes tied to this view instance.
                 *
                 * @param {String} message  The error message to set.
-                * @param {String} dashicon A dashicon ID (optional). {@link https://developer.wordpress.org/resource/dashicons/}
+                * @param {String} dashicon A dashicon ID. Optional. {@link https://developer.wordpress.org/resource/dashicons/}
                 */
                setError: function( message, dashicon ) {
                        this.setContent(
@@ -643,7 +668,7 @@ window.wp = window.wp || {};
                 * @return {Object}
                 */
                match: function( content ) {
-                       var match = wp.shortcode.next( this.type, content );
+                       var match = shortcode.next( this.type, content );
 
                        if ( match ) {
                                return {
@@ -662,15 +687,16 @@ window.wp = window.wp || {};
                 * @param {String}         text   The new text.
                 * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
                 * @param {HTMLElement}    node   The view node to update.
+                * @param {Boolean}        force  Recreate the instance. Optional.
                 */
-               update: function( text, editor, node ) {
+               update: function( text, editor, node, force ) {
                        _.find( views, function( view, type ) {
                                var match = view.prototype.match( text );
 
                                if ( match ) {
                                        $( node ).data( 'rendered', false );
                                        editor.dom.setAttrib( node, 'data-wpview-text', encodeURIComponent( text ) );
-                                       wp.mce.views.createInstance( type, text, match.options ).render();
+                                       wp.mce.views.createInstance( type, text, match.options, force ).render();
                                        editor.focus();
 
                                        return true;
@@ -685,35 +711,52 @@ window.wp = window.wp || {};
                 * @param {HTMLElement}    node   The view node to remove.
                 */
                remove: function( editor, node ) {
-                       this.unbindNode.call( this, editor, node, $( node ).find( '.wpview-content' ).get( 0 ) );
-                       $( node ).trigger( 'wp-mce-view-unbind' );
+                       this.unbindNode.call( this, editor, node );
                        editor.dom.remove( node );
                        editor.focus();
                }
        } );
-} )( window, window.wp, window.jQuery );
+} )( window, window.wp, window.wp.shortcode, window.jQuery );
 
 /*
  * The WordPress core TinyMCE views.
  * Views for the gallery, audio, video, playlist and embed shortcodes,
  * and a view for embeddable URLs.
  */
-( function( window, views, $ ) {
-       var postID = $( '#post_ID' ).val() || 0,
-               media, gallery, av, embed;
+( function( window, views, media, $ ) {
+       var base, gallery, av, embed,
+               schema, parser, serializer;
+
+       function verifyHTML( string ) {
+               var settings = {};
+
+               if ( ! window.tinymce ) {
+                       return string.replace( /<[^>]+>/g, '' );
+               }
+
+               if ( ! string || ( string.indexOf( '<' ) === -1 && string.indexOf( '>' ) === -1 ) ) {
+                       return string;
+               }
+
+               schema = schema || new window.tinymce.html.Schema( settings );
+               parser = parser || new window.tinymce.html.DomParser( settings, schema );
+               serializer = serializer || new window.tinymce.html.Serializer( settings, schema );
+
+               return serializer.serialize( parser.parse( string, { forced_root_block: false } ) );
+       }
 
-       media = {
+       base = {
                state: [],
 
                edit: function( text, update ) {
-                       var media = wp.media[ this.type ],
-                               frame = media.edit( text );
+                       var type = this.type,
+                               frame = media[ type ].edit( text );
 
                        this.pausePlayers && this.pausePlayers();
 
                        _.each( this.state, function( state ) {
                                frame.state( state ).on( 'update', function( selection ) {
-                                       update( media.shortcode( selection ).string() );
+                                       update( media[ type ].shortcode( selection ).string(), type === 'gallery' );
                                } );
                        } );
 
@@ -725,12 +768,12 @@ window.wp = window.wp || {};
                }
        };
 
-       gallery = _.extend( {}, media, {
+       gallery = _.extend( {}, base, {
                state: [ 'gallery-edit' ],
-               template: wp.media.template( 'editor-gallery' ),
+               template: media.template( 'editor-gallery' ),
 
                initialize: function() {
-                       var attachments = wp.media.gallery.attachments( this.shortcode, postID ),
+                       var attachments = media.gallery.attachments( this.shortcode, media.view.settings.post.id ),
                                attrs = this.shortcode.attrs.named,
                                self = this;
 
@@ -751,8 +794,9 @@ window.wp = window.wp || {};
                                } );
 
                                self.render( self.template( {
+                                       verifyHTML: verifyHTML,
                                        attachments: attachments,
-                                       columns: attrs.columns ? parseInt( attrs.columns, 10 ) : wp.media.galleryDefaults.columns
+                                       columns: attrs.columns ? parseInt( attrs.columns, 10 ) : media.galleryDefaults.columns
                                } ) );
                        } )
                        .fail( function( jqXHR, textStatus ) {
@@ -761,7 +805,7 @@ window.wp = window.wp || {};
                }
        } );
 
-       av = _.extend( {}, media, {
+       av = _.extend( {}, base, {
                action: 'parse-media-shortcode',
 
                initialize: function() {
@@ -769,13 +813,13 @@ window.wp = window.wp || {};
 
                        if ( this.url ) {
                                this.loader = false;
-                               this.shortcode = wp.media.embed.shortcode( {
+                               this.shortcode = media.embed.shortcode( {
                                        url: this.text
                                } );
                        }
 
                        wp.ajax.post( this.action, {
-                               post_ID: postID,
+                               post_ID: media.view.settings.post.id,
                                type: this.shortcode.tag,
                                shortcode: this.shortcode.string()
                        } )
@@ -784,6 +828,7 @@ window.wp = window.wp || {};
                        } )
                        .fail( function( response ) {
                                if ( self.url ) {
+                                       self.ignore = true;
                                        self.removeMarkers();
                                } else {
                                        self.setError( response.message || response.statusText, 'admin-media' );
@@ -816,8 +861,7 @@ window.wp = window.wp || {};
                action: 'parse-embed',
 
                edit: function( text, update ) {
-                       var media = wp.media.embed,
-                               frame = media.edit( text, this.url ),
+                       var frame = media.embed.edit( text, this.url ),
                                self = this;
 
                        this.pausePlayers();
@@ -834,7 +878,7 @@ window.wp = window.wp || {};
                                if ( self.url ) {
                                        update( data.url );
                                } else {
-                                       update( media.shortcode( data ).string() );
+                                       update( media.embed.shortcode( data ).string() );
                                }
                        } );
 
@@ -878,4 +922,4 @@ window.wp = window.wp || {};
                        }
                }
        } ) );
-} )( window, window.wp.mce.views, window.jQuery );
+} )( window, window.wp.mce.views, window.wp.media, window.jQuery );