jquery.fileupload-rails 0.1.2 → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
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
  }