Copy-paste data fixes

* Assign cut/copy/paste events to the document so that
  they are triggered when the CE doesn't have real focus
  e.g. only a FocusableNode is selected
* Use clipboardData.setData when available. The key can
  be stored and retrieved perfectly in text/xcustom but
  this technique doesn't work in FF or IE
* Just use text content for key as this is what is written
  to the clipboard, so this can be used directly without
  having to get the content from the pasteTarget.

Bug: 48604
Bug: 49396
Change-Id: Ib3702f9441f6ee3fa34ec071f00994dd7e591d99
This commit is contained in:
Ed Sanders 2013-08-21 22:00:48 +01:00
parent 517a09943e
commit 99692230b1

View file

@ -63,13 +63,16 @@ ve.ce.Surface = function VeCeSurface( model, surface, options ) {
$documentNode.focus();
} );
this.$.on( {
this.$document.on( {
'cut': ve.bind( this.onCut, this ),
'copy': ve.bind( this.onCopy, this ),
'paste': ve.bind( this.onPaste, this ),
'paste': ve.bind( this.onPaste, this )
} );
this.$.on( {
'dragover': ve.bind( this.onDocumentDragOver, this ),
'drop': ve.bind( this.onDocumentDrop, this )
} );
if ( $.browser.msie ) {
this.$.on( 'beforepaste', ve.bind( this.onPaste, this ) );
}
@ -590,21 +593,31 @@ ve.ce.Surface.prototype.onCut = function ( e ) {
* @method
* @param {jQuery.Event} e Copy event
*/
ve.ce.Surface.prototype.onCopy = function () {
ve.ce.Surface.prototype.onCopy = function ( e ) {
var sel = rangy.getSelection( this.$document[0] ),
$frag = this.$$( sel.getRangeAt(0).cloneContents() ),
$frag = sel.rangeCount ? this.$$( sel.getRangeAt(0).cloneContents() ) : null,
slice = this.documentView.model.getSlice( this.model.getSelection() ),
key = '';
clipboardData = e.originalEvent.clipboardData,
fragText = $frag ? $frag.text() : '',
key = 've-' + fragText.replace( /\s/gm, '' );
// CLone the elements in the slice
slice.cloneElements();
// Create key from text and element names
$frag.contents().each( function () {
key += this.textContent || this.nodeName;
} );
key = 've-' + key.replace( /\s/gm, '' );
// Check we have setData and that it actually works (returns true)
if ( clipboardData && clipboardData.setData && clipboardData.setData( 'text/xcustom', '' ) ) {
// Webkit
e.preventDefault();
clipboardData.setData( 'text/xcustom', key );
// As we've disabled the default event we need to set the normal clipboard data
clipboardData.setData( 'text/plain', fragText );
} else {
if ( window.clipboardData ) {
// IE
e.originalEvent.returnValue = false;
window.clipboardData.setData( 'text/plain', fragText );
}
}
// Set clipboard
this.clipboard[key] = slice;
};
@ -615,7 +628,7 @@ ve.ce.Surface.prototype.onCopy = function () {
* @method
* @param {jQuery.Event} e Paste event
*/
ve.ce.Surface.prototype.onPaste = function () {
ve.ce.Surface.prototype.onPaste = function ( e ) {
// Prevent pasting until after we are done
if ( this.pasting ) {
return false;
@ -625,7 +638,10 @@ ve.ce.Surface.prototype.onPaste = function () {
var tx, scrollTop,
$window = $( ve.Element.getWindow( this.$$.context ) ),
view = this,
selection = this.model.getSelection();
selection = this.model.getSelection(),
clipboardData = e.originalEvent.clipboardData,
eventPasteKey = clipboardData.getData( 'text/xcustom' ) || null,
eventPasteText = clipboardData.getData( 'text/plain' ) || null;
this.surfaceObserver.stop( false, true );
@ -644,11 +660,16 @@ ve.ce.Surface.prototype.onPaste = function () {
var pasteData, slice, tx,
key = '';
// Create key from text and element names
view.$pasteTarget.hide().contents().each( function () {
key += this.textContent || this.nodeName;
} );
key = 've-' + key.replace( /\s/gm, '' );
if ( eventPasteKey ) {
key = eventPasteKey;
} else {
if ( eventPasteText ) {
key = eventPasteText;
} else {
key = view.$pasteTarget.text();
}
key = 've-' + key.replace( /\s/gm, '' );
}
// Get linear model from clipboard or create array from unknown pasted content
if ( view.clipboard[key] ) {