3e2606c9874664a414abdaf018a48e72a1d9bbaa
[ckeditor.git] / skins / ckeditor / plugins / plinn_image / plugin.js
1 /* © 2011 Benoît Pin, MINES ParisTech */
2
3
4 (function(){
5
6 var reImage = /^image\//;
7 var MAX_PREVIEW = 2;
8
9 var PlinnCKDDUploader = function(editor) {
10 this.editor = 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;
18 var self = this;
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);});
22 };
23
24 // Drag and drop
25 PlinnCKDDUploader.prototype.dragenter = function(e) {
26 var evt = e.data.$;
27 disableDefault(evt);
28 disablePropagation(evt);
29 };
30
31 PlinnCKDDUploader.prototype.dragover = function(e) {
32 var evt = e.data.$;
33 disableDefault(evt);
34 disablePropagation(evt);
35 evt = getEventObject(evt);
36 var dt = evt.dataTransfer;
37 dt.dropEffect = 'copy';
38 };
39
40 PlinnCKDDUploader.prototype.drop = function(e) {
41 var evt = e.data.$;
42 disableDefault(evt);
43 disablePropagation(evt);
44 getEventObject(evt);
45 var dt = evt.dataTransfer;
46 dt.dropEffect = 'copy';
47 this.handleFiles(dt.files);
48 };
49
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);
62
63 var proxy = {};
64 proxy.file = file;
65 proxy.type = 'link';
66 proxy.container = container;
67 proxy.progressBar = progressBar;
68 proxy.link = link;
69 return proxy;
70 };
71
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);
79
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;
87 var self = this;
88
89 img.on('load', function(e) {
90 if (e.sender.placeholder) {
91 e.sender.placeholder = false;
92 return;
93 };
94 var img$ = e.data.$.target;
95 if (img$.naturalWidth > img$.naturalHeight) { // landscape
96 img$.height = Math.round(size * img$.naturalHeight / img$.naturalWidth);
97 img$.width = size;
98 }
99 else {
100 img$.width = Math.round(size * img$.naturalWidth / img$.naturalHeight);
101 img$.height = size;
102 }
103 self.progressBarMaxSize = img$.width;
104 img$.style.opacity = 0.2;
105 });
106
107 container.append(img);
108
109 var proxy = {};
110 proxy.file = file;
111 proxy.type = 'image';
112 proxy.container = container;
113 proxy.progressBar = progressBar;
114 proxy.img = img;
115 return proxy;
116 };
117
118 // Methods about upload
119 PlinnCKDDUploader.prototype.handleFiles = function(files) {
120 var file, i, proxy;
121 for (i=0 ; i<files.length ; i++) {
122 file = files[i];
123 if (reImage.test(file.type)) {
124 proxy = this.createImageProxy(file);
125 this.previewQueuePush(proxy);
126 }
127 else {
128 proxy = this.createLinkProxy(file);
129 }
130 this.editor.insertElement(proxy.container);
131 if (files.length > 1 && i < files.length-1) {
132 this.editor.insertText('\n'); }
133 this.uploadQueuePush(proxy);
134 }
135 };
136
137
138
139 PlinnCKDDUploader.prototype.beforeUpload = function(item) {
140 this.uploadedItem = item;
141 this.progressBar = item.progressBar;
142 this.progressBarMaxSize = item.container.getSize('width');
143 };
144
145
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;
152
153 var self = this;
154
155 addListener(req.upload, 'progress', function(evt){self.progressHandler(evt);});
156 addListener(req, 'readystatechange',
157 function(evt) {
158 if (req.readyState === 4) {
159 self.uploadCompleteHandler(req);
160 }
161 });
162
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',
167 function(evt){
168 try {
169 req.sendAsBinary(evt.target.result);
170 }
171 catch(e){}
172 });
173 reader.readAsBinaryString(file);
174 };
175
176
177 PlinnCKDDUploader.prototype.uploadCompleteHandlerCB = function(req) {
178 var item = this.uploadedItem;
179 var data = req.responseXML.documentElement;
180 switch (item.type) {
181 case 'link' :
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);
186 break;
187 case 'image' :
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();
196 break;
197 }
198 };
199
200 PlinnCKDDUploader.prototype.uploadCompleteHandler = function(req) {
201 this.uploadCompleteHandlerCB(req);
202 this.uploadQueueLoadNext();
203 };
204
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');
210 var currentOpacity;
211 switch(this.uploadedItem.type) {
212 case 'link' :
213 currentOpacity = this.uploadedItem.link.getStyle('opacity');
214 this.uploadedItem.link.setStyle('opacity', Math.max(currentOpacity, progress));
215 break;
216 case 'image' :
217 currentOpacity = this.uploadedItem.img.getStyle('opacity');
218 this.uploadedItem.img.setStyle('opacity', Math.max(currentOpacity, progress));
219 break;
220 }
221 };
222
223 PlinnCKDDUploader.prototype.progressHandler = function(evt) {
224 if (evt.lengthComputable) {
225 var progress = evt.loaded / evt.total;
226 this.progressHandlerCB(progress);
227 }
228 };
229
230 // Methods about upload queue
231 PlinnCKDDUploader.prototype.uploadQueuePush = function(item) {
232 this.uploadQueue.push(item);
233 if (!this._uploadQueueRunning) {
234 this.startUploadQueue();
235 }
236 };
237
238 PlinnCKDDUploader.prototype.startUploadQueue = function() {
239 this._uploadQueueRunning = true;
240 this.uploadQueueLoadNext();
241 };
242
243 PlinnCKDDUploader.prototype.uploadQueueLoadNext = function() {
244 var item = this.uploadQueue.shift();
245 if (item) {
246 this.upload(item);
247 }
248 else {
249 this._uploadQueueRunning = false;
250 }
251 };
252
253 // Methods about image preview queue.
254 PlinnCKDDUploader.prototype.previewQueuePush = function(proxy) {
255 this.previewQueue.push(proxy);
256 if (!this._previewQueueRunning) {
257 this.startPreviewQueue();
258 }
259 };
260
261 PlinnCKDDUploader.prototype.startPreviewQueue = function() {
262 this._previewQueueRunning = true;
263 this.previewQueueLoadNext();
264 };
265
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++;
271 }
272 else {
273 this._previewQueueRunning = false;
274 }
275 };
276
277 PlinnCKDDUploader.prototype.previewUploadedImage = function(proxy) {
278 var reader = new FileReader();
279 var size = this.thumbnailSize;
280 var self = this;
281
282 reader.onload = function(evt) {
283 proxy.img.setAttribute('src', evt.target.result);
284 setTimeout(function(){self.previewQueueLoadNext();}, 500);
285 };
286 reader.readAsDataURL(proxy.file);
287 };
288
289
290
291 var reSize = /getResizedImage\?size=(\d+)_(\d+)$/;
292
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);
298
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);
301
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;
306 img.src = newUrl;
307 }
308 }
309 }
310
311 function openPlinnImageDialog(path, editor) {
312 var winOptions = "location=no,menubar=no,toolbar=no,dependent=yes,dialog=yes,minimizable=no,modal=yes,alwaysRaised=yes" +
313 ",resizable=yes" +
314 ",width=801" +
315 ",height=600";
316 //",top=" + iTop +
317 //",left=" + iLeft ;
318
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;
324 }
325
326
327 CKEDITOR.plugins.add( 'plinn_image',
328 {
329 init : function( editor )
330 {
331 /* Add listener on getData event to compute images
332 src attributes before saving data.
333 */
334 editor.on('instanceReady', function(){
335 editor.on('getData',
336 function(evt) {
337 var tmpDiv = document.createElement('div');
338 tmpDiv.innerHTML = evt.data.dataValue;
339 var images = tmpDiv.getElementsByTagName('IMG');
340 var i;
341 for (i = 0 ; i < images.length ; i++) {
342 updateImageSizeUrlParameters(images[i]);}
343 evt.data.dataValue = tmpDiv.innerHTML;
344 }
345 );
346 // drag & drop upload initialisation
347 var dd = new PlinnCKDDUploader(editor);
348 });
349
350
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',
355 {
356 exec : function(editor){openPlinnImageDialog(pluginPath, editor);},
357 allowedContent: allowed,
358 requiredContent: required
359 }
360 );
361
362 editor.ui.addButton('PlinnImage',
363 {
364 label : editor.lang.common.image,
365 icon : pluginPath + 'dialog/plinn_image.png',
366 command : 'plinn_image'
367 });
368 }
369
370 });
371
372 }());