PageSourceSearch

https://www.arabamerica.com/wp-content/plugins/business-directory-…/js/jquery.fileupload.js?ver=10.32.0

js arabamerica.com collected 2026-09-24 08:56:01 UTC 56,631 bytes, 1,604 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      // Access 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(
493          /(?: \(([\d]+)\))?(\.[^.]+)?$/,
494          function (_, p1, p2) {
495            var index = p1 ? Number(p1) + 1 : 1;
496            var ext = p2 || '';
497            return ' (' + index + ')' + ext;
498          }
499        );
500        return this._getUniqueFilename(name, map);
501      }
502      map[name] = true;
503      return name;
504    },
505
506    _initXHRData: function (options) {
507      var that = this,
508        formData,
509        file = options.files[0],
510        // Ignore non-multipart setting if not supported:
511        multipart = options.multipart || !$.support.xhrFileUpload,
512        paramName =
513          $.type(options.paramName) === 'array'
514            ? options.paramName[0]
515            : options.paramName;
516      options.headers = $.extend({}, options.headers);
517      if (options.contentRange) {
518        options.headers['Content-Range'] = options.contentRange;
519      }
520      if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
521        options.headers['Content-Disposition'] =
522          'attachment; filename="' +
523          encodeURI(file.uploadName || file.name) +
524          '"';
525      }
526      if (!multipart) {
527        options.contentType = file.type || 'application/octet-stream';
528        options.data = options.blob || file;
529      } else if ($.support.xhrFormDataFileUpload) {
530        if (options.postMessage) {
531          // window.postMessage does not allow sending FormData
532          // objects, so we just add the File/Blob objects to
533          // the formData array and let the postMessage window
534          // create the FormData object out of this array:
535          formData = this._getFormData(options);
536          if (options.blob) {
537            formData.push({
538              name: paramName,
539              value: options.blob
540            });
541          } else {
542            $.each(options.files, function (index, file) {
543              formData.push({
544                name:
545                  ($.type(options.paramName) === 'array' &&
546                    options.paramName[index]) ||
547                  paramName,
548                value: file
549              });
550            });
551          }
552        } else {
553          if (that._isInstanceOf('FormData', options.formData)) {
554            formData = options.formData;
555          } else {
556            formData = new FormData();
557            $.each(this._getFormData(options), function (index, field) {
558              formData.append(field.name, field.value);
559            });
560          }
561          if (options.blob) {
562            formData.append(
563              paramName,
564              options.blob,
565              file.uploadName || file.name
566            );
567          } else {
568            $.each(options.files, function (index, file) {
569              // This check allows the tests to run with
570              // dummy objects:
571              if (
572                that._isInstanceOf('File', file) ||
573                that._isInstanceOf('Blob', file)
574              ) {
575                var fileName = file.uploadName || file.name;
576                if (options.uniqueFilenames) {
577                  fileName = that._getUniqueFilename(
578                    fileName,
579                    options.uniqueFilenames
580                  );
581                }
582                formData.append(
583                  ($.type(options.paramName) === 'array' &&
584                    options.paramName[index]) ||
585                    paramName,
586                  file,
587                  fileName
588                );
589              }
590            });
591          }
592        }
593        options.data = formData;
594      }
595      // Blob reference is not needed anymore, free memory:
596      options.blob = null;
597    },
598
599    _initIframeSettings: function (options) {
600      var targetHost = $('<a></a>').prop('href', options.url).prop('host');
601      // Setting the dataType to iframe enables the iframe transport:
602      options.dataType = 'iframe ' + (options.dataType || '');
603      // The iframe transport accepts a serialized array as form data:
604      options.formData = this._getFormData(options);
605      // Add redirect url to form data on cross-domain uploads:
606      if (options.redirect && targetHost && targetHost !== location.host) {
607        options.formData.push({
608          name: options.redirectParamName || 'redirect',
609          value: options.redirect
610        });
611      }
612    },
613
614    _initDataSettings: function (options) {
615      if (this._isXHRUpload(options)) {
616        if (!this._chunkedUpload(options, true)) {
617          if (!options.data) {
618            this._initXHRData(options);
619          }
620          this._initProgressListener(options);
621        }
622        if (options.postMessage) {
623          // Setting the dataType to postmessage enables the
624          // postMessage transport:
625          options.dataType = 'postmessage ' + (options.dataType || '');
626        }
627      } else {
628        this._initIframeSettings(options);
629      }
630    },
631
632    _getParamName: function (options) {
633      var fileInput = $(options.fileInput),
634        paramName = options.paramName;
635      if (!paramName) {
636        paramName = [];
637        fileInput.each(function () {
638          var input = $(this),
639            name = input.prop('name') || 'files[]',
640            i = (input.prop('files') || [1]).length;
641          while (i) {
642            paramName.push(name);
643            i -= 1;
644          }
645        });
646        if (!paramName.length) {
647          paramName = [fileInput.prop('name') || 'files[]'];
648        }
649      } else if (!$.isArray(paramName)) {
650        paramName = [paramName];
651      }
652      return paramName;
653    },
654
655    _initFormSettings: function (options) {
656      // Retrieve missing options from the input field and the
657      // associated form, if available:
658      if (!options.form || !options.form.length) {
659        options.form = $(options.fileInput.prop('form'));
660        // If the given file input doesn't have an associated form,
661        // use the default widget file input's form:
662        if (!options.form.length) {
663          options.form = $(this.options.fileInput.prop('form'));
664        }
665      }
666      options.paramName = this._getParamName(options);
667      if (!options.url) {
668        options.url = options.form.prop('action') || location.href;
669      }
670      // The HTTP request method must be "POST" or "PUT":
671      options.type = (
672        options.type ||
673        ($.type(options.form.prop('method')) === 'string' &&
674          options.form.prop('method')) ||
675        ''
676      ).toUpperCase();
677      if (
678        options.type !== 'POST' &&
679        options.type !== 'PUT' &&
680        options.type !== 'PATCH'
681      ) {
682        options.type = 'POST';
683      }
684      if (!options.formAcceptCharset) {
685        options.formAcceptCharset = options.form.attr('accept-charset');
686      }
687    },
688
689    _getAJAXSettings: function (data) {
690      var options = $.extend({}, this.options, data);
691      this._initFormSettings(options);
692      this._initDataSettings(options);
693      return options;
694    },
695
696    // jQuery 1.6 doesn't provide .state(),
697    // while jQuery 1.8+ removed .isRejected() and .isResolved():
698    _getDeferredState: function (deferred) {
699      if (deferred.state) {
700        return deferred.state();
701      }
702      if (deferred.isResolved()) {
703        return 'resolved';
704      }
705      if (deferred.isRejected()) {
706        return 'rejected';
707      }
708      return 'pending';
709    },
710
711    // Maps jqXHR callbacks to the equivalent
712    // methods of the given Promise object:
713    _enhancePromise: function (promise) {
714      promise.success = promise.done;
715      promise.error = promise.fail;
716      promise.complete = promise.always;
717      return promise;
718    },
719
720    // Creates and returns a Promise object enhanced with
721    // the jqXHR methods abort, success, error and complete:
722    _getXHRPromise: function (resolveOrReject, context, args) {
723      var dfd = $.Deferred(),
724        promise = dfd.promise();
725      // eslint-disable-next-line no-param-reassign
726      context = context || this.options.context || promise;
727      if (resolveOrReject === true) {
728        dfd.resolveWith(context, args);
729      } else if (resolveOrReject === false) {
730        dfd.rejectWith(context, args);
731      }
732      promise.abort = dfd.promise;
733      return this._enhancePromise(promise);
734    },
735
736    // Adds convenience methods to the data callback argument:
737    _addConvenienceMethods: function (e, data) {
738      var that = this,
739        getPromise = function (args) {
740          return $.Deferred().resolveWith(that, args).promise();
741        };
742      data.process = function (resolveFunc, rejectFunc) {
743        if (resolveFunc || rejectFunc) {
744          data._processQueue = this._processQueue = (this._processQueue ||
745            getPromise([this]))
746            [that._promisePipe](function () {
747              if (data.errorThrown) {
748                return $.Deferred().rejectWith(that, [data]).promise();
749              }
750              return getPromise(arguments);
751            })
752            [that._promisePipe](resolveFunc, rejectFunc);
753        }
754        return this._processQueue || getPromise([this]);
755      };
756      data.submit = function () {
757        if (this.state() !== 'pending') {
758          data.jqXHR = this.jqXHR =
759            that._trigger(
760              'submit',
761              $.Event('submit', { delegatedEvent: e }),
762              this
763            ) !== false && that._onSend(e, this);
764        }
765        return this.jqXHR || that._getXHRPromise();
766      };
767      data.abort = function () {
768        if (this.jqXHR) {
769          return this.jqXHR.abort();
770        }
771        this.errorThrown = 'abort';
772        that._trigger('fail', null, this);
773        return that._getXHRPromise(false);
774      };
775      data.state = function () {
776        if (this.jqXHR) {
777          return that._getDeferredState(this.jqXHR);
778        }
779        if (this._processQueue) {
780          return that._getDeferredState(this._processQueue);
781        }
782      };
783      data.processing = function () {
784        return (
785          !this.jqXHR &&
786          this._processQueue &&
787          that._getDeferredState(this._processQueue) === 'pending'
788        );
789      };
790      data.progress = function () {
791        return this._progress;
792      };
793      data.response = function () {
794        return this._response;
795      };
796    },
797
798    // Parses the Range header from the server response
799    // and returns the uploaded bytes:
800    _getUploadedBytes: function (jqXHR) {
801      var range = jqXHR.getResponseHeader('Range'),
802        parts = range && range.split('-'),
803        upperBytesPos = parts && parts.length > 1 && parseInt(parts[1], 10);
804      return upperBytesPos && upperBytesPos + 1;
805    },
806
807    // Uploads a file in multiple, sequential requests
808    // by splitting the file up in multiple blob chunks.
809    // If the second parameter is true, only tests if the file
810    // should be uploaded in chunks, but does not invoke any
811    // upload requests:
812    _chunkedUpload: function (options, testOnly) {
813      options.uploadedBytes = options.uploadedBytes || 0;
814      var that = this,
815        file = options.files[0],
816        fs = file.size,
817        ub = options.uploadedBytes,
818        mcs = options.maxChunkSize || fs,
819        slice = this._blobSlice,
820        dfd = $.Deferred(),
821        promise = dfd.promise(),
822        jqXHR,
823        upload;
824      if (
825        !(
826          this._isXHRUpload(options) &&
827          slice &&
828          (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)
829        ) ||
830        options.data
831      ) {
832        return false;
833      }
834      if (testOnly) {
835        return true;
836      }
837      if (ub >= fs) {
838        file.error = options.i18n('uploadedBytes');
839        return this._getXHRPromise(false, options.context, [
840          null,
841          'error',
842          file.error
843        ]);
844      }
845      // The chunk upload method:
846      upload = function () {
847        // Clone the options object for each chunk upload:
848        var o = $.extend({}, options),
849          currentLoaded = o._progress.loaded;
850        o.blob = slice.call(
851          file,
852          ub,
853          ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),
854          file.type
855        );
856        // Store the current chunk size, as the blob itself
857        // will be dereferenced after data processing:
858        o.chunkSize = o.blob.size;
859        // Expose the chunk bytes position range:
860        o.contentRange =
861          'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs;
862        // Trigger chunkbeforesend to allow form data to be updated for this chunk
863        that._trigger('chunkbeforesend', null, o);
864        // Process the upload data (the blob and potential form data):
865        that._initXHRData(o);
866        // Add progress listeners for this chunk upload:
867        that._initProgressListener(o);
868        jqXHR = (
869          (that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
870          that._getXHRPromise(false, o.context)
871        )
872          .done(function (result, textStatus, jqXHR) {
873            ub = that._getUploadedBytes(jqXHR) || ub + o.chunkSize;
874            // Create a progress event if no final progress event
875            // with loaded equaling total has been triggered
876            // for this chunk:
877            if (currentLoaded + o.chunkSize - o._progress.loaded) {
878              that._onProgress(
879                $.Event('progress', {
880                  lengthComputable: true,
881                  loaded: ub - o.uploadedBytes,
882                  total: ub - o.uploadedBytes
883                }),
884                o
885              );
886            }
887            options.uploadedBytes = o.uploadedBytes = ub;
888            o.result = result;
889            o.textStatus = textStatus;
890            o.jqXHR = jqXHR;
891            that._trigger('chunkdone', null, o);
892            that._trigger('chunkalways', null, o);
893            if (ub < fs) {
894              // File upload not yet complete,
895              // continue with the next chunk:
896              upload();
897            } else {
898              dfd.resolveWith(o.context, [result, textStatus, jqXHR]);
899            }
900          })
901          .fail(function (jqXHR, textStatus, errorThrown) {
902            o.jqXHR = jqXHR;
903            o.textStatus = textStatus;
904            o.errorThrown = errorThrown;
905            that._trigger('chunkfail', null, o);
906            that._trigger('chunkalways', null, o);
907            dfd.rejectWith(o.context, [jqXHR, textStatus, errorThrown]);
908          })
909          .always(function () {
910            that._deinitProgressListener(o);
911          });
912      };
913      this._enhancePromise(promise);
914      promise.abort = function () {
915        return jqXHR.abort();
916      };
917      upload();
918      return promise;
919    },
920
921    _beforeSend: function (e, data) {
922      if (this._active === 0) {
923        // the start callback is triggered when an upload starts
924        // and no other uploads are currently running,
925        // equivalent to the global ajaxStart event:
926        this._trigger('start');
927        // Set timer for global bitrate progress calculation:
928        this._bitrateTimer = new this._BitrateTimer();
929        // Reset the global progress values:
930        this._progress.loaded = this._progress.total = 0;
931        this._progress.bitrate = 0;
932      }
933      // Make sure the container objects for the .response() and
934      // .progress() methods on the data object are available
935      // and reset to their initial state:
936      this._initResponseObject(data);
937      this._initProgressObject(data);
938      data._progress.loaded = data.loaded = data.uploadedBytes || 0;
939      data._progress.total = data.total = this._getTotal(data.files) || 1;
940      data._progress.bitrate = data.bitrate = 0;
941      this._active += 1;
942      // Initialize the global progress values:
943      this._progress.loaded += data.loaded;
944      this._progress.total += data.total;
945    },
946
947    _onDone: function (result, textStatus, jqXHR, options) {
948      var total = options._progress.total,
949        response = options._response;
950      if (options._progress.loaded < total) {
951        // Create a progress event if no final progress event
952        // with loaded equaling total has been triggered:
953        this._onProgress(
954          $.Event('progress', {
955            lengthComputable: true,
956            loaded: total,
957            total: total
958          }),
959          options
960        );
961      }
962      response.result = options.result = result;
963      response.textStatus = options.textStatus = textStatus;
964      response.jqXHR = options.jqXHR = jqXHR;
965      this._trigger('done', null, options);
966    },
967
968    _onFail: function (jqXHR, textStatus, errorThrown, options) {
969      var response = options._response;
970      if (options.recalculateProgress) {
971        // Remove the failed (error or abort) file upload from
972        // the global progress calculation:
973        this._progress.loaded -= options._progress.loaded;
974        this._progress.total -= options._progress.total;
975      }
976      response.jqXHR = options.jqXHR = jqXHR;
977      response.textStatus = options.textStatus = textStatus;
978      response.errorThrown = options.errorThrown = errorThrown;
979      this._trigger('fail', null, options);
980    },
981
982    _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
983      // jqXHRorResult, textStatus and jqXHRorError are added to the
984      // options object via done and fail callbacks
985      this._trigger('always', null, options);
986    },
987
988    _onSend: function (e, data) {
989      if (!data.submit) {
990        this._addConvenienceMethods(e, data);
991      }
992      var that = this,
993        jqXHR,
994        aborted,
995        slot,
996        pipe,
997        options = that._getAJAXSettings(data),
998        send = function () {
999          that._sending += 1;
1000          // Set timer for bitrate progress calculation:
1001          options._bitrateTimer = new that._BitrateTimer();
1002          jqXHR =
1003            jqXHR ||
1004            (
1005              ((aborted ||
1006                that._trigger(
1007                  'send',
1008                  $.Event('send', { delegatedEvent: e }),
1009                  options
1010                ) === false) &&
1011                that._getXHRPromise(false, options.context, aborted)) ||
1012              that._chunkedUpload(options) ||
1013              $.ajax(options)
1014            )
1015              .done(function (result, textStatus, jqXHR) {
1016                that._onDone(result, textStatus, jqXHR, options);
1017              })
1018              .fail(function (jqXHR, textStatus, errorThrown) {
1019                that._onFail(jqXHR, textStatus, errorThrown, options);
1020              })
1021              .always(function (jqXHRorResult, textStatus, jqXHRorError) {
1022                that._deinitProgressListener(options);
1023                that._onAlways(
1024                  jqXHRorResult,
1025                  textStatus,
1026                  jqXHRorError,
1027                  options
1028                );
1029                that._sending -= 1;
1030                that._active -= 1;
1031                if (
1032                  options.limitConcurrentUploads &&
1033                  options.limitConcurrentUploads > that._sending
1034                ) {
1035                  // Start the next queued upload,
1036                  // that has not been aborted:
1037                  var nextSlot = that._slots.shift();
1038                  while (nextSlot) {
1039                    if (that._getDeferredState(nextSlot) === 'pending') {
1040                      nextSlot.resolve();
1041                      break;
1042                    }
1043                    nextSlot = that._slots.shift();
1044                  }
1045                }
1046                if (that._active === 0) {
1047                  // The stop callback is triggered when all uploads have
1048                  // been completed, equivalent to the global ajaxStop event:
1049                  that._trigger('stop');
1050                }
1051              });
1052          return jqXHR;
1053        };
1054      this._beforeSend(e, options);
1055      if (
1056        this.options.sequentialUploads ||
1057        (this.options.limitConcurrentUploads &&
1058          this.options.limitConcurrentUploads <= this._sending)
1059      ) {
1060        if (this.options.limitConcurrentUploads > 1) {
1061          slot = $.Deferred();
1062          this._slots.push(slot);
1063          pipe = slot[that._promisePipe](send);
1064        } else {
1065          this._sequence = this._sequence[that._promisePipe](send, send);
1066          pipe = this._sequence;
1067        }
1068        // Return the piped Promise object, enhanced with an abort method,
1069        // which is delegated to the jqXHR object of the current upload,
1070        // and jqXHR callbacks mapped to the equivalent Promise methods:
1071        pipe.abort = function () {
1072          aborted = [undefined, 'abort', 'abort'];
1073          if (!jqXHR) {
1074            if (slot) {
1075              slot.rejectWith(options.context, aborted);
1076            }
1077            return send();
1078          }
1079          return jqXHR.abort();
1080        };
1081        return this._enhancePromise(pipe);
1082      }
1083      return send();
1084    },
1085
1086    _onAdd: function (e, data) {
1087      var that = this,
1088        result = true,
1089        options = $.extend({}, this.options, data),
1090        files = data.files,
1091        filesLength = files.length,
1092        limit = options.limitMultiFileUploads,
1093        limitSize = options.limitMultiFileUploadSize,
1094        overhead = options.limitMultiFileUploadSizeOverhead,
1095        batchSize = 0,
1096        paramName = this._getParamName(options),
1097        paramNameSet,
1098        paramNameSlice,
1099        fileSet,
1100        i,
1101        j = 0;
1102      if (!filesLength) {
1103        return false;
1104      }
1105      if (limitSize && files[0].size === undefined) {
1106        limitSize = undefined;
1107      }
1108      if (
1109        !(options.singleFileUploads || limit || limitSize) ||
1110        !this._isXHRUpload(options)
1111      ) {
1112        fileSet = [files];
1113        paramNameSet = [paramName];
1114      } else if (!(options.singleFileUploads || limitSize) && limit) {
1115        fileSet = [];
1116        paramNameSet = [];
1117        for (i = 0; i < filesLength; i += limit) {
1118          fileSet.push(files.slice(i, i + limit));
1119          paramNameSlice = paramName.slice(i, i + limit);
1120          if (!paramNameSlice.length) {
1121            paramNameSlice = paramName;
1122          }
1123          paramNameSet.push(paramNameSlice);
1124        }
1125      } else if (!options.singleFileUploads && limitSize) {
1126        fileSet = [];
1127        paramNameSet = [];
1128        for (i = 0; i < filesLength; i = i + 1) {
1129          batchSize += files[i].size + overhead;
1130          if (
1131            i + 1 === filesLength ||
1132            batchSize + files[i + 1].size + overhead > limitSize ||
1133            (limit && i + 1 - j >= limit)
1134          ) {
1135            fileSet.push(files.slice(j, i + 1));
1136            paramNameSlice = paramName.slice(j, i + 1);
1137            if (!paramNameSlice.length) {
1138              paramNameSlice = paramName;
1139            }
1140            paramNameSet.push(paramNameSlice);
1141            j = i + 1;
1142            batchSize = 0;
1143          }
1144        }
1145      } else {
1146        paramNameSet = paramName;
1147      }
1148      data.originalFiles = files;
1149      $.each(fileSet || files, function (index, element) {
1150        var newData = $.extend({}, data);
1151        newData.files = fileSet ? element : [element];
1152        newData.paramName = paramNameSet[index];
1153        that._initResponseObject(newData);
1154        that._initProgressObject(newData);
1155        that._addConvenienceMethods(e, newData);
1156        result = that._trigger(
1157          'add',
1158          $.Event('add', { delegatedEvent: e }),
1159          newData
1160        );
1161        return result;
1162      });
1163      return result;
1164    },
1165
1166    _replaceFileInput: function (data) {
1167      var input = data.fileInput,
1168        inputClone = input.clone(true),
1169        restoreFocus = input.is(document.activeElement);
1170      // Add a reference for the new cloned file input to the data argument:
1171      data.fileInputClone = inputClone;
1172      $('<form></form>').append(inputClone)[0].reset();
1173      // Detaching allows to insert the fileInput on another form
1174      // without losing 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.trigger('focus');
1180      }
1181      // Avoid memory leaks with the detached file input:
1182      $.cleanData(input.off('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        [this._promisePipe](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().resolve($.makeArray(dataTransfer.files)).promise();
1295    },
1296
1297    _getSingleFileInputFiles: function (fileInput) {
1298      // eslint-disable-next-line no-param-reassign
1299      fileInput = $(fileInput);
1300      var entries = fileInput.prop('entries'),
1301        files,
1302        value;
1303      if (entries && entries.length) {
1304        return this._handleFileTreeEntries(entries);
1305      }
1306      files = $.makeArray(fileInput.prop('files'));
1307      if (!files.length) {
1308        value = fileInput.prop('value');
1309        if (!value) {
1310          return $.Deferred().resolve([]).promise();
1311        }
1312        // If the files property is not available, the browser does not
1313        // support the File API and we add a pseudo File object with
1314        // the input value as name with path information removed:
1315        files = [{ name: value.replace(/^.*\\/, '') }];
1316      } else if (files[0].name === undefined && files[0].fileName) {
1317        // File normalization for Safari 4 and Firefox 3:
1318        $.each(files, function (index, file) {
1319          file.name = file.fileName;
1320          file.size = file.fileSize;
1321        });
1322      }
1323      return $.Deferred().resolve(files).promise();
1324    },
1325
1326    _getFileInputFiles: function (fileInput) {
1327      if (!(fileInput instanceof $) || fileInput.length === 1) {
1328        return this._getSingleFileInputFiles(fileInput);
1329      }
1330      return $.when
1331        .apply($, $.map(fileInput, this._getSingleFileInputFiles))
1332        [this._promisePipe](function () {
1333          return Array.prototype.concat.apply([], arguments);
1334        });
1335    },
1336
1337    _onChange: function (e) {
1338      var that = this,
1339        data = {
1340          fileInput: $(e.target),
1341          form: $(e.target.form)
1342        };
1343      this._getFileInputFiles(data.fileInput).always(function (files) {
1344        data.files = files;
1345        if (that.options.replaceFileInput) {
1346          that._replaceFileInput(data);
1347        }
1348        if (
1349          that._trigger(
1350            'change',
1351            $.Event('change', { delegatedEvent: e }),
1352            data
1353          ) !== false
1354        ) {
1355          that._onAdd(e, data);
1356        }
1357      });
1358    },
1359
1360    _onPaste: function (e) {
1361      var items =
1362          e.originalEvent &&
1363          e.originalEvent.clipboardData &&
1364          e.originalEvent.clipboardData.items,
1365        data = { files: [] };
1366      if (items && items.length) {
1367        $.each(items, function (index, item) {
1368          var file = item.getAsFile && item.getAsFile();
1369          if (file) {
1370            data.files.push(file);
1371          }
1372        });
1373        if (
1374          this._trigger(
1375            'paste',
1376            $.Event('paste', { delegatedEvent: e }),
1377            data
1378          ) !== false
1379        ) {
1380          this._onAdd(e, data);
1381        }
1382      }
1383    },
1384
1385    _onDrop: function (e) {
1386      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
1387      var that = this,
1388        dataTransfer = e.dataTransfer,
1389        data = {};
1390      if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
1391        e.preventDefault();
1392        this._getDroppedFiles(dataTransfer).always(function (files) {
1393          data.files = files;
1394          if (
1395            that._trigger(
1396              'drop',
1397              $.Event('drop', { delegatedEvent: e }),
1398              data
1399            ) !== false
1400          ) {
1401            that._onAdd(e, data);
1402          }
1403        });
1404      }
1405    },
1406
1407    _onDragOver: getDragHandler('dragover'),
1408
1409    _onDragEnter: getDragHandler('dragenter'),
1410
1411    _onDragLeave: getDragHandler('dragleave'),
1412
1413    _initEventHandlers: function () {
1414      if (this._isXHRUpload(this.options)) {
1415        this._on(this.options.dropZone, {
1416          dragover: this._onDragOver,
1417          drop: this._onDrop,
1418          // event.preventDefault() on dragenter is required for IE10+:
1419          dragenter: this._onDragEnter,
1420          // dragleave is not required, but added for completeness:
1421          dragleave: this._onDragLeave
1422        });
1423        this._on(this.options.pasteZone, {
1424          paste: this._onPaste
1425        });
1426      }
1427      if ($.support.fileInput) {
1428        this._on(this.options.fileInput, {
1429          change: this._onChange
1430        });
1431      }
1432    },
1433
1434    _destroyEventHandlers: function () {
1435      this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
1436      this._off(this.options.pasteZone, 'paste');
1437      this._off(this.options.fileInput, 'change');
1438    },
1439
1440    _destroy: function () {
1441      this._destroyEventHandlers();
1442    },
1443
1444    _setOption: function (key, value) {
1445      var reinit = $.inArray(key, this._specialOptions) !== -1;
1446      if (reinit) {
1447        this._destroyEventHandlers();
1448      }
1449      this._super(key, value);
1450      if (reinit) {
1451        this._initSpecialOptions();
1452        this._initEventHandlers();
1453      }
1454    },
1455
1456    _initSpecialOptions: function () {
1457      var options = this.options;
1458      if (options.fileInput === undefined) {
1459        options.fileInput = this.element.is('input[type="file"]')
1460          ? this.element
1461          : this.element.find('input[type="file"]');
1462      } else if (!(options.fileInput instanceof $)) {
1463        options.fileInput = $(options.fileInput);
1464      }
1465      if (!(options.dropZone instanceof $)) {
1466        options.dropZone = $(options.dropZone);
1467      }
1468      if (!(options.pasteZone instanceof $)) {
1469        options.pasteZone = $(options.pasteZone);
1470      }
1471    },
1472
1473    _getRegExp: function (str) {
1474      var parts = str.split('/'),
1475        modifiers = parts.pop();
1476      parts.shift();
1477      return new RegExp(parts.join('/'), modifiers);
1478    },
1479
1480    _isRegExpOption: function (key, value) {
1481      return (
1482        key !== 'url' &&
1483        $.type(value) === 'string' &&
1484        /^\/.*\/[igm]{0,3}$/.test(value)
1485      );
1486    },
1487
1488    _initDataAttributes: function () {
1489      var that = this,
1490        options = this.options,
1491        data = this.element.data();
1492      // Initialize options set via HTML5 data-attributes:
1493      $.each(this.element[0].attributes, function (index, attr) {
1494        var key = attr.name.toLowerCase(),
1495          value;
1496        if (/^data-/.test(key)) {
1497          // Convert hyphen-ated key to camelCase:
1498          key = key.slice(5).replace(/-[a-z]/g, function (str) {
1499            return str.charAt(1).toUpperCase();
1500          });
1501          value = data[key];
1502          if (that._isRegExpOption(key, value)) {
1503            value = that._getRegExp(value);
1504          }
1505          options[key] = value;
1506        }
1507      });
1508    },
1509
1510    _create: function () {
1511      this._initDataAttributes();
1512      this._initSpecialOptions();
1513      this._slots = [];
1514      this._sequence = this._getXHRPromise(true);
1515      this._sending = this._active = 0;
1516      this._initProgressObject(this);
1517      this._initEventHandlers();
1518    },
1519
1520    // This method is exposed to the widget API and allows to query
1521    // the number of active uploads:
1522    active: function () {
1523      return this._active;
1524    },
1525
1526    // This method is exposed to the widget API and allows to query
1527    // the widget upload progress.
1528    // It returns an object with loaded, total and bitrate properties
1529    // for the running uploads:
1530    progress: function () {
1531      return this._progress;
1532    },
1533
1534    // This method is exposed to the widget API and allows adding files
1535    // using the fileupload API. The data parameter accepts an object which
1536    // must have a files property and can contain additional options:
1537    // .fileupload('add', {files: filesList});
1538    add: function (data) {
1539      var that = this;
1540      if (!data || this.options.disabled) {
1541        return;
1542      }
1543      if (data.fileInput && !data.files) {
1544        this._getFileInputFiles(data.fileInput).always(function (files) {
1545          data.files = files;
1546          that._onAdd(null, data);
1547        });
1548      } else {
1549        data.files = $.makeArray(data.files);
1550        this._onAdd(null, data);
1551      }
1552    },
1553
1554    // This method is exposed to the widget API and allows sending files
1555    // using the fileupload API. The data parameter accepts an object which
1556    // must have a files or fileInput property and can contain additional options:
1557    // .fileupload('send', {files: filesList});
1558    // The method returns a Promise object for the file upload call.
1559    send: function (data) {
1560      if (data && !this.options.disabled) {
1561        if (data.fileInput && !data.files) {
1562          var that = this,
1563            dfd = $.Deferred(),
1564            promise = dfd.promise(),
1565            jqXHR,
1566            aborted;
1567          promise.abort = function () {
1568            aborted = true;
1569            if (jqXHR) {
1570              return jqXHR.abort();
1571            }
1572            dfd.reject(null, 'abort', 'abort');
1573            return promise;
1574          };
1575          this._getFileInputFiles(data.fileInput).always(function (files) {
1576            if (aborted) {
1577              return;
1578            }
1579            if (!files.length) {
1580              dfd.reject();
1581              return;
1582            }
1583            data.files = files;
1584            jqXHR = that._onSend(null, data);
1585            jqXHR.then(
1586              function (result, textStatus, jqXHR) {
1587                dfd.resolve(result, textStatus, jqXHR);
1588              },
1589              function (jqXHR, textStatus, errorThrown) {
1590                dfd.reject(jqXHR, textStatus, errorThrown);
1591              }
1592            );
1593          });
1594          return this._enhancePromise(promise);
1595        }
1596        data.files = $.makeArray(data.files);
1597        if (data.files.length) {
1598          return this._onSend(null, data);
1599        }
1600      }
1601      return this._getXHRPromise(false, data && data.context);
1602    }
1603  });
1604});

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.