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