PageSourceSearch

https://www.briefbutler.com/toolkit/jquery_fileupload/jquery.fileupload-ui.js

js briefbutler.com collected 2026-10-06 15:03:47 UTC 27,514 bytes, 756 lines download raw bytes

1/*
2 * jQuery File Upload User Interface Plugin 8.7.1
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, URL, webkitURL, FileReader */
14
15(function( factory )
16{
17    'use strict';
18    if ( typeof define === 'function' && define.amd )
19    {
20        // Register as an anonymous AMD module:
21        define( [
22            'jquery',
23            'tmpl',
24            './jquery.fileupload-image',
25            './jquery.fileupload-audio',
26            './jquery.fileupload-video',
27            './jquery.fileupload-validate'
28        ], factory );
29    } else
30    {
31        // Browser globals:
32        factory(
33            window.jQuery,
34            window.tmpl
35        );
36    }
37}( function( $, tmpl, loadImage )
38{
39    'use strict';
40
41    $.blueimp.fileupload.prototype._specialOptions.push(
42        'filesContainer',
43        'uploadTemplateId',
44        'downloadTemplateId'
45    );
46
47    // The UI version extends the file upload widget
48    // and adds complete user interface interaction:
49    $.widget( 'blueimp.fileupload', $.blueimp.fileupload, {
50
51        options: {
52            // By default, files added to the widget are uploaded as soon
53            // as the user clicks on the start buttons. To enable automatic
54            // uploads, set the following option to true:
55            autoUpload: false,
56            // The ID of the upload template:
57            uploadTemplateId: 'template-upload',
58            // The ID of the download template:
59            downloadTemplateId: 'template-download',
60            // The container for the list of files. If undefined, it is set to
61            // an element with class "files" inside of the widget element:
62            filesContainer: undefined,
63            // By default, files are appended to the files container.
64            // Set the following option to true, to prepend files instead:
65            prependFiles: false,
66            // The expected data type of the upload response, sets the dataType
67            // option of the $.ajax upload requests:
68            dataType: 'json',
69
70            // Function returning the current number of files,
71            // used by the maxNumberOfFiles validation:
72            getNumberOfFiles: function()
73            {
74                return this.filesContainer.children().length;
75            },
76
77            // Callback to retrieve the list of files from the server response:
78            getFilesFromResponse: function( data )
79            {
80                if ( data.result && $.isArray( data.result.files ) )
81                {
82                    return data.result.files;
83                }
84                return [];
85            },
86
87            // The add callback is invoked as soon as files are added to the fileupload
88            // widget (via file input selection, drag & drop or add API call).
89            // See the basic file upload widget for more information:
90            add: function( e, data )
91            {
92                var $this = $( this ),
93                    that = $this.data( 'blueimp-fileupload' ) ||
94                        $this.data( 'fileupload' ),
95                    options = that.options,
96                    files = data.files;
97                data.process( function()
98                {
99                    return $this.fileupload( 'process', data );
100                } ).always( function()
101                {
102                    data.context = that._renderUpload( files ).data( 'data', data );
103                    that._renderPreviews( data );
104                    options.filesContainer[
105                        options.prependFiles ? 'prepend' : 'append'
106                        ]( data.context );
107                    that._forceReflow( data.context );
108                    that._transition( data.context ).done(
109                        function()
110                        {
111                            if ( (that._trigger( 'added', e, data ) !== false) &&
112                                (options.autoUpload || data.autoUpload) &&
113                                data.autoUpload !== false && !data.files.error )
114                            {
115                                data.submit();
116                            }
117                        }
118                    );
119                } );
120            },
121            // Callback for the start of each file upload request:
122            send: function( e, data )
123            {
124                var that = $( this ).data( 'blueimp-fileupload' ) ||
125                    $( this ).data( 'fileupload' );
126                if ( data.context && data.dataType &&
127                    data.dataType.substr( 0, 6 ) === 'iframe' )
128                {
129                    // Iframe Transport does not support progress events.
130                    // In lack of an indeterminate progress bar, we set
131                    // the progress to 100%, showing the full animated bar:
132                    data.context
133                        .find( '.progress' ).addClass(
134                        !$.support.transition && 'progress-animated'
135                    )
136                        .attr( 'aria-valuenow', 100 )
137                        .find( '.bar' ).css(
138                        'width',
139                        '100%'
140                    );
141                }
142                return that._trigger( 'sent', e, data );
143            },
144            // Callback for successful uploads:
145            done: function( e, data )
146            {
147                var that = $( this ).data( 'blueimp-fileupload' ) ||
148                        $( this ).data( 'fileupload' ),
149                    getFilesFromResponse = data.getFilesFromResponse ||
150                        that.options.getFilesFromResponse,
151                    files = getFilesFromResponse( data ),
152                    template,
153                    deferred;
154                if ( data.context )
155                {
156                    data.context.each( function( index )
157                    {
158                        var file = files[ index ] ||
159                            { error: 'Empty file upload result' };
160                        deferred = that._addFinishedDeferreds();
161                        that._transition( $( this ) ).done(
162                            function()
163                            {
164                                var node = $( this );
165                                template = that._renderDownload( [ file ] )
166                                    .replaceAll( node );
167                                that._forceReflow( template );
168                                that._transition( template ).done(
169                                    function()
170                                    {
171                                        data.context = $( this );
172                                        that._trigger( 'completed', e, data );
173                                        that._trigger( 'finished', e, data );
174                                        deferred.resolve();
175                                    }
176                                );
177                            }
178                        );
179                    } );
180                } else
181                {
182                    template = that._renderDownload( files )[
183                        that.options.prependFiles ? 'prependTo' : 'appendTo'
184                        ]( that.options.filesContainer );
185                    that._forceReflow( template );
186                    deferred = that._addFinishedDeferreds();
187                    that._transition( template ).done(
188                        function()
189                        {
190                            data.context = $( this );
191                            that._trigger( 'completed', e, data );
192                            that._trigger( 'finished', e, data );
193                            deferred.resolve();
194                        }
195                    );
196                }
197            },
198            // Callback for failed (abort or error) uploads:
199            fail: function( e, data )
200            {
201                var that = $( this ).data( 'blueimp-fileupload' ) ||
202                        $( this ).data( 'fileupload' ),
203                    template,
204                    deferred;
205                if ( data.context )
206                {
207                    data.context.each( function( index )
208                    {
209                        if ( data.errorThrown !== 'abort' )
210                        {
211                            var file = data.files[ index ];
212                            file.error = file.error || data.errorThrown ||
213                            true;
214                            deferred = that._addFinishedDeferreds();
215                            that._transition( $( this ) ).done(
216                                function()
217                                {
218                                    var node = $( this );
219                                    template = that._renderDownload( [ file ] )
220                                        .replaceAll( node );
221                                    that._forceReflow( template );
222                                    that._transition( template ).done(
223                                        function()
224                                        {
225                                            data.context = $( this );
226                                            that._trigger( 'failed', e, data );
227                                            that._trigger( 'finished', e, data );
228                                            deferred.resolve();
229                                        }
230                                    );
231                                }
232                            );
233                        } else
234                        {
235                            deferred = that._addFinishedDeferreds();
236                            that._transition( $( this ) ).done(
237                                function()
238                                {
239                                    $( this ).remove();
240                                    that._trigger( 'failed', e, data );
241                                    that._trigger( 'finished', e, data );
242                                    deferred.resolve();
243                                }
244                            );
245                        }
246                    } );
247                } else if ( data.errorThrown !== 'abort' )
248                {
249                    data.context = that._renderUpload( data.files )[
250                        that.options.prependFiles ? 'prependTo' : 'appendTo'
251                        ]( that.options.filesContainer )
252                        .data( 'data', data );
253                    that._forceReflow( data.context );
254                    deferred = that._addFinishedDeferreds();
255                    that._transition( data.context ).done(
256                        function()
257                        {
258                            data.context = $( this );
259                            that._trigger( 'failed', e, data );
260                            that._trigger( 'finished', e, data );
261                            deferred.resolve();
262                        }
263                    );
264                } else
265                {
266                    that._trigger( 'failed', e, data );
267                    that._trigger( 'finished', e, data );
268                    that._addFinishedDeferreds().resolve();
269                }
270            },
271            // Callback for upload progress events:
272            progress: function( e, data )
273            {
274                if ( data.context )
275                {
276                    var progress = Math.floor( data.loaded / data.total * 100 );
277                    data.context.find( '.progress' )
278                        .attr( 'aria-valuenow', progress )
279                        .find( '.bar' ).css(
280                        'width',
281                        progress + '%'
282                    );
283                }
284            },
285            // Callback for global upload progress events:
286            progressall: function( e, data )
287            {
288                var $this = $( this ),
289                    progress = Math.floor( data.loaded / data.total * 100 ),
290                    globalProgressNode = $this.find( '.fileupload-progress' ),
291                    extendedProgressNode = globalProgressNode
292                        .find( '.progress-extended' );
293                if ( extendedProgressNode.length )
294                {
295                    extendedProgressNode.html(
296                        ($this.data( 'blueimp-fileupload' ) || $this.data( 'fileupload' ))
297                            ._renderExtendedProgress( data )
298                    );
299                }
300                globalProgressNode
301                    .find( '.progress' )
302                    .attr( 'aria-valuenow', progress )
303                    .find( '.bar' ).css(
304                    'width',
305                    progress + '%'
306                );
307            },
308            // Callback for uploads start, equivalent to the global ajaxStart event:
309            start: function( e )
310            {
311                var that = $( this ).data( 'blueimp-fileupload' ) ||
312                    $( this ).data( 'fileupload' );
313                that._resetFinishedDeferreds();
314                that._transition( $( this ).find( '.fileupload-progress' ) ).done(
315                    function()
316                    {
317                        that._trigger( 'started', e );
318                    }
319                );
320            },
321            // Callback for uploads stop, equivalent to the global ajaxStop event:
322            stop: function( e )
323            {
324                var that = $( this ).data( 'blueimp-fileupload' ) ||
325                        $( this ).data( 'fileupload' ),
326                    deferred = that._addFinishedDeferreds();
327                $.when.apply( $, that._getFinishedDeferreds() )
328                    .done( function()
329                    {
330                        that._trigger( 'stopped', e );
331                    } );
332                that._transition( $( this ).find( '.fileupload-progress' ) ).done(
333                    function()
334                    {
335                        $( this ).find( '.progress' )
336                            .attr( 'aria-valuenow', '0' )
337                            .find( '.bar' ).css( 'width', '0%' );
338                        $( this ).find( '.progress-extended' ).html( ' ' );
339                        deferred.resolve();
340                    }
341                );
342            },
343            processstart: function()
344            {
345                $( this ).addClass( 'fileupload-processing' );
346            },
347            processstop: function()
348            {
349                $( this ).removeClass( 'fileupload-processing' );
350            },
351            // Callback for file deletion:
352            destroy: function( e, data )
353            {
354                var that = $( this ).data( 'blueimp-fileupload' ) ||
355                        $( this ).data( 'fileupload' ),
356                    removeNode = function()
357                    {
358                        that._transition( data.context ).done(
359                            function()
360                            {
361                                $( this ).remove();
362                                that._trigger( 'destroyed', e, data );
363                            }
364                        );
365                    };
366                if ( data.url )
367                {
368                    $.ajax( data ).done( removeNode );
369                } else
370                {
371                    removeNode();
372                }
373            }
374        },
375
376        _resetFinishedDeferreds: function()
377        {
378            this._finishedUploads = [];
379        },
380
381        _addFinishedDeferreds: function( deferred )
382        {
383            if ( !deferred )
384            {
385                deferred = $.Deferred();
386            }
387            this._finishedUploads.push( deferred );
388            return deferred;
389        },
390
391        _getFinishedDeferreds: function()
392        {
393            return this._finishedUploads;
394        },
395
396        // Link handler, that allows to download files
397        // by drag & drop of the links to the desktop:
398        _enableDragToDesktop: function()
399        {
400            var link = $( this ),
401                url = link.prop( 'href' ),
402                name = link.prop( 'download' ),
403                type = 'application/octet-stream';
404            link.bind( 'dragstart', function( e )
405            {
406                try
407                {
408                    e.originalEvent.dataTransfer.setData(
409                        'DownloadURL',
410                        [ type, name, url ].join( ':' )
411                    );
412                } catch ( ignore )
413                {
414                }
415            } );
416        },
417
418        _formatFileSize: function( bytes )
419        {
420            if ( typeof bytes !== 'number' )
421            {
422                return '';
423            }
424            if ( bytes >= 1000000000 )
425            {
426                return (bytes / 1000000000).toFixed( 2 ) + ' GB';
427            }
428            if ( bytes >= 1000000 )
429            {
430                return (bytes / 1000000).toFixed( 2 ) + ' MB';
431            }
432            return (bytes / 1000).toFixed( 2 ) + ' KB';
433        },
434
435        _formatBitrate: function( bits )
436        {
437            if ( typeof bits !== 'number' )
438            {
439                return '';
440            }
441            if ( bits >= 1000000000 )
442            {
443                return (bits / 1000000000).toFixed( 2 ) + ' Gbit/s';
444            }
445            if ( bits >= 1000000 )
446            {
447                return (bits / 1000000).toFixed( 2 ) + ' Mbit/s';
448            }
449            if ( bits >= 1000 )
450            {
451                return (bits / 1000).toFixed( 2 ) + ' kbit/s';
452            }
453            return bits.toFixed( 2 ) + ' bit/s';
454        },
455
456        _formatTime: function( seconds )
457        {
458            var date = new Date( seconds * 1000 ),
459                days = Math.floor( seconds / 86400 );
460            days = days ? days + 'd ' : '';
461            return days +
462                ('0' + date.getUTCHours()).slice( -2 ) + ':' +
463                ('0' + date.getUTCMinutes()).slice( -2 ) + ':' +
464                ('0' + date.getUTCSeconds()).slice( -2 );
465        },
466
467        _formatPercentage: function( floatValue )
468        {
469            return (floatValue * 100).toFixed( 2 ) + ' %';
470        },
471
472        _renderExtendedProgress: function( data )
473        {
474            return this._formatBitrate( data.bitrate ) + ' | ' +
475                this._formatTime(
476                    (data.total - data.loaded) * 8 / data.bitrate
477                ) + ' | ' +
478                this._formatPercentage(
479                    data.loaded / data.total
480                ) + ' | ' +
481                this._formatFileSize( data.loaded ) + ' / ' +
482                this._formatFileSize( data.total );
483        },
484
485        _renderTemplate: function( func, files )
486        {
487            if ( !func )
488            {
489                return $();
490            }
491            var result = func( {
492                files: files,
493                formatFileSize: this._formatFileSize,
494                options: this.options
495            } );
496            if ( result instanceof $ )
497            {
498                return result;
499            }
500            return $( this.options.templatesContainer ).html( result ).children();
501        },
502
503        _renderPreviews: function( data )
504        {
505            data.context.find( '.preview' ).each( function( index, elm )
506            {
507                $( elm ).append( data.files[ index ].preview );
508            } );
509        },
510
511        _renderUpload: function( files )
512        {
513            return this._renderTemplate(
514                this.options.uploadTemplate,
515                files
516            );
517        },
518
519        _renderDownload: function( files )
520        {
521            return this._renderTemplate(
522                this.options.downloadTemplate,
523                files
524            ).find( 'a[download]' ).each( this._enableDragToDesktop ).end();
525        },
526
527        _startHandler: function( e )
528        {
529            e.preventDefault();
530            var button = $( e.currentTarget ),
531                template = button.closest( '.template-upload' ),
532                data = template.data( 'data' );
533            if ( data && data.submit && !data.jqXHR && data.submit() )
534            {
535                button.prop( 'disabled', true );
536            }
537        },
538
539        _cancelHandler: function( e )
540        {
541            e.preventDefault();
542            var template = $( e.currentTarget ).closest( '.template-upload' ),
543                data = template.data( 'data' ) || {};
544            if ( !data.jqXHR )
545            {
546                data.errorThrown = 'abort';
547                this._trigger( 'fail', e, data );
548            } else
549            {
550                data.jqXHR.abort();
551            }
552        },
553
554        _deleteHandler: function( e )
555        {
556            e.preventDefault();
557            var button = $( e.currentTarget );
558            this._trigger( 'destroy', e, $.extend( {
559                context: button.closest( '.template-download' ),
560                type: 'DELETE'
561            }, button.data() ) );
562        },
563
564        _forceReflow: function( node )
565        {
566            return $.support.transition && node.length &&
567                node[ 0 ].offsetWidth;
568        },
569
570        _transition: function( node )
571        {
572            var dfd = $.Deferred();
573            if ( $.support.transition && node.hasClass( 'fade' ) && node.is( ':visible' ) )
574            {
575                node.bind(
576                    $.support.transition.end,
577                    function( e )
578                    {
579                        // Make sure we don't respond to other transitions events
580                        // in the container element, e.g. from button elements:
581                        if ( e.target === node[ 0 ] )
582                        {
583                            node.unbind( $.support.transition.end );
584                            dfd.resolveWith( node );
585                        }
586                    }
587                ).toggleClass( 'in' );
588            } else
589            {
590                node.toggleClass( 'in' );
591                dfd.resolveWith( node );
592            }
593            return dfd;
594        },
595
596        _initButtonBarEventHandlers: function()
597        {
598            var fileUploadButtonBar = this.element.find( '.fileupload-buttonbar' ),
599                filesList = this.options.filesContainer;
600            this._on( fileUploadButtonBar.find( '.start' ), {
601                click: function( e )
602                {
603                    e.preventDefault();
604                    filesList.find( '.start' ).click();
605                }
606            } );
607            this._on( fileUploadButtonBar.find( '.cancel' ), {
608                click: function( e )
609                {
610                    e.preventDefault();
611                    filesList.find( '.cancel' ).click();
612                }
613            } );
614            this._on( fileUploadButtonBar.find( '.delete' ), {
615                click: function( e )
616                {
617                    e.preventDefault();
618                    filesList.find( '.toggle:checked' )
619                        .closest( '.template-download' )
620                        .find( '.delete' ).click();
621                    fileUploadButtonBar.find( '.toggle' )
622                        .prop( 'checked', false );
623                }
624            } );
625            this._on( fileUploadButtonBar.find( '.toggle' ), {
626                change: function( e )
627                {
628                    filesList.find( '.toggle' ).prop(
629                        'checked',
630                        $( e.currentTarget ).is( ':checked' )
631                    );
632                }
633            } );
634        },
635
636        _destroyButtonBarEventHandlers: function()
637        {
638            this._off(
639                this.element.find( '.fileupload-buttonbar' )
640                    .find( '.start, .cancel, .delete' ),
641                'click'
642            );
643            this._off(
644                this.element.find( '.fileupload-buttonbar .toggle' ),
645                'change.'
646            );
647        },
648
649        _initEventHandlers: function()
650        {
651            this._super();
652            this._on( this.options.filesContainer, {
653                'click .start': this._startHandler,
654                'click .cancel': this._cancelHandler,
655                'click .delete': this._deleteHandler
656            } );
657            this._initButtonBarEventHandlers();
658        },
659
660        _destroyEventHandlers: function()
661        {
662            this._destroyButtonBarEventHandlers();
663            this._off( this.options.filesContainer, 'click' );
664            this._super();
665        },
666
667        _enableFileInputButton: function()
668        {
669            this.element.find( '.fileinput-button input' )
670                .prop( 'disabled', false )
671                .parent().removeClass( 'disabled' );
672        },
673
674        _disableFileInputButton: function()
675        {
676            this.element.find( '.fileinput-button input' )
677                .prop( 'disabled', true )
678                .parent().addClass( 'disabled' );
679        },
680
681        _initTemplates: function()
682        {
683            var options = this.options;
684            options.templatesContainer = this.document[ 0 ].createElement(
685                options.filesContainer.prop( 'nodeName' )
686            );
687            if ( tmpl )
688            {
689                if ( options.uploadTemplateId )
690                {
691                    options.uploadTemplate = tmpl( options.uploadTemplateId );
692                }
693                if ( options.downloadTemplateId )
694                {
695                    options.downloadTemplate = tmpl( options.downloadTemplateId );
696                }
697            }
698        },
699
700        _initFilesContainer: function()
701        {
702            var options = this.options;
703            if ( options.filesContainer === undefined )
704            {
705                options.filesContainer = this.element.find( '.files' );
706            } else if ( !(options.filesContainer instanceof $) )
707            {
708                options.filesContainer = $( options.filesContainer );
709            }
710        },
711
712        _initSpecialOptions: function()
713        {
714            this._super();
715            this._initFilesContainer();
716            this._initTemplates();
717        },
718
719        _create: function()
720        {
721            this._super();
722            this._resetFinishedDeferreds();
723            if ( !$.support.fileInput )
724            {
725                this._disableFileInputButton();
726            }
727        },
728
729        enable: function()
730        {
731            var wasDisabled = false;
732            if ( this.options.disabled )
733            {
734                wasDisabled = true;
735            }
736            this._super();
737            if ( wasDisabled )
738            {
739                this.element.find( 'input, button' ).prop( 'disabled', false );
740                this._enableFileInputButton();
741            }
742        },
743
744        disable: function()
745        {
746            if ( !this.options.disabled )
747            {
748                this.element.find( 'input, button' ).prop( 'disabled', true );
749                this._disableFileInputButton();
750            }
751            this._super();
752        }
753
754    } );
755
756} ));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.