PageSourceSearch

https://www.sga-ag.de/typo3temp/assets/compressed/jquery.fileuploa…ef0ae619411e35283186aeebe804a.js?1789380814

js sga-ag.de collected 2026-10-02 18:38:49 UTC 56,206 bytes, 1,611 lines download raw bytes

1/*
2 * jQuery File Upload 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/* eslint-disable new-cap */
14
15(function(factory) {
16  'use strict';
17  if (typeof define === 'function' && define.amd) {
18    // Register as an anonymous AMD module:
19    define(['jquery', 'jquery-ui/ui/widget'], factory);
20  } else if (typeof exports === 'object') {
21    // Node/CommonJS:
22    factory(require('jquery'), require('./vendor/jquery.ui.widget'));
23  } else {
24    // Browser globals:
25    factory(window.jQuery);
26  }
27})(function($) {
28  'use strict';
29
30  // Detect file input support, based on
31  // https://viljamis.com/2012/file-upload-support-on-mobile/
32  $.support.fileInput = !(
33    new RegExp(
34      // Handle devices which give false positives for the feature detection:
35      '(Android (1\\.[0156]|2\\.[01]))' +
36        '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
37        '|(w(eb)?OSBrowser)|(webOS)' +
38        '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
39    ).test(window.navigator.userAgent) ||
40    // Feature detection for all other devices:
41    $('<input type="file"/>').prop('disabled')
42  );
43
44  // The FileReader API is not actually used, but works as feature detection,
45  // as some Safari versions (5?) support XHR file uploads via the FormData API,
46  // but not non-multipart XHR file uploads.
47  // window.XMLHttpRequestUpload is not available on IE10, so we check for
48  // window.ProgressEvent instead to detect XHR2 file upload capability:
49  $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
50  $.support.xhrFormDataFileUpload = !!window.FormData;
51
52  // Detect support for Blob slicing (required for chunked uploads):
53  $.support.blobSlice =
54    window.Blob &&
55    (Blob.prototype.slice ||
56      Blob.prototype.webkitSlice ||
57      Blob.prototype.mozSlice);
58
59  /**
60   * Helper function to create drag handlers for dragover/dragenter/dragleave
61   *
62   * @param {string} type Event type
63   * @returns {Function} Drag handler
64   */
65  function getDragHandler(type) {
66    var isDragOver = type === 'dragover';
67    return function(e) {
68      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
69      var dataTransfer = e.dataTransfer;
70      if (
71        dataTransfer &&
72        $.inArray('Files', dataTransfer.types) !== -1 &&
73        this._trigger(type, $.Event(type, { delegatedEvent: e })) !== false
74      ) {
75        e.preventDefault();
76        if (isDragOver) {
77          dataTransfer.dropEffect = 'copy';
78        }
79      }
80    };
81  }
82
83  // The fileupload widget listens for change events on file input fields defined
84  // via fileInput setting and paste or drop events of the given dropZone.
85  // In addition to the default jQuery Widget methods, the fileupload widget
86  // exposes the "add" and "send" methods, to add or directly send files using
87  // the fileupload API.
88  // By default, files added via file input selection, paste, drag & drop or
89  // "add" method are uploaded immediately, but it is possible to override
90  // the "add" callback option to queue file uploads.
91  $.widget('blueimp.fileupload', {
92    options: {
93      // The drop target element(s), by the default the complete document.
94      // Set to null to disable drag & drop support:
95      dropZone: $(document),
96      // The paste target element(s), by the default undefined.
97      // Set to a DOM node or jQuery object to enable file pasting:
98      pasteZone: undefined,
99      // The file input field(s), that are listened to for change events.
100      // If undefined, it is set to the file input fields inside
101      // of the widget element on plugin initialization.
102      // Set to null to disable the change listener.
103      fileInput: undefined,
104      // By default, the file input field is replaced with a clone after
105      // each input field change event. This is required for iframe transport
106      // queues and allows change events to be fired for the same file
107      // selection, but can be disabled by setting the following option to fal
107se:
108      replaceFileInput: true,
109      // The parameter name for the file form data (the request argument name).
110      // If undefined or empty, the name property of the file input field is
111      // used, or "files[]" if the file input name property is also empty,
112      // can be a string or an array of strings:
113      paramName: undefined,
114      // By default, each file of a selection is uploaded using an individual
115      // request for XHR type uploads. Set to false to upload file
116      // selections in one request each:
117      singleFileUploads: true,
118      // To limit the number of files uploaded with one XHR request,
119      // set the following option to an integer greater than 0:
120      limitMultiFileUploads: undefined,
121      // The following option limits the number of files uploaded with one
122      // XHR request to keep the request size under or equal to the defined
123      // limit in bytes:
124      limitMultiFileUploadSize: undefined,
125      // Multipart file uploads add a number of bytes to each uploaded file,
126      // therefore the following option adds an overhead for each file used
127      // in the limitMultiFileUploadSize configuration:
128      limitMultiFileUploadSizeOverhead: 512,
129      // Set the following option to true to issue all file upload requests
130      // in a sequential order:
131      sequentialUploads: false,
132      // To limit the number of concurrent uploads,
133      // set the following option to an integer greater than 0:
134      limitConcurrentUploads: undefined,
135      // Set the following option to true to force iframe transport uploads:
136      forceIframeTransport: false,
137      // Set the following option to the location of a redirect url on the
138      // origin server, for cross-domain iframe transport uploads:
139      redirect: undefined,
140      // The parameter name for the redirect url, sent as part of the form
141      // data and set to 'redirect' if this option is empty:
142      redirectParamName: undefined,
143      // Set the following option to the location of a postMessage window,
144      // to enable postMessage transport uploads:
145      postMessage: undefined,
146      // By default, XHR file uploads are sent as multipart/form-data.
147      // The iframe transport is always using multipart/form-data.
148      // Set to false to enable non-multipart XHR uploads:
149      multipart: true,
150      // To upload large files in smaller chunks, set the following option
151      // to a preferred maximum chunk size. If set to 0, null or undefined,
152      // or the browser does not support the required Blob API, files will
153      // be uploaded as a whole.
154      maxChunkSize: undefined,
155      // When a non-multipart upload or a chunked multipart upload has been
156      // aborted, this option can be used to resume the upload by setting
157      // it to the size of the already uploaded bytes. This option is most
158      // useful when modifying the options object inside of the "add" or
159      // "send" callbacks, as the options are cloned for each file upload.
160      uploadedBytes: undefined,
161      // By default, failed (abort or error) file uploads are removed from the
162      // global progress calculation. Set the following option to false to
163      // prevent recalculating the global progress data:
164      recalculateProgress: true,
165      // Interval in milliseconds to calculate and trigger progress events:
166      progressInterval: 100,
167      // Interval in milliseconds to calculate progress bitrate:
168      bitrateInterval: 500,
169      // By default, uploads are started automatically when adding files:
170      autoUpload: true,
171      // By default, duplicate file names are expected to be handled on
172      // the server-side. If this is not possible (e.g. when uploading
173      // files directly to Amazon S3), the following option can be set to
174      // an empty object or an object mapping existing filenames, e.g.:
175      // { "image.jpg": true, "image (1).jpg": true }
176      // If it is set, all files will be uploaded with unique filenames,
177      // adding increasing number suffixes if necessary, e.g.:
178      // "image (2).jpg"
179      uniqueFilenames: undefined,
180
181      // Error and info messages:
182      messages: {
183        uploadedBytes: 'Uploaded bytes exceed file size'
184      },
185
186      // Translation function, gets the message key to be translated
187      // and an object with context specific data as arguments:
188      i18n: function(message, context) {
189        // eslint-disable-next-line no-param-reassign
190        message = this.messages[message] || message.toString();
191        if (context) {
192          $.each(context, function(key, value) {
193            // eslint-disable-next-line no-param-reassign
194            message = message.replace('{' + key + '}', value);
195          });
196        }
197        return message;
198      },
199
200      // Additional form data to be sent along with the file uploads can be set
201      // using this option, which accepts an array of objects with name and
202      // value properties, a function returning such an array, a FormData
203      // object (for XHR file uploads), or a simple object.
204      // The form of the first fileInput is given as parameter to the function:
205      formData: function(form) {
206        return form.serializeArray();
207      },
208
209      // The add callback is invoked as soon as files are added to the fileupload
210      // widget (via file input selection, drag & drop, paste or add API call).
211      // If the singleFileUploads option is enabled, this callback will be
212      // called once for each file in the selection for XHR file uploads, else
213      // once for each file selection.
214      //
215      // The upload starts when the submit method is invoked on the data parameter.
216      // The data object contains a files property holding the added files
217      // and allows you to override plugin options as well as define ajax settings.
218      //
219      // Listeners for this callback can also be bound the following way:
220      // .bind('fileuploadadd', func);
221      //
222      // data.submit() returns a Promise object and allows to attach additional
223      // handlers using jQuery's Deferred callbacks:
224      // data.submit().done(func).fail(func).always(func);
225      add: function(e, data) {
226        if (e.isDefaultPrevented()) {
227          return false;
228        }
229        if (
230          data.autoUpload ||
231          (data.autoUpload !== false &&
232            $(this).fileupload('option', 'autoUpload'))
233        ) {
234          data.process().done(function() {
235            data.submit();
236          });
237        }
238      },
239
240      // Other callbacks:
241
242      // Callback for the submit event of each file upload:
243      // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
244
245      // Callback for the start of each file upload request:
246      // send: function (e, data) {}, // .bind('fileuploadsend', func);
247
248      // Callback for successful uploads:
249      // done: function (e, data) {}, // .bind('fileuploaddone', func);
250
251      // Callback for failed (abort or error) uploads:
252      // fail: function (e, data) {}, // .bind('fileuploadfail', func);
253
254      // Callback for completed (success, abort or error) requests:
255      // always: function (e, data) {}, // .bind('fileuploadalways', func);
256
257      // Callback for upload progress events:
258      // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
259
260      // Callback for global upload progress events:
261      // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
262
263      // Callback for uploads start, equivalent to the global ajaxStart event:
264      // start: function (e) {}, // .bind('fileuploadstart', func);
265
266      // Callback for uploads stop, equivalent to the global ajaxStop event:
267      // stop: function (e) {}, // .bind('fileuploadstop', func);
268
269      // Callback for change events of the fileInput(s):
270      // change: function (e, data) {}, // .bind('fileuploadchange', func);
271
272      // Callback for paste events to the pasteZone(s):
273      // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
274
275      // Callback for drop events of the dropZone(s):
276      // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
277
278      // Callback for dragover events of the dropZone(s):
279      // dragover: function (e) {}, // .bind('fileuploaddragover', func);
280
281      // Callback before the start of each chunk upload request (before form data initialization):
282      // chunkbeforesend: function (e, data) {}, // .bind('fileuploadchunkbeforesend', func);
283
284      // Callback for the start of each chunk upload request:
285      // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);
286
287      // Callback for successful chunk uploads:
288      // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);
289
290      // Callback for failed (abort or error) chunk uploads:
291      // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);
292
293      // Callback for completed (success, abort or error) chunk upload requests:
294      // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);
295
296      // The plugin options are used as settings object for the ajax calls.
297      // The following are jQuery ajax settings required for the file uploads:
298      processData: false,
299      contentType: false,
300      cache: false,
301      timeout: 0
302    },
303
304    // A list of options that require reinitializing event listeners and/or
305    // special initialization code:
306    _specialOptions: [
307      'fileInput',
308      'dropZone',
309      'pasteZone',
310      'multipart',
311      'forceIframeTransport'
312    ],
313
314    _blobSlice:
315      $.support.blobSlice &&
316      function() {
317        var slice = this.slice || this.webkitSlice || this.mozSlice;
318        return slice.apply(this, arguments);
319      },
320
321    _BitrateTimer: function() {
322      this.timestamp = Date.now ? Date.now() : new Date().getTime();
323      this.loaded = 0;
324      this.bitrate = 0;
325      this.getBitrate = function(now, loaded, interval) {
326        var timeDiff = now - this.timestamp;
327        if (!this.bitrate || !interval || timeDiff > interval) {
328          this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
329          this.loaded = loaded;
330          this.timestamp = now;
331        }
332        return this.bitrate;
333      };
334    },
335
336    _isXHRUpload: function(options) {
337      return (
338        !options.forceIframeTransport &&
339        ((!options.multipart && $.support.xhrFileUpload) ||
340          $.support.xhrFormDataFileUpload)
341      );
342    },
343
344    _getFormData: function(options) {
345      var formData;
346      if ($.type(options.formData) === 'function') {
347        return options.formData(options.form);
348      }
349      if ($.isArray(options.formData)) {
350        return options.formData;
351      }
352      if ($.type(options.formData) === 'object') {
353        formData = [];
354        $.each(options.formData, function(name, value) {
355          formData.push({ name: name, value: value });
356        });
357        return formData;
358      }
359      return [];
360    },
361
362    _getTotal: function(files) {
363      var total = 0;
364      $.each(files, function(index, file) {
365        total += file.size || 1;
366      });
367      return total;
368    },
369
370    _initProgressObject: function(obj) {
371      var progress = {
372        loaded: 0,
373        total: 0,
374        bitrate: 0
375      };
376      if (obj._progress) {
377        $.extend(obj._progress, progress);
378      } else {
379        obj._progress = progress;
380      }
381    },
382
383    _initResponseObject: function(obj) {
384      var prop;
385      if (obj._response) {
386        for (prop in obj._response) {
387          if (Object.prototype.hasOwnProperty.call(obj._response, prop)) {
388            delete obj._response[prop];
389          }
390        }
391      } else {
392        obj._response = {};
393      }
394    },
395
396    _onProgress: function(e, data) {
397      if (e.lengthComputable) {
398        var now = Date.now ? Date.now() : new Date().getTime(),
399          loaded;
400        if (
401          data._time &&
402          data.progressInterval &&
403          now - data._time < data.progressInterval &&
404          e.loaded !== e.total
405        ) {
406          return;
407        }
408        data._time = now;
409        loaded =
410          Math.floor(
411            (e.loaded / e.total) * (data.chunkSize || data._progress.total)
412          ) + (data.uploadedBytes || 0);
413        // Add the difference from the previously loaded state
414        // to the global loaded counter:
415        this._progress.loaded += loaded - data._progress.loaded;
416        this._progress.bitrate = this._bitrateTimer.getBitrate(
417          now,
418          this._progress.loaded,
419          data.bitrateInterval
420        );
421        data._progress.loaded = data.loaded = loaded;
422        data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
423          now,
424          loaded,
425          data.bitrateInterval
426        );
427        // Trigger a custom progress event with a total data property set
428        // to the file size(s) of the current upload and a loaded data
429        // property calculated accordingly:
430        this._trigger(
431          'progress',
432          $.Event('progress', { delegatedEvent: e }),
433          data
434        );
435        // Trigger a global progress event for all current file uploads,
436        // including ajax calls queued for sequential file uploads:
437        this._trigger(
438          'progressall',
439          $.Event('progressall', { delegatedEvent: e }),
440          this._progress
441        );
442      }
443    },
444
445    _initProgressListener: function(options) {
446      var that = this,
447        xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
448      // Accesss to the native XHR object is required to add event listeners
449      // for the upload progress event:
450      if (xhr.upload) {
451        $(xhr.upload).bind('progress', function(e) {
452          var oe = e.originalEvent;
453          // Make sure the progress event properties get copied over:
454          e.lengthComputable = oe.lengthComputable;
455          e.loaded = oe.loaded;
456          e.total = oe.total;
457          that._onProgress(e, options);
458        });
459        options.xhr = function() {
460          return xhr;
461        };
462      }
463    },
464
465    _deinitProgressListener: function(options) {
466      var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
467      if (xhr.upload) {
468        $(xhr.upload).unbind('progress');
469      }
470    },
471
472    _isInstanceOf: function(type, obj) {
473      // Cross-frame instanceof check
474      return Object.prototype.toString.call(obj) === '[object ' + type + ']';
475    },
476
477    _getUniqueFilename: function(name, map) {
478      // eslint-disable-next-line no-param-reassign
479      name = String(name);
480      if (map[name]) {
481        // eslint-disable-next-line no-param-reassign
482        name = name.replace(/(?: \(([\d]+)\))?(\.[^.]+)?$/, function(
483          _,
484          p1,
485          p2
486        ) {
487          var index = p1 ? Number(p1) + 1 : 1;
488          var ext = p2 || '';
489          return ' (' + index + ')' + ext;
490        });
491        return this._getUniqueFilename(name, map);
492      }
493      map[name] = true;
494      return name;
495    },
496
497    _initXHRData: function(options) {
498      var that = this,
499        formData,
500        file = options.files[0],
501        // Ignore non-multipart setting if not supported:
502        multipart = options.multipart || !$.support.xhrFileUpload,
503        paramName =
504          $.type(options.paramName) === 'array'
505            ? options.paramName[0]
506            : options.paramName;
507      options.headers = $.extend({}, options.headers);
508      if (options.contentRange) {
509        options.headers['Content-Range'] = options.contentRange;
510      }
511      if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
512        options.headers['Content-Disposition'] =
513          'attachment; filename="' +
514          encodeURI(file.uploadName || file.name) +
515          '"';
516      }
517      if (!multipart) {
518        options.contentType = file.type || 'application/octet-stream';
519        options.data = options.blob || file;
520      } else if ($.support.xhrFormDataFileUpload) {
521        if (options.postMessage) {
522          // window.postMessage does not allow sending FormData
523          // objects, so we just add the File/Blob objects to
524          // the formData array and let the postMessage window
525          // create the FormData object out of this array:
526          formData = this._getFormData(options);
527          if (options.blob) {
528            formData.push({
529              name: paramName,
530              value: options.blob
531            });
532          } else {
533            $.each(options.files, function(index, file) {
534              formData.push({
535                name:
536                  ($.type(options.paramName) === 'array' &&
537                    options.paramName[index]) ||
538                  paramName,
539                value: file
540              });
541            });
542          }
543        } else {
544          if (that._isInstanceOf('FormData', options.formData)) {
545            formData = options.formData;
546          } else {
547            formData = new FormData();
548            $.each(this._getFormData(options), function(index, field) {
549              formData.append(field.name, field.value);
550            });
551          }
552          if (options.blob) {
553            formData.append(
554              paramName,
555              options.blob,
556              file.uploadName || file.name
557            );
558          } else {
559            $.each(options.files, function(index, file) {
560              // This check allows the tests to run with
561              // dummy objects:
562              if (
563                that._isInstanceOf('File', file) ||
564                that._isInstanceOf('Blob', file)
565              ) {
566                var fileName = file.uploadName || file.name;
567                if (options.uniqueFilenames) {
568                  fileName = that._getUniqueFilename(
569                    fileName,
570                    options.uniqueFilenames
571                  );
572                }
573                formData.append(
574                  ($.type(options.paramName) === 'array' &&
575                    options.paramName[index]) ||
576                    paramName,
577                  file,
578                  fileName
579                );
580              }
581            });
582          }
583        }
584        options.data = formData;
585      }
586      // Blob reference is not needed anymore, free memory:
587      options.blob = null;
588    },
589
590    _initIframeSettings: function(options) {
591      var targetHost = $('<a></a>')
592        .prop('href', options.url)
593        .prop('host');
594      // Setting the dataType to iframe enables the iframe transport:
595      options.dataType = 'iframe ' + (options.dataType || '');
596      // The iframe transport accepts a serialized array as form data:
597      options.formData = this._getFormData(options);
598      // Add redirect url to form data on cross-domain uploads:
599      if (options.redirect && targetHost && targetHost !== location.host) {
600        options.formData.push({
601          name: options.redirectParamName || 'redirect',
602          value: options.redirect
603        });
604      }
605    },
606
607    _initDataSettings: function(options) {
608      if (this._isXHRUpload(options)) {
609        if (!this._chunkedUpload(options, true)) {
610          if (!options.data) {
611            this._initXHRData(options);
612          }
613          this._initProgressListener(options);
614        }
615        if (options.postMessage) {
616          // Setting the dataType to postmessage enables the
617          // postMessage transport:
618          options.dataType = 'postmessage ' + (options.dataType || '');
619        }
620      } else {
621        this._initIframeSettings(options);
622      }
623    },
624
625    _getParamName: function(options) {
626      var fileInput = $(options.fileInput),
627        paramName = options.paramName;
628      if (!paramName) {
629        paramName = [];
630        fileInput.each(function() {
631          var input = $(this),
632            name = input.prop('name') || 'files[]',
633            i = (input.prop('files') || [1]).length;
634          while (i) {
635            paramName.push(name);
636            i -= 1;
637          }
638        });
639        if (!paramName.length) {
640          paramName = [fileInput.prop('name') || 'files[]'];
641        }
642      } else if (!$.isArray(paramName)) {
643        paramName = [paramName];
644      }
645      return paramName;
646    },
647
648    _initFormSettings: function(options) {
649      // Retrieve missing options from the input field and the
650      // associated form, if available:
651      if (!options.form || !options.form.length) {
652        options.form = $(options.fileInput.prop('form'));
653        // If the given file input doesn't have an associated form,
654        // use the default widget file input's form:
655        if (!options.form.length) {
656          options.form = $(this.options.fileInput.prop('form'));
657        }
658      }
659      options.paramName = this._getParamName(options);
660      if (!options.url) {
661        options.url = options.form.prop('action') || location.href;
662      }
663      // The HTTP request method must be "POST" or "PUT":
664      options.type = (
665        options.type ||
666        ($.type(options.form.prop('method')) === 'string' &&
667          options.form.prop('method')) ||
668        ''
669      ).toUpperCase();
670      if (
671        options.type !== 'POST' &&
672        options.type !== 'PUT' &&
673        options.type !== 'PATCH'
674      ) {
675        options.type = 'POST';
676      }
677      if (!options.formAcceptCharset) {
678        options.formAcceptCharset = options.form.attr('accept-charset');
679      }
680    },
681
682    _getAJAXSettings: function(data) {
683      var options = $.extend({}, this.options, data);
684      this._initFormSettings(options);
685      this._initDataSettings(options);
686      return options;
687    },
688
689    // jQuery 1.6 doesn't provide .state(),
690    // while jQuery 1.8+ removed .isRejected() and .isResolved():
691    _getDeferredState: function(deferred) {
692      if (deferred.state) {
693        return deferred.state();
694      }
695      if (deferred.isResolved()) {
696        return 'resolved';
697      }
698      if (deferred.isRejected()) {
699        return 'rejected';
700      }
701      return 'pending';
702    },
703
704    // Maps jqXHR callbacks to the equivalent
705    // methods of the given Promise object:
706    _enhancePromise: function(promise) {
707      promise.success = promise.done;
708      promise.error = promise.fail;
709      promise.complete = promise.always;
710      return promise;
711    },
712
713    // Creates and returns a Promise object enhanced with
714    // the jqXHR methods abort, success, error and complete:
715    _getXHRPromise: function(resolveOrReject, context, args) {
716      var dfd = $.Deferred(),
717        promise = dfd.promise();
718      // eslint-disable-next-line no-param-reassign
719      context = context || this.options.context || promise;
720      if (resolveOrReject === true) {
721        dfd.resolveWith(context, args);
722      } else if (resolveOrReject === false) {
723        dfd.rejectWith(context, args);
724      }
725      promise.abort = dfd.promise;
726      return this._enhancePromise(promise);
727    },
728
729    // Adds convenience methods to the data callback argument:
730    _addConvenienceMethods: function(e, data) {
731      var that = this,
732        getPromise = function(args) {
733          return $.Deferred()
734            .resolveWith(that, args)
735            .promise();
736        };
737      data.process = function(resolveFunc, rejectFunc) {
738        if (resolveFunc || rejectFunc) {
739          data._processQueue = this._processQueue = (
740            this._processQueue || getPromise([this])
741          )
742            .then(function() {
743              if (data.errorThrown) {
744                return $.Deferred()
745                  .rejectWith(that, [data])
746                  .promise();
747              }
748              return getPromise(arguments);
749            })
750            .then(resolveFunc, rejectFunc);
751        }
752        return this._processQueue || getPromise([this]);
753      };
754      data.submit = function() {
755        if (this.state() !== 'pending') {
756          data.jqXHR = this.jqXHR =
757            that._trigger(
758              'submit',
759              $.Event('submit', { delegatedEvent: e }),
760              this
761            ) !== false && that._onSend(e, this);
762        }
763        return this.jqXHR || that._getXHRPromise();
764      };
765      data.abort = function() {
766        if (this.jqXHR) {
767          return this.jqXHR.abort();
768        }
769        this.errorThrown = 'abort';
770        that._trigger('fail', null, this);
771        return that._getXHRPromise(false);
772      };
773      data.state = function() {
774        if (this.jqXHR) {
775          return that._getDeferredState(this.jqXHR);
776        }
777        if (this._processQueue) {
778          return that._getDeferredState(this._processQueue);
779        }
780      };
781      data.processing = function() {
782        return (
783          !this.jqXHR &&
784          this._processQueue &&
785          that._getDeferredState(this._processQueue) === 'pending'
786        );
787      };
788      data.progress = function() {
789        return this._progress;
790      };
791      data.response = function() {
792        return this._response;
793      };
794    },
795
796    // Parses the Range header from the server response
797    // and returns the uploaded bytes:
798    _getUploadedBytes: function(jqXHR) {
799      var range = jqXHR.getResponseHeader('Range'),
800        parts = range && range.split('-'),
801        upperBytesPos = parts && parts.length > 1 && parseInt(parts[1], 10);
802      return upperBytesPos && upperBytesPos + 1;
803    },
804
805    // Uploads a file in multiple, sequential requests
806    // by splitting the file up in multiple blob chunks.
807    // If the second parameter is true, only tests if the file
808    // should be uploaded in chunks, but does not invoke any
809    // upload requests:
810    _chunkedUpload: function(options, testOnly) {
811      options.uploadedBytes = options.uploadedBytes || 0;
812      var that = this,
813        file = options.files[0],
814        fs = file.size,
815        ub = options.uploadedBytes,
816        mcs = options.maxChunkSize || fs,
817        slice = this._blobSlice,
818        dfd = $.Deferred(),
819        promise = dfd.promise(),
820        jqXHR,
821        upload;
822      if (
823        !(
824          this._isXHRUpload(options) &&
825          slice &&
826          (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)
827        ) ||
828        options.data
829      ) {
830        return false;
831      }
832      if (testOnly) {
833        return true;
834      }
835      if (ub >= fs) {
836        file.error = options.i18n('uploadedBytes');
837        return this._getXHRPromise(false, options.context, [
838          null,
839          'error',
840          file.error
841        ]);
842      }
843      // The chunk upload method:
844      upload = function() {
845        // Clone the options object for each chunk upload:
846        var o = $.extend({}, options),
847          currentLoaded = o._progress.loaded;
848        o.blob = slice.call(
849          file,
850          ub,
851          ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),
852          file.type
853        );
854        // Store the current chunk size, as the blob itself
855        // will be dereferenced after data processing:
856        o.chunkSize = o.blob.size;
857        // Expose the chunk bytes position range:
858        o.contentRange =
859          'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs;
860        // Trigger chunkbeforesend to allow form data to be updated for this chunk
861        that._trigger('chunkbeforesend', null, o);
862        // Process the upload data (the blob and potential form data):
863        that._initXHRData(o);
864        // Add progress listeners for this chunk upload:
865        that._initProgressListener(o);
866        jqXHR = (
867          (that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
868          that._getXHRPromise(false, o.context)
869        )
870          .done(function(result, textStatus, jqXHR) {
871            ub = that._getUploadedBytes(jqXHR) || ub + o.chunkSize;
872            // Create a progress event if no final progress event
873            // with loaded equaling total has been triggered
874            // for this chunk:
875            if (currentLoaded + o.chunkSize - o._progress.loaded) {
876              that._onProgress(
877                $.Event('progress', {
878                  lengthComputable: true,
879                  loaded: ub - o.uploadedBytes,
880                  total: ub - o.uploadedBytes
881                }),
882                o
883              );
884            }
885            options.uploadedBytes = o.uploadedBytes = ub;
886            o.result = result;
887            o.textStatus = textStatus;
888            o.jqXHR = jqXHR;
889            that._trigger('chunkdone', null, o);
890            that._trigger('chunkalways', null, o);
891            if (ub < fs) {
892              // File upload not yet complete,
893              // continue with the next chunk:
894              upload();
895            } else {
896              dfd.resolveWith(o.context, [result, textStatus, jqXHR]);
897            }
898          })
899          .fail(function(jqXHR, textStatus, errorThrown) {
900            o.jqXHR = jqXHR;
901            o.textStatus = textStatus;
902            o.errorThrown = errorThrown;
903            that._trigger('chunkfail', null, o);
904            that._trigger('chunkalways', null, o);
905            dfd.rejectWith(o.context, [jqXHR, textStatus, errorThrown]);
906          })
907          .always(function() {
908            that._deinitProgressListener(o);
909          });
910      };
911      this._enhancePromise(promise);
912      promise.abort = function() {
913        return jqXHR.abort();
914      };
915      upload();
916      return promise;
917    },
918
919    _beforeSend: function(e, data) {
920      if (this._active === 0) {
921        // the start callback is triggered when an upload starts
922        // and no other uploads are currently running,
923        // equivalent to the global ajaxStart event:
924        this._trigger('start');
925        // Set timer for global bitrate progress calculation:
926        this._bitrateTimer = new this._BitrateTimer();
927        // Reset the global progress values:
928        this._progress.loaded = this._progress.total = 0;
929        this._progress.bitrate = 0;
930      }
931      // Make sure the container objects for the .response() and
932      // .progress() methods on the data object are available
933      // and reset to their initial state:
934      this._initResponseObject(data);
935      this._initProgressObject(data);
936      data._progress.loaded = data.loaded = data.uploadedBytes || 0;
937      data._progress.total = data.total = this._getTotal(data.files) || 1;
938      data._progress.bitrate = data.bitrate = 0;
939      this._active += 1;
940      // Initialize the global progress values:
941      this._progress.loaded += data.loaded;
942      this._progress.total += data.total;
943    },
944
945    _onDone: function(result, textStatus, jqXHR, options) {
946      var total = options._progress.total,
947        response = options._response;
948      if (options._progress.loaded < total) {
949        // Create a progress event if no final progress event
950        // with loaded equaling total has been triggered:
951        this._onProgress(
952          $.Event('progress', {
953            lengthComputable: true,
954            loaded: total,
955            total: total
956          }),
957          options
958        );
959      }
960      response.result = options.result = result;
961      response.textStatus = options.textStatus = textStatus;
962      response.jqXHR = options.jqXHR = jqXHR;
963      this._trigger('done', null, options);
964    },
965
966    _onFail: function(jqXHR, textStatus, errorThrown, options) {
967      var response = options._response;
968      if (options.recalculateProgress) {
969        // Remove the failed (error or abort) file upload from
970        // the global progress calculation:
971        this._progress.loaded -= options._progress.loaded;
972        this._progress.total -= options._progress.total;
973      }
974      response.jqXHR = options.jqXHR = jqXHR;
975      response.textStatus = options.textStatus = textStatus;
976      response.errorThrown = options.errorThrown = errorThrown;
977      this._trigger('fail', null, options);
978    },
979
980    _onAlways: function(jqXHRorResult, textStatus, jqXHRorError, options) {
981      // jqXHRorResult, textStatus and jqXHRorError are added to the
982      // options object via done and fail callbacks
983      this._trigger('always', null, options);
984    },
985
986    _onSend: function(e, data) {
987      if (!data.submit) {
988        this._addConvenienceMethods(e, data);
989      }
990      var that = this,
991        jqXHR,
992        aborted,
993        slot,
994        pipe,
995        options = that._getAJAXSettings(data),
996        send = function() {
997          that._sending += 1;
998          // Set timer for bitrate progress calculation:
999          options._bitrateTimer = new that._BitrateTimer();
1000          jqXHR =
1001            jqXHR ||
1002            (
1003              ((aborted ||
1004                that._trigger(
1005                  'send',
1006                  $.Event('send', { delegatedEvent: e }),
1007                  options
1008                ) === false) &&
1009                that._getXHRPromise(false, options.context, aborted)) ||
1010              that._chunkedUpload(options) ||
1011              $.ajax(options)
1012            )
1013              .done(function(result, textStatus, jqXHR) {
1014                that._onDone(result, textStatus, jqXHR, options);
1015              })
1016              .fail(function(jqXHR, textStatus, errorThrown) {
1017                that._onFail(jqXHR, textStatus, errorThrown, options);
1018              })
1019              .always(function(jqXHRorResult, textStatus, jqXHRorError) {
1020                that._deinitProgressListener(options);
1021                that._onAlways(
1022                  jqXHRorResult,
1023                  textStatus,
1024                  jqXHRorError,
1025                  options
1026                );
1027                that._sending -= 1;
1028                that._active -= 1;
1029                if (
1030                  options.limitConcurrentUploads &&
1031                  options.limitConcurrentUploads > that._sending
1032                ) {
1033                  // Start the next queued upload,
1034                  // that has not been aborted:
1035                  var nextSlot = that._slots.shift();
1036                  while (nextSlot) {
1037                    if (that._getDeferredState(nextSlot) === 'pending') {
1038                      nextSlot.resolve();
1039                      break;
1040                    }
1041                    nextSlot = that._slots.shift();
1042                  }
1043                }
1044                if (that._active === 0) {
1045                  // The stop callback is triggered when all uploads have
1046                  // been completed, equivalent to the global ajaxStop event:
1047                  that._trigger('stop');
1048                }
1049              });
1050          return jqXHR;
1051        };
1052      this._beforeSend(e, options);
1053      if (
1054        this.options.sequentialUploads ||
1055        (this.options.limitConcurrentUploads &&
1056          this.options.limitConcurrentUploads <= this._sending)
1057      ) {
1058        if (this.options.limitConcurrentUploads > 1) {
1059          slot = $.Deferred();
1060          this._slots.push(slot);
1061          pipe = slot.then(send);
1062        } else {
1063          this._sequence = this._sequence.then(send, send);
1064          pipe = this._sequence;
1065        }
1066        // Return the piped Promise object, enhanced with an abort method,
1067        // which is delegated to the jqXHR object of the current upload,
1068        // and jqXHR callbacks mapped to the equivalent Promise methods:
1069        pipe.abort = function() {
1070          aborted = [undefined, 'abort', 'abort'];
1071          if (!jqXHR) {
1072            if (slot) {
1073              slot.rejectWith(options.context, aborted);
1074            }
1075            return send();
1076          }
1077          return jqXHR.abort();
1078        };
1079        return this._enhancePromise(pipe);
1080      }
1081      return send();
1082    },
1083
1084    _onAdd: function(e, data) {
1085      var that = this,
1086        result = true,
1087        options = $.extend({}, this.options, data),
1088        files = data.files,
1089        filesLength = files.length,
1090        limit = options.limitMultiFileUploads,
1091        limitSize = options.limitMultiFileUploadSize,
1092        overhead = options.limitMultiFileUploadSizeOverhead,
1093        batchSize = 0,
1094        paramName = this._getParamName(options),
1095        paramNameSet,
1096        paramNameSlice,
1097        fileSet,
1098        i,
1099        j = 0;
1100      if (!filesLength) {
1101        return false;
1102      }
1103      if (limitSize && files[0].size === undefined) {
1104        limitSize = undefined;
1105      }
1106      if (
1107        !(options.singleFileUploads || limit || limitSize) ||
1108        !this._isXHRUpload(options)
1109      ) {
1110        fileSet = [files];
1111        paramNameSet = [paramName];
1112      } else if (!(options.singleFileUploads || limitSize) && limit) {
1113        fileSet = [];
1114        paramNameSet = [];
1115        for (i = 0; i < filesLength; i += limit) {
1116          fileSet.push(files.slice(i, i + limit));
1117          paramNameSlice = paramName.slice(i, i + limit);
1118          if (!paramNameSlice.length) {
1119            paramNameSlice = paramName;
1120          }
1121          paramNameSet.push(paramNameSlice);
1122        }
1123      } else if (!options.singleFileUploads && limitSize) {
1124        fileSet = [];
1125        paramNameSet = [];
1126        for (i = 0; i < filesLength; i = i + 1) {
1127          batchSize += files[i].size + overhead;
1128          if (
1129            i + 1 === filesLength ||
1130            batchSize + files[i + 1].size + overhead > limitSize ||
1131            (limit && i + 1 - j >= limit)
1132          ) {
1133            fileSet.push(files.slice(j, i + 1));
1134            paramNameSlice = paramName.slice(j, i + 1);
1135            if (!paramNameSlice.length) {
1136              paramNameSlice = paramName;
1137            }
1138            paramNameSet.push(paramNameSlice);
1139            j = i + 1;
1140            batchSize = 0;
1141          }
1142        }
1143      } else {
1144        paramNameSet = paramName;
1145      }
1146      data.originalFiles = files;
1147      $.each(fileSet || files, function(index, element) {
1148        var newData = $.extend({}, data);
1149        newData.files = fileSet ? element : [element];
1150        newData.paramName = paramNameSet[index];
1151        that._initResponseObject(newData);
1152        that._initProgressObject(newData);
1153        that._addConvenienceMethods(e, newData);
1154        result = that._trigger(
1155          'add',
1156          $.Event('add', { delegatedEvent: e }),
1157          newData
1158        );
1159        return result;
1160      });
1161      return result;
1162    },
1163
1164    _replaceFileInput: function(data) {
1165      var input = data.fileInput,
1166        inputClone = input.clone(true),
1167        restoreFocus = input.is(document.activeElement);
1168      // Add a reference for the new cloned file input to the data argument:
1169      data.fileInputClone = inputClone;
1170      $('<form></form>')
1171        .append(inputClone)[0]
1172        .reset();
1173      // Detaching allows to insert the fileInput on another form
1174      // without loosing the file input value:
1175      input.after(inputClone).detach();
1176      // If the fileInput had focus before it was detached,
1177      // restore focus to the inputClone.
1178      if (restoreFocus) {
1179        inputClone.focus();
1180      }
1181      // Avoid memory leaks with the detached file input:
1182      $.cleanData(input.unbind('remove'));
1183      // Replace the original file input element in the fileInput
1184      // elements set with the clone, which has been copied including
1185      // event handlers:
1186      this.options.fileInput = this.options.fileInput.map(function(i, el) {
1187        if (el === input[0]) {
1188          return inputClone[0];
1189        }
1190        return el;
1191      });
1192      // If the widget has been initialized on the file input itself,
1193      // override this.element with the file input clone:
1194      if (input[0] === this.element[0]) {
1195        this.element = inputClone;
1196      }
1197    },
1198
1199    _handleFileTreeEntry: function(entry, path) {
1200      var that = this,
1201        dfd = $.Deferred(),
1202        entries = [],
1203        dirReader,
1204        errorHandler = function(e) {
1205          if (e && !e.entry) {
1206            e.entry = entry;
1207          }
1208          // Since $.when returns immediately if one
1209          // Deferred is rejected, we use resolve instead.
1210          // This allows valid files and invalid items
1211          // to be returned together in one set:
1212          dfd.resolve([e]);
1213        },
1214        successHandler = function(entries) {
1215          that
1216            ._handleFileTreeEntries(entries, path + entry.name + '/')
1217            .done(function(files) {
1218              dfd.resolve(files);
1219            })
1220            .fail(errorHandler);
1221        },
1222        readEntries = function() {
1223          dirReader.readEntries(function(results) {
1224            if (!results.length) {
1225              successHandler(entries);
1226            } else {
1227              entries = entries.concat(results);
1228              readEntries();
1229            }
1230          }, errorHandler);
1231        };
1232      // eslint-disable-next-line no-param-reassign
1233      path = path || '';
1234      if (entry.isFile) {
1235        if (entry._file) {
1236          // Workaround for Chrome bug #149735
1237          entry._file.relativePath = path;
1238          dfd.resolve(entry._file);
1239        } else {
1240          entry.file(function(file) {
1241            file.relativePath = path;
1242            dfd.resolve(file);
1243          }, errorHandler);
1244        }
1245      } else if (entry.isDirectory) {
1246        dirReader = entry.createReader();
1247        readEntries();
1248      } else {
1249        // Return an empty list for file system items
1250        // other than files or directories:
1251        dfd.resolve([]);
1252      }
1253      return dfd.promise();
1254    },
1255
1256    _handleFileTreeEntries: function(entries, path) {
1257      var that = this;
1258      return $.when
1259        .apply(
1260          $,
1261          $.map(entries, function(entry) {
1262            return that._handleFileTreeEntry(entry, path);
1263          })
1264        )
1265        .then(function() {
1266          return Array.prototype.concat.apply([], arguments);
1267        });
1268    },
1269
1270    _getDroppedFiles: function(dataTransfer) {
1271      // eslint-disable-next-line no-param-reassign
1272      dataTransfer = dataTransfer || {};
1273      var items = dataTransfer.items;
1274      if (
1275        items &&
1276        items.length &&
1277        (items[0].webkitGetAsEntry || items[0].getAsEntry)
1278      ) {
1279        return this._handleFileTreeEntries(
1280          $.map(items, function(item) {
1281            var entry;
1282            if (item.webkitGetAsEntry) {
1283              entry = item.webkitGetAsEntry();
1284              if (entry) {
1285                // Workaround for Chrome bug #149735:
1286                entry._file = item.getAsFile();
1287              }
1288              return entry;
1289            }
1290            return item.getAsEntry();
1291          })
1292        );
1293      }
1294      return $.Deferred()
1295        .resolve($.makeArray(dataTransfer.files))
1296        .promise();
1297    },
1298
1299    _getSingleFileInputFiles: function(fileInput) {
1300      // eslint-disable-next-line no-param-reassign
1301      fileInput = $(fileInput);
1302      var entries =
1303          fileInput.prop('webkitEntries') || fileInput.prop('entries'),
1304        files,
1305        value;
1306      if (entries && entries.length) {
1307        return this._handleFileTreeEntries(entries);
1308      }
1309      files = $.makeArray(fileInput.prop('files'));
1310      if (!files.length) {
1311        value = fileInput.prop('value');
1312        if (!value) {
1313          return $.Deferred()
1314            .resolve([])
1315            .promise();
1316        }
1317        // If the files property is not available, the browser does not
1318        // support the File API and we add a pseudo File object with
1319        // the input value as name with path information removed:
1320        files = [{ name: value.replace(/^.*\\/, '') }];
1321      } else if (files[0].name === undefined && files[0].fileName) {
1322        // File normalization for Safari 4 and Firefox 3:
1323        $.each(files, function(index, file) {
1324          file.name = file.fileName;
1325          file.size = file.fileSize;
1326        });
1327      }
1328      return $.Deferred()
1329        .resolve(files)
1330        .promise();
1331    },
1332
1333    _getFileInputFiles: function(fileInput) {
1334      if (!(fileInput instanceof $) || fileInput.length === 1) {
1335        return this._getSingleFileInputFiles(fileInput);
1336      }
1337      return $.when
1338        .apply($, $.map(fileInput, this._getSingleFileInputFiles))
1339        .then(function() {
1340          return Array.prototype.concat.apply([], arguments);
1341        });
1342    },
1343
1344    _onChange: function(e) {
1345      var that = this,
1346        data = {
1347          fileInput: $(e.target),
1348          form: $(e.target.form)
1349        };
1350      this._getFileInputFiles(data.fileInput).always(function(files) {
1351        data.files = files;
1352        if (that.options.replaceFileInput) {
1353          that._replaceFileInput(data);
1354        }
1355        if (
1356          that._trigger(
1357            'change',
1358            $.Event('change', { delegatedEvent: e }),
1359            data
1360          ) !== false
1361        ) {
1362          that._onAdd(e, data);
1363        }
1364      });
1365    },
1366
1367    _onPaste: function(e) {
1368      var items =
1369          e.originalEvent &&
1370          e.originalEvent.clipboardData &&
1371          e.originalEvent.clipboardData.items,
1372        data = { files: [] };
1373      if (items && items.length) {
1374        $.each(items, function(index, item) {
1375          var file = item.getAsFile && item.getAsFile();
1376          if (file) {
1377            data.files.push(file);
1378          }
1379        });
1380        if (
1381          this._trigger(
1382            'paste',
1383            $.Event('paste', { delegatedEvent: e }),
1384            data
1385          ) !== false
1386        ) {
1387          this._onAdd(e, data);
1388        }
1389      }
1390    },
1391
1392    _onDrop: function(e) {
1393      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
1394      var that = this,
1395        dataTransfer = e.dataTransfer,
1396        data = {};
1397      if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
1398        e.preventDefault();
1399        this._getDroppedFiles(dataTransfer).always(function(files) {
1400          data.files = files;
1401          if (
1402            that._trigger(
1403              'drop',
1404              $.Event('drop', { delegatedEvent: e }),
1405              data
1406            ) !== false
1407          ) {
1408            that._onAdd(e, data);
1409          }
1410        });
1411      }
1412    },
1413
1414    _onDragOver: getDragHandler('dragover'),
1415
1416    _onDragEnter: getDragHandler('dragenter'),
1417
1418    _onDragLeave: getDragHandler('dragleave'),
1419
1420    _initEventHandlers: function() {
1421      if (this._isXHRUpload(this.options)) {
1422        this._on(this.options.dropZone, {
1423          dragover: this._onDragOver,
1424          drop: this._onDrop,
1425          // event.preventDefault() on dragenter is required for IE10+:
1426          dragenter: this._onDragEnter,
1427          // dragleave is not required, but added for completeness:
1428          dragleave: this._onDragLeave
1429        });
1430        this._on(this.options.pasteZone, {
1431          paste: this._onPaste
1432        });
1433      }
1434      if ($.support.fileInput) {
1435        this._on(this.options.fileInput, {
1436          change: this._onChange
1437        });
1438      }
1439    },
1440
1441    _destroyEventHandlers: function() {
1442      this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
1443      this._off(this.options.pasteZone, 'paste');
1444      this._off(this.options.fileInput, 'change');
1445    },
1446
1447    _destroy: function() {
1448      this._destroyEventHandlers();
1449    },
1450
1451    _setOption: function(key, value) {
1452      var reinit = $.inArray(key, this._specialOptions) !== -1;
1453      if (reinit) {
1454        this._destroyEventHandlers();
1455      }
1456      this._super(key, value);
1457      if (reinit) {
1458        this._initSpecialOptions();
1459        this._initEventHandlers();
1460      }
1461    },
1462
1463    _initSpecialOptions: function() {
1464      var options = this.options;
1465      if (options.fileInput === undefined) {
1466        options.fileInput = this.element.is('input[type="file"]')
1467          ? this.element
1468          : this.element.find('input[type="file"]');
1469      } else if (!(options.fileInput instanceof $)) {
1470        options.fileInput = $(options.fileInput);
1471      }
1472      if (!(options.dropZone instanceof $)) {
1473        options.dropZone = $(options.dropZone);
1474      }
1475      if (!(options.pasteZone instanceof $)) {
1476        options.pasteZone = $(options.pasteZone);
1477      }
1478    },
1479
1480    _getRegExp: function(str) {
1481      var parts = str.split('/'),
1482        modifiers = parts.pop();
1483      parts.shift();
1484      return new RegExp(parts.join('/'), modifiers);
1485    },
1486
1487    _isRegExpOption: function(key, value) {
1488      return (
1489        key !== 'url' &&
1490        $.type(value) === 'string' &&
1491        /^\/.*\/[igm]{0,3}$/.test(value)
1492      );
1493    },
1494
1495    _initDataAttributes: function() {
1496      var that = this,
1497        options = this.options,
1498        data = this.element.data();
1499      // Initialize options set via HTML5 data-attributes:
1500      $.each(this.element[0].attributes, function(index, attr) {
1501        var key = attr.name.toLowerCase(),
1502          value;
1503        if (/^data-/.test(key)) {
1504          // Convert hyphen-ated key to camelCase:
1505          key = key.slice(5).replace(/-[a-z]/g, function(str) {
1506            return str.charAt(1).toUpperCase();
1507          });
1508          value = data[key];
1509          if (that._isRegExpOption(key, value)) {
1510            value = that._getRegExp(value);
1511          }
1512          options[key] = value;
1513        }
1514      });
1515    },
1516
1517    _create: function() {
1518      this._initDataAttributes();
1519      this._initSpecialOptions();
1520      this._slots = [];
1521      this._sequence = this._getXHRPromise(true);
1522      this._sending = this._active = 0;
1523      this._initProgressObject(this);
1524      this._initEventHandlers();
1525    },
1526
1527    // This method is exposed to the widget API and allows to query
1528    // the number of active uploads:
1529    active: function() {
1530      return this._active;
1531    },
1532
1533    // This method is exposed to the widget API and allows to query
1534    // the widget upload progress.
1535    // It returns an object with loaded, total and bitrate properties
1536    // for the running uploads:
1537    progress: function() {
1538      return this._progress;
1539    },
1540
1541    // This method is exposed to the widget API and allows adding files
1542    // using the fileupload API. The data parameter accepts an object which
1543    // must have a files property and can contain additional options:
1544    // .fileupload('add', {files: filesList});
1545    add: function(data) {
1546      var that = this;
1547      if (!data || this.options.disabled) {
1548        return;
1549      }
1550      if (data.fileInput && !data.files) {
1551        this._getFileInputFiles(data.fileInput).always(function(files) {
1552          data.files = files;
1553          that._onAdd(null, data);
1554        });
1555      } else {
1556        data.files = $.makeArray(data.files);
1557        this._onAdd(null, data);
1558      }
1559    },
1560
1561    // This method is exposed to the widget API and allows sending files
1562    // using the fileupload API. The data parameter accepts an object which
1563    // must have a files or fileInput property and can contain additional options:
1564    // .fileupload('send', {files: filesList});
1565    // The method returns a Promise object for the file upload call.
1566    send: function(data) {
1567      if (data && !this.options.disabled) {
1568        if (data.fileInput && !data.files) {
1569          var that = this,
1570            dfd = $.Deferred(),
1571            promise = dfd.promise(),
1572            jqXHR,
1573            aborted;
1574          promise.abort = function() {
1575            aborted = true;
1576            if (jqXHR) {
1577              return jqXHR.abort();
1578            }
1579            dfd.reject(null, 'abort', 'abort');
1580            return promise;
1581          };
1582          this._getFileInputFiles(data.fileInput).always(function(files) {
1583            if (aborted) {
1584              return;
1585            }
1586            if (!files.length) {
1587              dfd.reject();
1588              return;
1589            }
1590            data.files = files;
1591            jqXHR = that._onSend(null, data);
1592            jqXHR.then(
1593              function(result, textStatus, jqXHR) {
1594                dfd.resolve(result, textStatus, jqXHR);
1595              },
1596              function(jqXHR, textStatus, errorThrown) {
1597                dfd.reject(jqXHR, textStatus, errorThrown);
1598              }
1599            );
1600          });
1601          return this._enhancePromise(promise);
1602        }
1603        data.files = $.makeArray(data.files);
1604        if (data.files.length) {
1605          return this._onSend(null, data);
1606        }
1607      }
1608      return this._getXHRPromise(false, data && data.context);
1609    }
1610  });
1611});

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.