cfb676949497fece16b655ba4bd2078f75aeb39e
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / DragUploader.js
1 /***************************************************************
2 *
3 * Copyright notice
4 *
5 * (c) 2013 Steffen Ritter <steffen.ritter@typo3.org>
6 * All rights reserved
7 *
8 * Released under GNU/GPL2+ (see license file in the main directory)
9 *
10 * This script is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * This copyright notice MUST APPEAR in all copies of this script
15 *
16 ***************************************************************/
17 /**
18 * JavaScript RequireJS module called "TYPO3/CMS/Backend/DragUploader"
19 *
20 */
21 define('TYPO3/CMS/Backend/DragUploader', ['jquery'], function($) {
22
23 /*
24 * part 1: a generic jQuery plugin "$.dragUploader"
25 */
26
27 // register the constructor
28 var DragUploaderPlugin = function() {
29 var me = this;
30
31 me.$body = $('body');
32 me.$dragMask = $('<div />').addClass('t3-body-drag-mask').appendTo(me.$body);
33 me.$dropzone = $('<div />').addClass('t3-dropzone').hide().insertAfter('#typo3-inner-docbody h1:first');
34 me.$dropzoneMask = $('<div />').addClass('t3-dropzone-mask').appendTo(me.$dropzone);
35 me.$fileInput = $('<input type="file" multiple name="files[]" />').addClass('t3-upload-file-picker').appendTo(me.$body);
36 me.$fileList = $('#typo3-filelist');
37 me.fileListColumnCount = $('thead tr:first td', me.$fileList).length;
38
39 me.fileDenyPattern = new RegExp($('[data-file-deny-pattern]').attr('data-file-deny-pattern'), 'i');
40 me.maxFileSize = parseInt($('[data-max-file-size]').attr('data-max-file-size'));
41 me.target = $('[data-target-folder]').attr('data-target-folder');
42
43 me.browserCapabilities = {
44 fileReader: typeof FileReader != 'undefined',
45 DnD: 'draggable' in document.createElement('span'),
46 FormData: !!window.FormData,
47 Progress: "upload" in new XMLHttpRequest
48 };
49
50 me.showDropzone = function() {
51 me.$dropzone.show();
52 };
53
54 me.hideDropzone = function(event) {
55 event.stopPropagation();
56 event.preventDefault();
57 me.$dropzone.hide();
58 }
59
60 me.dragFileIntoDocument = function(event) {
61 event.stopPropagation();
62 event.preventDefault();
63 me.$body.addClass('t3-drop-in-progress');
64 me.showDropzone();
65 return false;
66 };
67
68 me.dragAborted = function(event) {
69 event.stopPropagation();
70 event.preventDefault();
71 me.$body.removeClass('t3-drop-in-progress');
72 return false;
73 };
74
75 me.ignoreDrop = function(event) {
76 // stops the browser from redirecting.
77 event.stopPropagation();
78 event.preventDefault();
79 me.dragAborted(event);
80 return false;
81 };
82
83 me.handleDrop = function (event) {
84 me.ignoreDrop(event);
85 me.processFiles(event.originalEvent.dataTransfer.files);
86 me.$dropzone.removeClass('t3-dropzone-drop-ok');
87 };
88
89 me.processFiles = function (files) {
90
91 // ask user if we should override files
92 var override = confirm(TYPO3.l10n.localize('file_upload.overwriteExistingFiles'));
93
94 // Add each file to queue and start upload
95 $.each(files, function(i, file) {
96 new FileQueueItem(me, file, override);
97 });
98 };
99
100 me.fileInDropzone = function(event) {
101 me.$dropzone.addClass('t3-dropzone-drop-ok');
102 };
103
104 me.fileOutOfDropzone = function(event) {
105 me.$dropzone.removeClass('t3-dropzone-drop-ok');
106 };
107
108 if (me.browserCapabilities.DnD) {
109 me.$body.on('dragover', me.dragFileIntoDocument);
110 me.$body.on('dragend', me.dragAborted);
111 me.$body.on('drop', me.ignoreDrop);
112
113 me.$dropzone.on('dragenter', me.fileInDropzone);
114 me.$dropzoneMask.on('dragenter', me.fileInDropzone);
115 me.$dropzoneMask.on('dragleave', me.fileOutOfDropzone);
116 me.$dropzoneMask.on('drop', me.handleDrop);
117
118 me.$dropzone.prepend('<h4>'+TYPO3.l10n.localize('file_upload.dropzonehint.title')+'</h4><p>'+TYPO3.l10n.localize('file_upload.dropzonehint.message')+'</p>')
119 .click(function(){me.$fileInput.click()});
120 $('<span />').addClass('t3-icon t3-icon-actions t3-icon-actions-close t3-dropzone-close').html('&nbsp;').click(me.hideDropzone).appendTo(me.$dropzone);
121
122 me.$fileInput.on('change', function() {
123 me.processFiles(this.files);
124 });
125
126 // bind file picker to default upload button
127 $('#button-upload').click(function(event) {
128 event.preventDefault();
129 me.$fileInput.click();
130 me.showDropzone();
131 });
132 }
133 };
134
135 var FileQueueItem = function(dragUploader, file, override) {
136 var me = this;
137 me.dragUploader = dragUploader;
138 me.file = file;
139 me.override = override;
140
141 me.$row = $('<tr />').addClass('file_list_normal t3-upload-queue-item uploading');
142 me.$iconCol = $('<td />').addClass('col-icon').appendTo(me.$row);
143 me.$fileName = $('<td />').text(file.name).appendTo(me.$row);
144 me.$progress = $('<td />').addClass('t3-upload-queue-progress')
145 .attr('colspan', me.dragUploader.fileListColumnCount-2).appendTo(me.$row);
146 me.$progressContainer = $('<div />').addClass('t3-upload-queue-progress').appendTo(me.$progress);
147 me.$progressBar = $('<div />').addClass('t3-upload-queue-progress-bar').appendTo(me.$progressContainer);
148 me.$progressPercentage = $('<span />').addClass('t3-upload-queue-progress-percentage').appendTo(me.$progressContainer);
149 me.$progressMessage = $('<span />').addClass('t3-upload-queue-progress-message').appendTo(me.$progressContainer);
150
151 me.updateMessage = function(message) {
152 me.$progressMessage.text(message);
153 };
154
155 me.removeProgress = function() {
156 if (me.$progress) {
157 me.$progress.remove();
158 }
159 };
160
161 me.uploadStart = function() {
162 me.$progressPercentage.text('(0%)');
163 me.$progressBar.width('1%');
164 };
165
166 me.uploadError = function(response) {
167 me.updateMessage(TYPO3.l10n.localize('file_upload.uploadFailed').replace(/\{0\}/g, me.file.name));
168 var error = $(response.responseText);
169 if (error.is('t3err')) {
170 me.$progressPercentage.text(error.text());
171 } else {
172 me.$progressPercentage.text('(' + response.statusText + ')');
173 }
174 me.$row.addClass('error');
175 };
176
177 me.updateProgress = function(event) {
178 var percentage = Math.round((event.loaded / event.total) * 100) + '%'
179 me.$progressBar.width(percentage);
180 me.$progressPercentage.text(percentage);
181 };
182
183 me.uploadComplete = function(data) {
184 if (data.result.upload) {
185 me.$row.removeClass('uploading');
186 me.$fileName.text(data.result.upload[0].name);
187 me.$progressPercentage.text('');
188 me.$progressMessage.text('100%');
189 me.$progressBar.width('100%');
190
191 // replace file icon
192 if (data.result.upload[0].iconClasses) {
193 me.$iconCol.html('<span class="' + data.result.upload[0].iconClasses + '">&nbsp;</span>');
194 }
195 setTimeout(function() {me.showFileInfo(data.result.upload[0])}, 3000);
196 }
197 };
198
199 me.showFileInfo = function(fileInfo) {
200 me.removeProgress();
201 // add spacing cells when clibboard and/or extended view is enabled
202 for (i = 7; i < me.dragUploader.fileListColumnCount; i++) {
203 $('<td />').text('').appendTo(me.$row);
204 }
205 $('<td />').text(fileInfo.extension.toUpperCase()).appendTo(me.$row);
206 $('<td />').text(fileInfo.date).appendTo(me.$row);
207 $('<td />').text(me.fileSizeAsString(fileInfo.size)).appendTo(me.$row);
208 var permissions = '';
209 if (fileInfo.permissions.read) {
210 permissions += '<span class="typo3-red"><strong>R</strong></span>';
211 }
212 if (fileInfo.permissions.write) {
213 permissions += '<span class="typo3-red"><strong>W</strong></span>';
214 }
215 $('<td />').html(permissions).appendTo(me.$row);
216 $('<td />').text('-').appendTo(me.$row);
217 };
218
219 me.fileSizeAsString = function(size) {
220 var string = "";
221 var sizeKB = size / 1024;
222 if (parseInt(sizeKB) > 1024) {
223 var sizeMB = sizeKB / 1024;
224 string = sizeMB.toFixed(1) + " MB";
225 } else {
226 string = sizeKB.toFixed(1) + " KB";
227 }
228 return string;
229 };
230
231 // position queue item in file list
232 if ($('tbody tr.t3-upload-queue-item', me.dragUploader.$fileList).length === 0) {
233 me.$row.prependTo($('tbody', me.dragUploader.$fileList));
234 me.$row.addClass('last');
235 } else {
236 me.$row.insertBefore($('tbody tr.t3-upload-queue-item:first', me.dragUploader.$fileList));
237 }
238
239 // set dummy file icon
240 me.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>')
241
242 // check file size
243 if (me.file.size > me.dragUploader.maxFileSize) {
244 me.updateMessage(TYPO3.l10n.localize('file_upload.maxFileSizeExceeded')
245 .replace(/\{0\}/g, me.file.name)
246 .replace(/\{1\}/g, me.fileSizeAsString(me.dragUploader.maxFileSize)));
247 me.$row.addClass('error');
248
249 // check filename/extension
250 } else if (me.file.name.match(me.dragUploader.fileDenyPattern)) {
251 me.updateMessage(TYPO3.l10n.localize('file_upload.fileNotAllowed').replace(/\{0\}/g, me.file.name));
252 me.$row.addClass('error');
253
254 } else {
255 me.updateMessage('- ' + me.fileSizeAsString(me.file.size));
256
257 var formData = new FormData();
258 formData.append('file[upload][1][target]', me.dragUploader.target);
259 formData.append('file[upload][1][data]', '1');
260 if(me.override) {
261 formData.append('overwriteExistingFiles', '1');
262 }
263 formData.append('ajaxID', 'TYPO3_tcefile::process');
264 formData.append('redirect', '');
265 formData.append('upload_1', me.file);
266
267 var s = $.extend(true, {}, $.ajaxSettings, {
268 url: 'ajax.php',
269 contentType: false,
270 processData: false,
271 data: formData,
272 cache: false,
273 type: 'POST',
274 success: me.uploadComplete,
275 error: me.uploadError
276 });
277
278 s.xhr = function() {
279 var xhr = $.ajaxSettings.xhr();
280 xhr.upload.addEventListener('progress', me.updateProgress);
281 return xhr;
282 };
283
284 // start upload
285 me.upload = $.ajax(s);
286 }
287
288 }
289
290 /**
291 * part 2: The main module of this file
292 * - initialize the DragUploader module and register
293 * the jQuery plugin in the jQuery global object
294 * when initializing the DragUploader module
295 */
296 var DragUploader = {};
297
298 DragUploader.options = {
299 };
300
301 DragUploader.initialize = function() {
302 var
303 me = this
304 ,opts = me.options;
305
306 // register the jQuery plugin "DragUploaderPlugin"
307 $.fn.dragUploader = function(option) {
308 return this.each(function() {
309 var $this = $(this)
310 , data = $this.data('DragUploaderPlugin');
311 if (!data) {
312 $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(this)));
313 }
314 if (typeof option == 'string') {
315 data[option]();
316 }
317 })
318 };
319
320 $('body').dragUploader();
321
322 };
323
324
325
326 /**
327 * part 3: initialize the RequireJS module, require possible post-initialize hooks,
328 * and return the main object
329 */
330 var initialize = function() {
331
332 DragUploader.initialize();
333
334 // load required modules to hook in the post initialize function
335 if (undefined !== TYPO3.settings && undefined !== TYPO3.settings.RequireJS.PostInitializationModules && undefined !== TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']) {
336 $.each(TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], function(pos, moduleName) {
337 require([moduleName]);
338 });
339 }
340
341 // return the object in the global space
342 return DragUploader;
343 };
344
345 // call the main initialize function and execute the hooks
346 return initialize();
347
348 });