jquery.fileupload-rails 0.1.2 → 1.0.0.alpha

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/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "jQuery-File-Upload"]
2
+ path = jQuery-File-Upload
3
+ url = git://github.com/blueimp/jQuery-File-Upload.git
data/Rakefile CHANGED
@@ -1 +1,40 @@
1
- require "bundler/gem_tasks"
1
+ require 'json'
2
+ require 'bundler/gem_tasks'
3
+
4
+ DEPENDENCY_HASH = JSON.load(File.read('dependencies.json'))
5
+ PROJECT_NAME = "jQuery-File-Upload"
6
+
7
+ task :submodule do
8
+ sh 'git submodule update --init' unless File.exist?("#{PROJECT_NAME}/README.md")
9
+ end
10
+
11
+ desc "Remove the vendor directory"
12
+ task :clean do
13
+ rm_rf 'vendor'
14
+ end
15
+
16
+ desc "Generate the JavaScript assets"
17
+ task :javascripts => :submodule do
18
+ target_dir = "vendor/assets/javascripts"
19
+ mkdir_p target_dir
20
+ Rake.rake_output_message 'Generating javascripts'
21
+ DEPENDENCY_HASH.each do |name, dep_modules|
22
+ path = "#{PROJECT_NAME}/js/#{name}.js"
23
+ File.open("#{target_dir}/#{name}.js", "w") do |out|
24
+ dep_modules.each do |mod|
25
+ out.write("//= require #{mod}\n")
26
+ end
27
+ out.write("\n") unless dep_modules.empty?
28
+ source_code = File.read(path)
29
+ out.write(source_code)
30
+ end
31
+ end
32
+ end
33
+
34
+ desc "Clean and then generate everything (default)"
35
+ task :assets => [:clean, :javascripts]
36
+
37
+ task :build => :assets
38
+
39
+ task :default => :assets
40
+
data/Readme.md CHANGED
@@ -9,6 +9,12 @@ This is a simple gem package for this plugin.
9
9
 
10
10
  ## Changelog
11
11
 
12
+ 1.0.0.alpha. Core 5.17.2, added jquery-ui-rails dependency.
13
+
14
+ Now rake task generates assets from official repo and adds dependencies automatically.
15
+ That means you can just require jquery.fileupload, no extra requires needed.
16
+ UI stuff is removed temporarily.
17
+
12
18
  0.1.2. Fixed CSS that makes SASS 3.2 raise an error on rake assets:precompile
13
19
 
14
20
  0.1.1. Core 5.11.2, UI 6.9.1, minor gemspec change.
@@ -19,8 +25,8 @@ This is a simple gem package for this plugin.
19
25
 
20
26
  ## Todo
21
27
 
22
- * Add convinient file-package with jquery.fileupload and all it's dependencies.
28
+ * Import UI js/css/images in rake task too. Need to fix relative paths to images. See [jquery-ui-rails][2].
23
29
  * Add example usage in Rails here.
24
- * Add script to automate updating the library from [the official repo][1].
25
30
 
26
31
  [1]: https://github.com/blueimp/jQuery-File-Upload
32
+ [2]: https://github.com/joliss/jquery-ui-rails
data/dependencies.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "jquery.fileupload": ["jquery.ui.widget", "jquery.iframe-transport"],
3
+ "jquery.iframe-transport": []
4
+ }
@@ -19,4 +19,5 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_dependency 'sprockets', '~> 2.0'
22
+ s.add_dependency 'jquery-ui-rails'
22
23
  end
@@ -1,5 +1,5 @@
1
1
  module JqueryFileUpload
2
2
  module Rails
3
- VERSION = "0.1.2"
3
+ VERSION = "1.0.0.alpha"
4
4
  end
5
5
  end
@@ -1,5 +1,8 @@
1
+ //= require jquery.ui.widget
2
+ //= require jquery.iframe-transport
3
+
1
4
  /*
2
- * jQuery File Upload Plugin 5.11.2
5
+ * jQuery File Upload Plugin 5.17.2
3
6
  * https://github.com/blueimp/jQuery-File-Upload
4
7
  *
5
8
  * Copyright 2010, Sebastian Tschan
@@ -44,17 +47,20 @@
44
47
  $.widget('blueimp.fileupload', {
45
48
 
46
49
  options: {
47
- // The namespace used for event handler binding on the dropZone and
48
- // fileInput collections.
50
+ // The namespace used for event handler binding on the fileInput,
51
+ // dropZone and pasteZone document nodes.
49
52
  // If not set, the name of the widget ("fileupload") is used.
50
53
  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:
54
+ // The drop target element(s), by the default the complete document.
55
+ // Set to null to disable drag & drop support:
53
56
  dropZone: $(document),
54
- // The file input field collection, that is listened for change events.
57
+ // The paste target element(s), by the default the complete document.
58
+ // Set to null to disable paste support:
59
+ pasteZone: $(document),
60
+ // The file input field(s), that are listened to for change events.
55
61
  // If undefined, it is set to the file input fields inside
56
62
  // of the widget element on plugin initialization.
57
- // Set to null or an empty collection to disable the change listener.
63
+ // Set to null to disable the change listener.
58
64
  fileInput: undefined,
59
65
  // By default, the file input field is replaced with a clone after
60
66
  // each input field change event. This is required for iframe transport
@@ -159,13 +165,13 @@
159
165
  // start: function (e) {}, // .bind('fileuploadstart', func);
160
166
  // Callback for uploads stop, equivalent to the global ajaxStop event:
161
167
  // stop: function (e) {}, // .bind('fileuploadstop', func);
162
- // Callback for change events of the fileInput collection:
168
+ // Callback for change events of the fileInput(s):
163
169
  // change: function (e, data) {}, // .bind('fileuploadchange', func);
164
- // Callback for paste events to the dropZone collection:
170
+ // Callback for paste events to the pasteZone(s):
165
171
  // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
166
- // Callback for drop events of the dropZone collection:
172
+ // Callback for drop events of the dropZone(s):
167
173
  // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
168
- // Callback for dragover events of the dropZone collection:
174
+ // Callback for dragover events of the dropZone(s):
169
175
  // dragover: function (e) {}, // .bind('fileuploaddragover', func);
170
176
 
171
177
  // The plugin options are used as settings object for the ajax calls.
@@ -178,8 +184,9 @@
178
184
  // A list of options that require a refresh after assigning a new value:
179
185
  _refreshOptionsList: [
180
186
  'namespace',
181
- 'dropZone',
182
187
  'fileInput',
188
+ 'dropZone',
189
+ 'pasteZone',
183
190
  'multipart',
184
191
  'forceIframeTransport'
185
192
  ],
@@ -436,6 +443,11 @@
436
443
  // associated form, if available:
437
444
  if (!options.form || !options.form.length) {
438
445
  options.form = $(options.fileInput.prop('form'));
446
+ // If the given file input doesn't have an associated form,
447
+ // use the default widget file input's form:
448
+ if (!options.form.length) {
449
+ options.form = $(this.options.fileInput.prop('form'));
450
+ }
439
451
  }
440
452
  options.paramName = this._getParamName(options);
441
453
  if (!options.url) {
@@ -447,6 +459,9 @@
447
459
  if (options.type !== 'POST' && options.type !== 'PUT') {
448
460
  options.type = 'POST';
449
461
  }
462
+ if (!options.formAcceptCharset) {
463
+ options.formAcceptCharset = options.form.attr('accept-charset');
464
+ }
450
465
  },
451
466
 
452
467
  _getAJAXSettings: function (data) {
@@ -530,6 +545,10 @@
530
545
  ub + i * mcs,
531
546
  ub + (i + 1) * mcs
532
547
  );
548
+ // Expose the chunk index:
549
+ o.chunkIndex = i;
550
+ // Expose the number of chunks:
551
+ o.chunksNumber = n;
533
552
  // Store the current chunk size, as the blob itself
534
553
  // will be dereferenced after data processing:
535
554
  o.chunkSize = o.blob.size;
@@ -659,9 +678,15 @@
659
678
  options.limitConcurrentUploads > that._sending) {
660
679
  // Start the next queued upload,
661
680
  // that has not been aborted:
662
- var nextSlot = that._slots.shift();
681
+ var nextSlot = that._slots.shift(),
682
+ isPending;
663
683
  while (nextSlot) {
664
- if (!nextSlot.isRejected()) {
684
+ // jQuery 1.6 doesn't provide .state(),
685
+ // while jQuery 1.8+ removed .isRejected():
686
+ isPending = nextSlot.state ?
687
+ nextSlot.state() === 'pending' :
688
+ !nextSlot.isRejected();
689
+ if (isPending) {
665
690
  nextSlot.resolve();
666
691
  break;
667
692
  }
@@ -689,7 +714,7 @@
689
714
  var args = [undefined, 'abort', 'abort'];
690
715
  if (!jqXHR) {
691
716
  if (slot) {
692
- slot.rejectWith(args);
717
+ slot.rejectWith(pipe, args);
693
718
  }
694
719
  return send(false, args);
695
720
  }
@@ -744,14 +769,6 @@
744
769
  return result;
745
770
  },
746
771
 
747
- // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
748
- _normalizeFile: function (index, file) {
749
- if (file.name === undefined && file.size === undefined) {
750
- file.name = file.fileName;
751
- file.size = file.fileSize;
752
- }
753
- },
754
-
755
772
  _replaceFileInput: function (input) {
756
773
  var inputClone = input.clone(true);
757
774
  $('<form></form>').append(inputClone)[0].reset();
@@ -761,7 +778,7 @@
761
778
  // Avoid memory leaks with the detached file input:
762
779
  $.cleanData(input.unbind('remove'));
763
780
  // Replace the original file input element in the fileInput
764
- // collection with the clone, which has been copied including
781
+ // elements set with the clone, which has been copied including
765
782
  // event handlers:
766
783
  this.options.fileInput = this.options.fileInput.map(function (i, el) {
767
784
  if (el === input[0]) {
@@ -776,26 +793,131 @@
776
793
  }
777
794
  },
778
795
 
796
+ _handleFileTreeEntry: function (entry, path) {
797
+ var that = this,
798
+ dfd = $.Deferred(),
799
+ errorHandler = function (e) {
800
+ if (e && !e.entry) {
801
+ e.entry = entry;
802
+ }
803
+ // Since $.when returns immediately if one
804
+ // Deferred is rejected, we use resolve instead.
805
+ // This allows valid files and invalid items
806
+ // to be returned together in one set:
807
+ dfd.resolve([e]);
808
+ },
809
+ dirReader;
810
+ path = path || '';
811
+ if (entry.isFile) {
812
+ entry.file(function (file) {
813
+ file.relativePath = path;
814
+ dfd.resolve(file);
815
+ }, errorHandler);
816
+ } else if (entry.isDirectory) {
817
+ dirReader = entry.createReader();
818
+ dirReader.readEntries(function (entries) {
819
+ that._handleFileTreeEntries(
820
+ entries,
821
+ path + entry.name + '/'
822
+ ).done(function (files) {
823
+ dfd.resolve(files);
824
+ }).fail(errorHandler);
825
+ }, errorHandler);
826
+ } else {
827
+ // Return an empy list for file system items
828
+ // other than files or directories:
829
+ dfd.resolve([]);
830
+ }
831
+ return dfd.promise();
832
+ },
833
+
834
+ _handleFileTreeEntries: function (entries, path) {
835
+ var that = this;
836
+ return $.when.apply(
837
+ $,
838
+ $.map(entries, function (entry) {
839
+ return that._handleFileTreeEntry(entry, path);
840
+ })
841
+ ).pipe(function () {
842
+ return Array.prototype.concat.apply(
843
+ [],
844
+ arguments
845
+ );
846
+ });
847
+ },
848
+
849
+ _getDroppedFiles: function (dataTransfer) {
850
+ dataTransfer = dataTransfer || {};
851
+ var items = dataTransfer.items;
852
+ if (items && items.length && (items[0].webkitGetAsEntry ||
853
+ items[0].getAsEntry)) {
854
+ return this._handleFileTreeEntries(
855
+ $.map(items, function (item) {
856
+ if (item.webkitGetAsEntry) {
857
+ return item.webkitGetAsEntry();
858
+ }
859
+ return item.getAsEntry();
860
+ })
861
+ );
862
+ }
863
+ return $.Deferred().resolve(
864
+ $.makeArray(dataTransfer.files)
865
+ ).promise();
866
+ },
867
+
868
+ _getSingleFileInputFiles: function (fileInput) {
869
+ fileInput = $(fileInput);
870
+ var entries = fileInput.prop('webkitEntries') ||
871
+ fileInput.prop('entries'),
872
+ files,
873
+ value;
874
+ if (entries && entries.length) {
875
+ return this._handleFileTreeEntries(entries);
876
+ }
877
+ files = $.makeArray(fileInput.prop('files'));
878
+ if (!files.length) {
879
+ value = fileInput.prop('value');
880
+ if (!value) {
881
+ return $.Deferred().resolve([]).promise();
882
+ }
883
+ // If the files property is not available, the browser does not
884
+ // support the File API and we add a pseudo File object with
885
+ // the input value as name with path information removed:
886
+ files = [{name: value.replace(/^.*\\/, '')}];
887
+ }
888
+ return $.Deferred().resolve(files).promise();
889
+ },
890
+
891
+ _getFileInputFiles: function (fileInput) {
892
+ if (!(fileInput instanceof $) || fileInput.length === 1) {
893
+ return this._getSingleFileInputFiles(fileInput);
894
+ }
895
+ return $.when.apply(
896
+ $,
897
+ $.map(fileInput, this._getSingleFileInputFiles)
898
+ ).pipe(function () {
899
+ return Array.prototype.concat.apply(
900
+ [],
901
+ arguments
902
+ );
903
+ });
904
+ },
905
+
779
906
  _onChange: function (e) {
780
907
  var that = e.data.fileupload,
781
908
  data = {
782
- files: $.each($.makeArray(e.target.files), that._normalizeFile),
783
909
  fileInput: $(e.target),
784
910
  form: $(e.target.form)
785
911
  };
786
- if (!data.files.length) {
787
- // If the files property is not available, the browser does not
788
- // support the File API and we add a pseudo File object with
789
- // the input value as name with path information removed:
790
- data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
791
- }
792
- if (that.options.replaceFileInput) {
793
- that._replaceFileInput(data.fileInput);
794
- }
795
- if (that._trigger('change', e, data) === false ||
796
- that._onAdd(e, data) === false) {
797
- return false;
798
- }
912
+ that._getFileInputFiles(data.fileInput).always(function (files) {
913
+ data.files = files;
914
+ if (that.options.replaceFileInput) {
915
+ that._replaceFileInput(data.fileInput);
916
+ }
917
+ if (that._trigger('change', e, data) !== false) {
918
+ that._onAdd(e, data);
919
+ }
920
+ });
799
921
  },
800
922
 
801
923
  _onPaste: function (e) {
@@ -816,19 +938,16 @@
816
938
  },
817
939
 
818
940
  _onDrop: function (e) {
941
+ e.preventDefault();
819
942
  var that = e.data.fileupload,
820
943
  dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
821
- data = {
822
- files: $.each(
823
- $.makeArray(dataTransfer && dataTransfer.files),
824
- that._normalizeFile
825
- )
826
- };
827
- if (that._trigger('drop', e, data) === false ||
828
- that._onAdd(e, data) === false) {
829
- return false;
830
- }
831
- e.preventDefault();
944
+ data = {};
945
+ that._getDroppedFiles(dataTransfer).always(function (files) {
946
+ data.files = files;
947
+ if (that._trigger('drop', e, data) !== false) {
948
+ that._onAdd(e, data);
949
+ }
950
+ });
832
951
  },
833
952
 
834
953
  _onDragOver: function (e) {
@@ -838,7 +957,7 @@
838
957
  return false;
839
958
  }
840
959
  if (dataTransfer) {
841
- dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
960
+ dataTransfer.dropEffect = 'copy';
842
961
  }
843
962
  e.preventDefault();
844
963
  },
@@ -848,7 +967,8 @@
848
967
  if (this._isXHRUpload(this.options)) {
849
968
  this.options.dropZone
850
969
  .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
851
- .bind('drop.' + ns, {fileupload: this}, this._onDrop)
970
+ .bind('drop.' + ns, {fileupload: this}, this._onDrop);
971
+ this.options.pasteZone
852
972
  .bind('paste.' + ns, {fileupload: this}, this._onPaste);
853
973
  }
854
974
  this.options.fileInput
@@ -859,7 +979,8 @@
859
979
  var ns = this.options.namespace;
860
980
  this.options.dropZone
861
981
  .unbind('dragover.' + ns, this._onDragOver)
862
- .unbind('drop.' + ns, this._onDrop)
982
+ .unbind('drop.' + ns, this._onDrop);
983
+ this.options.pasteZone
863
984
  .unbind('paste.' + ns, this._onPaste);
864
985
  this.options.fileInput
865
986
  .unbind('change.' + ns, this._onChange);
@@ -880,14 +1001,17 @@
880
1001
  _initSpecialOptions: function () {
881
1002
  var options = this.options;
882
1003
  if (options.fileInput === undefined) {
883
- options.fileInput = this.element.is('input:file') ?
884
- this.element : this.element.find('input:file');
1004
+ options.fileInput = this.element.is('input[type="file"]') ?
1005
+ this.element : this.element.find('input[type="file"]');
885
1006
  } else if (!(options.fileInput instanceof $)) {
886
1007
  options.fileInput = $(options.fileInput);
887
1008
  }
888
1009
  if (!(options.dropZone instanceof $)) {
889
1010
  options.dropZone = $(options.dropZone);
890
1011
  }
1012
+ if (!(options.pasteZone instanceof $)) {
1013
+ options.pasteZone = $(options.pasteZone);
1014
+ }
891
1015
  },
892
1016
 
893
1017
  _create: function () {
@@ -908,12 +1032,20 @@
908
1032
  },
909
1033
 
910
1034
  enable: function () {
1035
+ var wasDisabled = false;
1036
+ if (this.options.disabled) {
1037
+ wasDisabled = true;
1038
+ }
911
1039
  $.Widget.prototype.enable.call(this);
912
- this._initEventHandlers();
1040
+ if (wasDisabled) {
1041
+ this._initEventHandlers();
1042
+ }
913
1043
  },
914
1044
 
915
1045
  disable: function () {
916
- this._destroyEventHandlers();
1046
+ if (!this.options.disabled) {
1047
+ this._destroyEventHandlers();
1048
+ }
917
1049
  $.Widget.prototype.disable.call(this);
918
1050
  },
919
1051
 
@@ -922,21 +1054,61 @@
922
1054
  // must have a files property and can contain additional options:
923
1055
  // .fileupload('add', {files: filesList});
924
1056
  add: function (data) {
1057
+ var that = this;
925
1058
  if (!data || this.options.disabled) {
926
1059
  return;
927
1060
  }
928
- data.files = $.each($.makeArray(data.files), this._normalizeFile);
929
- this._onAdd(null, data);
1061
+ if (data.fileInput && !data.files) {
1062
+ this._getFileInputFiles(data.fileInput).always(function (files) {
1063
+ data.files = files;
1064
+ that._onAdd(null, data);
1065
+ });
1066
+ } else {
1067
+ data.files = $.makeArray(data.files);
1068
+ this._onAdd(null, data);
1069
+ }
930
1070
  },
931
1071
 
932
1072
  // This method is exposed to the widget API and allows sending files
933
1073
  // using the fileupload API. The data parameter accepts an object which
934
- // must have a files property and can contain additional options:
1074
+ // must have a files or fileInput property and can contain additional options:
935
1075
  // .fileupload('send', {files: filesList});
936
1076
  // The method returns a Promise object for the file upload call.
937
1077
  send: function (data) {
938
1078
  if (data && !this.options.disabled) {
939
- data.files = $.each($.makeArray(data.files), this._normalizeFile);
1079
+ if (data.fileInput && !data.files) {
1080
+ var that = this,
1081
+ dfd = $.Deferred(),
1082
+ promise = dfd.promise(),
1083
+ jqXHR,
1084
+ aborted;
1085
+ promise.abort = function () {
1086
+ aborted = true;
1087
+ if (jqXHR) {
1088
+ return jqXHR.abort();
1089
+ }
1090
+ dfd.reject(null, 'abort', 'abort');
1091
+ return promise;
1092
+ };
1093
+ this._getFileInputFiles(data.fileInput).always(
1094
+ function (files) {
1095
+ if (aborted) {
1096
+ return;
1097
+ }
1098
+ data.files = files;
1099
+ jqXHR = that._onSend(null, data).then(
1100
+ function (result, textStatus, jqXHR) {
1101
+ dfd.resolve(result, textStatus, jqXHR);
1102
+ },
1103
+ function (jqXHR, textStatus, errorThrown) {
1104
+ dfd.reject(jqXHR, textStatus, errorThrown);
1105
+ }
1106
+ );
1107
+ }
1108
+ );
1109
+ return this._enhancePromise(promise);
1110
+ }
1111
+ data.files = $.makeArray(data.files);
940
1112
  if (data.files.length) {
941
1113
  return this._onSend(null, data);
942
1114
  }