PageSourceSearch

https://www.infinitescuba.com/js/jquery.fileupload.js

js infinitescuba.com collected 2026-10-09 00:56:35 UTC 58,488 bytes, 1,367 lines download raw bytes

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

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.