1 /* © 2011 Benoît Pin, MINES ParisTech */
6 var reImage
= /^image\//;
9 var PlinnCKDDUploader = function(editor
) {
11 this.uploadUrl
= editor
.config
.baseHref
+ 'attachments/put_upload';
12 this.uploadQueue
= [];
13 this._uploadQueueRunning
= false;
14 this.previewQueue
= [];
15 this._previewQueueRunning
= false;
16 this.previewsLoaded
= 0;
17 this.thumbnailSize
= 310;
19 editor
.document
.on('dragenter', function(e
) {self
.dragenter(e
);});
20 editor
.document
.on('dragover', function(e
) {self
.dragover(e
);});
21 editor
.document
.on('drop', function(e
) {self
.drop(e
);});
25 PlinnCKDDUploader
.prototype.dragenter = function(e
) {
28 disablePropagation(evt
);
31 PlinnCKDDUploader
.prototype.dragover = function(e
) {
34 disablePropagation(evt
);
35 evt
= getEventObject(evt
);
36 var dt
= evt
.dataTransfer
;
37 dt
.dropEffect
= 'copy';
40 PlinnCKDDUploader
.prototype.drop = function(e
) {
43 disablePropagation(evt
);
45 var dt
= evt
.dataTransfer
;
46 dt
.dropEffect
= 'copy';
47 this.handleFiles(dt
.files
);
50 PlinnCKDDUploader
.prototype.createLinkProxy = function(file
) {
51 var container
= new CKEDITOR
.dom
.element('span');
52 var rel
= CKEDITOR
.dom
.element
.createFromHtml('<span style="position:relative"/>');
53 container
.append(rel
);
54 var progressBar
= CKEDITOR
.dom
.element
.createFromHtml(
55 '<span style="display:block; position:absolute; background:#ef8e32; height:4px; border-radius:2px; width:0; left:0; top:1em"/>');
56 rel
.append(progressBar
);
57 var link
= new CKEDITOR
.dom
.element('a');
58 link
.setAttribute('href', '#');
59 link
.setStyle('opacity', 0.2);
60 link
.appendText(file
.name
);
61 container
.append(link
);
66 proxy
.container
= container
;
67 proxy
.progressBar
= progressBar
;
72 PlinnCKDDUploader
.prototype.createImageProxy = function(file
) {
73 var container
= new CKEDITOR
.dom
.element('span');
74 var rel
= CKEDITOR
.dom
.element
.createFromHtml('<span style="position:relative"/>');
75 container
.append(rel
);
76 var progressBar
= CKEDITOR
.dom
.element
.createFromHtml(
77 '<span style="display:block; position:absolute; background:#ef8e32; height:4px; border-radius:2px; width:0; left:0; top:1em"/>');
78 rel
.append(progressBar
);
80 var img
= new CKEDITOR
.dom
.element('img');
81 img
.setAttribute('width', 310);
82 img
.setAttribute('height', 290);
83 img
.setStyle('opacity', 0.2);
84 img
.setAttribute('src', 'no_image.jpg');
85 img
.placeholder
= true;
86 var size
= this.thumbnailSize
;
89 img
.on('load', function(e
) {
90 if (e
.sender
.placeholder
) {
91 e
.sender
.placeholder
= false;
94 var img
$ = e
.data
.$.target
;
95 if (img
$.naturalWidth
> img
$.naturalHeight
) { // landscape
96 img
$.height
= Math
.round(size
* img
$.naturalHeight
/ img
$.naturalWidth
);
100 img
$.width
= Math
.round(size
* img
$.naturalWidth
/ img
$.naturalHeight
);
103 self
.progressBarMaxSize
= img
$.width
;
104 img
$.style
.opacity
= 0.2;
107 container
.append(img
);
111 proxy
.type
= 'image';
112 proxy
.container
= container
;
113 proxy
.progressBar
= progressBar
;
118 // Methods about upload
119 PlinnCKDDUploader
.prototype.handleFiles = function(files
) {
121 for (i
=0 ; i
<files
.length
; i
++) {
123 if (reImage
.test(file
.type
)) {
124 proxy
= this.createImageProxy(file
);
125 this.previewQueuePush(proxy
);
128 proxy
= this.createLinkProxy(file
);
130 this.editor
.insertElement(proxy
.container
);
131 if (files
.length
> 1 && i
< files
.length
-1) {
132 this.editor
.insertText('\n'); }
133 this.uploadQueuePush(proxy
);
139 PlinnCKDDUploader
.prototype.beforeUpload = function(item
) {
140 this.uploadedItem
= item
;
141 this.progressBar
= item
.progressBar
;
142 this.progressBarMaxSize
= item
.container
.getSize('width');
146 PlinnCKDDUploader
.prototype.upload = function(item
) {
147 // item.file must be the file to be uploaded
148 this.beforeUpload(item
);
149 var reader
= new FileReader();
150 var req
= new XMLHttpRequest();
151 var file
= item
.file
;
155 addListener(req
.upload
, 'progress', function(evt
){self
.progressHandler(evt
);});
156 addListener(req
, 'readystatechange',
158 if (req
.readyState
=== 4) {
159 self
.uploadCompleteHandler(req
);
163 req
.open("PUT", this.uploadUrl
);
164 req
.setRequestHeader("Content-Type", file
.type
);
165 req
.setRequestHeader("X-File-Name", encodeURI(file
.name
));
166 addListener(reader
, 'load',
169 req
.sendAsBinary(evt
.target
.result
);
173 reader
.readAsBinaryString(file
);
177 PlinnCKDDUploader
.prototype.uploadCompleteHandlerCB = function(req
) {
178 var item
= this.uploadedItem
;
179 var data
= req
.responseXML
.documentElement
;
182 var link
= new CKEDITOR
.dom
.element('a');
183 link
.setAttribute('href', 'attachments/' + data
.getAttribute('id'));
184 link
.appendText(data
.getAttribute('title'));
185 link
.replace(item
.container
);
188 var img
= new CKEDITOR
.dom
.element('img');
189 img
.setAttribute('src', data
.getAttribute('src'));
190 img
.setAttribute('alt', data
.getAttribute('title'));
191 img
.setAttribute('width', data
.getAttribute('width'));
192 img
.setAttribute('height', data
.getAttribute('height'));
193 img
.replace(item
.container
);
194 this.previewsLoaded
--;
195 this.previewQueueLoadNext();
200 PlinnCKDDUploader
.prototype.uploadCompleteHandler = function(req
) {
201 this.uploadCompleteHandlerCB(req
);
202 this.uploadQueueLoadNext();
205 PlinnCKDDUploader
.prototype.progressHandlerCB = function(progress
) {
206 // 0 <= progress <= 1
207 var size
= this.progressBarMaxSize
* progress
;
208 size
= Math
.round(size
);
209 this.progressBar
.setStyle('width', String(size
) + 'px');
211 switch(this.uploadedItem
.type
) {
213 currentOpacity
= this.uploadedItem
.link
.getStyle('opacity');
214 this.uploadedItem
.link
.setStyle('opacity', Math
.max(currentOpacity
, progress
));
217 currentOpacity
= this.uploadedItem
.img
.getStyle('opacity');
218 this.uploadedItem
.img
.setStyle('opacity', Math
.max(currentOpacity
, progress
));
223 PlinnCKDDUploader
.prototype.progressHandler = function(evt
) {
224 if (evt
.lengthComputable
) {
225 var progress
= evt
.loaded
/ evt
.total
;
226 this.progressHandlerCB(progress
);
230 // Methods about upload queue
231 PlinnCKDDUploader
.prototype.uploadQueuePush = function(item
) {
232 this.uploadQueue
.push(item
);
233 if (!this._uploadQueueRunning
) {
234 this.startUploadQueue();
238 PlinnCKDDUploader
.prototype.startUploadQueue = function() {
239 this._uploadQueueRunning
= true;
240 this.uploadQueueLoadNext();
243 PlinnCKDDUploader
.prototype.uploadQueueLoadNext = function() {
244 var item
= this.uploadQueue
.shift();
249 this._uploadQueueRunning
= false;
253 // Methods about image preview queue.
254 PlinnCKDDUploader
.prototype.previewQueuePush = function(proxy
) {
255 this.previewQueue
.push(proxy
);
256 if (!this._previewQueueRunning
) {
257 this.startPreviewQueue();
261 PlinnCKDDUploader
.prototype.startPreviewQueue = function() {
262 this._previewQueueRunning
= true;
263 this.previewQueueLoadNext();
266 PlinnCKDDUploader
.prototype.previewQueueLoadNext = function() {
267 if (this.previewQueue
.length
&& this.previewsLoaded
< MAX_PREVIEW
) {
268 var proxy
= this.previewQueue
.shift();
269 this.previewUploadedImage(proxy
);
270 this.previewsLoaded
++;
273 this._previewQueueRunning
= false;
277 PlinnCKDDUploader
.prototype.previewUploadedImage = function(proxy
) {
278 var reader
= new FileReader();
279 var size
= this.thumbnailSize
;
282 reader
.onload = function(evt
) {
283 proxy
.img
.setAttribute('src', evt
.target
.result
);
284 setTimeout(function(){self
.previewQueueLoadNext();}, 500);
286 reader
.readAsDataURL(proxy
.file
);
291 var reSize
= /getResizedImage\?size=(\d+)_(\d+)$/;
293 function updateImageSizeUrlParameters(img
) {
294 if (reSize
.test(img
.src
)){
295 var matches
= reSize
.exec(img
.src
);
296 var srcWidth
= parseInt(matches
[1], 10);
297 var srcHeight
= parseInt(matches
[2], 10);
299 var imgWidth
= parseInt((img
.style
.width
) ? img
.style
.width
: img
.width
, 10);
300 var imgHeight
= parseInt((img
.style
.height
) ? img
.style
.height
: img
.height
, 10);
302 if ((imgWidth
&& imgHeight
) && srcWidth
!== imgWidth
&& srcHeight
!== imgHeight
) {
303 var newUrl
= img
.getAttribute('src', 2).replace(reSize
, 'getResizedImage?size=' + imgWidth
+ '_' + imgHeight
);
304 img
.width
= imgWidth
;
305 img
.height
= imgHeight
;
311 function openPlinnImageDialog(path
, editor
) {
312 var winOptions
= "location=no,menubar=no,toolbar=no,dependent=yes,dialog=yes,minimizable=no,modal=yes,alwaysRaised=yes" +
319 var win
= open(path
+ 'dialog/plinn_image.html', 'PlinnImageDialog', winOptions
);
320 win
.dialogArguments
= {};
321 win
.dialogArguments
.editor
= editor
;
322 win
.dialogArguments
.pluginPath
= path
;
323 win
.dialogArguments
.CKEDITOR
= CKEDITOR
;
327 CKEDITOR
.plugins
.add( 'plinn_image',
329 init : function( editor
)
331 /* Add listener on getData event to compute images
332 src attributes before saving data.
334 editor
.on('instanceReady', function(){
337 var tmpDiv
= document
.createElement('div');
338 tmpDiv
.innerHTML
= evt
.data
.dataValue
;
339 var images
= tmpDiv
.getElementsByTagName('IMG');
341 for (i
= 0 ; i
< images
.length
; i
++) {
342 updateImageSizeUrlParameters(images
[i
]);}
343 evt
.data
.dataValue
= tmpDiv
.innerHTML
;
346 // drag & drop upload initialisation
347 var dd
= new PlinnCKDDUploader(editor
);
351 var pluginPath
= this.path
;
352 var allowed
= 'img[alt,!src]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}';
353 var required
= 'img[alt,src]';
354 var command
= editor
.addCommand('plinn_image',
356 exec : function(editor
){openPlinnImageDialog(pluginPath
, editor
);},
357 allowedContent
: allowed
,
358 requiredContent
: required
362 editor
.ui
.addButton('PlinnImage',
364 label
: editor
.lang
.common
.image
,
365 icon
: pluginPath
+ 'dialog/plinn_image.png',
366 command
: 'plinn_image'