xref: /dokuwiki/lib/scripts/fileuploaderextended.js (revision 44dae8a743d9c3d83f22e6f38a1685c8326a3b62)
1qq.extend(qq.FileUploader.prototype, {
2    _createUploadHandler: function(){
3        var self = this,
4            handlerClass;
5
6        if(qq.UploadHandlerXhr.isSupported()){
7            handlerClass = 'UploadHandlerXhr';
8            //handlerClass = 'UploadHandlerForm';
9        } else {
10            handlerClass = 'UploadHandlerForm';
11        }
12
13        var handler = new qq[handlerClass]({
14            debug: this._options.debug,
15            action: this._options.action,
16            maxConnections: this._options.maxConnections,
17            onProgress: function(id, fileName, loaded, total){
18                self._onProgress(id, fileName, loaded, total);
19                self._options.onProgress(id, fileName, loaded, total);
20            },
21            onComplete: function(id, fileName, result){
22                self._onComplete(id, fileName, result);
23                self._options.onComplete(id, fileName, result);
24            },
25            onCancel: function(id, fileName){
26                self._onCancel(id, fileName);
27                self._options.onCancel(id, fileName);
28            },
29            onUpload: function(){
30                self._onUpload();
31            }
32        });
33
34        return handler;
35    },
36
37    _onUpload: function(){
38        this._handler.uploadAll(this._options.params);
39    },
40
41    _uploadFile: function(fileContainer){
42        var id = this._handler.add(fileContainer);
43        var fileName = this._handler.getName(id);
44
45        if (this._options.onSubmit(id, fileName) !== false){
46            this._onSubmit(id, fileName);
47        }
48    },
49
50    _addToList: function(id, fileName){
51        var item = qq.toElement(this._options.fileTemplate);
52        item.qqFileId = id;
53
54        var fileElement = this._find(item, 'file');
55        qq.setText(fileElement, fileName);
56        this._find(item, 'size').style.display = 'none';
57
58        // name suggestion (simplified cleanID)
59        var nameElement = this._find(item, 'nameInput');
60        fileName = fileName.toLowerCase();
61        fileName = fileName.replace(/([ !"#$%&\'()+,\/;<=>?@[\]^`{|}~:]+)/g, '_');
62        fileName = fileName.replace(/^_+/,'');
63        nameElement.value = fileName;
64        nameElement.id = 'mediamanager__upload_item'+id;
65
66        this._listElement.appendChild(item);
67    }
68
69});
70
71qq.FileUploaderExtended = function(o){
72    // call parent constructor
73    qq.FileUploaderBasic.apply(this, arguments);
74
75    qq.extend(this._options, {
76        element: null,
77        // if set, will be used instead of qq-upload-list in template
78        listElement: null,
79
80        template: '<div class="qq-uploader">' +
81            '<div class="qq-upload-drop-area"><span>' + LANG.media_drop + '</span></div>' +
82            '<div class="qq-upload-button">' + LANG.media_select + '</div>' +
83            '<ul class="qq-upload-list"></ul>' +
84            '<div class="qq-action-container">' +
85            '  <input class="qq-upload-action button" type="submit" value="' + LANG.media_upload_btn + '" id="mediamanager__upload_button">' +
86            '  <label class="qq-overwrite-check"><input type="checkbox" value="1" name="ow" class="dw__ow"> <span>' + LANG.media_overwrt + '</span></label>' +
87            '</div>' +
88            '</div>',
89
90        // template for one item in file list
91        fileTemplate: '<li>' +
92              '<span class="qq-upload-file hidden"></span>' +
93            '  <input class="qq-upload-name-input edit" type="text" value="" />' +
94            '  <span class="qq-upload-spinner hidden"></span>' +
95            '  <span class="qq-upload-size"></span>' +
96            '  <a class="qq-upload-cancel" href="#">' + LANG.media_cancel + '</a>' +
97            '  <span class="qq-upload-failed-text error">Failed</span>' +
98            '</li>',
99
100        classes: {
101            // used to get elements from templates
102            button: 'qq-upload-button',
103            drop: 'qq-upload-drop-area',
104            dropActive: 'qq-upload-drop-area-active',
105            list: 'qq-upload-list',
106            nameInput: 'qq-upload-name-input',
107            overwriteInput: 'qq-overwrite-check',
108            uploadButton: 'qq-upload-action',
109            file: 'qq-upload-file',
110
111            spinner: 'qq-upload-spinner',
112            size: 'qq-upload-size',
113            cancel: 'qq-upload-cancel',
114
115            // added to list item when upload completes
116            // used in css to hide progress spinner
117            success: 'qq-upload-success',
118            fail: 'qq-upload-fail',
119            failedText: 'qq-upload-failed-text'
120        }
121    });
122
123    qq.extend(this._options, o);
124
125    this._element = this._options.element;
126    this._element.innerHTML = this._options.template;
127    this._listElement = this._options.listElement || this._find(this._element, 'list');
128
129    this._classes = this._options.classes;
130
131    this._button = this._createUploadButton(this._find(this._element, 'button'));
132
133    this._bindCancelEvent();
134    this._bindUploadEvent();
135    this._setupDragDrop();
136};
137
138qq.extend(qq.FileUploaderExtended.prototype, qq.FileUploader.prototype);
139
140qq.extend(qq.FileUploaderExtended.prototype, {
141    _bindUploadEvent: function(){
142        var self = this,
143            list = this._listElement;
144
145        qq.attach(document.getElementById('mediamanager__upload_button'), 'click', function(e){
146            e = e || window.event;
147            var target = e.target || e.srcElement;
148            qq.preventDefault(e);
149            self._handler._options.onUpload();
150
151            jQuery(".qq-upload-name-input").each(function (i) {
152                jQuery(this).attr('disabled', 'disabled');
153            });
154        });
155    },
156
157    _onComplete: function(id, fileName, result){
158        this._filesInProgress--;
159
160        // mark completed
161        var item = this._getItemByFileId(id);
162        qq.remove(this._find(item, 'cancel'));
163        qq.remove(this._find(item, 'spinner'));
164
165        var nameInput = this._find(item, 'nameInput');
166        var fileElement = this._find(item, 'file');
167        qq.setText(fileElement, nameInput.value);
168        qq.removeClass(fileElement, 'hidden');
169        qq.remove(nameInput);
170        jQuery('.qq-upload-button, #mediamanager__upload_button').remove();
171        jQuery('.dw__ow').parent().hide();
172        jQuery('.qq-upload-drop-area').remove();
173
174        if (result.success){
175            qq.addClass(item, this._classes.success);
176            $link = '<a href="' + result.link + '" name="h_:' + result.id + '" class="select">' + nameInput.value + '</a>';
177            jQuery(fileElement).html($link);
178
179        } else {
180            qq.addClass(item, this._classes.fail);
181            var fail = this._find(item, 'failedText');
182            if (result.error) qq.setText(fail, result.error);
183        }
184
185        if (document.getElementById('media__content') && !document.getElementById('mediamanager__done_form')) {
186            var action = document.location.href;
187            var i = action.indexOf('?');
188            if (i) action = action.substr(0, i);
189            var button = '<form method="post" action="' + action + '" id="mediamanager__done_form"><div>';
190            button += '<input type="hidden" value="' + result.ns + '" name="ns">';
191            button += '<input class="button" type="submit" value="' + LANG.media_done_btn + '"></div></form>';
192            jQuery('#mediamanager__uploader').append(button);
193        }
194    }
195
196});
197
198qq.extend(qq.UploadHandlerForm.prototype, {
199    uploadAll: function(params){
200        this._uploadAll(params);
201    },
202
203    getName: function(id){
204        var file = this._inputs[id];
205        var name = document.getElementById('mediamanager__upload_item'+id);
206        if (name != null) {
207            return name.value;
208        } else {
209            if (file != null) {
210                // get input value and remove path to normalize
211                return file.value.replace(/.*(\/|\\)/, "");
212            } else {
213                return null;
214            }
215        }
216    },
217
218    _uploadAll: function(params){
219         jQuery(".qq-upload-spinner").each(function (i) {
220            jQuery(this).removeClass('hidden');
221        });
222        for (key in this._inputs) {
223            this.upload(key, params);
224        }
225
226    },
227
228    _upload: function(id, params){
229        var input = this._inputs[id];
230
231        if (!input){
232            throw new Error('file with passed id was not added, or already uploaded or cancelled');
233        }
234
235        var fileName = this.getName(id);
236
237        var iframe = this._createIframe(id);
238        var form = this._createForm(iframe, params);
239        form.appendChild(input);
240
241        var nameInput = qq.toElement('<input name="mediaid" value="' + fileName + '" type="text">');
242        form.appendChild(nameInput);
243
244        var checked = jQuery('.dw__ow').attr('checked');
245        var owCheckbox = jQuery('.dw__ow').clone();
246        owCheckbox.attr('checked', checked);
247        jQuery(form).append(owCheckbox);
248
249        var self = this;
250        this._attachLoadEvent(iframe, function(){
251            self.log('iframe loaded');
252
253            var response = self._getIframeContentJSON(iframe);
254
255            self._options.onComplete(id, fileName, response);
256            self._dequeue(id);
257
258            delete self._inputs[id];
259            // timeout added to fix busy state in FF3.6
260            setTimeout(function(){
261                qq.remove(iframe);
262            }, 1);
263        });
264
265        form.submit();
266        qq.remove(form);
267
268        return id;
269    }
270});
271
272qq.extend(qq.UploadHandlerXhr.prototype, {
273    uploadAll: function(params){
274        this._uploadAll(params);
275    },
276
277    getName: function(id){
278        var file = this._files[id];
279        var name = document.getElementById('mediamanager__upload_item'+id);
280        if (name != null) {
281            return name.value;
282        } else {
283            if (file != null) {
284                // fix missing name in Safari 4
285                return file.fileName != null ? file.fileName : file.name;
286            } else {
287                return null;
288            }
289        }
290    },
291
292    getSize: function(id){
293        var file = this._files[id];
294        if (file == null) return null;
295        return file.fileSize != null ? file.fileSize : file.size;
296    },
297
298    _upload: function(id, params){
299        var file = this._files[id],
300            name = this.getName(id),
301            size = this.getSize(id);
302        if (name == null || size == null) return;
303
304        this._loaded[id] = 0;
305
306        var xhr = this._xhrs[id] = new XMLHttpRequest();
307        var self = this;
308
309        xhr.upload.onprogress = function(e){
310            if (e.lengthComputable){
311                self._loaded[id] = e.loaded;
312                self._options.onProgress(id, name, e.loaded, e.total);
313            }
314        };
315
316        xhr.onreadystatechange = function(){
317            if (xhr.readyState == 4){
318                self._onComplete(id, xhr);
319            }
320        };
321
322        // build query string
323        params = params || {};
324        params['qqfile'] = name;
325        params['ow'] = jQuery('.dw__ow').attr('checked');
326        var queryString = qq.obj2url(params, this._options.action);
327
328        xhr.open("POST", queryString, true);
329        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
330        xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
331        xhr.setRequestHeader("Content-Type", "application/octet-stream");
332        xhr.send(file);
333    },
334
335    _uploadAll: function(params){
336        jQuery(".qq-upload-spinner").each(function (i) {
337            jQuery(this).removeClass('hidden');
338        });
339        for (key in this._files) {
340            this.upload(key, params);
341        }
342
343    }
344});
345