Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 lars 1
/*
2
 * jQuery File Upload User Interface Plugin 5.0.17
3
 * https://github.com/blueimp/jQuery-File-Upload
4
 *
5
 * Copyright 2010, Sebastian Tschan
6
 * https://blueimp.net
7
 *
8
 * Licensed under the MIT license:
9
 * http://creativecommons.org/licenses/MIT/
10
 */
11
 
12
/*jslint nomen: true, unparam: true, regexp: true */
13
/*global window, document, URL, webkitURL, FileReader, jQuery */
14
 
15
(function ($) {
16
    'use strict';
17
 
18
    // The UI version extends the basic fileupload widget and adds
19
    // a complete user interface based on the given upload/download
20
    // templates.
21
    $.widget('blueimpUI.fileupload', $.blueimp.fileupload, {
22
 
23
        options: {
24
            // By default, files added to the widget are uploaded as soon
25
            // as the user clicks on the start buttons. To enable automatic
26
            // uploads, set the following option to true:
27
            autoUpload: false,
28
            // The following option limits the number of files that are
29
            // allowed to be uploaded using this widget:
30
            maxNumberOfFiles: undefined,
31
            // The maximum allowed file size:
32
            maxFileSize: undefined,
33
            // The minimum allowed file size:
34
            minFileSize: 1,
35
            // The regular expression for allowed file types, matches
36
            // against either file type or file name:
37
            acceptFileTypes:  /.+$/i,
38
            // The regular expression to define for which files a preview
39
            // image is shown, matched against the file type:
40
            previewFileTypes: /^image\/(gif|jpeg|png)$/,
41
            // The maximum width of the preview images:
42
            previewMaxWidth: 80,
43
            // The maximum height of the preview images:
44
            previewMaxHeight: 80,
45
            // By default, preview images are displayed as canvas elements
46
            // if supported by the browser. Set the following option to false
47
            // to always display preview images as img elements:
48
            previewAsCanvas: true,
49
            // The file upload template that is given as first argument to the
50
            // jQuery.tmpl method to render the file uploads:
51
            uploadTemplate: $('#template-upload'),
52
            // The file download template, that is given as first argument to the
53
            // jQuery.tmpl method to render the file downloads:
54
            downloadTemplate: $('#template-download'),
55
            // The expected data type of the upload response, sets the dataType
56
            // option of the $.ajax upload requests:
57
            dataType: 'json',
58
 
59
            // The add callback is invoked as soon as files are added to the fileupload
60
            // widget (via file input selection, drag & drop or add API call).
61
            // See the basic file upload widget for more information:
62
            add: function (e, data) {
63
                var that = $(this).data('fileupload');
64
                that._adjustMaxNumberOfFiles(-data.files.length);
65
                data.isAdjusted = true;
66
                data.isValidated = that._validate(data.files);
67
                data.context = that._renderUpload(data.files)
68
                    .appendTo($(this).find('.files')).fadeIn(function () {
69
                        // Fix for IE7 and lower:
70
                        $(this).show();
71
                    }).data('data', data);
72
                if ((that.options.autoUpload || data.autoUpload) &&
73
                        data.isValidated) {
74
                    data.jqXHR = data.submit();
75
                }
76
            },
77
            // Callback for the start of each file upload request:
78
            send: function (e, data) {
79
                if (!data.isValidated) {
80
                    var that = $(this).data('fileupload');
81
                    if (!data.isAdjusted) {
82
                        that._adjustMaxNumberOfFiles(-data.files.length);
83
                    }
84
                    if (!that._validate(data.files)) {
85
                        return false;
86
                    }
87
                }
88
                if (data.context && data.dataType &&
89
                        data.dataType.substr(0, 6) === 'iframe') {
90
                    // Iframe Transport does not support progress events.
91
                    // In lack of an indeterminate progress bar, we set
92
                    // the progress to 100%, showing the full animated bar:
93
                    data.context.find('.ui-progressbar').progressbar(
94
                        'value',
95
                        parseInt(100, 10)
96
                    );
97
                }
98
            },
99
            // Callback for successful uploads:
100
            done: function (e, data) {
101
                var that = $(this).data('fileupload');
102
                if (data.context) {
103
                    data.context.each(function (index) {
104
                        var file = ($.isArray(data.result) &&
105
                                data.result[index]) || {error: 'emptyResult'};
106
                        if (file.error) {
107
                            that._adjustMaxNumberOfFiles(1);
108
                        }
109
                        $(this).fadeOut(function () {
110
                            that._renderDownload([file])
111
                                .css('display', 'none')
112
                                .replaceAll(this)
113
                                .fadeIn(function () {
114
                                    // Fix for IE7 and lower:
115
                                    $(this).show();
116
                                });
117
                        });
118
                    });
119
                } else {
120
                    that._renderDownload(data.result)
121
                        .css('display', 'none')
122
                        .appendTo($(this).find('.files'))
123
                        .fadeIn(function () {
124
                            // Fix for IE7 and lower:
125
                            $(this).show();
126
                        });
127
                }
128
            },
129
            // Callback for failed (abort or error) uploads:
130
            fail: function (e, data) {
131
                var that = $(this).data('fileupload');
132
                that._adjustMaxNumberOfFiles(data.files.length);
133
                if (data.context) {
134
                    data.context.each(function (index) {
135
                        $(this).fadeOut(function () {
136
                            if (data.errorThrown !== 'abort') {
137
                                var file = data.files[index];
138
                                file.error = file.error || data.errorThrown
139
                                    || true;
140
                                that._renderDownload([file])
141
                                    .css('display', 'none')
142
                                    .replaceAll(this)
143
                                    .fadeIn(function () {
144
                                        // Fix for IE7 and lower:
145
                                        $(this).show();
146
                                    });
147
                            } else {
148
                                data.context.remove();
149
                            }
150
                        });
151
                    });
152
                } else if (data.errorThrown !== 'abort') {
153
                    that._adjustMaxNumberOfFiles(-data.files.length);
154
                    data.context = that._renderUpload(data.files)
155
                        .css('display', 'none')
156
                        .appendTo($(this).find('.files'))
157
                        .fadeIn(function () {
158
                            // Fix for IE7 and lower:
159
                            $(this).show();
160
                        }).data('data', data);
161
                }
162
            },
163
            // Callback for upload progress events:
164
            progress: function (e, data) {
165
                if (data.context) {
166
                    data.context.find('.ui-progressbar').progressbar(
167
                        'value',
168
                        parseInt(data.loaded / data.total * 100, 10)
169
                    );
170
                }
171
            },
172
            // Callback for global upload progress events:
173
            progressall: function (e, data) {
174
                $(this).find('.fileupload-progressbar').progressbar(
175
                    'value',
176
                    parseInt(data.loaded / data.total * 100, 10)
177
                );
178
            },
179
            // Callback for uploads start, equivalent to the global ajaxStart event:
180
            start: function () {
181
                $(this).find('.fileupload-progressbar')
182
                    .progressbar('value', 0).fadeIn();
183
            },
184
            // Callback for uploads stop, equivalent to the global ajaxStop event:
185
            stop: function () {
186
                $(this).find('.fileupload-progressbar').fadeOut();
187
            },
188
            // Callback for file deletion:
189
            destroy: function (e, data) {
190
                var that = $(this).data('fileupload');
191
                if (data.url) {
192
                    $.ajax(data)
193
                        .success(function () {
194
                            that._adjustMaxNumberOfFiles(1);
195
                            $(this).fadeOut(function () {
196
                                $(this).remove();
197
                            });
198
                        });
199
                } else {
200
                    that._adjustMaxNumberOfFiles(1);
201
                    data.context.fadeOut(function () {
202
                        $(this).remove();
203
                    });
204
                }
205
            }
206
        },
207
 
208
        // Scales the given image (img HTML element)
209
        // using the given options.
210
        // Returns a canvas object if the canvas option is true
211
        // and the browser supports canvas, else the scaled image:
212
        _scaleImage: function (img, options) {
213
            options = options || {};
214
            var canvas = document.createElement('canvas'),
215
                scale = Math.min(
216
                    (options.maxWidth || img.width) / img.width,
217
                    (options.maxHeight || img.height) / img.height
218
                );
219
            if (scale >= 1) {
220
                scale = Math.max(
221
                    (options.minWidth || img.width) / img.width,
222
                    (options.minHeight || img.height) / img.height
223
                );
224
            }
225
            img.width = parseInt(img.width * scale, 10);
226
            img.height = parseInt(img.height * scale, 10);
227
            if (!options.canvas || !canvas.getContext) {
228
                return img;
229
            }
230
            canvas.width = img.width;
231
            canvas.height = img.height;
232
            canvas.getContext('2d')
233
                .drawImage(img, 0, 0, img.width, img.height);
234
            return canvas;
235
        },
236
 
237
        _createObjectURL: function (file) {
238
            var undef = 'undefined',
239
                urlAPI = (typeof window.createObjectURL !== undef && window) ||
240
                    (typeof URL !== undef && URL) ||
241
                    (typeof webkitURL !== undef && webkitURL);
242
            return urlAPI ? urlAPI.createObjectURL(file) : false;
243
        },
244
 
245
        _revokeObjectURL: function (url) {
246
            var undef = 'undefined',
247
                urlAPI = (typeof window.revokeObjectURL !== undef && window) ||
248
                    (typeof URL !== undef && URL) ||
249
                    (typeof webkitURL !== undef && webkitURL);
250
            return urlAPI ? urlAPI.revokeObjectURL(url) : false;
251
        },
252
 
253
        // Loads a given File object via FileReader interface,
254
        // invokes the callback with a data url:
255
        _loadFile: function (file, callback) {
256
            if (typeof FileReader !== 'undefined' &&
257
                    FileReader.prototype.readAsDataURL) {
258
                var fileReader = new FileReader();
259
                fileReader.onload = function (e) {
260
                    callback(e.target.result);
261
                };
262
                fileReader.readAsDataURL(file);
263
                return true;
264
            }
265
            return false;
266
        },
267
 
268
        // Loads an image for a given File object.
269
        // Invokes the callback with an img or optional canvas
270
        // element (if supported by the browser) as parameter:
271
        _loadImage: function (file, callback, options) {
272
            var that = this,
273
                url,
274
                img;
275
            if (!options || !options.fileTypes ||
276
                    options.fileTypes.test(file.type)) {
277
                url = this._createObjectURL(file);
278
                img = $('<img>').bind('load', function () {
279
                    $(this).unbind('load');
280
                    that._revokeObjectURL(url);
281
                    callback(that._scaleImage(img[0], options));
282
                });
283
                if (url) {
284
                    img.prop('src', url);
285
                    return true;
286
                } else {
287
                    return this._loadFile(file, function (url) {
288
                        img.prop('src', url);
289
                    });
290
                }
291
            }
292
            return false;
293
        },
294
 
295
        // Link handler, that allows to download files
296
        // by drag & drop of the links to the desktop:
297
        _enableDragToDesktop: function () {
298
            var link = $(this),
299
                url = link.prop('href'),
300
                name = decodeURIComponent(url.split('/').pop())
301
                    .replace(/:/g, '-'),
302
                type = 'application/octet-stream';
303
            link.bind('dragstart', function (e) {
304
                try {
305
                    e.originalEvent.dataTransfer.setData(
306
                        'DownloadURL',
307
                        [type, name, url].join(':')
308
                    );
309
                } catch (err) {}
310
            });
311
        },
312
 
313
        _adjustMaxNumberOfFiles: function (operand) {
314
            if (typeof this.options.maxNumberOfFiles === 'number') {
315
                this.options.maxNumberOfFiles += operand;
316
                if (this.options.maxNumberOfFiles < 1) {
317
                    this._disableFileInputButton();
318
                } else {
319
                    this._enableFileInputButton();
320
                }
321
            }
322
        },
323
 
324
        _formatFileSize: function (file) {
325
            if (typeof file.size !== 'number') {
326
                return '';
327
            }
328
            if (file.size >= 1000000000) {
329
                return (file.size / 1000000000).toFixed(2) + ' GB';
330
            }
331
            if (file.size >= 1000000) {
332
                return (file.size / 1000000).toFixed(2) + ' MB';
333
            }
334
            return (file.size / 1000).toFixed(2) + ' KB';
335
        },
336
 
337
        _hasError: function (file) {
338
            if (file.error) {
339
                return file.error;
340
            }
341
            // The number of added files is subtracted from
342
            // maxNumberOfFiles before validation, so we check if
343
            // maxNumberOfFiles is below 0 (instead of below 1):
344
            if (this.options.maxNumberOfFiles < 0) {
345
                return 'maxNumberOfFiles';
346
            }
347
            // Files are accepted if either the file type or the file name
348
            // matches against the acceptFileTypes regular expression, as
349
            // only browsers with support for the File API report the type:
350
            if (!(this.options.acceptFileTypes.test(file.type) ||
351
                    this.options.acceptFileTypes.test(file.name))) {
352
                return 'acceptFileTypes';
353
            }
354
            if (this.options.maxFileSize &&
355
                    file.size > this.options.maxFileSize) {
356
                return 'maxFileSize';
357
            }
358
            if (typeof file.size === 'number' &&
359
                    file.size < this.options.minFileSize) {
360
                return 'minFileSize';
361
            }
362
            return null;
363
        },
364
 
365
        _validate: function (files) {
366
            var that = this,
367
                valid = !!files.length;
368
            $.each(files, function (index, file) {
369
                file.error = that._hasError(file);
370
                if (file.error) {
371
                    valid = false;
372
                }
373
            });
374
            return valid;
375
        },
376
 
377
        _uploadTemplateHelper: function (file) {
378
            file.sizef = this._formatFileSize(file);
379
            return file;
380
        },
381
 
382
        _renderUploadTemplate: function (files) {
383
            var that = this;
384
            return $.tmpl(
385
                this.options.uploadTemplate,
386
                $.map(files, function (file) {
387
                    return that._uploadTemplateHelper(file);
388
                })
389
            );
390
        },
391
 
392
        _renderUpload: function (files) {
393
            var that = this,
394
                options = this.options,
395
                tmpl = this._renderUploadTemplate(files),
396
                isValidated = this._validate(files);
397
            if (!(tmpl instanceof $)) {
398
                return $();
399
            }
400
            tmpl.css('display', 'none');
401
            // .slice(1).remove().end().first() removes all but the first
402
            // element and selects only the first for the jQuery collection:
403
            tmpl.find('.progress div').slice(
404
                isValidated ? 1 : 0
405
            ).remove().end().first()
406
                .progressbar();
407
            tmpl.find('.start button').slice(
408
                this.options.autoUpload || !isValidated ? 0 : 1
409
            ).remove().end().first()
410
                .button({
411
                    text: false,
412
                    icons: {primary: 'ui-icon-circle-arrow-e'}
413
                });
414
            tmpl.find('.cancel button').slice(1).remove().end().first()
415
                .button({
416
                    text: false,
417
                    icons: {primary: 'ui-icon-cancel'}
418
                });
419
            tmpl.find('.preview').each(function (index, node) {
420
                that._loadImage(
421
                    files[index],
422
                    function (img) {
423
                        $(img).hide().appendTo(node).fadeIn();
424
                    },
425
                    {
426
                        maxWidth: options.previewMaxWidth,
427
                        maxHeight: options.previewMaxHeight,
428
                        fileTypes: options.previewFileTypes,
429
                        canvas: options.previewAsCanvas
430
                    }
431
                );
432
            });
433
            return tmpl;
434
        },
435
 
436
        _downloadTemplateHelper: function (file) {
437
            file.sizef = this._formatFileSize(file);
438
            return file;
439
        },
440
 
441
        _renderDownloadTemplate: function (files) {
442
            var that = this;
443
            return $.tmpl(
444
                this.options.downloadTemplate,
445
                $.map(files, function (file) {
446
                    return that._downloadTemplateHelper(file);
447
                })
448
            );
449
        },
450
 
451
        _renderDownload: function (files) {
452
            var tmpl = this._renderDownloadTemplate(files);
453
            if (!(tmpl instanceof $)) {
454
                return $();
455
            }
456
            tmpl.css('display', 'none');
457
            tmpl.find('.delete button').button({
458
                text: false,
459
                icons: {primary: 'ui-icon-trash'}
460
            });
461
            tmpl.find('a').each(this._enableDragToDesktop);
462
            return tmpl;
463
        },
464
 
465
        _startHandler: function (e) {
466
            e.preventDefault();
467
            var tmpl = $(this).closest('.template-upload'),
468
                data = tmpl.data('data');
469
            if (data && data.submit && !data.jqXHR) {
470
                data.jqXHR = data.submit();
471
                $(this).fadeOut();
472
            }
473
        },
474
 
475
        _cancelHandler: function (e) {
476
            e.preventDefault();
477
            var tmpl = $(this).closest('.template-upload'),
478
                data = tmpl.data('data') || {};
479
            if (!data.jqXHR) {
480
                data.errorThrown = 'abort';
481
                e.data.fileupload._trigger('fail', e, data);
482
            } else {
483
                data.jqXHR.abort();
484
            }
485
        },
486
 
487
        _deleteHandler: function (e) {
488
            e.preventDefault();
489
            var button = $(this);
490
            e.data.fileupload._trigger('destroy', e, {
491
                context: button.closest('.template-download'),
492
                url: button.attr('data-url'),
493
                type: button.attr('data-type'),
494
                dataType: e.data.fileupload.options.dataType
495
            });
496
        },
497
 
498
        _initEventHandlers: function () {
499
            $.blueimp.fileupload.prototype._initEventHandlers.call(this);
500
            var filesList = this.element.find('.files'),
501
                eventData = {fileupload: this};
502
            filesList.find('.start button')
503
                .live(
504
                    'click.' + this.options.namespace,
505
                    eventData,
506
                    this._startHandler
507
                );
508
            filesList.find('.cancel button')
509
                .live(
510
                    'click.' + this.options.namespace,
511
                    eventData,
512
                    this._cancelHandler
513
                );
514
            filesList.find('.delete button')
515
                .live(
516
                    'click.' + this.options.namespace,
517
                    eventData,
518
                    this._deleteHandler
519
                );
520
        },
521
 
522
        _destroyEventHandlers: function () {
523
            var filesList = this.element.find('.files');
524
            filesList.find('.start button')
525
                .die('click.' + this.options.namespace);
526
            filesList.find('.cancel button')
527
                .die('click.' + this.options.namespace);
528
            filesList.find('.delete button')
529
                .die('click.' + this.options.namespace);
530
            $.blueimp.fileupload.prototype._destroyEventHandlers.call(this);
531
        },
532
 
533
        _initFileUploadButtonBar: function () {
534
            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
535
                filesList = this.element.find('.files'),
536
                ns = this.options.namespace;
537
            fileUploadButtonBar
538
                .addClass('ui-widget-header ui-corner-top');
539
            this.element.find('.fileinput-button').each(function () {
540
                var fileInput = $(this).find('input:file').detach();
541
                $(this).button({icons: {primary: 'ui-icon-plusthick'}})
542
                    .append(fileInput);
543
            });
544
            fileUploadButtonBar.find('.start')
545
                .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
546
                .bind('click.' + ns, function (e) {
547
                    e.preventDefault();
548
                    filesList.find('.start button').click();
549
                });
550
            fileUploadButtonBar.find('.cancel')
551
                .button({icons: {primary: 'ui-icon-cancel'}})
552
                .bind('click.' + ns, function (e) {
553
                    e.preventDefault();
554
                    filesList.find('.cancel button').click();
555
                });
556
            fileUploadButtonBar.find('.delete')
557
                .button({icons: {primary: 'ui-icon-trash'}})
558
                .bind('click.' + ns, function (e) {
559
                    e.preventDefault();
560
                    filesList.find('.delete button').click();
561
                });
562
        },
563
 
564
        _destroyFileUploadButtonBar: function () {
565
            this.element.find('.fileupload-buttonbar')
566
                .removeClass('ui-widget-header ui-corner-top');
567
            this.element.find('.fileinput-button').each(function () {
568
                var fileInput = $(this).find('input:file').detach();
569
                $(this).button('destroy')
570
                    .append(fileInput);
571
            });
572
            this.element.find('.fileupload-buttonbar button')
573
                .unbind('click.' + this.options.namespace)
574
                .button('destroy');
575
        },
576
 
577
        _enableFileInputButton: function () {
578
            this.element.find('.fileinput-button input:file:disabled')
579
                .each(function () {
580
                    var fileInput = $(this),
581
                        button = fileInput.parent();
582
                    fileInput.detach().prop('disabled', false);
583
                    button.button('enable').append(fileInput);
584
                });
585
        },
586
 
587
        _disableFileInputButton: function () {
588
            this.element.find('.fileinput-button input:file:enabled')
589
                .each(function () {
590
                    var fileInput = $(this),
591
                        button = fileInput.parent();
592
                    fileInput.detach().prop('disabled', true);
593
                    button.button('disable').append(fileInput);
594
                });
595
        },
596
 
597
        _initTemplates: function () {
598
            // Handle cases where the templates are defined
599
            // after the widget library has been included:
600
            if (this.options.uploadTemplate instanceof $ &&
601
                    !this.options.uploadTemplate.length) {
602
                this.options.uploadTemplate = $(
603
                    this.options.uploadTemplate.selector
604
                );
605
            }
606
            if (this.options.downloadTemplate instanceof $ &&
607
                    !this.options.downloadTemplate.length) {
608
                this.options.downloadTemplate = $(
609
                    this.options.downloadTemplate.selector
610
                );
611
            }
612
        },
613
 
614
        _create: function () {
615
            $.blueimp.fileupload.prototype._create.call(this);
616
            this._initTemplates();
617
            this.element
618
                .addClass('ui-widget');
619
            this._initFileUploadButtonBar();
620
            this.element.find('.fileupload-content')
621
                .addClass('ui-widget-content ui-corner-bottom');
622
            this.element.find('.fileupload-progressbar')
623
                .hide().progressbar();
624
        },
625
 
626
        destroy: function () {
627
            this.element.find('.fileupload-progressbar')
628
                .progressbar('destroy');
629
            this.element.find('.fileupload-content')
630
                .removeClass('ui-widget-content ui-corner-bottom');
631
            this._destroyFileUploadButtonBar();
632
            this.element.removeClass('ui-widget');
633
            $.blueimp.fileupload.prototype.destroy.call(this);
634
        },
635
 
636
        enable: function () {
637
            $.blueimp.fileupload.prototype.enable.call(this);
638
            this.element.find(':ui-button').not('.fileinput-button')
639
                .button('enable');
640
            this._enableFileInputButton();
641
        },
642
 
643
        disable: function () {
644
            this.element.find(':ui-button').not('.fileinput-button')
645
                .button('disable');
646
            this._disableFileInputButton();
647
            $.blueimp.fileupload.prototype.disable.call(this);
648
        }
649
 
650
    });
651
 
652
}(jQuery));