mirror of
https://github.com/WordPress/WordPress.git
synced 2024-09-11 23:28:18 +02:00
* When a MCE view is selected, normal keystrokes (those without command/control) are blocked. * Backspace/delete will successfully remove the view. * Arrow handling will be added at a later point. * When a MCE view is selected and content is pasted or injected (e.g. when an attachment is added), the content is added to the first text node after the view. If the view is at the end of the document or the next element is another view, a text node is injected into the DOM immediately after the selected view. see #21390, #21812, #21813, #21815. git-svn-id: http://core.svn.wordpress.org/trunk@22210 1a063a9b-81f0-0310-95a4-ce76da25c4cd
188 lines
5.0 KiB
JavaScript
188 lines
5.0 KiB
JavaScript
/**
|
|
* WordPress View plugin.
|
|
*/
|
|
|
|
(function() {
|
|
var VK = tinymce.VK,
|
|
TreeWalker = tinymce.dom.TreeWalker,
|
|
selected;
|
|
|
|
tinymce.create('tinymce.plugins.wpView', {
|
|
init : function( editor, url ) {
|
|
var wpView = this;
|
|
|
|
// Check if the `wp.mce` API exists.
|
|
if ( typeof wp === 'undefined' || ! wp.mce )
|
|
return;
|
|
|
|
editor.onPreInit.add( function( editor ) {
|
|
// Add elements so we can set `contenteditable` to false.
|
|
editor.schema.addValidElements('div[*],span[*]');
|
|
});
|
|
|
|
// When the editor's content changes, scan the new content for
|
|
// matching view patterns, and transform the matches into
|
|
// view wrappers. Since the editor's DOM is outdated at this point,
|
|
// we'll wait to render the views.
|
|
editor.onBeforeSetContent.add( function( editor, o ) {
|
|
if ( ! o.content )
|
|
return;
|
|
|
|
o.content = wp.mce.view.toViews( o.content );
|
|
});
|
|
|
|
// When the editor's content has been updated and the DOM has been
|
|
// processed, render the views in the document.
|
|
editor.onSetContent.add( function( editor, o ) {
|
|
wp.mce.view.render( editor.getDoc() );
|
|
});
|
|
|
|
editor.onInit.add( function( editor ) {
|
|
|
|
// When a view is selected, ensure content that is being pasted
|
|
// or inserted is added to a text node (instead of the view).
|
|
editor.selection.onBeforeSetContent.add( function( selection, o ) {
|
|
var view = wpView.getParentView( selection.getNode() ),
|
|
walker, target;
|
|
|
|
// If the selection is not within a view, bail.
|
|
if ( ! view )
|
|
return;
|
|
|
|
// If there are no additional nodes or the next node is a
|
|
// view, create a text node after the current view.
|
|
if ( ! view.nextSibling || wpView.isView( view.nextSibling ) ) {
|
|
target = editor.getDoc().createTextNode('');
|
|
editor.dom.insertAfter( target, view );
|
|
|
|
// Otherwise, find the next text node.
|
|
} else {
|
|
walker = new TreeWalker( view.nextSibling, view.nextSibling );
|
|
target = walker.next();
|
|
}
|
|
|
|
// Select the `target` text node.
|
|
selection.select( target );
|
|
selection.collapse( true );
|
|
});
|
|
|
|
// When the selection's content changes, scan any new content
|
|
// for matching views and immediately render them.
|
|
//
|
|
// Runs on paste and on inserting nodes/html.
|
|
editor.selection.onSetContent.add( function( selection, o ) {
|
|
if ( ! o.context )
|
|
return;
|
|
|
|
var node = selection.getNode();
|
|
|
|
if ( ! node.innerHTML )
|
|
return;
|
|
|
|
node.innerHTML = wp.mce.view.toViews( node.innerHTML );
|
|
wp.mce.view.render( node );
|
|
});
|
|
});
|
|
|
|
// When the editor's contents are being accessed as a string,
|
|
// transform any views back to their text representations.
|
|
editor.onPostProcess.add( function( editor, o ) {
|
|
if ( ( ! o.get && ! o.save ) || ! o.content )
|
|
return;
|
|
|
|
o.content = wp.mce.view.toText( o.content );
|
|
});
|
|
|
|
// Triggers when the selection is changed.
|
|
// Add the event handler to the top of the stack.
|
|
editor.onNodeChange.addToTop( function( editor, controlManager, node, collapsed, o ) {
|
|
var view = wpView.getParentView( node );
|
|
|
|
// Update the selected view.
|
|
if ( view ) {
|
|
wpView.select( view );
|
|
|
|
// Prevent the selection from propagating to other plugins.
|
|
return false;
|
|
|
|
// If we've clicked off of the selected view, deselect it.
|
|
} else {
|
|
wpView.deselect();
|
|
}
|
|
});
|
|
|
|
editor.onKeyDown.addToTop( function( editor, event ) {
|
|
var keyCode = event.keyCode,
|
|
view, instance;
|
|
|
|
// If a view isn't selected, let the event go on its merry way.
|
|
if ( ! selected )
|
|
return;
|
|
|
|
// If the caret is not within the selected view, deselect the
|
|
// view and bail.
|
|
view = wpView.getParentView( editor.selection.getNode() );
|
|
if ( view !== selected ) {
|
|
wpView.deselect();
|
|
return;
|
|
}
|
|
|
|
// If delete or backspace is pressed, delete the view.
|
|
if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) {
|
|
if ( (instance = wp.mce.view.instance( selected )) ) {
|
|
instance.remove();
|
|
wpView.deselect();
|
|
}
|
|
}
|
|
|
|
// Let keypresses that involve the command or control keys through.
|
|
// Also, let any of the F# keys through.
|
|
if ( event.metaKey || event.ctrlKey || ( keyCode >= 112 && keyCode <= 123 ) )
|
|
return;
|
|
|
|
event.preventDefault();
|
|
});
|
|
},
|
|
|
|
getParentView : function( node ) {
|
|
while ( node ) {
|
|
if ( this.isView( node ) )
|
|
return node;
|
|
|
|
node = node.parentNode;
|
|
}
|
|
},
|
|
|
|
isView : function( node ) {
|
|
return (/(?:^|\s)wp-view-wrap(?:\s|$)/).test( node.className );
|
|
},
|
|
|
|
select : function( view ) {
|
|
if ( view === selected )
|
|
return;
|
|
|
|
this.deselect();
|
|
selected = view;
|
|
wp.mce.view.select( selected );
|
|
},
|
|
|
|
deselect : function() {
|
|
if ( selected )
|
|
wp.mce.view.deselect( selected );
|
|
selected = null;
|
|
},
|
|
|
|
getInfo : function() {
|
|
return {
|
|
longname : 'WordPress Views',
|
|
author : 'WordPress',
|
|
authorurl : 'http://wordpress.org',
|
|
infourl : 'http://wordpress.org',
|
|
version : '1.0'
|
|
};
|
|
}
|
|
});
|
|
|
|
// Register plugin
|
|
tinymce.PluginManager.add( 'wpview', tinymce.plugins.wpView );
|
|
})(); |