PageSourceSearch

https://www.fcvfra.com/apps/scripts/fileuploader/js/jquery.fileupload-ui.js

js fcvfra.com collected 2026-10-02 13:27:14 UTC 24,009 bytes, 759 lines download raw bytes

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

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.