PageSourceSearch

https://malthesorenssenutvalget.no/wp-content/plugins/ninja-forms-…/js/lib/jquery.fileupload.js?ver=3.3.33

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

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.