695d11445dcad13bdddc6e18c8a6d6fe5ba16ab5
[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 var size = this.thumbnailSize;
86
87 img.on('load', function(e) {
88 var img$ = img.$;
89 if (img$.width > img$.height) { // landscape
90 img$.height = Math.round(size * img$.height / img$.width);
91 img$.width = size;
92 }
93 else {
94 img$.width = Math.round(size * img$.width / img$.height);
95 img$.height = size;
96 }
97 img$.style.opacity = 0.2;
98 });
99
100 container.append(img);
101
102 var proxy = {};
103 proxy.file = file;
104 proxy.type = 'image';
105 proxy.container = container;
106 proxy.progressBar = progressBar;
107 proxy.img = img;
108 return proxy;
109 };
110
111 // Methods about upload
112 PlinnCKDDUploader.prototype.handleFiles = function(files) {
113 var file, i, proxy;
114 for (i=0 ; i<files.length ; i++) {
115 file = files[i];
116 if (reImage.test(file.type)) {
117 proxy = this.createImageProxy(file);
118 this.previewQueuePush(proxy);
119 }
120 else {
121 proxy = this.createLinkProxy(file);
122 }
123 this.editor.insertElement(proxy.container);
124 if (files.length > 1 && i < files.length-1) {
125 this.editor.insertText('\n'); }
126 this.uploadQueuePush(proxy);
127 }
128 };
129
130
131
132 PlinnCKDDUploader.prototype.beforeUpload = function(item) {
133 this.uploadedItem = item;
134 this.progressBar = item.progressBar;
135 this.progressBarMaxSize = item.container.getSize('width');
136 };
137
138
139 PlinnCKDDUploader.prototype.upload = function(item) {
140 // item.file must be the file to be uploaded
141 this.beforeUpload(item);
142 var reader = new FileReader();
143 var req = new XMLHttpRequest();
144 var file = item.file;
145
146 var self = this;
147
148 addListener(req.upload, 'progress', function(evt){self.progressHandler(evt);});
149 addListener(req, 'readystatechange',
150 function(evt) {
151 if (req.readyState === 4) {
152 self.uploadCompleteHandler(req);
153 }
154 });
155
156 req.open("PUT", this.uploadUrl);
157 req.setRequestHeader("Content-Type", file.type);
158 req.setRequestHeader("X-File-Name", encodeURI(file.name));
159 addListener(reader, 'load',
160 function(evt){
161 try {
162 req.sendAsBinary(evt.target.result);
163 }
164 catch(e){}
165 });
166 reader.readAsBinaryString(file);
167 };
168
169
170 PlinnCKDDUploader.prototype.uploadCompleteHandlerCB = function(req) {
171 var item = this.uploadedItem;
172 var data = req.responseXML.documentElement;
173 switch (item.type) {
174 case 'link' :
175 var link = new CKEDITOR.dom.element('a');
176 link.setAttribute('href', 'attachments/' + data.getAttribute('id'));
177 link.appendText(data.getAttribute('title'));
178 link.replace(item.container);
179 break;
180 case 'image' :
181 var img = new CKEDITOR.dom.element('img');
182 img.setAttribute('src', data.getAttribute('src'));
183 img.setAttribute('alt', data.getAttribute('title'));
184 img.setAttribute('width', data.getAttribute('width'));
185 img.setAttribute('height', data.getAttribute('height'));
186 img.replace(item.container);
187 break;
188 }
189 };
190
191 PlinnCKDDUploader.prototype.uploadCompleteHandler = function(req) {
192 this.uploadCompleteHandlerCB(req);
193 this.uploadQueueLoadNext();
194 };
195
196 PlinnCKDDUploader.prototype.progressHandlerCB = function(progress) {
197 // 0 <= progress <= 1
198 var size = this.progressBarMaxSize * progress;
199 size = Math.round(size);
200 this.progressBar.setStyle('width', String(size) + 'px');
201 var currentOpacity;
202 switch(this.uploadedItem.type) {
203 case 'link' :
204 currentOpacity = this.uploadedItem.link.getStyle('opacity');
205 this.uploadedItem.link.setStyle('opacity', Math.max(currentOpacity, progress));
206 break;
207 case 'image' :
208 currentOpacity = this.uploadedItem.img.getStyle('opacity');
209 this.uploadedItem.img.setStyle('opacity', Math.max(currentOpacity, progress));
210 break;
211 }
212 };
213
214 PlinnCKDDUploader.prototype.progressHandler = function(evt) {
215 if (evt.lengthComputable) {
216 var progress = evt.loaded / evt.total;
217 this.progressHandlerCB(progress);
218 }
219 };
220
221 // Methods about upload queue
222 PlinnCKDDUploader.prototype.uploadQueuePush = function(item) {
223 this.uploadQueue.push(item);
224 if (!this._uploadQueueRunning) {
225 this.startUploadQueue();
226 }
227 };
228
229 PlinnCKDDUploader.prototype.startUploadQueue = function() {
230 this._uploadQueueRunning = true;
231 this.uploadQueueLoadNext();
232 };
233
234 PlinnCKDDUploader.prototype.uploadQueueLoadNext = function() {
235 var item = this.uploadQueue.shift();
236 if (item) {
237 this.upload(item);
238 }
239 else {
240 this._uploadQueueRunning = false;
241 }
242 };
243
244 // Methods about image preview queue.
245 PlinnCKDDUploader.prototype.previewQueuePush = function(proxy) {
246 this.previewQueue.push(proxy);
247 if (!this._previewQueueRunning) {
248 this.startPreviewQueue();
249 }
250 };
251
252 PlinnCKDDUploader.prototype.startPreviewQueue = function() {
253 this._previewQueueRunning = true;
254 this.previewQueueLoadNext();
255 };
256
257 PlinnCKDDUploader.prototype.previewQueueLoadNext = function() {
258 if (this.previewQueue.length && this.previewsLoaded < MAX_PREVIEW) {
259 var proxy = this.previewQueue.shift();
260 this.previewUploadedImage(proxy);
261 this.previewsLoaded++;
262 }
263 else {
264 this._previewQueueRunning = false;
265 }
266 };
267
268 PlinnCKDDUploader.prototype.previewUploadedImage = function(proxy) {
269 var reader = new FileReader();
270 var size = this.thumbnailSize;
271 var self = this;
272
273 reader.onload = function(evt) {
274 proxy.img.setAttribute('src', evt.target.result);
275 // proxy.img.src = evt.target.result;
276 setTimeout(function(){self.previewQueueLoadNext();}, 500);
277 };
278 reader.readAsDataURL(proxy.file);
279 };
280
281
282
283 var reSize = /getResizedImage\?size=(\d+)_(\d+)$/;
284
285 function updateImageSizeUrlParameters(img) {
286 if (reSize.test(img.src)){
287 var matches = reSize.exec(img.src);
288 var srcWidth = parseInt(matches[1], 10);
289 var srcHeight = parseInt(matches[2], 10);
290
291 var imgWidth = parseInt((img.style.width) ? img.style.width : img.width, 10);
292 var imgHeight = parseInt((img.style.height) ? img.style.height : img.height, 10);
293
294 if ((imgWidth && imgHeight) && srcWidth !== imgWidth && srcHeight !== imgHeight) {
295 var newUrl = img.getAttribute('src', 2).replace(reSize, 'getResizedImage?size=' + imgWidth + '_' + imgHeight);
296 img.width = imgWidth;
297 img.height = imgHeight;
298 img.src = newUrl;
299 }
300 }
301 }
302
303 function openPlinnImageDialog(path, editor) {
304 var winOptions = "location=no,menubar=no,toolbar=no,dependent=yes,dialog=yes,minimizable=no,modal=yes,alwaysRaised=yes" +
305 ",resizable=yes" +
306 ",width=801" +
307 ",height=600";
308 //",top=" + iTop +
309 //",left=" + iLeft ;
310
311 var win = open(path + 'dialog/plinn_image.html', 'PlinnImageDialog', winOptions);
312 win.dialogArguments = {};
313 win.dialogArguments.editor = editor;
314 win.dialogArguments.pluginPath = path;
315 win.dialogArguments.CKEDITOR = CKEDITOR;
316 }
317
318
319 CKEDITOR.plugins.add( 'plinn_image',
320 {
321 init : function( editor )
322 {
323 /* Add listener on getData event to compute images
324 src attributes before saving data.
325 */
326 editor.on('instanceReady', function(){
327 editor.on('getData',
328 function(evt) {
329 var tmpDiv = document.createElement('div');
330 tmpDiv.innerHTML = evt.data.dataValue;
331 var images = tmpDiv.getElementsByTagName('IMG');
332 var i;
333 for (i = 0 ; i < images.length ; i++) {
334 updateImageSizeUrlParameters(images[i]);}
335 evt.data.dataValue = tmpDiv.innerHTML;
336 }
337 );
338 // drag & drop upload initialisation
339 var dd = new PlinnCKDDUploader(editor);
340 });
341
342
343 var pluginPath = this.path;
344 var allowed = 'img[alt,!src]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}';
345 var required = 'img[alt,src]';
346 var command = editor.addCommand('plinn_image',
347 {
348 exec : function(editor){openPlinnImageDialog(pluginPath, editor);},
349 allowedContent: allowed,
350 requiredContent: required
351 }
352 );
353
354 editor.ui.addButton('PlinnImage',
355 {
356 label : editor.lang.common.image,
357 icon : pluginPath + 'dialog/plinn_image.png',
358 command : 'plinn_image'
359 });
360 }
361
362 });
363
364 }());