TinyMCE wpViews:

- Prevent undo steps from being added when the body of a wpview changes.
- Add unbind() to handle cleanup on DOM rebuilding in TinyMCE.
- Ensure that MediaElement's cleanup routine is run on every player in all instances of the editor.
- Initialize the players after some delay to ensure CSS is loaded.
Props gcorne and wonderboymusic, fixes #27389

git-svn-id: https://develop.svn.wordpress.org/trunk@28084 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2014-04-12 00:45:48 +00:00
parent b5612f8b80
commit 17a90fff8b
3 changed files with 57 additions and 21 deletions

View File

@ -41,12 +41,15 @@ window.wp = window.wp || {};
doc = editor.getDoc(); doc = editor.getDoc();
$( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) { $( doc ).find( '[data-wpview-text="' + this.encodedText + '"]' ).each(function (i, elem) {
var node = $( elem ); var node = $( elem );
node.html( html ); // 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 ); $( self ).trigger( 'ready', elem );
}); });
} }
}, this ); }, this );
} },
unbind: function() {}
} ); } );
// take advantage of the Backbone extend method // take advantage of the Backbone extend method
@ -92,6 +95,17 @@ window.wp = window.wp || {};
delete views[ type ]; delete views[ type ];
}, },
/**
* wp.mce.views.unbind( editor )
*
* The editor DOM is being rebuilt, run cleanup.
*/
unbind: function() {
_.each( instances, function( instance ) {
instance.unbind();
} );
},
/** /**
* toViews( content ) * toViews( content )
* Scans a `content` string for each view's pattern, replacing any * Scans a `content` string for each view's pattern, replacing any
@ -339,6 +353,7 @@ window.wp = window.wp || {};
* @mixin * @mixin
*/ */
wp.mce.media = { wp.mce.media = {
loaded: false,
/** /**
* @global wp.shortcode * @global wp.shortcode
* *
@ -410,6 +425,7 @@ window.wp = window.wp || {};
*/ */
wp.mce.media.View = wp.mce.View.extend({ wp.mce.media.View = wp.mce.View.extend({
initialize: function( options ) { initialize: function( options ) {
this.players = [];
this.shortcode = options.shortcode; this.shortcode = options.shortcode;
_.bindAll( this, 'setPlayer' ); _.bindAll( this, 'setPlayer' );
$(this).on( 'ready', this.setPlayer ); $(this).on( 'ready', this.setPlayer );
@ -460,8 +476,9 @@ window.wp = window.wp || {};
media = wp.media.view.MediaDetails.prepareSrc( media.get(0) ); media = wp.media.view.MediaDetails.prepareSrc( media.get(0) );
setTimeout( function() { setTimeout( function() {
self.player = new MediaElementPlayer( media, this.mejsSettings ); wp.mce.media.loaded = true;
}, 75 ); self.players.push( new MediaElementPlayer( media, self.mejsSettings ) );
}, wp.mce.media.loaded ? 10 : 500 );
}, },
/** /**
@ -475,6 +492,15 @@ window.wp = window.wp || {};
wp.media[ this.shortcode.tag ].defaults wp.media[ this.shortcode.tag ].defaults
); );
return this.template({ model: attrs }); return this.template({ model: attrs });
},
unbind: function() {
var self = this;
this.pauseAllPlayers();
_.each( this.players, function (player) {
self.removePlayer( player );
} );
this.players = [];
} }
}); });
_.extend( wp.mce.media.View.prototype, wp.media.mixin ); _.extend( wp.mce.media.View.prototype, wp.media.mixin );

View File

@ -128,8 +128,8 @@
* MediaElement tries to pull the audio/video tag out of * MediaElement tries to pull the audio/video tag out of
* its container and re-add it to the DOM. * its container and re-add it to the DOM.
*/ */
removePlayer: function() { removePlayer: function(t) {
var t = this.player, featureIndex, feature; var featureIndex, feature;
// invoke features cleanup // invoke features cleanup
for ( featureIndex in t.options.features ) { for ( featureIndex in t.options.features ) {
@ -165,7 +165,7 @@
unsetPlayer : function() { unsetPlayer : function() {
if ( this.player ) { if ( this.player ) {
wp.media.mixin.pauseAllPlayers(); wp.media.mixin.pauseAllPlayers();
wp.media.mixin.removePlayer.apply( this ); wp.media.mixin.removePlayer( this.player );
this.player = false; this.player = false;
} }
} }

View File

@ -80,11 +80,12 @@ tinymce.PluginManager.add( 'wpview', function( editor ) {
'contenteditable': 'true' 'contenteditable': 'true'
}, getViewText( viewNode ) ); }, getViewText( viewNode ) );
viewNode.appendChild( clipboard ); // Prepend inside the wrapper
viewNode.insertBefore( clipboard, viewNode.firstChild );
// Both of the following are necessary to prevent manipulating the selection/focus // Both of the following are necessary to prevent manipulating the selection/focus
editor.dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop ); dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop );
editor.dom.bind( selected, 'beforedeactivate focusin focusout', _stop ); dom.bind( selected, 'beforedeactivate focusin focusout', _stop );
// Make sure that the editor is focused. // Make sure that the editor is focused.
// It is possible that the editor is not focused when the mouse event fires // It is possible that the editor is not focused when the mouse event fires
@ -140,8 +141,14 @@ tinymce.PluginManager.add( 'wpview', function( editor ) {
return; return;
} }
// Remove the content of view wrappers from HTML string
function emptyViews( content ) {
return content.replace(/(<div[^>]+wpview-wrap[^>]+>)[\s\S]+?data-wpview-end[^>]*><\/ins><\/div>/g, '$1</div>' );
}
// Prevent adding undo levels on changes inside a view wrapper
editor.on( 'BeforeAddUndo', function( event ) { editor.on( 'BeforeAddUndo', function( event ) {
if ( selected && ! toRemove ) { if ( event.lastLevel && emptyViews( event.level.content ) === emptyViews( event.lastLevel.content ) ) {
event.preventDefault(); event.preventDefault();
} }
}); });
@ -149,12 +156,16 @@ tinymce.PluginManager.add( 'wpview', function( editor ) {
// When the editor's content changes, scan the new content for // When the editor's content changes, scan the new content for
// matching view patterns, and transform the matches into // matching view patterns, and transform the matches into
// view wrappers. // view wrappers.
editor.on( 'BeforeSetContent', function( e ) { editor.on( 'BeforeSetContent', function( event ) {
if ( ! e.content ) { if ( ! event.content ) {
return; return;
} }
e.content = wp.mce.views.toViews( e.content ); if ( ! event.initial ) {
wp.mce.views.unbind( editor );
}
event.content = wp.mce.views.toViews( event.content );
}); });
// When the editor's content has been updated and the DOM has been // When the editor's content has been updated and the DOM has been
@ -162,11 +173,7 @@ tinymce.PluginManager.add( 'wpview', function( editor ) {
editor.on( 'SetContent', function( event ) { editor.on( 'SetContent', function( event ) {
var body, padNode; var body, padNode;
// don't (re-)render views if the format of the content is raw wp.mce.views.render();
// to avoid adding additional undo levels on undo/redo
if ( event.format !== 'raw' ) {
wp.mce.views.render();
}
// Add padding <p> if the noneditable node is last // Add padding <p> if the noneditable node is last
if ( event.load || ! event.set ) { if ( event.load || ! event.set ) {
@ -175,7 +182,10 @@ tinymce.PluginManager.add( 'wpview', function( editor ) {
if ( isView( body.lastChild ) ) { if ( isView( body.lastChild ) ) {
padNode = createPadNode(); padNode = createPadNode();
body.appendChild( padNode ); body.appendChild( padNode );
editor.selection.setCursorLocation( padNode, 0 );
if ( ! event.initial ) {
editor.selection.setCursorLocation( padNode, 0 );
}
} }
} }
}); });