jquery-fileupload-rails 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -4
- data/lib/jquery/fileupload/rails/version.rb +1 -1
- data/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js +18 -17
- data/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +72 -77
- data/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +179 -143
- data/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +363 -117
- metadata +3 -3
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# jQuery File Upload for Rails
|
1
|
+
# jQuery File Upload for Rails
|
2
2
|
|
3
3
|
[jQuery-File-Plugin](https://github.com/blueimp/jQuery-File-Upload) is a file upload plugin written by [Sebastian Tschan](https://github.com/blueimp). jQuery File Upload features multiple file selection, drag&drop support, progress bars and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
|
4
4
|
|
@@ -6,9 +6,9 @@ jquery-fileupload-rails is a library that integrates jQuery File Upload for Rail
|
|
6
6
|
|
7
7
|
## Plugin versions
|
8
8
|
|
9
|
-
* jQuery File Upload User Interface Plugin 6.
|
10
|
-
* jQuery File Upload Plugin 5.
|
11
|
-
* jQuery UI Widget 1.
|
9
|
+
* jQuery File Upload User Interface Plugin 6.11
|
10
|
+
* jQuery File Upload Plugin 5.19.2
|
11
|
+
* jQuery UI Widget 1.9.1+amd
|
12
12
|
|
13
13
|
## Installing Gem
|
14
14
|
|
@@ -51,6 +51,9 @@ Require the stylesheet file to app/assets/stylesheets/application.css
|
|
51
51
|
## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example)
|
52
52
|
This app uses paperclip and twitter-bootstrap-rails
|
53
53
|
|
54
|
+
You can also check out Ryan Bate's RailsCast [jQuery File Upload episode](http://railscasts.com/episodes/381-jquery-file-upload). You will
|
55
|
+
need a pro account to watch it though.
|
56
|
+
|
54
57
|
|
55
58
|
## Thanks
|
56
59
|
Thanks to [Sebastian Tschan](https://github.com/blueimp) for writing an awesome file upload plugin.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
* jQuery File Upload File Processing Plugin 1.
|
2
|
+
* jQuery File Upload File Processing Plugin 1.2
|
3
3
|
* https://github.com/blueimp/jQuery-File-Upload
|
4
4
|
*
|
5
5
|
* Copyright 2012, Sebastian Tschan
|
@@ -32,9 +32,9 @@
|
|
32
32
|
}(function ($, loadImage) {
|
33
33
|
'use strict';
|
34
34
|
|
35
|
-
// The File Upload
|
35
|
+
// The File Upload FP version extends the fileupload widget
|
36
36
|
// with file processing functionality:
|
37
|
-
$.widget('
|
37
|
+
$.widget('blueimp.fileupload', $.blueimp.fileupload, {
|
38
38
|
|
39
39
|
options: {
|
40
40
|
// The list of file processing actions:
|
@@ -70,7 +70,7 @@
|
|
70
70
|
|
71
71
|
processActions: {
|
72
72
|
// Loads the image given via data.files and data.index
|
73
|
-
// as
|
73
|
+
// as img element if the browser supports canvas.
|
74
74
|
// Accepts the options fileTypes (regular expression)
|
75
75
|
// and maxFileSize (integer) to limit the files to load:
|
76
76
|
load: function (data, options) {
|
@@ -85,28 +85,29 @@
|
|
85
85
|
options.fileTypes.test(file.type))) {
|
86
86
|
loadImage(
|
87
87
|
file,
|
88
|
-
function (
|
89
|
-
data.
|
88
|
+
function (img) {
|
89
|
+
data.img = img;
|
90
90
|
dfd.resolveWith(that, [data]);
|
91
|
-
}
|
92
|
-
{canvas: true}
|
91
|
+
}
|
93
92
|
);
|
94
93
|
} else {
|
95
94
|
dfd.rejectWith(that, [data]);
|
96
95
|
}
|
97
96
|
return dfd.promise();
|
98
97
|
},
|
99
|
-
// Resizes the image given as data.
|
100
|
-
// data.canvas with the resized image.
|
98
|
+
// Resizes the image given as data.img and updates
|
99
|
+
// data.canvas with the resized image as canvas element.
|
101
100
|
// Accepts the options maxWidth, maxHeight, minWidth and
|
102
101
|
// minHeight to scale the given image:
|
103
102
|
resize: function (data, options) {
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
103
|
+
var img = data.img,
|
104
|
+
canvas;
|
105
|
+
options = $.extend({canvas: true}, options);
|
106
|
+
if (img) {
|
107
|
+
canvas = loadImage.scale(img, options);
|
108
|
+
if (canvas.width !== img.width ||
|
109
|
+
canvas.height !== img.height) {
|
108
110
|
data.canvas = canvas;
|
109
|
-
data.processed = true;
|
110
111
|
}
|
111
112
|
}
|
112
113
|
return data;
|
@@ -115,7 +116,7 @@
|
|
115
116
|
// inplace at data.index of data.files:
|
116
117
|
save: function (data, options) {
|
117
118
|
// Do nothing if no processing has happened:
|
118
|
-
if (!data.canvas
|
119
|
+
if (!data.canvas) {
|
119
120
|
return data;
|
120
121
|
}
|
121
122
|
var that = this,
|
@@ -208,7 +209,7 @@
|
|
208
209
|
},
|
209
210
|
|
210
211
|
_create: function () {
|
211
|
-
|
212
|
+
this._super();
|
212
213
|
this._processing = 0;
|
213
214
|
this._processingQueue = $.Deferred().resolveWith(this)
|
214
215
|
.promise();
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
* jQuery File Upload User Interface Plugin 6.
|
2
|
+
* jQuery File Upload User Interface Plugin 6.11
|
3
3
|
* https://github.com/blueimp/jQuery-File-Upload
|
4
4
|
*
|
5
5
|
* Copyright 2010, Sebastian Tschan
|
@@ -10,7 +10,7 @@
|
|
10
10
|
*/
|
11
11
|
|
12
12
|
/*jslint nomen: true, unparam: true, regexp: true */
|
13
|
-
/*global define, window,
|
13
|
+
/*global define, window, URL, webkitURL, FileReader */
|
14
14
|
|
15
15
|
(function (factory) {
|
16
16
|
'use strict';
|
@@ -33,10 +33,9 @@
|
|
33
33
|
}(function ($, tmpl, loadImage) {
|
34
34
|
'use strict';
|
35
35
|
|
36
|
-
// The UI version extends the
|
37
|
-
//
|
38
|
-
|
39
|
-
$.widget('blueimpUI.fileupload', parentWidget, {
|
36
|
+
// The UI version extends the file upload widget
|
37
|
+
// and adds complete user interface interaction:
|
38
|
+
$.widget('blueimp.fileupload', $.blueimp.fileupload, {
|
40
39
|
|
41
40
|
options: {
|
42
41
|
// By default, files added to the widget are uploaded as soon
|
@@ -144,7 +143,8 @@
|
|
144
143
|
if (data.context) {
|
145
144
|
data.context.each(function (index) {
|
146
145
|
var file = ($.isArray(data.result) &&
|
147
|
-
data.result[index]) ||
|
146
|
+
data.result[index]) ||
|
147
|
+
{error: 'Empty file upload result'};
|
148
148
|
if (file.error) {
|
149
149
|
that._adjustMaxNumberOfFiles(1);
|
150
150
|
}
|
@@ -398,22 +398,22 @@
|
|
398
398
|
// maxNumberOfFiles before validation, so we check if
|
399
399
|
// maxNumberOfFiles is below 0 (instead of below 1):
|
400
400
|
if (this.options.maxNumberOfFiles < 0) {
|
401
|
-
return '
|
401
|
+
return 'Maximum number of files exceeded';
|
402
402
|
}
|
403
403
|
// Files are accepted if either the file type or the file name
|
404
404
|
// matches against the acceptFileTypes regular expression, as
|
405
405
|
// only browsers with support for the File API report the type:
|
406
406
|
if (!(this.options.acceptFileTypes.test(file.type) ||
|
407
407
|
this.options.acceptFileTypes.test(file.name))) {
|
408
|
-
return '
|
408
|
+
return 'Filetype not allowed';
|
409
409
|
}
|
410
410
|
if (this.options.maxFileSize &&
|
411
411
|
file.size > this.options.maxFileSize) {
|
412
|
-
return '
|
412
|
+
return 'File is too big';
|
413
413
|
}
|
414
414
|
if (typeof file.size === 'number' &&
|
415
415
|
file.size < this.options.minFileSize) {
|
416
|
-
return '
|
416
|
+
return 'File is too small';
|
417
417
|
}
|
418
418
|
return null;
|
419
419
|
},
|
@@ -457,7 +457,7 @@
|
|
457
457
|
that._transition(node).done(function () {
|
458
458
|
dfd.resolveWith(node);
|
459
459
|
});
|
460
|
-
if (!$.contains(document.body, node[0])) {
|
460
|
+
if (!$.contains(that.document[0].body, node[0])) {
|
461
461
|
// If the element is not part of the DOM,
|
462
462
|
// transition events are not triggered,
|
463
463
|
// so we have to resolve manually:
|
@@ -510,7 +510,7 @@
|
|
510
510
|
|
511
511
|
_startHandler: function (e) {
|
512
512
|
e.preventDefault();
|
513
|
-
var button = $(
|
513
|
+
var button = $(e.currentTarget),
|
514
514
|
template = button.closest('.template-upload'),
|
515
515
|
data = template.data('data');
|
516
516
|
if (data && data.submit && !data.jqXHR && data.submit()) {
|
@@ -520,11 +520,11 @@
|
|
520
520
|
|
521
521
|
_cancelHandler: function (e) {
|
522
522
|
e.preventDefault();
|
523
|
-
var template = $(
|
523
|
+
var template = $(e.currentTarget).closest('.template-upload'),
|
524
524
|
data = template.data('data') || {};
|
525
525
|
if (!data.jqXHR) {
|
526
526
|
data.errorThrown = 'abort';
|
527
|
-
|
527
|
+
this._trigger('fail', e, data);
|
528
528
|
} else {
|
529
529
|
data.jqXHR.abort();
|
530
530
|
}
|
@@ -532,13 +532,12 @@
|
|
532
532
|
|
533
533
|
_deleteHandler: function (e) {
|
534
534
|
e.preventDefault();
|
535
|
-
var button = $(
|
536
|
-
|
535
|
+
var button = $(e.currentTarget);
|
536
|
+
this._trigger('destroy', e, $.extend({
|
537
537
|
context: button.closest('.template-download'),
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
});
|
538
|
+
type: 'DELETE',
|
539
|
+
dataType: this.options.dataType
|
540
|
+
}, button.data()));
|
542
541
|
},
|
543
542
|
|
544
543
|
_forceReflow: function (node) {
|
@@ -569,75 +568,63 @@
|
|
569
568
|
|
570
569
|
_initButtonBarEventHandlers: function () {
|
571
570
|
var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
|
572
|
-
filesList = this.options.filesContainer
|
573
|
-
|
574
|
-
|
575
|
-
.bind('click.' + ns, function (e) {
|
571
|
+
filesList = this.options.filesContainer;
|
572
|
+
this._on(fileUploadButtonBar.find('.start'), {
|
573
|
+
click: function (e) {
|
576
574
|
e.preventDefault();
|
577
575
|
filesList.find('.start button').click();
|
578
|
-
}
|
579
|
-
|
580
|
-
|
576
|
+
}
|
577
|
+
});
|
578
|
+
this._on(fileUploadButtonBar.find('.cancel'), {
|
579
|
+
click: function (e) {
|
581
580
|
e.preventDefault();
|
582
581
|
filesList.find('.cancel button').click();
|
583
|
-
}
|
584
|
-
|
585
|
-
|
582
|
+
}
|
583
|
+
});
|
584
|
+
this._on(fileUploadButtonBar.find('.delete'), {
|
585
|
+
click: function (e) {
|
586
586
|
e.preventDefault();
|
587
587
|
filesList.find('.delete input:checked')
|
588
588
|
.siblings('button').click();
|
589
589
|
fileUploadButtonBar.find('.toggle')
|
590
590
|
.prop('checked', false);
|
591
|
-
}
|
592
|
-
|
593
|
-
|
591
|
+
}
|
592
|
+
});
|
593
|
+
this._on(fileUploadButtonBar.find('.toggle'), {
|
594
|
+
change: function (e) {
|
594
595
|
filesList.find('.delete input').prop(
|
595
596
|
'checked',
|
596
|
-
$(
|
597
|
+
$(e.currentTarget).is(':checked')
|
597
598
|
);
|
598
|
-
}
|
599
|
+
}
|
600
|
+
});
|
599
601
|
},
|
600
602
|
|
601
603
|
_destroyButtonBarEventHandlers: function () {
|
602
|
-
this.
|
603
|
-
.
|
604
|
-
|
605
|
-
|
604
|
+
this._off(
|
605
|
+
this.element.find('.fileupload-buttonbar button'),
|
606
|
+
'click'
|
607
|
+
);
|
608
|
+
this._off(
|
609
|
+
this.element.find('.fileupload-buttonbar .toggle'),
|
610
|
+
'change.'
|
611
|
+
);
|
606
612
|
},
|
607
613
|
|
608
614
|
_initEventHandlers: function () {
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
.
|
613
|
-
|
614
|
-
|
615
|
-
eventData,
|
616
|
-
this._startHandler
|
617
|
-
)
|
618
|
-
.delegate(
|
619
|
-
'.cancel button',
|
620
|
-
'click.' + this.options.namespace,
|
621
|
-
eventData,
|
622
|
-
this._cancelHandler
|
623
|
-
)
|
624
|
-
.delegate(
|
625
|
-
'.delete button',
|
626
|
-
'click.' + this.options.namespace,
|
627
|
-
eventData,
|
628
|
-
this._deleteHandler
|
629
|
-
);
|
615
|
+
this._super();
|
616
|
+
this._on(this.options.filesContainer, {
|
617
|
+
'click .start button': this._startHandler,
|
618
|
+
'click .cancel button': this._cancelHandler,
|
619
|
+
'click .delete button': this._deleteHandler
|
620
|
+
});
|
630
621
|
this._initButtonBarEventHandlers();
|
631
622
|
},
|
632
623
|
|
633
624
|
_destroyEventHandlers: function () {
|
634
|
-
var options = this.options;
|
635
625
|
this._destroyButtonBarEventHandlers();
|
636
|
-
options.filesContainer
|
637
|
-
|
638
|
-
.undelegate('.cancel button', 'click.' + options.namespace)
|
639
|
-
.undelegate('.delete button', 'click.' + options.namespace);
|
640
|
-
parentWidget.prototype._destroyEventHandlers.call(this);
|
626
|
+
this._off(this.options.filesContainer, 'click');
|
627
|
+
this._super();
|
641
628
|
},
|
642
629
|
|
643
630
|
_enableFileInputButton: function () {
|
@@ -654,7 +641,7 @@
|
|
654
641
|
|
655
642
|
_initTemplates: function () {
|
656
643
|
var options = this.options;
|
657
|
-
options.templatesContainer = document.createElement(
|
644
|
+
options.templatesContainer = this.document[0].createElement(
|
658
645
|
options.filesContainer.prop('nodeName')
|
659
646
|
);
|
660
647
|
if (tmpl) {
|
@@ -698,20 +685,20 @@
|
|
698
685
|
},
|
699
686
|
|
700
687
|
_initSpecialOptions: function () {
|
701
|
-
|
688
|
+
this._super();
|
702
689
|
this._initFilesContainer();
|
703
690
|
this._initTemplates();
|
704
691
|
this._initRegExpOptions();
|
705
692
|
},
|
706
693
|
|
707
694
|
_create: function () {
|
708
|
-
|
695
|
+
this._super();
|
709
696
|
this._refreshOptionsList.push(
|
710
697
|
'filesContainer',
|
711
698
|
'uploadTemplateId',
|
712
699
|
'downloadTemplateId'
|
713
700
|
);
|
714
|
-
if (
|
701
|
+
if (!this._processingQueue) {
|
715
702
|
this._processingQueue = $.Deferred().resolveWith(this).promise();
|
716
703
|
this.process = function () {
|
717
704
|
return this._processingQueue;
|
@@ -720,15 +707,23 @@
|
|
720
707
|
},
|
721
708
|
|
722
709
|
enable: function () {
|
723
|
-
|
724
|
-
this.
|
725
|
-
|
710
|
+
var wasDisabled = false;
|
711
|
+
if (this.options.disabled) {
|
712
|
+
wasDisabled = true;
|
713
|
+
}
|
714
|
+
this._super();
|
715
|
+
if (wasDisabled) {
|
716
|
+
this.element.find('input, button').prop('disabled', false);
|
717
|
+
this._enableFileInputButton();
|
718
|
+
}
|
726
719
|
},
|
727
720
|
|
728
721
|
disable: function () {
|
729
|
-
this.
|
730
|
-
|
731
|
-
|
722
|
+
if (!this.options.disabled) {
|
723
|
+
this.element.find('input, button').prop('disabled', true);
|
724
|
+
this._disableFileInputButton();
|
725
|
+
}
|
726
|
+
this._super();
|
732
727
|
}
|
733
728
|
|
734
729
|
});
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
* jQuery File Upload Plugin 5.
|
2
|
+
* jQuery File Upload Plugin 5.19.2
|
3
3
|
* https://github.com/blueimp/jQuery-File-Upload
|
4
4
|
*
|
5
5
|
* Copyright 2010, Sebastian Tschan
|
@@ -44,17 +44,16 @@
|
|
44
44
|
$.widget('blueimp.fileupload', {
|
45
45
|
|
46
46
|
options: {
|
47
|
-
// The
|
48
|
-
//
|
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:
|
47
|
+
// The drop target element(s), by the default the complete document.
|
48
|
+
// Set to null to disable drag & drop support:
|
53
49
|
dropZone: $(document),
|
54
|
-
// The
|
50
|
+
// The paste target element(s), by the default the complete document.
|
51
|
+
// Set to null to disable paste support:
|
52
|
+
pasteZone: $(document),
|
53
|
+
// The file input field(s), that are listened to for change events.
|
55
54
|
// If undefined, it is set to the file input fields inside
|
56
55
|
// of the widget element on plugin initialization.
|
57
|
-
// Set to null
|
56
|
+
// Set to null to disable the change listener.
|
58
57
|
fileInput: undefined,
|
59
58
|
// By default, the file input field is replaced with a clone after
|
60
59
|
// each input field change event. This is required for iframe transport
|
@@ -159,13 +158,13 @@
|
|
159
158
|
// start: function (e) {}, // .bind('fileuploadstart', func);
|
160
159
|
// Callback for uploads stop, equivalent to the global ajaxStop event:
|
161
160
|
// stop: function (e) {}, // .bind('fileuploadstop', func);
|
162
|
-
// Callback for change events of the fileInput
|
161
|
+
// Callback for change events of the fileInput(s):
|
163
162
|
// change: function (e, data) {}, // .bind('fileuploadchange', func);
|
164
|
-
// Callback for paste events to the
|
163
|
+
// Callback for paste events to the pasteZone(s):
|
165
164
|
// paste: function (e, data) {}, // .bind('fileuploadpaste', func);
|
166
|
-
// Callback for drop events of the dropZone
|
165
|
+
// Callback for drop events of the dropZone(s):
|
167
166
|
// drop: function (e, data) {}, // .bind('fileuploaddrop', func);
|
168
|
-
// Callback for dragover events of the dropZone
|
167
|
+
// Callback for dragover events of the dropZone(s):
|
169
168
|
// dragover: function (e) {}, // .bind('fileuploaddragover', func);
|
170
169
|
|
171
170
|
// The plugin options are used as settings object for the ajax calls.
|
@@ -177,9 +176,9 @@
|
|
177
176
|
|
178
177
|
// A list of options that require a refresh after assigning a new value:
|
179
178
|
_refreshOptionsList: [
|
180
|
-
'namespace',
|
181
|
-
'dropZone',
|
182
179
|
'fileInput',
|
180
|
+
'dropZone',
|
181
|
+
'pasteZone',
|
183
182
|
'multipart',
|
184
183
|
'forceIframeTransport'
|
185
184
|
],
|
@@ -301,29 +300,16 @@
|
|
301
300
|
// Ignore non-multipart setting if not supported:
|
302
301
|
multipart = options.multipart || !$.support.xhrFileUpload,
|
303
302
|
paramName = options.paramName[0];
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
// so we transmit this data as part of the HTTP headers.
|
308
|
-
// For cross domain requests, these headers must be allowed
|
309
|
-
// via Access-Control-Allow-Headers or removed using
|
310
|
-
// the beforeSend callback:
|
311
|
-
options.headers = $.extend(options.headers, {
|
312
|
-
'X-File-Name': file.name,
|
313
|
-
'X-File-Type': file.type,
|
314
|
-
'X-File-Size': file.size
|
315
|
-
});
|
316
|
-
if (!options.blob) {
|
317
|
-
// Non-chunked non-multipart upload:
|
318
|
-
options.contentType = file.type;
|
319
|
-
options.data = file;
|
320
|
-
} else if (!multipart) {
|
321
|
-
// Chunked non-multipart upload:
|
322
|
-
options.contentType = 'application/octet-stream';
|
323
|
-
options.data = options.blob;
|
324
|
-
}
|
303
|
+
options.headers = options.headers || {};
|
304
|
+
if (options.contentRange) {
|
305
|
+
options.headers['Content-Range'] = options.contentRange;
|
325
306
|
}
|
326
|
-
if (multipart
|
307
|
+
if (!multipart) {
|
308
|
+
options.headers['Content-Disposition'] = 'attachment; filename="' +
|
309
|
+
encodeURI(file.name) + '"';
|
310
|
+
options.contentType = file.type;
|
311
|
+
options.data = options.blob || file;
|
312
|
+
} else if ($.support.xhrFormDataFileUpload) {
|
327
313
|
if (options.postMessage) {
|
328
314
|
// window.postMessage does not allow sending FormData
|
329
315
|
// objects, so we just add the File/Blob objects to
|
@@ -353,6 +339,9 @@
|
|
353
339
|
});
|
354
340
|
}
|
355
341
|
if (options.blob) {
|
342
|
+
options.headers['Content-Disposition'] = 'attachment; filename="' +
|
343
|
+
encodeURI(file.name) + '"';
|
344
|
+
options.headers['Content-Description'] = encodeURI(file.type);
|
356
345
|
formData.append(paramName, options.blob, file.name);
|
357
346
|
} else {
|
358
347
|
$.each(options.files, function (index, file) {
|
@@ -436,6 +425,11 @@
|
|
436
425
|
// associated form, if available:
|
437
426
|
if (!options.form || !options.form.length) {
|
438
427
|
options.form = $(options.fileInput.prop('form'));
|
428
|
+
// If the given file input doesn't have an associated form,
|
429
|
+
// use the default widget file input's form:
|
430
|
+
if (!options.form.length) {
|
431
|
+
options.form = $(this.options.fileInput.prop('form'));
|
432
|
+
}
|
439
433
|
}
|
440
434
|
options.paramName = this._getParamName(options);
|
441
435
|
if (!options.url) {
|
@@ -483,6 +477,16 @@
|
|
483
477
|
return this._enhancePromise(promise);
|
484
478
|
},
|
485
479
|
|
480
|
+
// Parses the Range header from the server response
|
481
|
+
// and returns the uploaded bytes:
|
482
|
+
_getUploadedBytes: function (jqXHR) {
|
483
|
+
var range = jqXHR.getResponseHeader('Range'),
|
484
|
+
parts = range && range.split('-'),
|
485
|
+
upperBytesPos = parts && parts.length > 1 &&
|
486
|
+
parseInt(parts[1], 10);
|
487
|
+
return upperBytesPos && upperBytesPos + 1;
|
488
|
+
},
|
489
|
+
|
486
490
|
// Uploads a file in multiple, sequential requests
|
487
491
|
// by splitting the file up in multiple blob chunks.
|
488
492
|
// If the second parameter is true, only tests if the file
|
@@ -494,13 +498,11 @@
|
|
494
498
|
fs = file.size,
|
495
499
|
ub = options.uploadedBytes = options.uploadedBytes || 0,
|
496
500
|
mcs = options.maxChunkSize || fs,
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
upload,
|
501
|
-
n,
|
501
|
+
slice = file.slice || file.webkitSlice || file.mozSlice,
|
502
|
+
dfd = $.Deferred(),
|
503
|
+
promise = dfd.promise(),
|
502
504
|
jqXHR,
|
503
|
-
|
505
|
+
upload;
|
504
506
|
if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
|
505
507
|
options.data) {
|
506
508
|
return false;
|
@@ -509,66 +511,70 @@
|
|
509
511
|
return true;
|
510
512
|
}
|
511
513
|
if (ub >= fs) {
|
512
|
-
file.error = '
|
514
|
+
file.error = 'Uploaded bytes exceed file size';
|
513
515
|
return this._getXHRPromise(
|
514
516
|
false,
|
515
517
|
options.context,
|
516
518
|
[null, 'error', file.error]
|
517
519
|
);
|
518
520
|
}
|
519
|
-
//
|
520
|
-
// calculated via filesize, uploaded bytes and max chunk size:
|
521
|
-
n = Math.ceil((fs - ub) / mcs);
|
522
|
-
// The chunk upload method accepting the chunk number as parameter:
|
521
|
+
// The chunk upload method:
|
523
522
|
upload = function (i) {
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
523
|
+
// Clone the options object for each chunk upload:
|
524
|
+
var o = $.extend({}, options);
|
525
|
+
o.blob = slice.call(
|
526
|
+
file,
|
527
|
+
ub,
|
528
|
+
ub + mcs
|
529
|
+
);
|
530
|
+
// Store the current chunk size, as the blob itself
|
531
|
+
// will be dereferenced after data processing:
|
532
|
+
o.chunkSize = o.blob.size;
|
533
|
+
// Expose the chunk bytes position range:
|
534
|
+
o.contentRange = 'bytes ' + ub + '-' +
|
535
|
+
(ub + o.chunkSize - 1) + '/' + fs;
|
536
|
+
// Process the upload data (the blob and potential form data):
|
537
|
+
that._initXHRData(o);
|
538
|
+
// Add progress listeners for this chunk upload:
|
539
|
+
that._initProgressListener(o);
|
540
|
+
jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
|
541
|
+
.done(function (result, textStatus, jqXHR) {
|
542
|
+
ub = that._getUploadedBytes(jqXHR) ||
|
543
|
+
(ub + o.chunkSize);
|
544
|
+
// Create a progress event if upload is done and
|
545
|
+
// no progress event has been invoked for this chunk:
|
546
|
+
if (!o.loaded) {
|
547
|
+
that._onProgress($.Event('progress', {
|
548
|
+
lengthComputable: true,
|
549
|
+
loaded: ub - o.uploadedBytes,
|
550
|
+
total: ub - o.uploadedBytes
|
551
|
+
}), o);
|
552
|
+
}
|
553
|
+
options.uploadedBytes = o.uploadedBytes = ub;
|
554
|
+
if (ub < fs) {
|
555
|
+
// File upload not yet complete,
|
556
|
+
// continue with the next chunk:
|
557
|
+
upload();
|
558
|
+
} else {
|
559
|
+
dfd.resolveWith(
|
560
|
+
o.context,
|
561
|
+
[result, textStatus, jqXHR]
|
562
|
+
);
|
563
|
+
}
|
564
|
+
})
|
565
|
+
.fail(function (jqXHR, textStatus, errorThrown) {
|
566
|
+
dfd.rejectWith(
|
567
|
+
o.context,
|
568
|
+
[jqXHR, textStatus, errorThrown]
|
569
|
+
);
|
570
|
+
});
|
563
571
|
};
|
564
|
-
|
565
|
-
|
566
|
-
// and jqXHR callbacks mapped to the equivalent Promise methods:
|
567
|
-
pipe = upload(n);
|
568
|
-
pipe.abort = function () {
|
572
|
+
this._enhancePromise(promise);
|
573
|
+
promise.abort = function () {
|
569
574
|
return jqXHR.abort();
|
570
575
|
};
|
571
|
-
|
576
|
+
upload();
|
577
|
+
return promise;
|
572
578
|
},
|
573
579
|
|
574
580
|
_beforeSend: function (e, data) {
|
@@ -766,7 +772,7 @@
|
|
766
772
|
// Avoid memory leaks with the detached file input:
|
767
773
|
$.cleanData(input.unbind('remove'));
|
768
774
|
// Replace the original file input element in the fileInput
|
769
|
-
//
|
775
|
+
// elements set with the clone, which has been copied including
|
770
776
|
// event handlers:
|
771
777
|
this.options.fileInput = this.options.fileInput.map(function (i, el) {
|
772
778
|
if (el === input[0]) {
|
@@ -784,16 +790,29 @@
|
|
784
790
|
_handleFileTreeEntry: function (entry, path) {
|
785
791
|
var that = this,
|
786
792
|
dfd = $.Deferred(),
|
787
|
-
errorHandler = function () {
|
788
|
-
|
793
|
+
errorHandler = function (e) {
|
794
|
+
if (e && !e.entry) {
|
795
|
+
e.entry = entry;
|
796
|
+
}
|
797
|
+
// Since $.when returns immediately if one
|
798
|
+
// Deferred is rejected, we use resolve instead.
|
799
|
+
// This allows valid files and invalid items
|
800
|
+
// to be returned together in one set:
|
801
|
+
dfd.resolve([e]);
|
789
802
|
},
|
790
803
|
dirReader;
|
791
804
|
path = path || '';
|
792
805
|
if (entry.isFile) {
|
793
|
-
entry.
|
794
|
-
|
795
|
-
|
796
|
-
|
806
|
+
if (entry._file) {
|
807
|
+
// Workaround for Chrome bug #149735
|
808
|
+
entry._file.relativePath = path;
|
809
|
+
dfd.resolve(entry._file);
|
810
|
+
} else {
|
811
|
+
entry.file(function (file) {
|
812
|
+
file.relativePath = path;
|
813
|
+
dfd.resolve(file);
|
814
|
+
}, errorHandler);
|
815
|
+
}
|
797
816
|
} else if (entry.isDirectory) {
|
798
817
|
dirReader = entry.createReader();
|
799
818
|
dirReader.readEntries(function (entries) {
|
@@ -805,7 +824,9 @@
|
|
805
824
|
}).fail(errorHandler);
|
806
825
|
}, errorHandler);
|
807
826
|
} else {
|
808
|
-
|
827
|
+
// Return an empy list for file system items
|
828
|
+
// other than files or directories:
|
829
|
+
dfd.resolve([]);
|
809
830
|
}
|
810
831
|
return dfd.promise();
|
811
832
|
},
|
@@ -832,8 +853,14 @@
|
|
832
853
|
items[0].getAsEntry)) {
|
833
854
|
return this._handleFileTreeEntries(
|
834
855
|
$.map(items, function (item) {
|
856
|
+
var entry;
|
835
857
|
if (item.webkitGetAsEntry) {
|
836
|
-
|
858
|
+
entry = item.webkitGetAsEntry();
|
859
|
+
if (entry) {
|
860
|
+
// Workaround for Chrome bug #149735:
|
861
|
+
entry._file = item.getAsFile();
|
862
|
+
}
|
863
|
+
return entry;
|
837
864
|
}
|
838
865
|
return item.getAsEntry();
|
839
866
|
})
|
@@ -844,7 +871,7 @@
|
|
844
871
|
).promise();
|
845
872
|
},
|
846
873
|
|
847
|
-
|
874
|
+
_getSingleFileInputFiles: function (fileInput) {
|
848
875
|
fileInput = $(fileInput);
|
849
876
|
var entries = fileInput.prop('webkitEntries') ||
|
850
877
|
fileInput.prop('entries'),
|
@@ -857,23 +884,44 @@
|
|
857
884
|
if (!files.length) {
|
858
885
|
value = fileInput.prop('value');
|
859
886
|
if (!value) {
|
860
|
-
return $.Deferred().
|
887
|
+
return $.Deferred().resolve([]).promise();
|
861
888
|
}
|
862
889
|
// If the files property is not available, the browser does not
|
863
890
|
// support the File API and we add a pseudo File object with
|
864
891
|
// the input value as name with path information removed:
|
865
892
|
files = [{name: value.replace(/^.*\\/, '')}];
|
893
|
+
} else if (files[0].name === undefined && files[0].fileName) {
|
894
|
+
// File normalization for Safari 4 and Firefox 3:
|
895
|
+
$.each(files, function (index, file) {
|
896
|
+
file.name = file.fileName;
|
897
|
+
file.size = file.fileSize;
|
898
|
+
});
|
866
899
|
}
|
867
900
|
return $.Deferred().resolve(files).promise();
|
868
901
|
},
|
869
902
|
|
903
|
+
_getFileInputFiles: function (fileInput) {
|
904
|
+
if (!(fileInput instanceof $) || fileInput.length === 1) {
|
905
|
+
return this._getSingleFileInputFiles(fileInput);
|
906
|
+
}
|
907
|
+
return $.when.apply(
|
908
|
+
$,
|
909
|
+
$.map(fileInput, this._getSingleFileInputFiles)
|
910
|
+
).pipe(function () {
|
911
|
+
return Array.prototype.concat.apply(
|
912
|
+
[],
|
913
|
+
arguments
|
914
|
+
);
|
915
|
+
});
|
916
|
+
},
|
917
|
+
|
870
918
|
_onChange: function (e) {
|
871
|
-
var that =
|
919
|
+
var that = this,
|
872
920
|
data = {
|
873
921
|
fileInput: $(e.target),
|
874
922
|
form: $(e.target.form)
|
875
923
|
};
|
876
|
-
|
924
|
+
this._getFileInputFiles(data.fileInput).always(function (files) {
|
877
925
|
data.files = files;
|
878
926
|
if (that.options.replaceFileInput) {
|
879
927
|
that._replaceFileInput(data.fileInput);
|
@@ -885,8 +933,7 @@
|
|
885
933
|
},
|
886
934
|
|
887
935
|
_onPaste: function (e) {
|
888
|
-
var
|
889
|
-
cbd = e.originalEvent.clipboardData,
|
936
|
+
var cbd = e.originalEvent.clipboardData,
|
890
937
|
items = (cbd && cbd.items) || [],
|
891
938
|
data = {files: []};
|
892
939
|
$.each(items, function (index, item) {
|
@@ -895,18 +942,18 @@
|
|
895
942
|
data.files.push(file);
|
896
943
|
}
|
897
944
|
});
|
898
|
-
if (
|
899
|
-
|
945
|
+
if (this._trigger('paste', e, data) === false ||
|
946
|
+
this._onAdd(e, data) === false) {
|
900
947
|
return false;
|
901
948
|
}
|
902
949
|
},
|
903
950
|
|
904
951
|
_onDrop: function (e) {
|
905
952
|
e.preventDefault();
|
906
|
-
var that =
|
953
|
+
var that = this,
|
907
954
|
dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
|
908
955
|
data = {};
|
909
|
-
|
956
|
+
this._getDroppedFiles(dataTransfer).always(function (files) {
|
910
957
|
data.files = files;
|
911
958
|
if (that._trigger('drop', e, data) !== false) {
|
912
959
|
that._onAdd(e, data);
|
@@ -915,9 +962,8 @@
|
|
915
962
|
},
|
916
963
|
|
917
964
|
_onDragOver: function (e) {
|
918
|
-
var
|
919
|
-
|
920
|
-
if (that._trigger('dragover', e) === false) {
|
965
|
+
var dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
|
966
|
+
if (this._trigger('dragover', e) === false) {
|
921
967
|
return false;
|
922
968
|
}
|
923
969
|
if (dataTransfer) {
|
@@ -927,25 +973,24 @@
|
|
927
973
|
},
|
928
974
|
|
929
975
|
_initEventHandlers: function () {
|
930
|
-
var ns = this.options.namespace;
|
931
976
|
if (this._isXHRUpload(this.options)) {
|
932
|
-
this.options.dropZone
|
933
|
-
|
934
|
-
|
935
|
-
|
977
|
+
this._on(this.options.dropZone, {
|
978
|
+
dragover: this._onDragOver,
|
979
|
+
drop: this._onDrop
|
980
|
+
});
|
981
|
+
this._on(this.options.pasteZone, {
|
982
|
+
paste: this._onPaste
|
983
|
+
});
|
936
984
|
}
|
937
|
-
this.options.fileInput
|
938
|
-
|
985
|
+
this._on(this.options.fileInput, {
|
986
|
+
change: this._onChange
|
987
|
+
});
|
939
988
|
},
|
940
989
|
|
941
990
|
_destroyEventHandlers: function () {
|
942
|
-
|
943
|
-
this.options.
|
944
|
-
|
945
|
-
.unbind('drop.' + ns, this._onDrop)
|
946
|
-
.unbind('paste.' + ns, this._onPaste);
|
947
|
-
this.options.fileInput
|
948
|
-
.unbind('change.' + ns, this._onChange);
|
991
|
+
this._off(this.options.dropZone, 'dragover drop');
|
992
|
+
this._off(this.options.pasteZone, 'paste');
|
993
|
+
this._off(this.options.fileInput, 'change');
|
949
994
|
},
|
950
995
|
|
951
996
|
_setOption: function (key, value) {
|
@@ -953,7 +998,7 @@
|
|
953
998
|
if (refresh) {
|
954
999
|
this._destroyEventHandlers();
|
955
1000
|
}
|
956
|
-
|
1001
|
+
this._super(key, value);
|
957
1002
|
if (refresh) {
|
958
1003
|
this._initSpecialOptions();
|
959
1004
|
this._initEventHandlers();
|
@@ -971,13 +1016,15 @@
|
|
971
1016
|
if (!(options.dropZone instanceof $)) {
|
972
1017
|
options.dropZone = $(options.dropZone);
|
973
1018
|
}
|
1019
|
+
if (!(options.pasteZone instanceof $)) {
|
1020
|
+
options.pasteZone = $(options.pasteZone);
|
1021
|
+
}
|
974
1022
|
},
|
975
1023
|
|
976
1024
|
_create: function () {
|
977
1025
|
var options = this.options;
|
978
1026
|
// Initialize options set via HTML5 data-attributes:
|
979
1027
|
$.extend(options, $(this.element[0].cloneNode(false)).data());
|
980
|
-
options.namespace = options.namespace || this.widgetName;
|
981
1028
|
this._initSpecialOptions();
|
982
1029
|
this._slots = [];
|
983
1030
|
this._sequence = this._getXHRPromise(true);
|
@@ -985,19 +1032,8 @@
|
|
985
1032
|
this._initEventHandlers();
|
986
1033
|
},
|
987
1034
|
|
988
|
-
|
989
|
-
this._destroyEventHandlers();
|
990
|
-
$.Widget.prototype.destroy.call(this);
|
991
|
-
},
|
992
|
-
|
993
|
-
enable: function () {
|
994
|
-
$.Widget.prototype.enable.call(this);
|
995
|
-
this._initEventHandlers();
|
996
|
-
},
|
997
|
-
|
998
|
-
disable: function () {
|
1035
|
+
_destroy: function () {
|
999
1036
|
this._destroyEventHandlers();
|
1000
|
-
$.Widget.prototype.disable.call(this);
|
1001
1037
|
},
|
1002
1038
|
|
1003
1039
|
// This method is exposed to the widget API and allows adding files
|