source: trunk/prototype/plugins/fileupload/jquery.fileupload-ui.js @ 6107

Revision 6107, 25.5 KB checked in by gustavo, 12 years ago (diff)

Ticket #2676 - Falha ao anexar arquivo no expresso mail

Line 
1/*
2 * jQuery File Upload User Interface Plugin 6.7
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://www.opensource.org/licenses/MIT
10 */
11
12/*jslint nomen: true, unparam: true, regexp: true */
13/*global define, window, document, URL, webkitURL, FileReader */
14
15(function (factory) {
16    'use strict';
17    if (typeof define === 'function' && define.amd) {
18        // Register as an anonymous AMD module:
19        define([
20            'jquery',
21            'tmpl',
22            'load-image',
23            './jquery.fileupload-fp'
24        ], factory);
25    } else {
26        // Browser globals:
27        factory(
28            window.jQuery,
29            window.tmpl,
30            window.loadImage
31        );
32    }
33}(function ($, tmpl, loadImage) {
34    'use strict';
35
36    // The UI version extends the FP (file processing) version or the basic
37    // file upload widget and adds complete user interface interaction:
38    var parentWidget = ($.blueimpFP || $.blueimp).fileupload;
39    $.widget('blueimpUI.fileupload', parentWidget, {
40
41        options: {
42            // By default, files added to the widget are uploaded as soon
43            // as the user clicks on the start buttons. To enable automatic
44            // uploads, set the following option to true:
45            autoUpload: false,
46            // The following option limits the number of files that are
47            // allowed to be uploaded using this widget:
48            maxNumberOfFiles: undefined,
49            // The maximum allowed file size:
50            maxFileSize: undefined,
51            // The minimum allowed file size:
52            minFileSize: undefined,
53            // The regular expression for allowed file types, matches
54            // against either file type or file name:
55            acceptFileTypes:  /.+$/i,
56            // The regular expression to define for which files a preview
57            // image is shown, matched against the file type:
58            previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
59            // The maximum file size of images that are to be displayed as preview:
60            previewSourceMaxFileSize: 5000000, // 5MB
61            // The maximum width of the preview images:
62            previewMaxWidth: 80,
63            // The maximum height of the preview images:
64            previewMaxHeight: 80,
65            // By default, preview images are displayed as canvas elements
66            // if supported by the browser. Set the following option to false
67            // to always display preview images as img elements:
68            previewAsCanvas: true,
69            // The ID of the upload template:
70            uploadTemplateId: 'template-upload',
71            // The ID of the download template:
72            downloadTemplateId: 'template-download',
73            // The container for the list of files. If undefined, it is set to
74            // an element with class "files" inside of the widget element:
75            filesContainer: undefined,
76            // By default, files are appended to the files container.
77            // Set the following option to true, to prepend files instead:
78            prependFiles: false,
79            // The expected data type of the upload response, sets the dataType
80            // option of the $.ajax upload requests:
81            dataType: 'json',
82
83            // The add callback is invoked as soon as files are added to the fileupload
84            // widget (via file input selection, drag & drop or add API call).
85            // See the basic file upload widget for more information:
86            add: function (e, data) {
87                var that = $(this).data('fileupload'),
88                    options = that.options,
89                    files = data.files;
90                $(this).fileupload('process', data).done(function () {
91                    that._adjustMaxNumberOfFiles(-files.length);
92                    data.isAdjusted = true;
93                    data.files.valid = data.isValidated = that._validate(files);
94                    data.context = that._renderUpload(files).data('data', data);
95                    options.filesContainer[
96                        options.prependFiles ? 'prepend' : 'append'
97                    ](data.context);
98                    that._renderPreviews(files, data.context);
99                    that._forceReflow(data.context);
100                    that._transition(data.context).done(
101                        function () {
102                            if ((that._trigger('added', e, data) !== false) &&
103                                    (options.autoUpload || data.autoUpload) &&
104                                    data.autoUpload !== false && data.isValidated) {
105                                data.submit();
106                            }
107                        }
108                    );
109                });
110            },
111            // Callback for the start of each file upload request:
112            send: function (e, data) {
113                var that = $(this).data('fileupload');
114                if (!data.isValidated) {
115                    if (!data.isAdjusted) {
116                        that._adjustMaxNumberOfFiles(-data.files.length);
117                    }
118                    if (!that._validate(data.files)) {
119                        return false;
120                    }
121                }
122                if (data.context && data.dataType &&
123                        data.dataType.substr(0, 6) === 'iframe') {
124                    // Iframe Transport does not support progress events.
125                    // In lack of an indeterminate progress bar, we set
126                    // the progress to 100%, showing the full animated bar:
127                    data.context
128                        .find('.progress').addClass(
129                            !$.support.transition && 'progress-animated'
130                        )
131                        .find('.bar').css(
132                            'width',
133                            parseInt(100, 10) + '%'
134                        );
135                }
136                return that._trigger('sent', e, data);
137            },
138            // Callback for successful uploads:
139            done: function (e, data) {
140                var that = $(this).data('fileupload'),
141                    template,
142                    preview;
143                if (data.context) {
144                    data.context.each(function (index) {
145                        var file = ($.isArray(data.result) &&
146                                data.result[index]) || {error: 'emptyResult'};
147                        if (file.error) {
148                            that._adjustMaxNumberOfFiles(1);
149                        }
150                        that._transition($(this)).done(
151                            function () {
152                                var node = $(this);
153                                template = that._renderDownload([file])
154                                    .css('height', node.height())
155                                    .replaceAll(node);
156                                that._forceReflow(template);
157                                that._transition(template).done(
158                                    function () {
159                                        data.context = $(this);
160                                        that._trigger('completed', e, data);
161                                    }
162                                );
163                            }
164                        );
165                    });
166                } else {
167                    template = that._renderDownload(data.result)
168                        .appendTo(that.options.filesContainer);
169                    that._forceReflow(template);
170                    that._transition(template).done(
171                        function () {
172                            data.context = $(this);
173                            that._trigger('completed', e, data);
174                        }
175                    );
176                }
177            },
178            // Callback for failed (abort or error) uploads:
179            fail: function (e, data) {
180                var that = $(this).data('fileupload'),
181                    template;
182                that._adjustMaxNumberOfFiles(data.files.length);
183                if (data.context) {
184                    data.context.each(function (index) {
185                        if (data.errorThrown !== 'abort') {
186                            var file = data.files[index];
187                            file.error = file.error || data.errorThrown ||
188                                true;
189                            that._transition($(this)).done(
190                                function () {
191                                    var node = $(this);
192                                    template = that._renderDownload([file])
193                                        .replaceAll(node);
194                                    that._forceReflow(template);
195                                    that._transition(template).done(
196                                        function () {
197                                            data.context = $(this);
198                                            that._trigger('failed', e, data);
199                                        }
200                                    );
201                                }
202                            );
203                        } else {
204                            that._transition($(this)).done(
205                                function () {
206                                    $(this).remove();
207                                    that._trigger('failed', e, data);
208                                }
209                            );
210                        }
211                    });
212                } else if (data.errorThrown !== 'abort') {
213                    that._adjustMaxNumberOfFiles(-data.files.length);
214                    data.context = that._renderUpload(data.files)
215                        .appendTo(that.options.filesContainer)
216                        .data('data', data);
217                    that._forceReflow(data.context);
218                    that._transition(data.context).done(
219                        function () {
220                            data.context = $(this);
221                            that._trigger('failed', e, data);
222                        }
223                    );
224                } else {
225                    that._trigger('failed', e, data);
226                }
227            },
228            // Callback for upload progress events:
229            progress: function (e, data) {
230                if (data.context) {
231                    data.context.find('.progress .bar').css(
232                        'width',
233                        parseInt(data.loaded / data.total * 100, 10) + '%'
234                    );
235                }
236            },
237            // Callback for global upload progress events:
238            progressall: function (e, data) {
239                $(this).find('.fileupload-buttonbar .progress .bar').css(
240                    'width',
241                    parseInt(data.loaded / data.total * 100, 10) + '%'
242                );
243            },
244            // Callback for uploads start, equivalent to the global ajaxStart event:
245            start: function (e) {
246                var that = $(this).data('fileupload');
247                that._transition($(this).find('.fileupload-buttonbar .progress')).done(
248                    function () {
249                        that._trigger('started', e);
250                    }
251                );
252            },
253            // Callback for uploads stop, equivalent to the global ajaxStop event:
254            stop: function (e) {
255                var that = $(this).data('fileupload');
256                that._transition($(this).find('.fileupload-buttonbar .progress')).done(
257                    function () {
258                        $(this).find('.bar').css('width', '0%');
259                        that._trigger('stopped', e);
260                    }
261                );
262            },
263            // Callback for file deletion:
264            destroy: function (e, data) {
265                var that = $(this).data('fileupload');
266                if (data.url) {
267                    $.ajax(data);
268                }
269                that._adjustMaxNumberOfFiles(1);
270                that._transition(data.context).done(
271                    function () {
272                        $(this).remove();
273                        that._trigger('destroyed', e, data);
274                    }
275                );
276            }
277        },
278
279        // Link handler, that allows to download files
280        // by drag & drop of the links to the desktop:
281        _enableDragToDesktop: function () {
282            var link = $(this),
283                url = link.prop('href'),
284                name = link.prop('download'),
285                type = 'application/octet-stream';
286            link.bind('dragstart', function (e) {
287                try {
288                    e.originalEvent.dataTransfer.setData(
289                        'DownloadURL',
290                        [type, name, url].join(':')
291                    );
292                } catch (err) {}
293            });
294        },
295
296        _adjustMaxNumberOfFiles: function (operand) {
297            if (typeof this.options.maxNumberOfFiles === 'number') {
298                this.options.maxNumberOfFiles += operand;
299                if (this.options.maxNumberOfFiles < 1) {
300                    this._disableFileInputButton();
301                } else {
302                    this._enableFileInputButton();
303                }
304            }
305        },
306
307        _formatFileSize: function (bytes) {
308            if (typeof bytes !== 'number') {
309                return '';
310            }
311            if (bytes >= 1000000000) {
312                return (bytes / 1000000000).toFixed(2) + ' GB';
313            }
314            if (bytes >= 1000000) {
315                return (bytes / 1000000).toFixed(2) + ' MB';
316            }
317            return (bytes / 1000).toFixed(2) + ' KB';
318        },
319
320        _hasError: function (file) {
321            if (file.error) {
322                return file.error;
323            }
324            // The number of added files is subtracted from
325            // maxNumberOfFiles before validation, so we check if
326            // maxNumberOfFiles is below 0 (instead of below 1):
327            if (this.options.maxNumberOfFiles < 0) {
328                return 'maxNumberOfFiles';
329            }
330            // Files are accepted if either the file type or the file name
331            // matches against the acceptFileTypes regular expression, as
332            // only browsers with support for the File API report the type:
333            if (!(this.options.acceptFileTypes.test(file.type) ||
334                    this.options.acceptFileTypes.test(file.name))) {
335                return 'acceptFileTypes';
336            }
337            if (this.options.maxFileSize &&
338                    file.size > this.options.maxFileSize) {
339                return 'maxFileSize';
340            }
341            if (typeof file.size === 'number' &&
342                    file.size < this.options.minFileSize) {
343                return 'minFileSize';
344            }
345            return null;
346        },
347
348        _validate: function (files) {
349            var that = this,
350                valid = !!files.length;
351            $.each(files, function (index, file) {
352                file.error = that._hasError(file);
353                if (file.error) {
354                    valid = false;
355                }
356            });
357            return valid;
358        },
359
360        _renderTemplate: function (func, files) {
361            if (!func) {
362                return $();
363            }
364            var result = func({
365                files: files,
366                formatFileSize: this._formatFileSize,
367                options: this.options
368            });
369            if (result instanceof $) {
370                return result;
371            }
372            return $(this.options.templatesContainer).html(result).children();
373        },
374
375        _renderPreview: function (file, node) {
376            var that = this,
377                options = this.options,
378                dfd = $.Deferred();
379            return ((loadImage && loadImage(
380                file,
381                function (img) {
382                    node.append(img);
383                    that._forceReflow(node);
384                    that._transition(node).done(function () {
385                        dfd.resolveWith(node);
386                    });
387                    if (!$.contains(document.body, node[0])) {
388                        // If the element is not part of the DOM,
389                        // transition events are not triggered,
390                        // so we have to resolve manually:
391                        dfd.resolveWith(node);
392                    }
393                },
394                {
395                    maxWidth: options.previewMaxWidth,
396                    maxHeight: options.previewMaxHeight,
397                    canvas: options.previewAsCanvas
398                }
399            )) || dfd.resolveWith(node)) && dfd;
400        },
401
402        _renderPreviews: function (files, nodes) {
403            var that = this,
404                options = this.options;
405            nodes.find('.preview span').each(function (index, element) {
406                var file = files[index];
407                if (options.previewSourceFileTypes.test(file.type) &&
408                        ($.type(options.previewSourceMaxFileSize) !== 'number' ||
409                        file.size < options.previewSourceMaxFileSize)) {
410                    that._processingQueue = that._processingQueue.pipe(function () {
411                        var dfd = $.Deferred();
412                        that._renderPreview(file, $(element)).done(
413                            function () {
414                                dfd.resolveWith(that);
415                            }
416                        );
417                        return dfd.promise();
418                    });
419                }
420            });
421            return this._processingQueue;
422        },
423
424        _renderUpload: function (files) {
425            return this._renderTemplate(
426                this.options.uploadTemplate,
427                files
428            );
429        },
430
431        _renderDownload: function (files) {
432            return this._renderTemplate(
433                this.options.downloadTemplate,
434                files
435            ).find('a[download]').each(this._enableDragToDesktop).end();
436        },
437
438        _startHandler: function (e) {
439            e.preventDefault();
440            var button = $(this),
441                template = button.closest('.template-upload'),
442                data = template.data('data');
443            if (data && data.submit && !data.jqXHR && data.submit()) {
444                button.prop('disabled', true);
445            }
446        },
447
448        _cancelHandler: function (e) {
449            e.preventDefault();
450            var template = $(this).closest('.template-upload'),
451                data = template.data('data') || {};
452            if (!data.jqXHR) {
453                data.errorThrown = 'abort';
454                e.data.fileupload._trigger('fail', e, data);
455            } else {
456                data.jqXHR.abort();
457            }
458        },
459
460        _deleteHandler: function (e) {
461            e.preventDefault();
462            var button = $(this);
463            e.data.fileupload._trigger('destroy', e, {
464                context: button.closest('.template-download'),
465                url: button.attr('data-url'),
466                type: button.attr('data-type') || 'DELETE',
467                dataType: e.data.fileupload.options.dataType
468            });
469        },
470
471        _forceReflow: function (node) {
472            this._reflow = $.support.transition &&
473                node.length && node[0].offsetWidth;
474        },
475
476        _transition: function (node) {
477            var that = this,
478                dfd = $.Deferred();
479            if ($.support.transition && node.hasClass('fade')) {
480                node.bind(
481                    $.support.transition.end,
482                    function (e) {
483                        // Make sure we don't respond to other transitions events
484                        // in the container element, e.g. from button elements:
485                        if (e.target === node[0]) {
486                            node.unbind($.support.transition.end);
487                            dfd.resolveWith(node);
488                        }
489                    }
490                ).toggleClass('in');
491            } else {
492                node.toggleClass('in');
493                dfd.resolveWith(node);
494            }
495            return dfd;
496        },
497
498        _initButtonBarEventHandlers: function () {
499            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
500                filesList = this.options.filesContainer,
501                ns = this.options.namespace;
502            fileUploadButtonBar.find('.start')
503                .bind('click.' + ns, function (e) {
504                    e.preventDefault();
505                    filesList.find('.start button').click();
506                });
507            fileUploadButtonBar.find('.cancel')
508                .bind('click.' + ns, function (e) {
509                    e.preventDefault();
510                    filesList.find('.cancel button').click();
511                });
512            fileUploadButtonBar.find('.delete')
513                .bind('click.' + ns, function (e) {
514                    e.preventDefault();
515                    filesList.find('.delete input:checked')
516                        .siblings('button').click();
517                    fileUploadButtonBar.find('.toggle')
518                        .prop('checked', false);
519                });
520            fileUploadButtonBar.find('.toggle')
521                .bind('change.' + ns, function (e) {
522                    filesList.find('.delete input').prop(
523                        'checked',
524                        $(this).is(':checked')
525                    );
526                });
527        },
528
529        _destroyButtonBarEventHandlers: function () {
530            this.element.find('.fileupload-buttonbar button')
531                .unbind('click.' + this.options.namespace);
532            this.element.find('.fileupload-buttonbar .toggle')
533                .unbind('change.' + this.options.namespace);
534        },
535
536        _initEventHandlers: function () {
537            parentWidget.prototype._initEventHandlers.call(this);
538            var eventData = {fileupload: this};
539            this.options.filesContainer
540                .delegate(
541                    '.start button',
542                    'click.' + this.options.namespace,
543                    eventData,
544                    this._startHandler
545                )
546                .delegate(
547                    '.cancel button',
548                    'click.' + this.options.namespace,
549                    eventData,
550                    this._cancelHandler
551                )
552                .delegate(
553                    '.delete button',
554                    'click.' + this.options.namespace,
555                    eventData,
556                    this._deleteHandler
557                );
558            this._initButtonBarEventHandlers();
559        },
560
561        _destroyEventHandlers: function () {
562            var options = this.options;
563            this._destroyButtonBarEventHandlers();
564            options.filesContainer
565                .undelegate('.start button', 'click.' + options.namespace)
566                .undelegate('.cancel button', 'click.' + options.namespace)
567                .undelegate('.delete button', 'click.' + options.namespace);
568            parentWidget.prototype._destroyEventHandlers.call(this);
569        },
570
571        _enableFileInputButton: function () {
572            this.element.find('.fileinput-button input')
573                .prop('disabled', false)
574                .parent().removeClass('disabled');
575        },
576
577        _disableFileInputButton: function () {
578            this.element.find('.fileinput-button input')
579                .prop('disabled', true)
580                .parent().addClass('disabled');
581        },
582
583        _initTemplates: function () {
584            var options = this.options;
585            options.templatesContainer = document.createElement(
586                options.filesContainer.prop('nodeName')
587            );
588            if (tmpl) {
589                if (options.uploadTemplateId) {
590                    options.uploadTemplate = tmpl(options.uploadTemplateId);
591                }
592                if (options.downloadTemplateId) {
593                    options.downloadTemplate = tmpl(options.downloadTemplateId);
594                }
595            }
596        },
597
598        _initFilesContainer: function () {
599            var options = this.options;
600            if (options.filesContainer === undefined) {
601                options.filesContainer = this.element.find('.files');
602            } else if (!(options.filesContainer instanceof $)) {
603                options.filesContainer = $(options.filesContainer);
604            }
605        },
606
607        _initSpecialOptions: function () {
608            parentWidget.prototype._initSpecialOptions.call(this);
609            this._initFilesContainer();
610            this._initTemplates();
611        },
612
613        _create: function () {
614            parentWidget.prototype._create.call(this);
615            this._refreshOptionsList.push(
616                'filesContainer',
617                'uploadTemplateId',
618                'downloadTemplateId'
619            );
620            if (!$.blueimpFP) {
621                this._processingQueue = $.Deferred().resolveWith(this).promise();
622                this.process = function () {
623                    return this._processingQueue;
624                };
625            }
626        },
627
628        enable: function () {
629            parentWidget.prototype.enable.call(this);
630            this.element.find('input, button').prop('disabled', false);
631            this._enableFileInputButton();
632        },
633
634        disable: function () {
635            this.element.find('input, button').prop('disabled', true);
636            this._disableFileInputButton();
637            parentWidget.prototype.disable.call(this);
638        }
639
640    });
641
642}));
Note: See TracBrowser for help on using the repository browser.