cucumber-cinema 0.6.2 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,711 @@
1
+ /*
2
+ * jQuery File Upload Plugin 5.0.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://creativecommons.org/licenses/MIT/
10
+ */
11
+
12
+ /*jslint nomen: false, unparam: true, regexp: false */
13
+ /*global document, XMLHttpRequestUpload, Blob, File, FormData, location, jQuery */
14
+
15
+ (function ($) {
16
+ 'use strict';
17
+
18
+ // The fileupload widget listens for change events on file input fields
19
+ // defined via fileInput setting and drop events of the given dropZone.
20
+ // In addition to the default jQuery Widget methods, the fileupload widget
21
+ // exposes the "add" and "send" methods, to add or directly send files
22
+ // using the fileupload API.
23
+ // By default, files added via file input selection, drag & drop or
24
+ // "add" method are uploaded immediately, but it is possible to override
25
+ // the "add" callback option to queue file uploads.
26
+ $.widget('blueimp.fileupload', {
27
+
28
+ options: {
29
+ // The namespace used for event handler binding on the dropZone and
30
+ // fileInput collections.
31
+ // If not set, the name of the widget ("fileupload") is used.
32
+ namespace: undefined,
33
+ // The drop target collection, by the default the complete document.
34
+ // Set to null or an empty collection to disable drag & drop support:
35
+ dropZone: $(document),
36
+ // The file input field collection, that is listened for change events.
37
+ // If undefined, it is set to the file input fields inside
38
+ // of the widget element on plugin initialization.
39
+ // Set to null or an empty collection to disable the change listener.
40
+ fileInput: undefined,
41
+ // By default, the file input field is replaced with a clone after
42
+ // each input field change event. This is required for iframe transport
43
+ // queues and allows change events to be fired for the same file
44
+ // selection, but can be disabled by setting the following option to false:
45
+ replaceFileInput: true,
46
+ // The parameter name for the file form data (the request argument name).
47
+ // If undefined or empty, the name property of the file input field is
48
+ // used, or "files[]" if the file input name property is also empty:
49
+ paramName: undefined,
50
+ // By default, each file of a selection is uploaded using an individual
51
+ // request for XHR type uploads. Set to false to upload file
52
+ // selections in one request each:
53
+ singleFileUploads: true,
54
+ // Set the following option to true to issue all file upload requests
55
+ // in a sequential order:
56
+ sequentialUploads: false,
57
+ // Set the following option to true to force iframe transport uploads:
58
+ forceIframeTransport: false,
59
+ // By default, XHR file uploads are sent as multipart/form-data.
60
+ // The iframe transport is always using multipart/form-data.
61
+ // Set to false to enable non-multipart XHR uploads:
62
+ multipart: true,
63
+ // To upload large files in smaller chunks, set the following option
64
+ // to a preferred maximum chunk size. If set to 0, null or undefined,
65
+ // or the browser does not support the required Blob API, files will
66
+ // be uploaded as a whole.
67
+ maxChunkSize: undefined,
68
+ // When a non-multipart upload or a chunked multipart upload has been
69
+ // aborted, this option can be used to resume the upload by setting
70
+ // it to the size of the already uploaded bytes. This option is most
71
+ // useful when modifying the options object inside of the "add" or
72
+ // "send" callbacks, as the options are cloned for each file upload.
73
+ uploadedBytes: undefined,
74
+ // By default, failed (abort or error) file uploads are removed from the
75
+ // global progress calculation. Set the following option to false to
76
+ // prevent recalculating the global progress data:
77
+ recalculateProgress: true,
78
+
79
+ // Additional form data to be sent along with the file uploads can be set
80
+ // using this option, which accepts an array of objects with name and
81
+ // value properties, a function returning such an array, a FormData
82
+ // object (for XHR file uploads), or a simple object.
83
+ // The form of the first fileInput is given as parameter to the function:
84
+ formData: function (form) {
85
+ return form.serializeArray();
86
+ },
87
+
88
+ // The add callback is invoked as soon as files are added to the fileupload
89
+ // widget (via file input selection, drag & drop or add API call).
90
+ // If the singleFileUploads option is enabled, this callback will be
91
+ // called once for each file in the selection for XHR file uplaods, else
92
+ // once for each file selection.
93
+ // The upload starts when the submit method is invoked on the data parameter.
94
+ // The data object contains a files property holding the added files
95
+ // and allows to override plugin options as well as define ajax settings.
96
+ // Listeners for this callback can also be bound the following way:
97
+ // .bind('fileuploadadd', func);
98
+ // data.submit() returns a Promise object and allows to attach additional
99
+ // handlers using jQuery's Deferred callbacks:
100
+ // data.submit().done(func).fail(func).always(func);
101
+ add: function (e, data) {
102
+ data.submit();
103
+ },
104
+
105
+ // Other callbacks:
106
+ // Callback for the start of each file upload request:
107
+ // send: function (e, data) {}, // .bind('fileuploadsend', func);
108
+ // Callback for successful uploads:
109
+ // done: function (e, data) {}, // .bind('fileuploaddone', func);
110
+ // Callback for failed (abort or error) uploads:
111
+ // fail: function (e, data) {}, // .bind('fileuploadfail', func);
112
+ // Callback for completed (success, abort or error) requests:
113
+ // always: function (e, data) {}, // .bind('fileuploadalways', func);
114
+ // Callback for upload progress events:
115
+ // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
116
+ // Callback for global upload progress events:
117
+ // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
118
+ // Callback for uploads start, equivalent to the global ajaxStart event:
119
+ // start: function (e) {}, // .bind('fileuploadstart', func);
120
+ // Callback for uploads stop, equivalent to the global ajaxStop event:
121
+ // stop: function (e) {}, // .bind('fileuploadstop', func);
122
+ // Callback for change events of the fileInput collection:
123
+ // change: function (e, data) {}, // .bind('fileuploadchange', func);
124
+ // Callback for drop events of the dropZone collection:
125
+ // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
126
+ // Callback for dragover events of the dropZone collection:
127
+ // dragover: function (e) {}, // .bind('fileuploaddragover', func);
128
+
129
+ // The plugin options are used as settings object for the ajax calls.
130
+ // The following are jQuery ajax settings required for the file uploads:
131
+ processData: false,
132
+ contentType: false,
133
+ cache: false
134
+ },
135
+
136
+ // A list of options that require a refresh after assigning a new value:
137
+ _refreshOptionsList: ['namespace', 'dropZone', 'fileInput'],
138
+
139
+ _isXHRUpload: function (options) {
140
+ var undef = 'undefined';
141
+ return !options.forceIframeTransport &&
142
+ typeof XMLHttpRequestUpload !== undef && typeof File !== undef &&
143
+ (!options.multipart || typeof FormData !== undef);
144
+ },
145
+
146
+ _getFormData: function (options) {
147
+ var formData;
148
+ if (typeof options.formData === 'function') {
149
+ return options.formData(options.form);
150
+ } else if ($.isArray(options.formData)) {
151
+ return options.formData;
152
+ } else if (options.formData) {
153
+ formData = [];
154
+ $.each(options.formData, function (name, value) {
155
+ formData.push({name: name, value: value});
156
+ });
157
+ return formData;
158
+ }
159
+ return [];
160
+ },
161
+
162
+ _getTotal: function (files) {
163
+ var total = 0;
164
+ $.each(files, function (index, file) {
165
+ total += file.size || 1;
166
+ });
167
+ return total;
168
+ },
169
+
170
+ _onProgress: function (e, data) {
171
+ if (e.lengthComputable) {
172
+ var total = data.total || this._getTotal(data.files),
173
+ loaded = parseInt(
174
+ e.loaded / e.total * (data.chunkSize || total),
175
+ 10
176
+ ) + (data.uploadedBytes || 0);
177
+ this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
178
+ data.lengthComputable = true;
179
+ data.loaded = loaded;
180
+ data.total = total;
181
+ // Trigger a custom progress event with a total data property set
182
+ // to the file size(s) of the current upload and a loaded data
183
+ // property calculated accordingly:
184
+ this._trigger('progress', e, data);
185
+ // Trigger a global progress event for all current file uploads,
186
+ // including ajax calls queued for sequential file uploads:
187
+ this._trigger('progressall', e, {
188
+ lengthComputable: true,
189
+ loaded: this._loaded,
190
+ total: this._total
191
+ });
192
+ }
193
+ },
194
+
195
+ _initProgressListener: function (options) {
196
+ var that = this,
197
+ xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
198
+ // Accesss to the native XHR object is required to add event listeners
199
+ // for the upload progress event:
200
+ if (xhr.upload && xhr.upload.addEventListener) {
201
+ xhr.upload.addEventListener('progress', function (e) {
202
+ that._onProgress(e, options);
203
+ }, false);
204
+ options.xhr = function () {
205
+ return xhr;
206
+ };
207
+ }
208
+ },
209
+
210
+ _initXHRData: function (options) {
211
+ var formData,
212
+ file = options.files[0];
213
+ if (!options.multipart || options.blob) {
214
+ // For non-multipart uploads and chunked uploads,
215
+ // file meta data is not part of the request body,
216
+ // so we transmit this data as part of the HTTP headers.
217
+ // For cross domain requests, these headers must be allowed
218
+ // via Access-Control-Allow-Headers or removed using
219
+ // the beforeSend callback:
220
+ options.headers = $.extend(options.headers, {
221
+ 'X-File-Name': file.name,
222
+ 'X-File-Type': file.type,
223
+ 'X-File-Size': file.size
224
+ });
225
+ if (!options.blob) {
226
+ // Non-chunked non-multipart upload:
227
+ options.contentType = file.type;
228
+ options.data = file;
229
+ } else if (!options.multipart) {
230
+ // Chunked non-multipart upload:
231
+ options.contentType = 'application/octet-stream';
232
+ options.data = options.blob;
233
+ }
234
+ }
235
+ if (options.multipart) {
236
+ if (options.formData instanceof FormData) {
237
+ formData = options.formData;
238
+ } else {
239
+ formData = new FormData();
240
+ $.each(this._getFormData(options), function (index, field) {
241
+ formData.append(field.name, field.value);
242
+ });
243
+ }
244
+ if (options.blob) {
245
+ formData.append(options.paramName, options.blob);
246
+ } else {
247
+ $.each(options.files, function (index, file) {
248
+ // File objects are also Blob instances.
249
+ // This check allows the tests to run with
250
+ // dummy objects:
251
+ if (file instanceof Blob) {
252
+ formData.append(options.paramName, file);
253
+ }
254
+ });
255
+ }
256
+ options.data = formData;
257
+ }
258
+ // Blob reference is not needed anymore, free memory:
259
+ options.blob = null;
260
+ },
261
+
262
+ _initIframeSettings: function (options) {
263
+ // Setting the dataType to iframe enables the iframe transport:
264
+ options.dataType = 'iframe ' + (options.dataType || '');
265
+ // The iframe transport accepts a serialized array as form data:
266
+ options.formData = this._getFormData(options);
267
+ },
268
+
269
+ _initDataSettings: function (options) {
270
+ if (this._isXHRUpload(options)) {
271
+ if (!this._chunkedUpload(options, true)) {
272
+ if (!options.data) {
273
+ this._initXHRData(options);
274
+ }
275
+ this._initProgressListener(options);
276
+ }
277
+ } else {
278
+ this._initIframeSettings(options);
279
+ }
280
+ },
281
+
282
+ _initFormSettings: function (options) {
283
+ // Retrieve missing options from the input field and the
284
+ // associated form, if available:
285
+ if (!options.form || !options.form.length) {
286
+ options.form = $(options.fileInput.prop('form'));
287
+ }
288
+ if (!options.paramName) {
289
+ options.paramName = options.fileInput.prop('name') ||
290
+ 'files[]';
291
+ }
292
+ if (!options.url) {
293
+ options.url = options.form.prop('action') || location.href;
294
+ }
295
+ // The HTTP request method must be "POST" or "PUT":
296
+ options.type = (options.type || options.form.prop('method') || '')
297
+ .toUpperCase();
298
+ if (options.type !== 'POST' && options.type !== 'PUT') {
299
+ options.type = 'POST';
300
+ }
301
+ },
302
+
303
+ _getAJAXSettings: function (data) {
304
+ var options = $.extend({}, this.options, data);
305
+ this._initFormSettings(options);
306
+ this._initDataSettings(options);
307
+ return options;
308
+ },
309
+
310
+ // Maps jqXHR callbacks to the equivalent
311
+ // methods of the given Promise object:
312
+ _enhancePromise: function (promise) {
313
+ promise.success = promise.done;
314
+ promise.error = promise.fail;
315
+ promise.complete = promise.always;
316
+ return promise;
317
+ },
318
+
319
+ // Creates and returns a Promise object enhanced with
320
+ // the jqXHR methods abort, success, error and complete:
321
+ _getXHRPromise: function (resolveOrReject, context) {
322
+ var dfd = $.Deferred(),
323
+ promise = dfd.promise();
324
+ context = context || this.options.context || promise;
325
+ if (resolveOrReject === true) {
326
+ dfd.resolveWith(context);
327
+ } else if (resolveOrReject === false) {
328
+ dfd.rejectWith(context);
329
+ }
330
+ promise.abort = dfd.promise;
331
+ return this._enhancePromise(promise);
332
+ },
333
+
334
+ // Uploads a file in multiple, sequential requests
335
+ // by splitting the file up in multiple blob chunks.
336
+ // If the second parameter is true, only tests if the file
337
+ // should be uploaded in chunks, but does not invoke any
338
+ // upload requests:
339
+ _chunkedUpload: function (options, testOnly) {
340
+ var that = this,
341
+ file = options.files[0],
342
+ fs = file.size,
343
+ ub = options.uploadedBytes = options.uploadedBytes || 0,
344
+ mcs = options.maxChunkSize || fs,
345
+ // Use the Blob methods with the slice implementation
346
+ // according to the W3C Blob API specification:
347
+ slice = file.webkitSlice || file.mozSlice || file.slice,
348
+ upload,
349
+ n,
350
+ jqXHR,
351
+ pipe;
352
+ if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
353
+ options.data) {
354
+ return false;
355
+ }
356
+ if (testOnly) {
357
+ return true;
358
+ }
359
+ if (ub >= fs) {
360
+ file.error = 'uploadedBytes';
361
+ return this._getXHRPromise(false);
362
+ }
363
+ // n is the number of blobs to upload,
364
+ // calculated via filesize, uploaded bytes and max chunk size:
365
+ n = Math.ceil((fs - ub) / mcs);
366
+ // The chunk upload method accepting the chunk number as parameter:
367
+ upload = function (i) {
368
+ if (!i) {
369
+ return that._getXHRPromise(true);
370
+ }
371
+ // Upload the blobs in sequential order:
372
+ return upload(i -= 1).pipe(function () {
373
+ // Clone the options object for each chunk upload:
374
+ var o = $.extend({}, options);
375
+ o.blob = slice.call(
376
+ file,
377
+ ub + i * mcs,
378
+ ub + (i + 1) * mcs
379
+ );
380
+ // Store the current chunk size, as the blob itself
381
+ // will be dereferenced after data processing:
382
+ o.chunkSize = o.blob.size;
383
+ // Process the upload data (the blob and potential form data):
384
+ that._initXHRData(o);
385
+ // Add progress listeners for this chunk upload:
386
+ that._initProgressListener(o);
387
+ jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
388
+ .done(function () {
389
+ // Create a progress event if upload is done and
390
+ // no progress event has been invoked for this chunk:
391
+ if (!o.loaded) {
392
+ that._onProgress($.Event('progress', {
393
+ lengthComputable: true,
394
+ loaded: o.chunkSize,
395
+ total: o.chunkSize
396
+ }), o);
397
+ }
398
+ options.uploadedBytes = o.uploadedBytes
399
+ += o.chunkSize;
400
+ });
401
+ return jqXHR;
402
+ });
403
+ };
404
+ // Return the piped Promise object, enhanced with an abort method,
405
+ // which is delegated to the jqXHR object of the current upload,
406
+ // and jqXHR callbacks mapped to the equivalent Promise methods:
407
+ pipe = upload(n);
408
+ pipe.abort = function () {
409
+ return jqXHR.abort();
410
+ };
411
+ return this._enhancePromise(pipe);
412
+ },
413
+
414
+ _beforeSend: function (e, data) {
415
+ if (this._active === 0) {
416
+ // the start callback is triggered when an upload starts
417
+ // and no other uploads are currently running,
418
+ // equivalent to the global ajaxStart event:
419
+ this._trigger('start');
420
+ }
421
+ this._active += 1;
422
+ // Initialize the global progress values:
423
+ this._loaded += data.uploadedBytes || 0;
424
+ this._total += this._getTotal(data.files);
425
+ },
426
+
427
+ _onDone: function (result, textStatus, jqXHR, options) {
428
+ if (!this._isXHRUpload(options)) {
429
+ // Create a progress event for each iframe load:
430
+ this._onProgress($.Event('progress', {
431
+ lengthComputable: true,
432
+ loaded: 1,
433
+ total: 1
434
+ }), options);
435
+ }
436
+ options.result = result;
437
+ options.textStatus = textStatus;
438
+ options.jqXHR = jqXHR;
439
+ this._trigger('done', null, options);
440
+ },
441
+
442
+ _onFail: function (jqXHR, textStatus, errorThrown, options) {
443
+ options.jqXHR = jqXHR;
444
+ options.textStatus = textStatus;
445
+ options.errorThrown = errorThrown;
446
+ this._trigger('fail', null, options);
447
+ if (options.recalculateProgress) {
448
+ // Remove the failed (error or abort) file upload from
449
+ // the global progress calculation:
450
+ this._loaded -= options.loaded || options.uploadedBytes || 0;
451
+ this._total -= options.total || this._getTotal(options.files);
452
+ }
453
+ },
454
+
455
+ _onAlways: function (result, textStatus, jqXHR, options) {
456
+ this._active -= 1;
457
+ options.result = result;
458
+ options.textStatus = textStatus;
459
+ options.jqXHR = jqXHR;
460
+ this._trigger('always', null, options);
461
+ if (this._active === 0) {
462
+ // The stop callback is triggered when all uploads have
463
+ // been completed, equivalent to the global ajaxStop event:
464
+ this._trigger('stop');
465
+ // Reset the global progress values:
466
+ this._loaded = this._total = 0;
467
+ }
468
+ },
469
+
470
+ _onSend: function (e, data) {
471
+ var that = this,
472
+ jqXHR,
473
+ pipe,
474
+ options = that._getAJAXSettings(data),
475
+ send = function () {
476
+ jqXHR = ((that._trigger('send', e, options) !== false && (
477
+ that._chunkedUpload(options) ||
478
+ $.ajax(options)
479
+ )) || that._getXHRPromise(false, options.context)
480
+ ).done(function (result, textStatus, jqXHR) {
481
+ that._onDone(result, textStatus, jqXHR, options);
482
+ }).fail(function (jqXHR, textStatus, errorThrown) {
483
+ that._onFail(jqXHR, textStatus, errorThrown, options);
484
+ }).always(function (result, textStatus, jqXHR) {
485
+ that._onAlways(result, textStatus, jqXHR, options);
486
+ });
487
+ return jqXHR;
488
+ };
489
+ this._beforeSend(e, options);
490
+ if (this.options.sequentialUploads) {
491
+ // Return the piped Promise object, enhanced with an abort method,
492
+ // which is delegated to the jqXHR object of the current upload,
493
+ // and jqXHR callbacks mapped to the equivalent Promise methods:
494
+ pipe = (this._sequence = this._sequence.pipe(send, send));
495
+ pipe.abort = function () {
496
+ return jqXHR.abort();
497
+ };
498
+ return this._enhancePromise(pipe);
499
+ }
500
+ return send();
501
+ },
502
+
503
+ _onAdd: function (e, data) {
504
+ var that = this,
505
+ result = true,
506
+ options = $.extend({}, this.options, data);
507
+ if (options.singleFileUploads && this._isXHRUpload(options)) {
508
+ $.each(data.files, function (index, file) {
509
+ var newData = $.extend({}, data, {files: [file]});
510
+ newData.submit = function () {
511
+ return that._onSend(e, newData);
512
+ };
513
+ return (result = that._trigger('add', e, newData));
514
+ });
515
+ return result;
516
+ } else if (data.files.length) {
517
+ data = $.extend({}, data);
518
+ data.submit = function () {
519
+ return that._onSend(e, data);
520
+ };
521
+ return this._trigger('add', e, data);
522
+ }
523
+ },
524
+
525
+ // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
526
+ _normalizeFile: function (index, file) {
527
+ if (file.name === undefined && file.size === undefined) {
528
+ file.name = file.fileName;
529
+ file.size = file.fileSize;
530
+ }
531
+ },
532
+
533
+ _replaceFileInput: function (input) {
534
+ var inputClone = input.clone(true);
535
+ $('<form></form>').append(inputClone)[0].reset();
536
+ // Detaching allows to insert the fileInput on another form
537
+ // without loosing the file input value:
538
+ input.after(inputClone).detach();
539
+ // Replace the original file input element in the fileInput
540
+ // collection with the clone, which has been copied including
541
+ // event handlers:
542
+ this.options.fileInput = this.options.fileInput.map(function (i, el) {
543
+ if (el === input[0]) {
544
+ return inputClone[0];
545
+ }
546
+ return el;
547
+ });
548
+ },
549
+
550
+ _onChange: function (e) {
551
+ var that = e.data.fileupload,
552
+ data = {
553
+ files: $.each($.makeArray(e.target.files), that._normalizeFile),
554
+ fileInput: $(e.target),
555
+ form: $(e.target.form)
556
+ };
557
+ if (!data.files.length) {
558
+ // If the files property is not available, the browser does not
559
+ // support the File API and we add a pseudo File object with
560
+ // the input value as name with path information removed:
561
+ data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
562
+ }
563
+ // Store the form reference as jQuery data for other event handlers,
564
+ // as the form property is not available after replacing the file input:
565
+ if (data.form.length) {
566
+ data.fileInput.data('blueimp.fileupload.form', data.form);
567
+ } else {
568
+ data.form = data.fileInput.data('blueimp.fileupload.form');
569
+ }
570
+ if (that.options.replaceFileInput) {
571
+ that._replaceFileInput(data.fileInput);
572
+ }
573
+ if (that._trigger('change', e, data) === false ||
574
+ that._onAdd(e, data) === false) {
575
+ return false;
576
+ }
577
+ },
578
+
579
+ _onDrop: function (e) {
580
+ var that = e.data.fileupload,
581
+ dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
582
+ data = {
583
+ files: $.each(
584
+ $.makeArray(dataTransfer && dataTransfer.files),
585
+ that._normalizeFile
586
+ )
587
+ };
588
+ if (that._trigger('drop', e, data) === false ||
589
+ that._onAdd(e, data) === false) {
590
+ return false;
591
+ }
592
+ e.preventDefault();
593
+ },
594
+
595
+ _onDragOver: function (e) {
596
+ var that = e.data.fileupload,
597
+ dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
598
+ if (that._trigger('dragover', e) === false) {
599
+ return false;
600
+ }
601
+ if (dataTransfer) {
602
+ dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
603
+ }
604
+ e.preventDefault();
605
+ },
606
+
607
+ _initEventHandlers: function () {
608
+ var ns = this.options.namespace || this.name;
609
+ this.options.dropZone
610
+ .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
611
+ .bind('drop.' + ns, {fileupload: this}, this._onDrop);
612
+ this.options.fileInput
613
+ .bind('change.' + ns, {fileupload: this}, this._onChange);
614
+ },
615
+
616
+ _destroyEventHandlers: function () {
617
+ var ns = this.options.namespace || this.name;
618
+ this.options.dropZone
619
+ .unbind('dragover.' + ns, this._onDragOver)
620
+ .unbind('drop.' + ns, this._onDrop);
621
+ this.options.fileInput
622
+ .unbind('change.' + ns, this._onChange);
623
+ },
624
+
625
+ _beforeSetOption: function (key, value) {
626
+ this._destroyEventHandlers();
627
+ },
628
+
629
+ _afterSetOption: function (key, value) {
630
+ var options = this.options;
631
+ if (!options.fileInput) {
632
+ options.fileInput = $();
633
+ }
634
+ if (!options.dropZone) {
635
+ options.dropZone = $();
636
+ }
637
+ this._initEventHandlers();
638
+ },
639
+
640
+ _setOption: function (key, value) {
641
+ var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
642
+ if (refresh) {
643
+ this._beforeSetOption(key, value);
644
+ }
645
+ $.Widget.prototype._setOption.call(this, key, value);
646
+ if (refresh) {
647
+ this._afterSetOption(key, value);
648
+ }
649
+ },
650
+
651
+ _create: function () {
652
+ var options = this.options;
653
+ if (options.fileInput === undefined) {
654
+ options.fileInput = this.element.is('input:file') ?
655
+ this.element : this.element.find('input:file');
656
+ } else if (!options.fileInput) {
657
+ options.fileInput = $();
658
+ }
659
+ if (!options.dropZone) {
660
+ options.dropZone = $();
661
+ }
662
+ this._sequence = this._getXHRPromise(true);
663
+ this._active = this._loaded = this._total = 0;
664
+ this._initEventHandlers();
665
+ },
666
+
667
+ destroy: function () {
668
+ this._destroyEventHandlers();
669
+ $.Widget.prototype.destroy.call(this);
670
+ },
671
+
672
+ enable: function () {
673
+ $.Widget.prototype.enable.call(this);
674
+ this._initEventHandlers();
675
+ },
676
+
677
+ disable: function () {
678
+ this._destroyEventHandlers();
679
+ $.Widget.prototype.disable.call(this);
680
+ },
681
+
682
+ // This method is exposed to the widget API and allows adding files
683
+ // using the fileupload API. The data parameter accepts an object which
684
+ // must have a files property and can contain additional options:
685
+ // .fileupload('add', {files: filesList});
686
+ add: function (data) {
687
+ if (!data || this.options.disabled) {
688
+ return;
689
+ }
690
+ data.files = $.each($.makeArray(data.files), this._normalizeFile);
691
+ this._onAdd(null, data);
692
+ },
693
+
694
+ // This method is exposed to the widget API and allows sending files
695
+ // using the fileupload API. The data parameter accepts an object which
696
+ // must have a files property and can contain additional options:
697
+ // .fileupload('send', {files: filesList});
698
+ // The method returns a Promise object for the file upload call.
699
+ send: function (data) {
700
+ if (data && !this.options.disabled) {
701
+ data.files = $.each($.makeArray(data.files), this._normalizeFile);
702
+ if (data.files.length) {
703
+ return this._onSend(null, data);
704
+ }
705
+ }
706
+ return this._getXHRPromise(false, data && data.context);
707
+ }
708
+
709
+ });
710
+
711
+ }(jQuery));