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

Revision 5341, 26.0 KB checked in by wmerlotto, 12 years ago (diff)

Ticket #2434 - Commit inicial do novo módulo de agenda do Expresso - expressoCalendar

Line 
1/*
2 * jQuery File Upload User Interface Plugin 5.0.16
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                } else {
286                    this._loadFile(file, function (url) {
287                        img.prop('src', url);
288                    });
289                }
290            }
291        },
292
293        // Link handler, that allows to download files
294        // by drag & drop of the links to the desktop:
295        _enableDragToDesktop: function () {
296            var link = $(this),
297                url = link.prop('href'),
298                name = decodeURIComponent(url.split('/').pop())
299                    .replace(/:/g, '-'),
300                type = 'application/octet-stream';
301            link.bind('dragstart', function (e) {
302                try {
303                    e.originalEvent.dataTransfer.setData(
304                        'DownloadURL',
305                        [type, name, url].join(':')
306                    );
307                } catch (err) {}
308            });
309        },
310
311        _adjustMaxNumberOfFiles: function (operand) {
312            if (typeof this.options.maxNumberOfFiles === 'number') {
313                this.options.maxNumberOfFiles += operand;
314                if (this.options.maxNumberOfFiles < 1) {
315                    this._disableFileInputButton();
316                } else {
317                    this._enableFileInputButton();
318                }
319            }
320        },
321
322        _formatFileSize: function (file) {
323            if (typeof file.size !== 'number') {
324                return '';
325            }
326            if (file.size >= 1000000000) {
327                return (file.size / 1000000000).toFixed(2) + ' GB';
328            }
329            if (file.size >= 1000000) {
330                return (file.size / 1000000).toFixed(2) + ' MB';
331            }
332            return (file.size / 1000).toFixed(2) + ' KB';
333        },
334
335        _hasError: function (file) {
336            if (file.error) {
337                return file.error;
338            }
339            // The number of added files is subtracted from
340            // maxNumberOfFiles before validation, so we check if
341            // maxNumberOfFiles is below 0 (instead of below 1):
342            if (this.options.maxNumberOfFiles < 0) {
343                return 'maxNumberOfFiles';
344            }
345            // Files are accepted if either the file type or the file name
346            // matches against the acceptFileTypes regular expression, as
347            // only browsers with support for the File API report the type:
348            if (!(this.options.acceptFileTypes.test(file.type) ||
349                    this.options.acceptFileTypes.test(file.name))) {
350                return 'acceptFileTypes';
351            }
352            if (this.options.maxFileSize &&
353                    file.size > this.options.maxFileSize) {
354                return 'maxFileSize';
355            }
356            if (typeof file.size === 'number' &&
357                    file.size < this.options.minFileSize) {
358                return 'minFileSize';
359            }
360            return null;
361        },
362
363        _validate: function (files) {
364            var that = this,
365                valid = !!files.length;
366            $.each(files, function (index, file) {
367                file.error = that._hasError(file);
368                if (file.error) {
369                    valid = false;
370                }
371            });
372            return valid;
373        },
374
375        _uploadTemplateHelper: function (file) {
376            file.sizef = this._formatFileSize(file);
377            return file;
378        },
379
380        _renderUploadTemplate: function (files) {
381            var that = this;
382            return $.tmpl(
383                this.options.uploadTemplate,
384                $.map(files, function (file) {
385                    return that._uploadTemplateHelper(file);
386                })
387            );
388        },
389
390        _renderUpload: function (files) {
391            var that = this,
392                options = this.options,
393                tmpl = this._renderUploadTemplate(files),
394                isValidated = this._validate(files);
395            if (!(tmpl instanceof $)) {
396                return $();
397            }
398            tmpl.css('display', 'none');
399            // .slice(1).remove().end().first() removes all but the first
400            // element and selects only the first for the jQuery collection:
401            tmpl.find('.progress div').slice(
402                isValidated ? 1 : 0
403            ).remove().end().first()
404                .progressbar();
405            tmpl.find('.start button').slice(
406                this.options.autoUpload || !isValidated ? 0 : 1
407            ).remove().end().first()
408                .button({
409                    text: false,
410                    icons: {primary: 'ui-icon-circle-arrow-e'}
411                });
412            tmpl.find('.cancel button').slice(1).remove().end().first()
413                .button({
414                    text: false,
415                    icons: {primary: 'ui-icon-cancel'}
416                });
417            tmpl.find('.preview').each(function (index, node) {
418                that._loadImage(
419                    files[index],
420                    function (img) {
421                        $(img).hide().appendTo(node).fadeIn();
422                    },
423                    {
424                        maxWidth: options.previewMaxWidth,
425                        maxHeight: options.previewMaxHeight,
426                        fileTypes: options.previewFileTypes,
427                        canvas: options.previewAsCanvas
428                    }
429                );
430            });
431            return tmpl;
432        },
433
434        _downloadTemplateHelper: function (file) {
435            file.sizef = this._formatFileSize(file);
436            return file;
437        },
438
439        _renderDownloadTemplate: function (files) {
440            var that = this;
441            return $.tmpl(
442                this.options.downloadTemplate,
443                $.map(files, function (file) {
444                    return that._downloadTemplateHelper(file);
445                })
446            );
447        },
448       
449        _renderDownload: function (files) {
450            var tmpl = this._renderDownloadTemplate(files);
451            if (!(tmpl instanceof $)) {
452                return $();
453            }
454            tmpl.css('display', 'none');
455            tmpl.find('.delete button').button({
456                text: false,
457                icons: {primary: 'ui-icon-trash'}
458            });
459            tmpl.find('a').each(this._enableDragToDesktop);
460            return tmpl;
461        },
462       
463        _startHandler: function (e) {
464            e.preventDefault();
465            var tmpl = $(this).closest('.template-upload'),
466                data = tmpl.data('data');
467            if (data && data.submit && !data.jqXHR) {
468                data.jqXHR = data.submit();
469                $(this).fadeOut();
470            }
471        },
472       
473        _cancelHandler: function (e) {
474            e.preventDefault();
475            var tmpl = $(this).closest('.template-upload'),
476                data = tmpl.data('data') || {};
477            if (!data.jqXHR) {
478                data.errorThrown = 'abort';
479                e.data.fileupload._trigger('fail', e, data);
480            } else {
481                data.jqXHR.abort();
482            }
483        },
484       
485        _deleteHandler: function (e) {
486            e.preventDefault();
487            var button = $(this);
488            e.data.fileupload._trigger('destroy', e, {
489                context: button.closest('.template-download'),
490                url: button.attr('data-url'),
491                type: button.attr('data-type'),
492                dataType: e.data.fileupload.options.dataType
493            });
494        },
495       
496        _initEventHandlers: function () {
497            $.blueimp.fileupload.prototype._initEventHandlers.call(this);
498            var filesList = this.element.find('.files'),
499                eventData = {fileupload: this};
500            filesList.find('.start button')
501                .live(
502                    'click.' + this.options.namespace,
503                    eventData,
504                    this._startHandler
505                );
506            filesList.find('.cancel button')
507                .live(
508                    'click.' + this.options.namespace,
509                    eventData,
510                    this._cancelHandler
511                );
512            filesList.find('.delete button')
513                .live(
514                    'click.' + this.options.namespace,
515                    eventData,
516                    this._deleteHandler
517                );
518        },
519       
520        _destroyEventHandlers: function () {
521            var filesList = this.element.find('.files');
522            filesList.find('.start button')
523                .die('click.' + this.options.namespace);
524            filesList.find('.cancel button')
525                .die('click.' + this.options.namespace);
526            filesList.find('.delete button')
527                .die('click.' + this.options.namespace);
528            $.blueimp.fileupload.prototype._destroyEventHandlers.call(this);
529        },
530
531        _initFileUploadButtonBar: function () {
532            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
533                filesList = this.element.find('.files'),
534                ns = this.options.namespace;
535            fileUploadButtonBar
536                .addClass('ui-widget-header ui-corner-top');
537            this.element.find('.fileinput-button').each(function () {
538                var fileInput = $(this).find('input:file').detach();
539                $(this).button({icons: {primary: 'ui-icon-plusthick'}})
540                    .append(fileInput);
541            });
542            fileUploadButtonBar.find('.start')
543                .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
544                .bind('click.' + ns, function (e) {
545                    e.preventDefault();
546                    filesList.find('.start button').click();
547                });
548            fileUploadButtonBar.find('.cancel')
549                .button({icons: {primary: 'ui-icon-cancel'}})
550                .bind('click.' + ns, function (e) {
551                    e.preventDefault();
552                    filesList.find('.cancel button').click();
553                });
554            fileUploadButtonBar.find('.delete')
555                .button({icons: {primary: 'ui-icon-trash'}})
556                .bind('click.' + ns, function (e) {
557                    e.preventDefault();
558                    filesList.find('.delete button').click();
559                });
560        },
561       
562        _destroyFileUploadButtonBar: function () {
563            this.element.find('.fileupload-buttonbar')
564                .removeClass('ui-widget-header ui-corner-top');
565            this.element.find('.fileinput-button').each(function () {
566                var fileInput = $(this).find('input:file').detach();
567                $(this).button('destroy')
568                    .append(fileInput);
569            });
570            this.element.find('.fileupload-buttonbar button')
571                .unbind('click.' + this.options.namespace)
572                .button('destroy');
573        },
574
575        _enableFileInputButton: function () {
576            this.element.find('.fileinput-button input:file:disabled')
577                .each(function () {
578                    var fileInput = $(this),
579                        button = fileInput.parent();
580                    fileInput.detach().prop('disabled', false);
581                    button.button('enable').append(fileInput);
582                });
583        },
584
585        _disableFileInputButton: function () {
586            this.element.find('.fileinput-button input:file:enabled')
587                .each(function () {
588                    var fileInput = $(this),
589                        button = fileInput.parent();
590                    fileInput.detach().prop('disabled', true);
591                    button.button('disable').append(fileInput);
592                });
593        },
594
595        _initTemplates: function () {
596            // Handle cases where the templates are defined
597            // after the widget library has been included:
598            if (this.options.uploadTemplate instanceof $ &&
599                    !this.options.uploadTemplate.length) {
600                this.options.uploadTemplate = $(
601                    this.options.uploadTemplate.selector
602                );
603            }
604            if (this.options.downloadTemplate instanceof $ &&
605                    !this.options.downloadTemplate.length) {
606                this.options.downloadTemplate = $(
607                    this.options.downloadTemplate.selector
608                );
609            }
610        },
611
612        _create: function () {
613            $.blueimp.fileupload.prototype._create.call(this);
614            this._initTemplates();
615            this.element
616                .addClass('ui-widget');
617            this._initFileUploadButtonBar();
618            this.element.find('.fileupload-content')
619                .addClass('ui-widget-content ui-corner-bottom');
620            this.element.find('.fileupload-progressbar')
621                .hide().progressbar();
622        },
623       
624        destroy: function () {
625            this.element.find('.fileupload-progressbar')
626                .progressbar('destroy');
627            this.element.find('.fileupload-content')
628                .removeClass('ui-widget-content ui-corner-bottom');
629            this._destroyFileUploadButtonBar();
630            this.element.removeClass('ui-widget');
631            $.blueimp.fileupload.prototype.destroy.call(this);
632        },
633       
634        enable: function () {
635            $.blueimp.fileupload.prototype.enable.call(this);
636            this.element.find(':ui-button').not('.fileinput-button')
637                .button('enable');
638            this._enableFileInputButton();
639        },
640       
641        disable: function () {
642            this.element.find(':ui-button').not('.fileinput-button')
643                .button('disable');
644            this._disableFileInputButton();
645            $.blueimp.fileupload.prototype.disable.call(this);
646        }
647
648    });
649
650}(jQuery));
Note: See TracBrowser for help on using the repository browser.