attachinary 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,904 +0,0 @@
1
- /*
2
- * jQuery File Upload Plugin 5.10.1
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, Blob, FormData, location */
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
- // The FileReader API is not actually used, but works as feature detection,
31
- // as e.g. Safari supports XHR file uploads via the FormData API,
32
- // but not non-multipart XHR file uploads:
33
- $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
34
- $.support.xhrFormDataFileUpload = !!window.FormData;
35
-
36
- // The fileupload widget listens for change events on file input fields defined
37
- // via fileInput setting and paste or drop events of the given dropZone.
38
- // In addition to the default jQuery Widget methods, the fileupload widget
39
- // exposes the "add" and "send" methods, to add or directly send files using
40
- // the fileupload API.
41
- // By default, files added via file input selection, paste, drag & drop or
42
- // "add" method are uploaded immediately, but it is possible to override
43
- // the "add" callback option to queue file uploads.
44
- $.widget('blueimp.fileupload', {
45
-
46
- options: {
47
- // The namespace used for event handler binding on the dropZone and
48
- // fileInput collections.
49
- // If not set, the name of the widget ("fileupload") is used.
50
- namespace: undefined,
51
- // The drop target collection, by the default the complete document.
52
- // Set to null or an empty collection to disable drag & drop support:
53
- dropZone: $(document),
54
- // The file input field collection, that is listened for change events.
55
- // If undefined, it is set to the file input fields inside
56
- // of the widget element on plugin initialization.
57
- // Set to null or an empty collection to disable the change listener.
58
- fileInput: undefined,
59
- // By default, the file input field is replaced with a clone after
60
- // each input field change event. This is required for iframe transport
61
- // queues and allows change events to be fired for the same file
62
- // selection, but can be disabled by setting the following option to false:
63
- replaceFileInput: true,
64
- // The parameter name for the file form data (the request argument name).
65
- // If undefined or empty, the name property of the file input field is
66
- // used, or "files[]" if the file input name property is also empty,
67
- // can be a string or an array of strings:
68
- paramName: undefined,
69
- // By default, each file of a selection is uploaded using an individual
70
- // request for XHR type uploads. Set to false to upload file
71
- // selections in one request each:
72
- singleFileUploads: true,
73
- // To limit the number of files uploaded with one XHR request,
74
- // set the following option to an integer greater than 0:
75
- limitMultiFileUploads: undefined,
76
- // Set the following option to true to issue all file upload requests
77
- // in a sequential order:
78
- sequentialUploads: false,
79
- // To limit the number of concurrent uploads,
80
- // set the following option to an integer greater than 0:
81
- limitConcurrentUploads: undefined,
82
- // Set the following option to true to force iframe transport uploads:
83
- forceIframeTransport: false,
84
- // Set the following option to the location of a redirect url on the
85
- // origin server, for cross-domain iframe transport uploads:
86
- redirect: undefined,
87
- // The parameter name for the redirect url, sent as part of the form
88
- // data and set to 'redirect' if this option is empty:
89
- redirectParamName: undefined,
90
- // Set the following option to the location of a postMessage window,
91
- // to enable postMessage transport uploads:
92
- postMessage: undefined,
93
- // By default, XHR file uploads are sent as multipart/form-data.
94
- // The iframe transport is always using multipart/form-data.
95
- // Set to false to enable non-multipart XHR uploads:
96
- multipart: true,
97
- // To upload large files in smaller chunks, set the following option
98
- // to a preferred maximum chunk size. If set to 0, null or undefined,
99
- // or the browser does not support the required Blob API, files will
100
- // be uploaded as a whole.
101
- maxChunkSize: undefined,
102
- // When a non-multipart upload or a chunked multipart upload has been
103
- // aborted, this option can be used to resume the upload by setting
104
- // it to the size of the already uploaded bytes. This option is most
105
- // useful when modifying the options object inside of the "add" or
106
- // "send" callbacks, as the options are cloned for each file upload.
107
- uploadedBytes: undefined,
108
- // By default, failed (abort or error) file uploads are removed from the
109
- // global progress calculation. Set the following option to false to
110
- // prevent recalculating the global progress data:
111
- recalculateProgress: true,
112
-
113
- // Additional form data to be sent along with the file uploads can be set
114
- // using this option, which accepts an array of objects with name and
115
- // value properties, a function returning such an array, a FormData
116
- // object (for XHR file uploads), or a simple object.
117
- // The form of the first fileInput is given as parameter to the function:
118
- formData: function (form) {
119
- return form.serializeArray();
120
- },
121
-
122
- // The add callback is invoked as soon as files are added to the fileupload
123
- // widget (via file input selection, drag & drop, paste or add API call).
124
- // If the singleFileUploads option is enabled, this callback will be
125
- // called once for each file in the selection for XHR file uplaods, else
126
- // once for each file selection.
127
- // The upload starts when the submit method is invoked on the data parameter.
128
- // The data object contains a files property holding the added files
129
- // and allows to override plugin options as well as define ajax settings.
130
- // Listeners for this callback can also be bound the following way:
131
- // .bind('fileuploadadd', func);
132
- // data.submit() returns a Promise object and allows to attach additional
133
- // handlers using jQuery's Deferred callbacks:
134
- // data.submit().done(func).fail(func).always(func);
135
- add: function (e, data) {
136
- data.submit();
137
- },
138
-
139
- // Other callbacks:
140
- // Callback for the submit event of each file upload:
141
- // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
142
- // Callback for the start of each file upload request:
143
- // send: function (e, data) {}, // .bind('fileuploadsend', func);
144
- // Callback for successful uploads:
145
- // done: function (e, data) {}, // .bind('fileuploaddone', func);
146
- // Callback for failed (abort or error) uploads:
147
- // fail: function (e, data) {}, // .bind('fileuploadfail', func);
148
- // Callback for completed (success, abort or error) requests:
149
- // always: function (e, data) {}, // .bind('fileuploadalways', func);
150
- // Callback for upload progress events:
151
- // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
152
- // Callback for global upload progress events:
153
- // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
154
- // Callback for uploads start, equivalent to the global ajaxStart event:
155
- // start: function (e) {}, // .bind('fileuploadstart', func);
156
- // Callback for uploads stop, equivalent to the global ajaxStop event:
157
- // stop: function (e) {}, // .bind('fileuploadstop', func);
158
- // Callback for change events of the fileInput collection:
159
- // change: function (e, data) {}, // .bind('fileuploadchange', func);
160
- // Callback for paste events to the dropZone collection:
161
- // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
162
- // Callback for drop events of the dropZone collection:
163
- // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
164
- // Callback for dragover events of the dropZone collection:
165
- // dragover: function (e) {}, // .bind('fileuploaddragover', func);
166
-
167
- // The plugin options are used as settings object for the ajax calls.
168
- // The following are jQuery ajax settings required for the file uploads:
169
- processData: false,
170
- contentType: false,
171
- cache: false
172
- },
173
-
174
- // A list of options that require a refresh after assigning a new value:
175
- _refreshOptionsList: [
176
- 'namespace',
177
- 'dropZone',
178
- 'fileInput',
179
- 'multipart',
180
- 'forceIframeTransport'
181
- ],
182
-
183
- _isXHRUpload: function (options) {
184
- return !options.forceIframeTransport &&
185
- ((!options.multipart && $.support.xhrFileUpload) ||
186
- $.support.xhrFormDataFileUpload);
187
- },
188
-
189
- _getFormData: function (options) {
190
- var formData;
191
- if (typeof options.formData === 'function') {
192
- return options.formData(options.form);
193
- } else if ($.isArray(options.formData)) {
194
- return options.formData;
195
- } else if (options.formData) {
196
- formData = [];
197
- $.each(options.formData, function (name, value) {
198
- formData.push({name: name, value: value});
199
- });
200
- return formData;
201
- }
202
- return [];
203
- },
204
-
205
- _getTotal: function (files) {
206
- var total = 0;
207
- $.each(files, function (index, file) {
208
- total += file.size || 1;
209
- });
210
- return total;
211
- },
212
-
213
- _onProgress: function (e, data) {
214
- if (e.lengthComputable) {
215
- var total = data.total || this._getTotal(data.files),
216
- loaded = parseInt(
217
- e.loaded / e.total * (data.chunkSize || total),
218
- 10
219
- ) + (data.uploadedBytes || 0);
220
- this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
221
- data.lengthComputable = true;
222
- data.loaded = loaded;
223
- data.total = total;
224
- // Trigger a custom progress event with a total data property set
225
- // to the file size(s) of the current upload and a loaded data
226
- // property calculated accordingly:
227
- this._trigger('progress', e, data);
228
- // Trigger a global progress event for all current file uploads,
229
- // including ajax calls queued for sequential file uploads:
230
- this._trigger('progressall', e, {
231
- lengthComputable: true,
232
- loaded: this._loaded,
233
- total: this._total
234
- });
235
- }
236
- },
237
-
238
- _initProgressListener: function (options) {
239
- var that = this,
240
- xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
241
- // Accesss to the native XHR object is required to add event listeners
242
- // for the upload progress event:
243
- if (xhr.upload) {
244
- $(xhr.upload).bind('progress', function (e) {
245
- var oe = e.originalEvent;
246
- // Make sure the progress event properties get copied over:
247
- e.lengthComputable = oe.lengthComputable;
248
- e.loaded = oe.loaded;
249
- e.total = oe.total;
250
- that._onProgress(e, options);
251
- });
252
- options.xhr = function () {
253
- return xhr;
254
- };
255
- }
256
- },
257
-
258
- _initXHRData: function (options) {
259
- var formData,
260
- file = options.files[0],
261
- // Ignore non-multipart setting if not supported:
262
- multipart = options.multipart || !$.support.xhrFileUpload,
263
- paramName = options.paramName[0];
264
- if (!multipart || options.blob) {
265
- // For non-multipart uploads and chunked uploads,
266
- // file meta data is not part of the request body,
267
- // so we transmit this data as part of the HTTP headers.
268
- // For cross domain requests, these headers must be allowed
269
- // via Access-Control-Allow-Headers or removed using
270
- // the beforeSend callback:
271
- options.headers = $.extend(options.headers, {
272
- 'X-File-Name': file.name,
273
- 'X-File-Type': file.type,
274
- 'X-File-Size': file.size
275
- });
276
- if (!options.blob) {
277
- // Non-chunked non-multipart upload:
278
- options.contentType = file.type;
279
- options.data = file;
280
- } else if (!multipart) {
281
- // Chunked non-multipart upload:
282
- options.contentType = 'application/octet-stream';
283
- options.data = options.blob;
284
- }
285
- }
286
- if (multipart && $.support.xhrFormDataFileUpload) {
287
- if (options.postMessage) {
288
- // window.postMessage does not allow sending FormData
289
- // objects, so we just add the File/Blob objects to
290
- // the formData array and let the postMessage window
291
- // create the FormData object out of this array:
292
- formData = this._getFormData(options);
293
- if (options.blob) {
294
- formData.push({
295
- name: paramName,
296
- value: options.blob
297
- });
298
- } else {
299
- $.each(options.files, function (index, file) {
300
- formData.push({
301
- name: options.paramName[index] || paramName,
302
- value: file
303
- });
304
- });
305
- }
306
- } else {
307
- if (options.formData instanceof FormData) {
308
- formData = options.formData;
309
- } else {
310
- formData = new FormData();
311
- $.each(this._getFormData(options), function (index, field) {
312
- formData.append(field.name, field.value);
313
- });
314
- }
315
- if (options.blob) {
316
- formData.append(paramName, options.blob, file.name);
317
- } else {
318
- $.each(options.files, function (index, file) {
319
- // File objects are also Blob instances.
320
- // This check allows the tests to run with
321
- // dummy objects:
322
- if (file instanceof Blob) {
323
- formData.append(
324
- options.paramName[index] || paramName,
325
- file,
326
- file.name
327
- );
328
- }
329
- });
330
- }
331
- }
332
- options.data = formData;
333
- }
334
- // Blob reference is not needed anymore, free memory:
335
- options.blob = null;
336
- },
337
-
338
- _initIframeSettings: function (options) {
339
- // Setting the dataType to iframe enables the iframe transport:
340
- options.dataType = 'iframe ' + (options.dataType || '');
341
- // The iframe transport accepts a serialized array as form data:
342
- options.formData = this._getFormData(options);
343
- // Add redirect url to form data on cross-domain uploads:
344
- if (options.redirect && $('<a></a>').prop('href', options.url)
345
- .prop('host') !== location.host) {
346
- options.formData.push({
347
- name: options.redirectParamName || 'redirect',
348
- value: options.redirect
349
- });
350
- }
351
- },
352
-
353
- _initDataSettings: function (options) {
354
- if (this._isXHRUpload(options)) {
355
- if (!this._chunkedUpload(options, true)) {
356
- if (!options.data) {
357
- this._initXHRData(options);
358
- }
359
- this._initProgressListener(options);
360
- }
361
- if (options.postMessage) {
362
- // Setting the dataType to postmessage enables the
363
- // postMessage transport:
364
- options.dataType = 'postmessage ' + (options.dataType || '');
365
- }
366
- } else {
367
- this._initIframeSettings(options, 'iframe');
368
- }
369
- },
370
-
371
- _getParamName: function (options) {
372
- var fileInput = $(options.fileInput),
373
- paramName = options.paramName;
374
- if (!paramName) {
375
- paramName = [];
376
- fileInput.each(function () {
377
- var input = $(this),
378
- name = input.prop('name') || 'files[]',
379
- i = (input.prop('files') || [1]).length;
380
- while (i) {
381
- paramName.push(name);
382
- i -= 1;
383
- }
384
- });
385
- if (!paramName.length) {
386
- paramName = [fileInput.prop('name') || 'files[]'];
387
- }
388
- } else if (!$.isArray(paramName)) {
389
- paramName = [paramName];
390
- }
391
- return paramName;
392
- },
393
-
394
- _initFormSettings: function (options) {
395
- // Retrieve missing options from the input field and the
396
- // associated form, if available:
397
- if (!options.form || !options.form.length) {
398
- options.form = $(options.fileInput.prop('form'));
399
- }
400
- options.paramName = this._getParamName(options);
401
- if (!options.url) {
402
- options.url = options.form.prop('action') || location.href;
403
- }
404
- // The HTTP request method must be "POST" or "PUT":
405
- options.type = (options.type || options.form.prop('method') || '')
406
- .toUpperCase();
407
- if (options.type !== 'POST' && options.type !== 'PUT') {
408
- options.type = 'POST';
409
- }
410
- },
411
-
412
- _getAJAXSettings: function (data) {
413
- var options = $.extend({}, this.options, data);
414
- this._initFormSettings(options);
415
- this._initDataSettings(options);
416
- return options;
417
- },
418
-
419
- // Maps jqXHR callbacks to the equivalent
420
- // methods of the given Promise object:
421
- _enhancePromise: function (promise) {
422
- promise.success = promise.done;
423
- promise.error = promise.fail;
424
- promise.complete = promise.always;
425
- return promise;
426
- },
427
-
428
- // Creates and returns a Promise object enhanced with
429
- // the jqXHR methods abort, success, error and complete:
430
- _getXHRPromise: function (resolveOrReject, context, args) {
431
- var dfd = $.Deferred(),
432
- promise = dfd.promise();
433
- context = context || this.options.context || promise;
434
- if (resolveOrReject === true) {
435
- dfd.resolveWith(context, args);
436
- } else if (resolveOrReject === false) {
437
- dfd.rejectWith(context, args);
438
- }
439
- promise.abort = dfd.promise;
440
- return this._enhancePromise(promise);
441
- },
442
-
443
- // Uploads a file in multiple, sequential requests
444
- // by splitting the file up in multiple blob chunks.
445
- // If the second parameter is true, only tests if the file
446
- // should be uploaded in chunks, but does not invoke any
447
- // upload requests:
448
- _chunkedUpload: function (options, testOnly) {
449
- var that = this,
450
- file = options.files[0],
451
- fs = file.size,
452
- ub = options.uploadedBytes = options.uploadedBytes || 0,
453
- mcs = options.maxChunkSize || fs,
454
- // Use the Blob methods with the slice implementation
455
- // according to the W3C Blob API specification:
456
- slice = file.webkitSlice || file.mozSlice || file.slice,
457
- upload,
458
- n,
459
- jqXHR,
460
- pipe;
461
- if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
462
- options.data) {
463
- return false;
464
- }
465
- if (testOnly) {
466
- return true;
467
- }
468
- if (ub >= fs) {
469
- file.error = 'uploadedBytes';
470
- return this._getXHRPromise(
471
- false,
472
- options.context,
473
- [null, 'error', file.error]
474
- );
475
- }
476
- // n is the number of blobs to upload,
477
- // calculated via filesize, uploaded bytes and max chunk size:
478
- n = Math.ceil((fs - ub) / mcs);
479
- // The chunk upload method accepting the chunk number as parameter:
480
- upload = function (i) {
481
- if (!i) {
482
- return that._getXHRPromise(true, options.context);
483
- }
484
- // Upload the blobs in sequential order:
485
- return upload(i -= 1).pipe(function () {
486
- // Clone the options object for each chunk upload:
487
- var o = $.extend({}, options);
488
- o.blob = slice.call(
489
- file,
490
- ub + i * mcs,
491
- ub + (i + 1) * mcs
492
- );
493
- // Store the current chunk size, as the blob itself
494
- // will be dereferenced after data processing:
495
- o.chunkSize = o.blob.size;
496
- // Process the upload data (the blob and potential form data):
497
- that._initXHRData(o);
498
- // Add progress listeners for this chunk upload:
499
- that._initProgressListener(o);
500
- jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
501
- .done(function () {
502
- // Create a progress event if upload is done and
503
- // no progress event has been invoked for this chunk:
504
- if (!o.loaded) {
505
- that._onProgress($.Event('progress', {
506
- lengthComputable: true,
507
- loaded: o.chunkSize,
508
- total: o.chunkSize
509
- }), o);
510
- }
511
- options.uploadedBytes = o.uploadedBytes +=
512
- o.chunkSize;
513
- });
514
- return jqXHR;
515
- });
516
- };
517
- // Return the piped Promise object, enhanced with an abort method,
518
- // which is delegated to the jqXHR object of the current upload,
519
- // and jqXHR callbacks mapped to the equivalent Promise methods:
520
- pipe = upload(n);
521
- pipe.abort = function () {
522
- return jqXHR.abort();
523
- };
524
- return this._enhancePromise(pipe);
525
- },
526
-
527
- _beforeSend: function (e, data) {
528
- if (this._active === 0) {
529
- // the start callback is triggered when an upload starts
530
- // and no other uploads are currently running,
531
- // equivalent to the global ajaxStart event:
532
- this._trigger('start');
533
- }
534
- this._active += 1;
535
- // Initialize the global progress values:
536
- this._loaded += data.uploadedBytes || 0;
537
- this._total += this._getTotal(data.files);
538
- },
539
-
540
- _onDone: function (result, textStatus, jqXHR, options) {
541
- if (!this._isXHRUpload(options)) {
542
- // Create a progress event for each iframe load:
543
- this._onProgress($.Event('progress', {
544
- lengthComputable: true,
545
- loaded: 1,
546
- total: 1
547
- }), options);
548
- }
549
- options.result = result;
550
- options.textStatus = textStatus;
551
- options.jqXHR = jqXHR;
552
- this._trigger('done', null, options);
553
- },
554
-
555
- _onFail: function (jqXHR, textStatus, errorThrown, options) {
556
- options.jqXHR = jqXHR;
557
- options.textStatus = textStatus;
558
- options.errorThrown = errorThrown;
559
- this._trigger('fail', null, options);
560
- if (options.recalculateProgress) {
561
- // Remove the failed (error or abort) file upload from
562
- // the global progress calculation:
563
- this._loaded -= options.loaded || options.uploadedBytes || 0;
564
- this._total -= options.total || this._getTotal(options.files);
565
- }
566
- },
567
-
568
- _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
569
- this._active -= 1;
570
- options.textStatus = textStatus;
571
- if (jqXHRorError && jqXHRorError.always) {
572
- options.jqXHR = jqXHRorError;
573
- options.result = jqXHRorResult;
574
- } else {
575
- options.jqXHR = jqXHRorResult;
576
- options.errorThrown = jqXHRorError;
577
- }
578
- this._trigger('always', null, options);
579
- if (this._active === 0) {
580
- // The stop callback is triggered when all uploads have
581
- // been completed, equivalent to the global ajaxStop event:
582
- this._trigger('stop');
583
- // Reset the global progress values:
584
- this._loaded = this._total = 0;
585
- }
586
- },
587
-
588
- _onSend: function (e, data) {
589
- var that = this,
590
- jqXHR,
591
- slot,
592
- pipe,
593
- options = that._getAJAXSettings(data),
594
- send = function (resolve, args) {
595
- that._sending += 1;
596
- jqXHR = jqXHR || (
597
- (resolve !== false &&
598
- that._trigger('send', e, options) !== false &&
599
- (that._chunkedUpload(options) || $.ajax(options))) ||
600
- that._getXHRPromise(false, options.context, args)
601
- ).done(function (result, textStatus, jqXHR) {
602
- that._onDone(result, textStatus, jqXHR, options);
603
- }).fail(function (jqXHR, textStatus, errorThrown) {
604
- that._onFail(jqXHR, textStatus, errorThrown, options);
605
- }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
606
- that._sending -= 1;
607
- that._onAlways(
608
- jqXHRorResult,
609
- textStatus,
610
- jqXHRorError,
611
- options
612
- );
613
- if (options.limitConcurrentUploads &&
614
- options.limitConcurrentUploads > that._sending) {
615
- // Start the next queued upload,
616
- // that has not been aborted:
617
- var nextSlot = that._slots.shift();
618
- while (nextSlot) {
619
- if (!nextSlot.isRejected()) {
620
- nextSlot.resolve();
621
- break;
622
- }
623
- nextSlot = that._slots.shift();
624
- }
625
- }
626
- });
627
- return jqXHR;
628
- };
629
- this._beforeSend(e, options);
630
- if (this.options.sequentialUploads ||
631
- (this.options.limitConcurrentUploads &&
632
- this.options.limitConcurrentUploads <= this._sending)) {
633
- if (this.options.limitConcurrentUploads > 1) {
634
- slot = $.Deferred();
635
- this._slots.push(slot);
636
- pipe = slot.pipe(send);
637
- } else {
638
- pipe = (this._sequence = this._sequence.pipe(send, send));
639
- }
640
- // Return the piped Promise object, enhanced with an abort method,
641
- // which is delegated to the jqXHR object of the current upload,
642
- // and jqXHR callbacks mapped to the equivalent Promise methods:
643
- pipe.abort = function () {
644
- var args = [undefined, 'abort', 'abort'];
645
- if (!jqXHR) {
646
- if (slot) {
647
- slot.rejectWith(args);
648
- }
649
- return send(false, args);
650
- }
651
- return jqXHR.abort();
652
- };
653
- return this._enhancePromise(pipe);
654
- }
655
- return send();
656
- },
657
-
658
- _onAdd: function (e, data) {
659
- var that = this,
660
- result = true,
661
- options = $.extend({}, this.options, data),
662
- limit = options.limitMultiFileUploads,
663
- paramName = this._getParamName(options),
664
- paramNameSet,
665
- paramNameSlice,
666
- fileSet,
667
- i;
668
- if (!(options.singleFileUploads || limit) ||
669
- !this._isXHRUpload(options)) {
670
- fileSet = [data.files];
671
- paramNameSet = [paramName];
672
- } else if (!options.singleFileUploads && limit) {
673
- fileSet = [];
674
- paramNameSet = [];
675
- for (i = 0; i < data.files.length; i += limit) {
676
- fileSet.push(data.files.slice(i, i + limit));
677
- paramNameSlice = paramName.slice(i, i + limit);
678
- if (!paramNameSlice.length) {
679
- paramNameSlice = paramName;
680
- }
681
- paramNameSet.push(paramNameSlice);
682
- }
683
- } else {
684
- paramNameSet = paramName;
685
- }
686
- data.originalFiles = data.files;
687
- $.each(fileSet || data.files, function (index, element) {
688
- var newData = $.extend({}, data);
689
- newData.files = fileSet ? element : [element];
690
- newData.paramName = paramNameSet[index];
691
- newData.submit = function () {
692
- newData.jqXHR = this.jqXHR =
693
- (that._trigger('submit', e, this) !== false) &&
694
- that._onSend(e, this);
695
- return this.jqXHR;
696
- };
697
- return (result = that._trigger('add', e, newData));
698
- });
699
- return result;
700
- },
701
-
702
- // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
703
- _normalizeFile: function (index, file) {
704
- if (file.name === undefined && file.size === undefined) {
705
- file.name = file.fileName;
706
- file.size = file.fileSize;
707
- }
708
- },
709
-
710
- _replaceFileInput: function (input) {
711
- var inputClone = input.clone(true);
712
- $('<form></form>').append(inputClone)[0].reset();
713
- // Detaching allows to insert the fileInput on another form
714
- // without loosing the file input value:
715
- input.after(inputClone).detach();
716
- // Avoid memory leaks with the detached file input:
717
- $.cleanData(input.unbind('remove'));
718
- // Replace the original file input element in the fileInput
719
- // collection with the clone, which has been copied including
720
- // event handlers:
721
- this.options.fileInput = this.options.fileInput.map(function (i, el) {
722
- if (el === input[0]) {
723
- return inputClone[0];
724
- }
725
- return el;
726
- });
727
- // If the widget has been initialized on the file input itself,
728
- // override this.element with the file input clone:
729
- if (input[0] === this.element[0]) {
730
- this.element = inputClone;
731
- }
732
- },
733
-
734
- _onChange: function (e) {
735
- var that = e.data.fileupload,
736
- data = {
737
- files: $.each($.makeArray(e.target.files), that._normalizeFile),
738
- fileInput: $(e.target),
739
- form: $(e.target.form)
740
- };
741
- if (!data.files.length) {
742
- // If the files property is not available, the browser does not
743
- // support the File API and we add a pseudo File object with
744
- // the input value as name with path information removed:
745
- data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
746
- }
747
- if (that.options.replaceFileInput) {
748
- that._replaceFileInput(data.fileInput);
749
- }
750
- if (that._trigger('change', e, data) === false ||
751
- that._onAdd(e, data) === false) {
752
- return false;
753
- }
754
- },
755
-
756
- _onPaste: function (e) {
757
- var that = e.data.fileupload,
758
- cbd = e.originalEvent.clipboardData,
759
- items = (cbd && cbd.items) || [],
760
- data = {files: []};
761
- $.each(items, function (index, item) {
762
- var file = item.getAsFile && item.getAsFile();
763
- if (file) {
764
- data.files.push(file);
765
- }
766
- });
767
- if (that._trigger('paste', e, data) === false ||
768
- that._onAdd(e, data) === false) {
769
- return false;
770
- }
771
- },
772
-
773
- _onDrop: function (e) {
774
- var that = e.data.fileupload,
775
- dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
776
- data = {
777
- files: $.each(
778
- $.makeArray(dataTransfer && dataTransfer.files),
779
- that._normalizeFile
780
- )
781
- };
782
- if (that._trigger('drop', e, data) === false ||
783
- that._onAdd(e, data) === false) {
784
- return false;
785
- }
786
- e.preventDefault();
787
- },
788
-
789
- _onDragOver: function (e) {
790
- var that = e.data.fileupload,
791
- dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
792
- if (that._trigger('dragover', e) === false) {
793
- return false;
794
- }
795
- if (dataTransfer) {
796
- dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
797
- }
798
- e.preventDefault();
799
- },
800
-
801
- _initEventHandlers: function () {
802
- var ns = this.options.namespace;
803
- if (this._isXHRUpload(this.options)) {
804
- this.options.dropZone
805
- .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
806
- .bind('drop.' + ns, {fileupload: this}, this._onDrop)
807
- .bind('paste.' + ns, {fileupload: this}, this._onPaste);
808
- }
809
- this.options.fileInput
810
- .bind('change.' + ns, {fileupload: this}, this._onChange);
811
- },
812
-
813
- _destroyEventHandlers: function () {
814
- var ns = this.options.namespace;
815
- this.options.dropZone
816
- .unbind('dragover.' + ns, this._onDragOver)
817
- .unbind('drop.' + ns, this._onDrop)
818
- .unbind('paste.' + ns, this._onPaste);
819
- this.options.fileInput
820
- .unbind('change.' + ns, this._onChange);
821
- },
822
-
823
- _setOption: function (key, value) {
824
- var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
825
- if (refresh) {
826
- this._destroyEventHandlers();
827
- }
828
- $.Widget.prototype._setOption.call(this, key, value);
829
- if (refresh) {
830
- this._initSpecialOptions();
831
- this._initEventHandlers();
832
- }
833
- },
834
-
835
- _initSpecialOptions: function () {
836
- var options = this.options;
837
- if (options.fileInput === undefined) {
838
- options.fileInput = this.element.is('input:file') ?
839
- this.element : this.element.find('input:file');
840
- } else if (!(options.fileInput instanceof $)) {
841
- options.fileInput = $(options.fileInput);
842
- }
843
- if (!(options.dropZone instanceof $)) {
844
- options.dropZone = $(options.dropZone);
845
- }
846
- },
847
-
848
- _create: function () {
849
- var options = this.options;
850
- // Initialize options set via HTML5 data-attributes:
851
- $.extend(options, $(this.element[0].cloneNode(false)).data());
852
- options.namespace = options.namespace || this.widgetName;
853
- this._initSpecialOptions();
854
- this._slots = [];
855
- this._sequence = this._getXHRPromise(true);
856
- this._sending = this._active = this._loaded = this._total = 0;
857
- this._initEventHandlers();
858
- },
859
-
860
- destroy: function () {
861
- this._destroyEventHandlers();
862
- $.Widget.prototype.destroy.call(this);
863
- },
864
-
865
- enable: function () {
866
- $.Widget.prototype.enable.call(this);
867
- this._initEventHandlers();
868
- },
869
-
870
- disable: function () {
871
- this._destroyEventHandlers();
872
- $.Widget.prototype.disable.call(this);
873
- },
874
-
875
- // This method is exposed to the widget API and allows adding files
876
- // using the fileupload API. The data parameter accepts an object which
877
- // must have a files property and can contain additional options:
878
- // .fileupload('add', {files: filesList});
879
- add: function (data) {
880
- if (!data || this.options.disabled) {
881
- return;
882
- }
883
- data.files = $.each($.makeArray(data.files), this._normalizeFile);
884
- this._onAdd(null, data);
885
- },
886
-
887
- // This method is exposed to the widget API and allows sending files
888
- // using the fileupload API. The data parameter accepts an object which
889
- // must have a files property and can contain additional options:
890
- // .fileupload('send', {files: filesList});
891
- // The method returns a Promise object for the file upload call.
892
- send: function (data) {
893
- if (data && !this.options.disabled) {
894
- data.files = $.each($.makeArray(data.files), this._normalizeFile);
895
- if (data.files.length) {
896
- return this._onSend(null, data);
897
- }
898
- }
899
- return this._getXHRPromise(false, data && data.context);
900
- }
901
-
902
- });
903
-
904
- }));