Skip to content

Commit b941fec

Browse files
committed
Merge branch 't/16705'
2 parents 4b2c8f8 + 5f1549b commit b941fec

File tree

5 files changed

+224
-0
lines changed

5 files changed

+224
-0
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Fixed Issues:
1414
* [#10373](http://dev.ckeditor.com/ticket/10373): Fixed: Context menu items can be dragged into the editor.
1515
* [#16728](http://dev.ckeditor.com/ticket/16728): [IE] Fixed: [Copy Formatting](http://ckeditor.com/addon/copyformatting) breaks editor in Quirks Mode.
1616
* [#16753](http://dev.ckeditor.com/ticket/16753): Fixed: `element.setSize` sets wrong editor's dimensions if border's width is in fraction of pixels.
17+
* [#16705](http://dev.ckeditor.com/ticket/16705): [Firefox] Fixed: Unable to paste images as Base64 strings when using [Clipboard](http://ckeditor.com/addon/clipboard).
1718

1819
## CKEditor 4.6.1
1920

plugins/clipboard/plugin.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,50 @@
145145

146146
CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) );
147147

148+
// Convert image file (if present) to base64 string for Firefox. Do it as the first
149+
// step as the conversion is asynchronous and should hold all further paste processing.
150+
if ( CKEDITOR.env.gecko ) {
151+
var supportedImageTypes = [ 'image/png', 'image/jpeg', 'image/gif' ],
152+
latestId;
153+
154+
editor.on( 'paste', function( evt ) {
155+
var dataObj = evt.data,
156+
data = dataObj.dataValue,
157+
dataTransfer = dataObj.dataTransfer;
158+
159+
// If data empty check for image content inside data transfer. #16705
160+
if ( !data && dataObj.method == 'paste' && dataTransfer && dataTransfer.getFilesCount() == 1 && latestId != dataTransfer.id ) {
161+
var file = dataTransfer.getFile( 0 );
162+
163+
if ( CKEDITOR.tools.indexOf( supportedImageTypes, file.type ) != -1 ) {
164+
var fileReader = new FileReader();
165+
166+
// Convert image file to img tag with base64 image.
167+
fileReader.addEventListener( 'load', function() {
168+
evt.data.dataValue = '<img src="' + fileReader.result + '" />';
169+
editor.fire( 'paste', evt.data );
170+
}, false );
171+
172+
// Proceed with normal flow if reading file was aborted.
173+
fileReader.addEventListener( 'abort', function() {
174+
editor.fire( 'paste', evt.data );
175+
}, false );
176+
177+
// Proceed with normal flow if reading file failed.
178+
fileReader.addEventListener( 'error', function() {
179+
editor.fire( 'paste', evt.data );
180+
}, false );
181+
182+
fileReader.readAsDataURL( file );
183+
184+
latestId = dataObj.dataTransfer.id;
185+
186+
evt.stop();
187+
}
188+
}
189+
}, null, null, 1 );
190+
}
191+
148192
editor.on( 'paste', function( evt ) {
149193
// Init `dataTransfer` if `paste` event was fired without it, so it will be always available.
150194
if ( !evt.data.dataTransfer ) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div id="editor">
2+
<p>Paste image from clipboard here:</p>
3+
</div>
4+
5+
<script>
6+
if ( !CKEDITOR.env.gecko ) {
7+
bender.ignore();
8+
}
9+
10+
CKEDITOR.replace( 'editor' );
11+
</script>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@bender-ui: collapsed
2+
@bender-tags: 4.6.2, tc, clipboard
3+
@bender-ckeditor-plugins: wysiwygarea, toolbar, undo, basicstyles, image, clipboard, sourcearea
4+
5+
## Scenario:
6+
7+
* Paste image from your clipboard into the editor.
8+
9+
**Expected**: Image is pasted as base64 encoded file.
10+

tests/plugins/clipboard/pasteimage.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/* bender-tags: editor,unit */
2+
/* bender-ckeditor-plugins: clipboard */
3+
4+
( function() {
5+
'use strict';
6+
7+
// Mock FileReader.
8+
( function() {
9+
var fileMockBase64 = ';base64,fileMockBase64=',
10+
fileMockType,
11+
readResultMock;
12+
13+
function FileReaderMock() {
14+
this.listeners = {};
15+
}
16+
17+
// Any MIME type.
18+
FileReaderMock.setFileMockType = function( type ) {
19+
fileMockType = type;
20+
};
21+
22+
// Result can be: load, abort, error.
23+
FileReaderMock.setReadResult = function( readResult ) {
24+
readResultMock = readResult;
25+
if ( !readResultMock ) {
26+
readResultMock = 'load';
27+
}
28+
};
29+
30+
FileReaderMock.prototype.addEventListener = function( eventName, callback ) {
31+
this.listeners[ eventName ] = callback;
32+
};
33+
34+
FileReaderMock.prototype.readAsDataURL = function() {
35+
CKEDITOR.tools.setTimeout( function() {
36+
this.result = ( readResultMock == 'load' ? 'data:' + fileMockType + fileMockBase64 : null );
37+
38+
if ( this.listeners[ readResultMock ] ) {
39+
this.listeners[ readResultMock ]();
40+
}
41+
}, 15, this );
42+
};
43+
44+
/* jshint ignore:start */
45+
FileReader = FileReaderMock;
46+
/* jshint ignore:end */
47+
} )();
48+
49+
// Mock paste file from clipboard.
50+
function mockPasteFile( editor, type ) {
51+
var nativeData = bender.tools.mockNativeDataTransfer(),
52+
dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData );
53+
54+
nativeData.files.push( {
55+
name: 'mock.file',
56+
type: type
57+
} );
58+
59+
dataTransfer.cacheData();
60+
61+
editor.fire( 'paste', {
62+
dataTransfer: dataTransfer,
63+
dataValue: '',
64+
method: 'paste',
65+
type: 'auto'
66+
} );
67+
}
68+
69+
70+
bender.editor = {
71+
config: {
72+
allowedContent: true
73+
}
74+
};
75+
76+
bender.test( {
77+
setUp: function() {
78+
if ( !CKEDITOR.env.gecko ) {
79+
assert.ignore();
80+
}
81+
FileReader.setFileMockType();
82+
FileReader.setReadResult();
83+
this.editor.focus();
84+
},
85+
86+
'test paste .png from clipboard': function() {
87+
FileReader.setFileMockType( 'image/png' );
88+
FileReader.setReadResult( 'load' );
89+
90+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
91+
this.assertPaste( 'image/png',
92+
'<p>Paste image here: <img data-cke-saved-src="data:image/png;base64,fileMockBase64=" src="data:image/png;base64,fileMockBase64=" />^@</p>' );
93+
},
94+
95+
'test paste .jpeg from clipboard': function() {
96+
FileReader.setFileMockType( 'image/jpeg' );
97+
FileReader.setReadResult( 'load' );
98+
99+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
100+
this.assertPaste( 'image/jpeg',
101+
'<p>Paste image here: <img data-cke-saved-src="data:image/jpeg;base64,fileMockBase64=" src="data:image/jpeg;base64,fileMockBase64=" />^@</p>' );
102+
},
103+
104+
'test paste .gif from clipboard': function() {
105+
FileReader.setFileMockType( 'image/gif' );
106+
FileReader.setReadResult( 'load' );
107+
108+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
109+
this.assertPaste( 'image/gif',
110+
'<p>Paste image here: <img data-cke-saved-src="data:image/gif;base64,fileMockBase64=" src="data:image/gif;base64,fileMockBase64=" />^@</p>' );
111+
},
112+
113+
'test unsupported file type': function() {
114+
FileReader.setFileMockType( 'application/pdf' );
115+
FileReader.setReadResult( 'load' );
116+
117+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
118+
this.assertPaste( 'application/pdf',
119+
'<p>Paste image here: ^@</p>' );
120+
},
121+
122+
'test aborted paste': function() {
123+
FileReader.setFileMockType( 'image/png' );
124+
FileReader.setReadResult( 'abort' );
125+
126+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
127+
this.assertPaste( 'image/png',
128+
'<p>Paste image here: ^@</p>' );
129+
},
130+
131+
'test failed paste': function() {
132+
FileReader.setFileMockType( 'image/png' );
133+
FileReader.setReadResult( 'error' );
134+
135+
bender.tools.selection.setWithHtml( this.editor, '<p>Paste image here: {}</p>' );
136+
this.assertPaste( 'image/png',
137+
'<p>Paste image here: ^@</p>' );
138+
},
139+
140+
assertPaste: function( type, expected ) {
141+
this.editor.once( 'paste', function() {
142+
resume( function() {
143+
assert.isInnerHtmlMatching( expected, bender.tools.selection.getWithHtml( this.editor ), {
144+
noTempElements: true,
145+
fixStyles: true,
146+
compareSelection: true,
147+
normalizeSelection: true
148+
} );
149+
} );
150+
}, this, null, 9999 );
151+
152+
mockPasteFile( this.editor, type );
153+
154+
wait();
155+
}
156+
} );
157+
158+
} )();

0 commit comments

Comments
 (0)