jquery-fileupload-rails 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|