c202bfea0c08b31c7efa6056b0e2b232933b7c1a
[Plinn.git] / skins / fileupload.js
1 // © 2013 Benoît Pin MINES ParisTech
2 var DDFileUploader;
3
4 (function(){
5 // nombre maximun d'image chargées en local
6 var MAX_PREVIEW = 2;
7
8 DDFileUploader = function(dropbox, uploadUrl) {
9 this.dropbox = dropbox;
10 this.uploadUrl = uploadUrl;
11 this.slideSize = 222;
12 this.progressBarMaxSize = 200; // pixels
13 this.thumbnailSize = 180;
14 this.previewQueue = [];
15 this._previewQueueRunning = false;
16 this.previewsLoaded = 0;
17 this.uploadQueue = [];
18 this._uploadQueueRunning = false;
19 var self = this;
20 addListener(dropbox, 'dragenter', function(evt){self.dragenter(evt);});
21 addListener(dropbox, 'dragover', function(evt){self.dragover(evt);});
22 addListener(dropbox, 'drop', function(evt){self.drop(evt);});
23 };
24
25 // Drag and drop
26 DDFileUploader.prototype.dragenter = function(evt) {
27 disableDefault(evt);
28 disablePropagation(evt);
29 };
30
31 DDFileUploader.prototype.dragover = function(evt) {
32 disableDefault(evt);
33 disablePropagation(evt);
34 evt = getEventObject(evt);
35 var dt = evt.dataTransfer;
36 dt.dropEffect = 'copy';
37 };
38
39 DDFileUploader.prototype.drop = function(evt) {
40 disableDefault(evt);
41 disablePropagation(evt);
42 getEventObject(evt);
43 var dt = evt.dataTransfer;
44 dt.dropEffect = 'copy';
45 this.handleFiles(dt.files);
46 };
47
48 // Methods about upload
49 DDFileUploader.prototype.handleFiles = function(files) {
50 var file, i, slide;
51 for (i = 0; i < files.length; i++) {
52 file = files[i];
53 slide = this.createSlide(file);
54 this.previewQueuePush(slide);
55 this.uploadQueuePush(slide);
56 }
57 };
58
59 DDFileUploader.prototype.upload = function(slide) {
60 var reader = new FileReader();
61 var req = new XMLHttpRequest();
62 var file = slide.file;
63 this.uploadedSlide = slide;
64 this.previewImg = slide.img;
65 this.progressBar = slide.progressBar;
66 var self = this;
67
68 addListener(req.upload, 'progress', function(evt){self.progressHandler(evt);});
69 addListener(req, 'readystatechange',
70 function(evt) {
71 if (req.readyState === 4) {
72 self.uploadCompleteHandler(req);
73 }
74 });
75
76 req.open("PUT", this.uploadUrl);
77 req.setRequestHeader("Content-Type", file.type);
78 req.setRequestHeader("X-File-Name", file.name);
79 addListener(reader, 'load',
80 function(evt){
81 try {
82 req.sendAsBinary(evt.target.result);
83 }
84 catch(e){}
85 });
86 reader.readAsBinaryString(file);
87 };
88
89 DDFileUploader.prototype.uploadCompleteHandler = function(req) {
90 var slide = this.uploadedSlide;
91 this.uploadedSlide.removeChild(slide.label);
92 this.uploadedSlide.removeChild(slide.progressBar);
93 var fragment = getCopyOfNode(req.responseXML.documentElement.firstChild);
94 var img = fragment.getElementsByTagName('img')[0];
95 img.onload = function(evt) {
96 // accelerate GC before replacing
97 slide.img.src = '';
98 slide.img.parentNode.removeChild(slide.img);
99 slide.img = undefined;
100 slide.parentNode.replaceChild(fragment, slide);
101 };
102 this.previewsLoaded--;
103 this.previewQueueLoadNext();
104 this.uploadQueueLoadNext();
105 };
106
107 DDFileUploader.prototype.progressHandler = function(evt) {
108 if (evt.lengthComputable) {
109 var progress = evt.loaded / evt.total;
110 this.updateProgressBar(progress);
111 var currentOpacity = this.previewImg.style.opacity;
112 this.previewImg.style.opacity = Math.max(currentOpacity, progress);
113 }
114 };
115
116 // Method about queues
117
118 DDFileUploader.prototype.previewQueuePush = function(slide) {
119 this.previewQueue.push(slide);
120 if (!this._previewQueueRunning) {
121 this.startPreviewQueue();
122 }
123 };
124
125 DDFileUploader.prototype.startPreviewQueue = function() {
126 this._previewQueueRunning = true;
127 this.previewQueueLoadNext();
128 };
129
130 DDFileUploader.prototype.previewQueueLoadNext = function() {
131 if (this.previewQueue.length && this.previewsLoaded < MAX_PREVIEW) {
132 var slide = this.previewQueue.shift();
133 this.previewUploadedImage(slide);
134 this.previewsLoaded++;
135 }
136 else {
137 this._previewQueueRunning = false;
138 }
139 };
140
141 DDFileUploader.prototype.uploadQueuePush = function(slide) {
142 this.uploadQueue.push(slide);
143 if (!this._uploadQueueRunning) {
144 this.startUploadQueue();
145 }
146 };
147
148 DDFileUploader.prototype.startUploadQueue = function() {
149 this._uploadQueueRunning = true;
150 this.uploadQueueLoadNext();
151 };
152
153
154 DDFileUploader.prototype.uploadQueueLoadNext = function() {
155 var slide = this.uploadQueue.shift();
156 if (slide) {
157 this.upload(slide);
158 }
159 else {
160 this._uploadQueueRunning = false;
161 }
162 };
163
164
165 // User interface
166 DDFileUploader.prototype.createSlide = function(file) {
167 var slide = document.createElement('span');
168 slide.file = file;
169
170 var a = document.createElement('a');
171 a.href = '#';
172 a.className = 'slide';
173
174 var img = document.createElement('img');
175 img.className = 'hidden';
176 var size = this.thumbnailSize;
177 var self = this;
178 img.onload = function(evt) {
179 if (img.width > img.height) { // landscape
180 img.height = Math.round(size * img.height / img.width);
181 img.width = size;
182 }
183 else {
184 img.width = Math.round(size * img.width / img.height);
185 img.height = size;
186 }
187 img.style.marginLeft = Math.floor((self.slideSize - img.width) / 2) + 'px';
188 img.style.marginTop = Math.floor((self.slideSize - img.height) / 2) + 'px';
189 img.style.opacity = 0.2;
190 img.className = undefined;
191 };
192 a.appendChild(img);
193 slide.img = img;
194
195 var label = document.createElement('span');
196 slide.label = label;
197 label.className = 'label';
198 label.innerHTML = file.name;
199
200 var progressBar = document.createElement('span');
201 progressBar.className = 'upload-progress';
202 slide.progressBar = progressBar;
203
204 slide.appendChild(a);
205 slide.appendChild(progressBar);
206 slide.appendChild(label);
207 this.dropbox.appendChild(slide);
208
209 return slide;
210 };
211
212 DDFileUploader.prototype.updateProgressBar = function(progress) {
213 // 0 <= progress <= 1
214 var size = this.progressBarMaxSize * progress;
215 size = Math.round(size);
216 this.progressBar.style.width = size + 'px';
217 };
218
219 DDFileUploader.prototype.previewUploadedImage = function(slide) {
220 var reader = new FileReader();
221 var size = this.thumbnailSize;
222 var self = this;
223
224 reader.onload = function(evt) {
225 slide.img.src = evt.target.result;
226 setTimeout(function(){self.previewQueueLoadNext();}, 500);
227 };
228 reader.readAsDataURL(slide.file);
229 };
230
231 }());