-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Paste replacement #976
Paste replacement #976
Changes from 16 commits
d84763d
eae8e25
c434d75
242c8cf
08b1490
693e3f1
091aa2f
5da4676
22f7e81
5d870ea
54d2498
17cc5dd
d2d8a13
2e852c0
ab1ff15
aa75e08
bd65d9c
b9b878a
31f7874
2055b4d
6388b13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,9 +8,55 @@ | |
|
||
CKEDITOR.plugins.add( 'pastefromwordimage', { | ||
requires: 'pastefromword', | ||
init: function() {} | ||
init: function( editor ) { | ||
if ( CKEDITOR.env.ie || CKEDITOR.env.iOS ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was there any particular reason why it was changed from If it is about iOS you may always go with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've change wrong file :( |
||
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 ] ); | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know the above two looks nice and super clear, but |
||
} | ||
|
||
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. | ||
* | ||
|
@@ -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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,20 @@ | ||
/* global promisePasteEvent */ | ||
/* exported assertWordFilter */ | ||
function assertWordFilter( editor, compareRawData ) { | ||
return function( input, output ) { | ||
return function( inputHtml, output, inputRtf ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you could use an object here |
||
var nativeDataTransfer = bender.tools.mockNativeDataTransfer(), | ||
dataTransfer; | ||
|
||
if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { | ||
nativeDataTransfer.setData( 'text/html', input ); | ||
nativeDataTransfer.setData( 'text/html', inputHtml ); | ||
if ( inputRtf ) { | ||
nativeDataTransfer.setData( 'text/rtf', inputRtf ); | ||
} | ||
} | ||
|
||
dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer ); | ||
|
||
return promisePasteEvent( editor, { dataValue: input, dataTransfer: dataTransfer } ) | ||
return promisePasteEvent( editor, { dataValue: inputHtml, dataTransfer: dataTransfer } ) | ||
.then( function( data ) { | ||
return [ | ||
// Lowercase, since old IE versions paste the HTML tags in uppercase. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,11 +15,13 @@ | |
* @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 Flag infor if RTF clipboard should be loaded in test case | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make it optional and |
||
* @returns {Function} | ||
*/ | ||
function createTestCase( options ) { | ||
return function() { | ||
var inputPath = [ '_fixtures', options.name, options.wordVersion, options.browser ].join( '/' ) + '.html', | ||
var inputPathHtml = [ '_fixtures', options.name, options.wordVersion, options.browser ].join( '/' ) + '.html', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may extract
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also |
||
inputPathRtf = options.includeRTF ? [ '_fixtures', options.name, options.wordVersion, options.browser ].join( '/' ) + '.rtf' : false, | ||
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. | ||
|
@@ -34,24 +36,40 @@ 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 avaialble 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. | ||
|
@@ -67,7 +85,7 @@ function createTestCase( options ) { | |
|
||
assert.isNotNull( expectedValue, '"expected.html" missing.' ); | ||
|
||
assertWordFilter( editor, options.compareRawData )( inputFixture, expectedValue ) | ||
assertWordFilter( editor, options.compareRawData )( inputFixtureHtml, expectedValue, inputFixtureRtf ) | ||
.then( function( values ) { | ||
resume( function() { | ||
nbspListener.removeListener(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,8 @@ function createTestSuite( options ) { | |
testData: { _should: { ignore: {} } }, | ||
ignoreAll: false, | ||
compareRawData: false, | ||
customFilters: null | ||
customFilters: null, | ||
includeRTF: false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing docs for |
||
} ); | ||
|
||
var testData = options.testData, | ||
|
@@ -52,7 +53,8 @@ function createTestSuite( options ) { | |
wordVersion: wordVersion, | ||
browser: options.browsers[ j ], | ||
compareRawData: options.compareRawData, | ||
customFilters: options.customFilters | ||
customFilters: options.customFilters, | ||
includeRTF: options.includeRTF | ||
} ); | ||
} | ||
} | ||
|
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==" /> 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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you could also add a container which will show all images pasted to the CKEditor (in the form already transformed by CKEditor), something like
Extracted Images
? If it's relatively easy to do (like 0,5h) you could take a look at it. From the other hand you can see pasted images inside editor instance, so not sure if it is needed. WDYT?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On this stage of implementation it might be problematic. I could process RTF clipboard, but it might contains also Word Shapes, which are ignored for processing while pasting. That's why preparing similar solution here might be repeating functionality of the plugin, which seems to be not a good thing.
It should be easier, when further changes related to file transfer will be implemented:
https://github.com/ckeditor/ckeditor-dev/blob/128516a52485505176189743962eaf97d52aa067/plugins/pastefromwordimage/plugin.js#L60-L63
There is nice event when will be exposed processed URL and image which is going to be embed. With that implementation it should be like 0,5h of work :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@msamsel Ok, so let's leave it as it is for now.