text = tinymce.DOM.decode( text );
+ if ( text.indexOf( '[' ) !== -1 && text.indexOf( ']' ) !== -1 ) {
+ // Looks like a shortcode? Remove any line breaks from inside of shortcodes
+ // or autop will replace them with <p> and <br> later and the string won't match.
+ text = text.replace( /\[[^\]]+\]/g, function( match ) {
+ return match.replace( /[\r\n]/g, '' );
+ });
+ }
+
if ( ! force ) {
instance = this.getInstance( text );
*/
render: function( force ) {
_.each( instances, function( instance ) {
- instance.render( force );
+ instance.render( null, force );
} );
},
initialize: function() {},
/**
- * Retuns the content to render in the view node.
+ * Returns the content to render in the view node.
*
* @return {*}
*/
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();
* 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 );
},
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 */ );
} );
} );
},
*/
replaceMarkers: function() {
this.getMarkers( function( editor, node ) {
- var selected = node === editor.selection.getNode(),
- $viewNode;
+ var $viewNode;
if ( ! this.loader && $( node ).text() !== this.text ) {
editor.dom.setAttrib( node, 'data-wpview-marker', null );
}
$viewNode = editor.$(
- '<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>'
+ '<div class="wpview wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '" contenteditable="false"></div>'
);
editor.$( node ).replaceWith( $viewNode );
-
- if ( selected ) {
- editor.wp.setViewCursor( false, $viewNode[0] );
- }
} );
},
} 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 );
}
},
* @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 || '',
- editorHead = editor.getDoc().getElementsByTagName( 'head' )[0];
+ editorHead = editor.getDoc().getElementsByTagName( 'head' )[0],
+ iframe, iframeWin, iframeDoc, MutationObserver, observer, i, block;
tinymce.each( dom.$( 'link[rel="stylesheet"]', editorHead ), function( link ) {
if ( link.href && link.href.indexOf( 'skins/lightgray/content.min.css' ) === -1 &&
} );
if ( self.iframeHeight ) {
- dom.add( contentNode, 'div', { style: {
- width: '100%',
- height: 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, block;
-
- contentNode.innerHTML = '';
+ editor.undoManager.transact( function() {
+ node.innerHTML = '';
- iframe = dom.add( contentNode, 'iframe', {
+ iframe = dom.add( node, 'iframe', {
/* jshint scripturl: true */
src: tinymce.Env.ie ? 'javascript:""' : '',
frameBorder: '0',
height: self.iframeHeight
} );
- dom.add( contentNode, 'div', { 'class': 'wpview-overlay' } );
-
- 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();
-
- function resize() {
- var $iframe;
-
- if ( block ) {
- return;
- }
+ dom.add( node, 'span', { 'class': 'mce-shim' } );
+ dom.add( node, 'span', { 'class': 'wpview-end' } );
+ } );
- // Make sure the iframe still exists.
- if ( iframe.contentWindow ) {
- $iframe = $( iframe );
- self.iframeHeight = $( iframeDoc.body ).height();
+ // 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;
+ }
- if ( $iframe.height() !== self.iframeHeight ) {
- $iframe.height( self.iframeHeight );
- editor.nodeChanged();
- }
- }
+ iframeWin = iframe.contentWindow;
+ iframeDoc = iframeWin.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();
+
+ function resize() {
+ var $iframe;
+
+ if ( block ) {
+ return;
}
- if ( self.iframeHeight ) {
- block = true;
+ // Make sure the iframe still exists.
+ if ( iframe.contentWindow ) {
+ $iframe = $( iframe );
+ self.iframeHeight = $( iframeDoc.body ).height();
- setTimeout( function() {
- block = false;
- resize();
- }, 3000 );
+ if ( $iframe.height() !== self.iframeHeight ) {
+ $iframe.height( self.iframeHeight );
+ editor.nodeChanged();
+ }
}
+ }
- $( iframe.contentWindow ).on( 'load', resize );
+ if ( self.iframeHeight ) {
+ block = true;
- if ( MutationObserver ) {
- observer = new MutationObserver( _.debounce( resize, 100 ) );
+ setTimeout( function() {
+ block = false;
+ resize();
+ }, 3000 );
+ }
- observer.observe( iframeDoc.body, {
- attributes: true,
- childList: true,
- subtree: true
- } );
+ function reload() {
+ $( node ).data( 'rendered', null );
- $( node ).one( 'wp-mce-view-unbind', function() {
- observer.disconnect();
- } );
- } else {
- for ( i = 1; i < 6; i++ ) {
- setTimeout( resize, i * 700 );
- }
- }
+ setTimeout( function() {
+ wp.mce.views.render();
+ } );
+ }
- function classChange() {
- iframeDoc.body.className = editor.getBody().className;
- }
+ $( iframeWin ).on( 'load', resize ).on( 'unload', reload );
- editor.on( 'wp-body-class-change', classChange );
+ MutationObserver = iframeWin.MutationObserver || iframeWin.WebKitMutationObserver || iframeWin.MozMutationObserver;
- $( node ).one( 'wp-mce-view-unbind', function() {
- editor.off( 'wp-body-class-change', classChange );
+ if ( MutationObserver ) {
+ observer = new MutationObserver( _.debounce( resize, 100 ) );
+
+ observer.observe( iframeDoc.body, {
+ attributes: true,
+ childList: true,
+ subtree: true
} );
+ } else {
+ for ( i = 1; i < 6; i++ ) {
+ setTimeout( resize, i * 700 );
+ }
+ }
- callback && callback.call( self, editor, node, contentNode );
- }, 50 );
+ callback && callback.call( self, editor, node );
}, rendered );
},
/**
* Sets a loader for all view nodes tied to this view instance.
*/
- setLoader: function() {
+ setLoader: function( dashicon ) {
this.setContent(
'<div class="loading-placeholder">' +
- '<div class="dashicons dashicons-admin-media"></div>' +
+ '<div class="dashicons dashicons-' + ( dashicon || 'admin-media' ) + '"></div>' +
'<div class="wpview-loading"><ins></ins></div>' +
'</div>'
);
* @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();
}