Skip to content

Commit

Permalink
Merge pull request #976 from ckeditor/t/662-2974
Browse files Browse the repository at this point in the history
Paste replacement
  • Loading branch information
f1ames authored Oct 25, 2017
2 parents 93ca7e9 + 6388b13 commit 734f957
Show file tree
Hide file tree
Showing 76 changed files with 61,649 additions and 19 deletions.
1 change: 1 addition & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ CKEDITOR.editorConfig = function( config ) {
'newpage,' +
'pagebreak,' +
'pastefromword,' +
'pastefromwordimage,' +
'pastetext,' +
'preview,' +
'print,' +
Expand Down
97 changes: 97 additions & 0 deletions dev/pastefromwordimage/getclipboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<title>Get Clipboard HTML and RTF</title>
<style>

textarea {
border: 1px solid #808080;
float: left;
height: 200px;
width: 100%;
overflow: auto;
margin-bottom: 20px;;
}

</style>
<script src="../../ckeditor.js"></script>
</head>
<body>
<p>Paste Inside the Editor:</p>
<div><textarea id="input"></textarea></div>
<div>
<div style="float: left; width: 49%">
<p>Raw HTML Data Received:</p>
<textarea data-name="input.html" id="rawHtml" readonly="readonly"></textarea>
<button id="htmlData">Save HTML Data</button>
</div>
<div style="float: right; width: 49%">
<p>Raw RTF Data Received:</p>
<textarea data-name="input.rtf" id="rawRtf" readonly="readonly"></textarea>
<button id="rtfData">Save RTF Data</button>
</div>
</div>
<div style="width: 100%; float: left;">
<p>After Paste Processing:</p>
<textarea id="output" readonly="readonly"></textarea>
</div>

<script>
var editor = CKEDITOR.replace( 'input', {
height: 100,
allowedContent: true,
plugins: 'pastefromword,pastefromwordimage,wysiwygarea'
} );

editor.on( 'paste', function( evt ) {
var val = evt.data.dataValue;

if ( evt.data.dataTransfer && evt.data.dataTransfer.getData( 'text/html', true ) ) {
val = evt.data.dataTransfer.getData( 'text/html', true );
}
document.getElementById( 'rawHtml' ).value = val;

if ( evt.data.dataTransfer && evt.data.dataTransfer.getData( 'text/rtf', true ) ) {
val = evt.data.dataTransfer.getData( 'text/rtf', true );
}
document.getElementById( 'rawRtf' ).value = val;

}, null, null, -1 );

editor.on( 'paste', function( evt ) {
setTimeout( function() {
document.getElementById( 'output' ).value = editor.getData();
}, 0 );
}, null, null, 999 );

var rtfButton = document.getElementById( 'rtfData' ),
htmlButton = document.getElementById( 'htmlData' );

rtfButton.onclick = save( document.getElementById( 'rawRtf' ) );
htmlButton.onclick = save( document.getElementById( 'rawHtml' ) );

function save( input ) {
return function() {
var textBlob = new Blob( [ input.value ], { type: 'text/plain' } );
var saveLink = document.createElement( 'a' );

saveLink.download = input.dataset.name;
saveLink.innerHTML = 'Save file';
if ( CKEDITOR.env.webkit ) {
saveLink.href = window.URL.createObjectURL( textBlob );
} else {
saveLink.href = window.URL.createObjectURL( textBlob );
saveLink.onclick = function( evt ) {
document.body.removeChild( evt.target )
};
saveLink.style.display = 'none';
document.body.appendChild( saveLink );
}
saveLink.click();
}
}


</script>
</body>
</html>
5 changes: 4 additions & 1 deletion plugins/pastefromword/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@
var data = evt.data,
dataTransferHtml = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ?
data.dataTransfer.getData( 'text/html', true ) : null,
// Required in paste from word image plugin (#662).
dataTransferRtf = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ?
data.dataTransfer.getData( 'text/rtf', true ) : null,
// Some commands fire paste event without setting dataTransfer property. In such case
// dataValue should be used.
mswordHtml = dataTransferHtml || data.dataValue,
pfwEvtData = { dataValue: mswordHtml },
pfwEvtData = { dataValue: mswordHtml, dataTransfer: { 'text/rtf': dataTransferRtf } },
officeMetaRegexp = /<meta\s*name=(?:\"|\')?generator(?:\"|\')?\s*content=(?:\"|\')?microsoft/gi,
wordRegexp = /(class=\"?Mso|style=(?:\"|\')[^\"]*?\bmso\-|w:WordDocument|<o:\w+>|<\/font>)/,
isOfficeContent = officeMetaRegexp.test( mswordHtml ) || wordRegexp.test( mswordHtml );
Expand Down
49 changes: 47 additions & 2 deletions plugins/pastefromwordimage/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,55 @@

CKEDITOR.plugins.add( 'pastefromwordimage', {
requires: 'pastefromword',
init: function() {}
init: function( editor ) {
if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {
return;
}

// Register a proper filter, so that images are not stripped out.
editor.filter.allow( 'img[src]' );

editor.on( 'afterPasteFromWord', pasteListener );
}
} );


function pasteListener( evt ) {
var pfwi = CKEDITOR.plugins.pastefromwordimage,
imgTags,
hexImages,
newSrcValues = [],
i;

imgTags = pfwi.extractImgTagsFromHtml( evt.data.dataValue );
if ( imgTags.length === 0 ) {
return;
}

hexImages = pfwi.extractImagesFromRtf( evt.data.dataTransfer[ 'text/rtf' ] );
if ( hexImages.length === 0 ) {
return;
}

CKEDITOR.tools.array.forEach( hexImages, function( img ) {
newSrcValues.push( createSrcWithBase64( img ) );
}, this );

// Assumption there is equal amount of Images in RTF and HTML source, so we can match them accordingly to existing order.
if ( imgTags.length === newSrcValues.length ) {
for ( i = 0; i < imgTags.length; i++ ) {
// Replace only `file` urls of images ( shapes get newSrcValue with null ).
if ( ( imgTags[ i ].indexOf( 'file://' ) === 0 ) && newSrcValues[ i ] ) {
evt.data.dataValue = evt.data.dataValue.replace( imgTags[ i ], newSrcValues[ i ] );
}
}
}
}

function createSrcWithBase64( img ) {
return img.type ? 'data:' + img.type + ';base64,' + CKEDITOR.tools.convertBytesToBase64( CKEDITOR.tools.convertHexStringToBytes( img.hex ) ) : null;
}

/**
* Help methods used by paste from word image plugin.
*
Expand All @@ -36,7 +82,6 @@
rePictureOrShape = new RegExp( '(?:(' + rePictureHeader.source + ')|(' + reShapeHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g' ),
wholeImages,
imageType;

wholeImages = rtfContent.match( rePictureOrShape );
if ( !wholeImages ) {
return ret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ function assertWordFilter( editor, compareRawData ) {
dataTransfer;

if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {
nativeDataTransfer.setData( 'text/html', input );
nativeDataTransfer.setData( 'text/html', input[ 'text/html' ] );
if ( input[ 'text/rtf' ] ) {
nativeDataTransfer.setData( 'text/rtf', input[ 'text/rtf' ] );
}
}

dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer );

return promisePasteEvent( editor, { dataValue: input, dataTransfer: dataTransfer } )
return promisePasteEvent( editor, { dataValue: input[ 'text/html' ], dataTransfer: dataTransfer } )
.then( function( data ) {
return [
// Lowercase, since old IE versions paste the HTML tags in uppercase.
Expand Down
39 changes: 28 additions & 11 deletions tests/plugins/pastefromword/generated/_helpers/createTestCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
* @param {Boolean} [options.compareRawData=false] If `true` test case will assert against raw paste's `data.dataValue` rather than
* what will appear in the editor after all transformations and filtering.
* @param {Array} [options.customFilters] Array of custom filters (like [ pfwTools.filters.font ]) which will be used during assertions.
* @param {Boolean} [options.includeRTF=false] Whether RTF clipboard should be loaded in test case.
* @returns {Function}
*/
function createTestCase( options ) {
return function() {
var inputPath = [ '_fixtures', options.name, options.wordVersion, options.browser ].join( '/' ) + '.html',
var inputPath = [ '_fixtures', options.name, options.wordVersion, options.browser ].join( '/' ),
inputPathHtml = inputPath + '.html',
inputPathRtf = inputPath + '.rtf',
outputPath = [ '_fixtures', options.name, '/expected.html' ].join( '/' ),
specialCasePath = [ '_fixtures', options.name, options.wordVersion, 'expected_' + options.browser ].join( '/' ) + '.html',
deCasher = '?' + Math.random().toString( 36 ).replace( /^../, '' ), // Used to trick the browser into not caching the html files.
Expand All @@ -34,24 +37,38 @@ function createTestCase( options ) {
} );

return deferred.promise;
};
},
loadQueue = [
load( inputPathHtml + deCasher ),
load( outputPath + deCasher ),
load( specialCasePath + deCasher )
];
if ( options.includeRTF ) {
loadQueue.push( load( inputPathRtf + deCasher ) );
}

Q.all( [
load( inputPath + deCasher ),
load( outputPath + deCasher ),
load( specialCasePath + deCasher )
] ).done( function( values ) {
var inputFixture = values[ 0 ],

Q.all( loadQueue ).done( function( values ) {
var inputFixtureHtml = values[ 0 ],
inputFixtureRtf = options.includeRTF ? values[ 3 ] : null ,
// If browser-customized expected result was found, use it. Otherwise go with the regular expected.
expectedValue = values[ 2 ] !== null ? values[ 2 ] : values[ 1 ];

// null means that fixture file was not found - skipping test.
if ( inputFixture === null ) {
// Null means that fixture file was not found in case of regular test - skipping test.
// In case of using RTF clipboard it's required to have both nulls.
if ( inputFixtureHtml === null && ( !options.includeRTF || inputFixtureRtf === null ) ) {
resume( function() {
assert.ignore();
} );
return;
}
// Single null when RTF is available means that one of 2 required files is missing.
else if ( options.includeRTF && ( inputFixtureHtml === null || inputFixtureRtf === null ) ) {
resume( function() {
assert.isNotNull( inputFixtureHtml, '"' + inputPathHtml + '" file is missing' );
assert.isNotNull( inputFixtureRtf, '"' + inputPathRtf + '" file is missing' );
} );
}

var nbspListener = editor.once( 'paste', function( evt ) {
// Clipboard strips white spaces from pasted content if those are not encoded.
Expand All @@ -67,7 +84,7 @@ function createTestCase( options ) {

assert.isNotNull( expectedValue, '"expected.html" missing.' );

assertWordFilter( editor, options.compareRawData )( inputFixture, expectedValue )
assertWordFilter( editor, options.compareRawData )( { 'text/html': inputFixtureHtml, 'text/rtf': inputFixtureRtf }, expectedValue )
.then( function( values ) {
resume( function() {
nbspListener.removeListener();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @param {Boolean} [options.compareRawData=false] If `true` test case will assert against raw paste's `data.dataValue` rather than
* what will appear in the editor after all transformations and filtering.
* @param {Boolean} [options.ignoreAll=false] Whenever to ignore all tests.
* @param {Boolean} [options.includeRTF=false] Whether RTF clipboard should be loaded in test case.
* @returns {Object} Test data object which should be passed to `bender.test` function.
*/
function createTestSuite( options ) {
Expand All @@ -24,7 +25,8 @@ function createTestSuite( options ) {
testData: { _should: { ignore: {} } },
ignoreAll: false,
compareRawData: false,
customFilters: null
customFilters: null,
includeRTF: false
} );

var testData = options.testData,
Expand Down Expand Up @@ -52,7 +54,8 @@ function createTestSuite( options ) {
wordVersion: wordVersion,
browser: options.browsers[ j ],
compareRawData: options.compareRawData,
customFilters: options.customFilters
customFilters: options.customFilters,
includeRTF: options.includeRTF
} );
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/plugins/pastefromword/generated/_helpers/pfwTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
'unicode-bidi,direction,dir,lang,page-break-after};td[valign]',
disallowedContent: 'td{vertical-align}'
},
// Preferred editor config for generated tests with PFW Image.
imageDefaultConfig: {
language: 'en',
removePlugins: 'dialogadvtab,flash,showborders,horizontalrule',
colorButton_normalizeBackground: false,
extraAllowedContent: 'span{line-height,background,font-weight,font-style,text-decoration,text-underline,display,' +
'page-break-before,height,tab-stops,layout-grid-mode,text-justify,-ms-layout-grid-mode,-ms-text-justify,' +
'unicode-bidi,direction,dir,lang,page-break-after};td[valign]',
disallowedContent: 'td{vertical-align};*[data-cke-*];span{font-family}'
},

// Filters for use in compatHtml in tests.
filters: {
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p style="margin-left:0in; margin-right:0in"><span style="font-size:11pt"><span style="line-height:107%"><span>Kitty from internet: <img alt="http://placekitten.com/200/305" style="width:200px; height:305px" src="http://placekitten.com/200/305" /></span></span></span></p><p style="margin-left:0in; margin-right:0in"><span style="font-size:11pt"><span style="line-height:107%"><span>My drawing: <img style="width:32px; height:32px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAOwgAADsIBFShKgAAAAOdJREFUSEvtljEWgjAQRA0HsLG19P4nsrS18QK6efFBMpMsS1hS+KQDNn+YAbIbntfb5XE/lcf7BRdMp+GMZQIPvLSPPnNAphDYia5qLAI6ne0b678CrWrm2iNNazUBC33Wqz6iEKIA39uEVjSigBc9yTBtMn3h5iK2jgJ94SgPUAi40AHiHBFbGSjgkk9ykKMGOjB/itsK/w5W81oi8uo2sCP92DtwSQkgwyPaaaLSvo5umfWIOnw055JWL+V9sfVP6SPPqMFL97G6H0BB3g9w+O1IX6HLrUkm7Lwijko0hRsd8FqBfwBKE0DK1O0llgAAAABJRU5ErkJggg==" />&nbsp;hehehehe :D <img style="width:32px; height:32px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAOwgAADsIBFShKgAAAAU1JREFUSEu9ljEOwjAMRZvuXZhhAiQOADvszNwBcQkugbgDMzvscAAkxNQZBjhAsIjaJnaT2m1p1aWN/Z/tOK3Vsz/qpfcIXR+F33CeE42sXoOx0hq/jeqp59ouxgU0lC5jWICg+vC0ROk/FsdQ2bI8MoBHnepSUS/pxwgBOOo5rxyT6B/ADX++unJaxmdzPkyLpUTHTXsmHMtHxU2C5fhigKjuYkAr6khE6XcRh1kb7Ld2aOnaeaRRU3u7ozrcA1F9Lpsb3L49sKUEGRjRsDRFcgE03tluIu4in0OgGpUMVgYQbH5XKiIDFoCKMusDjgWg4vueQUwq4Tw6PQfVJ1ladDj53gyYVeLXx9kDaaRMe9xFrSRhs+OITEvM0Fhmpf9k4yn69oF98KcP638fW/wMX0LCwcvI/GF0dLuolQ13RWKYsJ1+gOXaGOIL4l+GAZPNqBWlOAAAAABJRU5ErkJggg==" /></span></span></span></p>
Loading

0 comments on commit 734f957

Please sign in to comment.