glebtv-rails-uploader 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +17 -10
  3. data/{vendor → app}/assets/images/uploader/but_del_tag2.png +0 -0
  4. data/{vendor → app}/assets/images/uploader/ico_attach.png +0 -0
  5. data/{vendor → app}/assets/images/uploader/preloader.gif +0 -0
  6. data/{vendor → app}/assets/images/uploader/progressBarFillBg.png +0 -0
  7. data/app/assets/javascripts/uploader/application.js +2 -2
  8. data/app/assets/javascripts/uploader/canvas-to-blob.js +95 -0
  9. data/app/assets/javascripts/uploader/jquery.fileupload-angular.js +348 -0
  10. data/app/assets/javascripts/uploader/jquery.fileupload-process.js +158 -0
  11. data/app/assets/javascripts/uploader/jquery.fileupload-resize.js +212 -0
  12. data/{vendor → app}/assets/javascripts/uploader/jquery.fileupload-ui.js +265 -269
  13. data/app/assets/javascripts/uploader/jquery.fileupload-validate.js +116 -0
  14. data/{vendor → app}/assets/javascripts/uploader/jquery.fileupload.js +655 -258
  15. data/{vendor → app}/assets/javascripts/uploader/jquery.iframe-transport.js +29 -9
  16. data/app/assets/javascripts/uploader/jquery.ui.widget.js +530 -0
  17. data/app/assets/javascripts/uploader/load-image.js +381 -0
  18. data/{vendor → app}/assets/javascripts/uploader/locales/en.js +0 -0
  19. data/{vendor → app}/assets/javascripts/uploader/locales/ru.js +0 -0
  20. data/{vendor → app}/assets/javascripts/uploader/locales/uk.js +0 -0
  21. data/app/assets/javascripts/uploader/rails_admin.js +26 -24
  22. data/app/assets/javascripts/uploader/tmpl.js +86 -0
  23. data/{vendor → app}/assets/stylesheets/uploader/default.css +0 -0
  24. data/{vendor → app}/assets/stylesheets/uploader/jquery.fileupload-ui.css +0 -0
  25. data/app/controllers/uploader/attachments_controller.rb +13 -10
  26. data/app/views/rails_admin/main/_form_rails_uploader.haml +9 -9
  27. data/app/views/uploader/default/_container.html.erb +1 -2
  28. data/app/views/uploader/default/_download.html.erb +1 -1
  29. data/lib/uploader/asset.rb +2 -2
  30. data/lib/uploader/engine.rb +4 -4
  31. data/lib/uploader/fileuploads.rb +18 -18
  32. data/lib/uploader/helpers/field_tag.rb +16 -17
  33. data/lib/uploader/helpers/form_builder.rb +1 -1
  34. data/lib/uploader/helpers/form_tag_helper.rb +1 -1
  35. data/lib/uploader/hooks/formtastic.rb +1 -2
  36. data/lib/uploader/rails_admin/field.rb +27 -27
  37. data/lib/uploader/version.rb +1 -1
  38. metadata +115 -131
  39. data/vendor/assets/javascripts/uploader/jquery.fileupload-ip.js +0 -160
  40. data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +0 -282
  41. data/vendor/assets/javascripts/uploader/load-image.min.js +0 -1
  42. data/vendor/assets/javascripts/uploader/tmpl.min.js +0 -1
@@ -0,0 +1,158 @@
1
+ /*
2
+ * jQuery File Upload Processing Plugin 1.1
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2012, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true */
13
+ /*global define, window */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ './jquery.fileupload'
22
+ ], factory);
23
+ } else {
24
+ // Browser globals:
25
+ factory(
26
+ window.jQuery
27
+ );
28
+ }
29
+ }(function ($) {
30
+ 'use strict';
31
+
32
+ var originalAdd = $.blueimp.fileupload.prototype.options.add;
33
+
34
+ // The File Upload Processing plugin extends the fileupload widget
35
+ // with file processing functionality:
36
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
37
+
38
+ options: {
39
+ // The list of processing actions:
40
+ processQueue: [
41
+ /*
42
+ {
43
+ action: 'log',
44
+ type: 'debug'
45
+ }
46
+ */
47
+ ],
48
+ add: function (e, data) {
49
+ var $this = $(this);
50
+ data.process(function () {
51
+ return $this.fileupload('process', data);
52
+ });
53
+ originalAdd.call(this, e, data);
54
+ }
55
+ },
56
+
57
+ processActions: {
58
+ /*
59
+ log: function (data, options) {
60
+ console[options.type](
61
+ 'Processing "' + data.files[data.index].name + '"'
62
+ );
63
+ }
64
+ */
65
+ },
66
+
67
+ _processFile: function (data) {
68
+ var that = this,
69
+ dfd = $.Deferred().resolveWith(that, [data]),
70
+ chain = dfd.promise();
71
+ this._trigger('process', null, data);
72
+ $.each(data.processQueue, function (i, settings) {
73
+ var func = function (data) {
74
+ return that.processActions[settings.action].call(
75
+ that,
76
+ data,
77
+ settings
78
+ );
79
+ };
80
+ chain = chain.pipe(func, settings.always && func);
81
+ });
82
+ chain
83
+ .done(function () {
84
+ that._trigger('processdone', null, data);
85
+ that._trigger('processalways', null, data);
86
+ })
87
+ .fail(function () {
88
+ that._trigger('processfail', null, data);
89
+ that._trigger('processalways', null, data);
90
+ });
91
+ return chain;
92
+ },
93
+
94
+ // Replaces the settings of each processQueue item that
95
+ // are strings starting with an "@", using the remaining
96
+ // substring as key for the option map,
97
+ // e.g. "@autoUpload" is replaced with options.autoUpload:
98
+ _transformProcessQueue: function (options) {
99
+ var processQueue = [];
100
+ $.each(options.processQueue, function () {
101
+ var settings = {};
102
+ $.each(this, function (key, value) {
103
+ if ($.type(value) === 'string' &&
104
+ value.charAt(0) === '@') {
105
+ settings[key] = options[value.slice(1)];
106
+ } else {
107
+ settings[key] = value;
108
+ }
109
+ });
110
+ processQueue.push(settings);
111
+ });
112
+ options.processQueue = processQueue;
113
+ },
114
+
115
+ // Returns the number of files currently in the processsing queue:
116
+ processing: function () {
117
+ return this._processing;
118
+ },
119
+
120
+ // Processes the files given as files property of the data parameter,
121
+ // returns a Promise object that allows to bind callbacks:
122
+ process: function (data) {
123
+ var that = this,
124
+ options = $.extend({}, this.options, data);
125
+ if (options.processQueue && options.processQueue.length) {
126
+ this._transformProcessQueue(options);
127
+ if (this._processing === 0) {
128
+ this._trigger('processstart');
129
+ }
130
+ $.each(data.files, function (index, file) {
131
+ var opts = index ? $.extend({}, options) : options,
132
+ func = function () {
133
+ return that._processFile(opts);
134
+ };
135
+ opts.index = index;
136
+ that._processing += 1;
137
+ that._processingQueue = that._processingQueue.pipe(func, func)
138
+ .always(function () {
139
+ that._processing -= 1;
140
+ if (that._processing === 0) {
141
+ that._trigger('processstop');
142
+ }
143
+ });
144
+ });
145
+ }
146
+ return this._processingQueue;
147
+ },
148
+
149
+ _create: function () {
150
+ this._super();
151
+ this._processing = 0;
152
+ this._processingQueue = $.Deferred().resolveWith(this)
153
+ .promise();
154
+ }
155
+
156
+ });
157
+
158
+ }));
@@ -0,0 +1,212 @@
1
+ /*
2
+ * jQuery File Upload Image Resize Plugin 1.1.2
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2013, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true, regexp: true */
13
+ /*global define, window */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'load-image',
22
+ 'canvas-to-blob',
23
+ './jquery.fileupload-process'
24
+ ], factory);
25
+ } else {
26
+ // Browser globals:
27
+ factory(
28
+ window.jQuery,
29
+ window.loadImage
30
+ );
31
+ }
32
+ }(function ($, loadImage) {
33
+ 'use strict';
34
+
35
+ // Prepend to the default processQueue:
36
+ $.blueimp.fileupload.prototype.options.processQueue.unshift(
37
+ {
38
+ action: 'loadImage',
39
+ fileTypes: '@loadImageFileTypes',
40
+ maxFileSize: '@loadImageMaxFileSize',
41
+ noRevoke: '@loadImageNoRevoke',
42
+ disabled: '@disableImageLoad'
43
+ },
44
+ {
45
+ action: 'resizeImage',
46
+ maxWidth: '@imageMaxWidth',
47
+ maxHeight: '@imageMaxHeight',
48
+ minWidth: '@imageMinWidth',
49
+ minHeight: '@imageMinHeight',
50
+ crop: '@imageCrop',
51
+ disabled: '@disableImageResize'
52
+ },
53
+ {
54
+ action: 'saveImage',
55
+ disabled: '@disableImageResize'
56
+ },
57
+ {
58
+ action: 'resizeImage',
59
+ maxWidth: '@previewMaxWidth',
60
+ maxHeight: '@previewMaxHeight',
61
+ minWidth: '@previewMinWidth',
62
+ minHeight: '@previewMinHeight',
63
+ crop: '@previewCrop',
64
+ canvas: '@previewAsCanvas',
65
+ disabled: '@disableImagePreview'
66
+ },
67
+ {
68
+ action: 'setImage',
69
+ // The name of the property the resized image
70
+ // is saved as on the associated file object:
71
+ name: 'preview',
72
+ disabled: '@disableImagePreview'
73
+ }
74
+ );
75
+
76
+ // The File Upload Resize plugin extends the fileupload widget
77
+ // with image resize functionality:
78
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
79
+
80
+ options: {
81
+ // The regular expression for the types of images to load:
82
+ // matched against the file type:
83
+ loadImageFileTypes: /^image\/(gif|jpeg|png)$/,
84
+ // The maximum file size of images to load:
85
+ loadImageMaxFileSize: 5000000, // 5MB
86
+ // The maximum width of resized images:
87
+ imageMaxWidth: 1920,
88
+ // The maximum height of resized images:
89
+ imageMaxHeight: 1080,
90
+ // Define if resized images should be cropped or only scaled:
91
+ imageCrop: false,
92
+ // Disable the resize image functionality by default:
93
+ disableImageResize: true,
94
+ // The maximum width of the preview images:
95
+ previewMaxWidth: 80,
96
+ // The maximum height of the preview images:
97
+ previewMaxHeight: 80,
98
+ // Define if preview images should be cropped or only scaled:
99
+ previewCrop: false,
100
+ // Define if preview images should be resized as canvas elements:
101
+ previewAsCanvas: true
102
+ },
103
+
104
+ processActions: {
105
+
106
+ // Loads the image given via data.files and data.index
107
+ // as img element if the browser supports canvas.
108
+ // Accepts the options fileTypes (regular expression)
109
+ // and maxFileSize (integer) to limit the files to load:
110
+ loadImage: function (data, options) {
111
+ if (options.disabled) {
112
+ return data;
113
+ }
114
+ var that = this,
115
+ file = data.files[data.index],
116
+ dfd = $.Deferred();
117
+ if (($.type(options.maxFileSize) === 'number' &&
118
+ file.size > options.maxFileSize) ||
119
+ (options.fileTypes &&
120
+ !options.fileTypes.test(file.type)) ||
121
+ !loadImage(
122
+ file,
123
+ function (img) {
124
+ if (!img.src) {
125
+ return dfd.rejectWith(that, [data]);
126
+ }
127
+ data.img = img;
128
+ dfd.resolveWith(that, [data]);
129
+ },
130
+ options
131
+ )) {
132
+ dfd.rejectWith(that, [data]);
133
+ }
134
+ return dfd.promise();
135
+ },
136
+
137
+ // Resizes the image given as data.canvas or data.img
138
+ // and updates data.canvas or data.img with the resized image.
139
+ // Accepts the options maxWidth, maxHeight, minWidth,
140
+ // minHeight, canvas and crop:
141
+ resizeImage: function (data, options) {
142
+ options = $.extend({canvas: true}, options);
143
+ var img = (options.canvas && data.canvas) || data.img,
144
+ canvas;
145
+ if (img && !options.disabled) {
146
+ canvas = loadImage.scale(img, options);
147
+ if (canvas && (canvas.width !== img.width ||
148
+ canvas.height !== img.height)) {
149
+ data[canvas.getContext ? 'canvas' : 'img'] = canvas;
150
+ }
151
+ }
152
+ return data;
153
+ },
154
+
155
+ // Saves the processed image given as data.canvas
156
+ // inplace at data.index of data.files:
157
+ saveImage: function (data, options) {
158
+ if (!data.canvas || options.disabled) {
159
+ return data;
160
+ }
161
+ var that = this,
162
+ file = data.files[data.index],
163
+ name = file.name,
164
+ dfd = $.Deferred(),
165
+ callback = function (blob) {
166
+ if (!blob.name) {
167
+ if (file.type === blob.type) {
168
+ blob.name = file.name;
169
+ } else if (file.name) {
170
+ blob.name = file.name.replace(
171
+ /\..+$/,
172
+ '.' + blob.type.substr(6)
173
+ );
174
+ }
175
+ }
176
+ // Store the created blob at the position
177
+ // of the original file in the files list:
178
+ data.files[data.index] = blob;
179
+ dfd.resolveWith(that, [data]);
180
+ };
181
+ // Use canvas.mozGetAsFile directly, to retain the filename, as
182
+ // Gecko doesn't support the filename option for FormData.append:
183
+ if (data.canvas.mozGetAsFile) {
184
+ callback(data.canvas.mozGetAsFile(
185
+ (/^image\/(jpeg|png)$/.test(file.type) && name) ||
186
+ ((name && name.replace(/\..+$/, '')) ||
187
+ 'blob') + '.png',
188
+ file.type
189
+ ));
190
+ } else if (data.canvas.toBlob) {
191
+ data.canvas.toBlob(callback, file.type);
192
+ } else {
193
+ return data;
194
+ }
195
+ return dfd.promise();
196
+ },
197
+
198
+ // Sets the resized version of the image as a property of the
199
+ // file object, must be called after "saveImage":
200
+ setImage: function (data, options) {
201
+ var img = data.canvas || data.img;
202
+ if (img && !options.disabled) {
203
+ data.files[data.index][options.name] = img;
204
+ }
205
+ return data;
206
+ }
207
+
208
+ }
209
+
210
+ });
211
+
212
+ }));
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery File Upload User Interface Plugin 6.6.4
2
+ * jQuery File Upload User Interface Plugin 8.2.1
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, document, URL, webkitURL, FileReader */
13
+ /*global define, window, URL, webkitURL, FileReader */
14
14
 
15
15
  (function (factory) {
16
16
  'use strict';
@@ -19,82 +19,85 @@
19
19
  define([
20
20
  'jquery',
21
21
  'tmpl',
22
- 'load-image',
23
- './jquery.fileupload-ip'
22
+ './jquery.fileupload-resize',
23
+ './jquery.fileupload-validate'
24
24
  ], factory);
25
25
  } else {
26
26
  // Browser globals:
27
27
  factory(
28
28
  window.jQuery,
29
- window.tmpl,
30
- window.loadImage
29
+ window.tmpl
31
30
  );
32
31
  }
33
32
  }(function ($, tmpl, loadImage) {
34
33
  'use strict';
35
34
 
36
- // The UI version extends the IP (image processing) version or the basic
37
- // file upload widget and adds complete user interface interaction:
38
- var parentWidget = ($.blueimpIP || $.blueimp).fileupload;
39
- $.widget('blueimpUI.fileupload', parentWidget, {
35
+ $.blueimp.fileupload.prototype._specialOptions.push(
36
+ 'filesContainer',
37
+ 'uploadTemplateId',
38
+ 'downloadTemplateId'
39
+ );
40
+
41
+ // The UI version extends the file upload widget
42
+ // and adds complete user interface interaction:
43
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
40
44
 
41
45
  options: {
42
46
  // By default, files added to the widget are uploaded as soon
43
47
  // as the user clicks on the start buttons. To enable automatic
44
48
  // uploads, set the following option to true:
45
49
  autoUpload: false,
46
- // The following option limits the number of files that are
47
- // allowed to be uploaded using this widget:
48
- maxNumberOfFiles: undefined,
49
- // The maximum allowed file size:
50
- maxFileSize: undefined,
51
- // The minimum allowed file size:
52
- minFileSize: undefined,
53
- // The regular expression for allowed file types, matches
54
- // against either file type or file name:
55
- acceptFileTypes: /.+$/i,
56
- // The regular expression to define for which files a preview
57
- // image is shown, matched against the file type:
58
- previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
59
- // The maximum file size of images that are to be displayed as preview:
60
- previewSourceMaxFileSize: 5000000, // 5MB
61
- // The maximum width of the preview images:
62
- previewMaxWidth: 80,
63
- // The maximum height of the preview images:
64
- previewMaxHeight: 80,
65
- // By default, preview images are displayed as canvas elements
66
- // if supported by the browser. Set the following option to false
67
- // to always display preview images as img elements:
68
- previewAsCanvas: true,
69
50
  // The ID of the upload template:
70
51
  uploadTemplateId: 'template-upload',
71
52
  // The ID of the download template:
72
53
  downloadTemplateId: 'template-download',
54
+ // The container for the list of files. If undefined, it is set to
55
+ // an element with class "files" inside of the widget element:
56
+ filesContainer: undefined,
57
+ // By default, files are appended to the files container.
58
+ // Set the following option to true, to prepend files instead:
59
+ prependFiles: false,
73
60
  // The expected data type of the upload response, sets the dataType
74
61
  // option of the $.ajax upload requests:
75
62
  dataType: 'json',
76
63
 
64
+ // Function returning the current number of files,
65
+ // used by the maxNumberOfFiles validation:
66
+ getNumberOfFiles: function () {
67
+ return this.filesContainer.children().length;
68
+ },
69
+
70
+ // Callback to retrieve the list of files from the server response:
71
+ getFilesFromResponse: function (data) {
72
+ if (data.result && $.isArray(data.result.files)) {
73
+ return data.result.files;
74
+ }
75
+ return [];
76
+ },
77
+
77
78
  // The add callback is invoked as soon as files are added to the fileupload
78
79
  // widget (via file input selection, drag & drop or add API call).
79
80
  // See the basic file upload widget for more information:
80
81
  add: function (e, data) {
81
- var that = $(this).data('fileupload'),
82
+ var $this = $(this),
83
+ that = $this.data('blueimp-fileupload') ||
84
+ $this.data('fileupload'),
82
85
  options = that.options,
83
86
  files = data.files;
84
- $(this).fileupload('resize', data).done(data, function () {
85
- that._adjustMaxNumberOfFiles(-files.length);
86
- data.isAdjusted = true;
87
- data.files.valid = data.isValidated = that._validate(files);
88
- data.context = that._renderUpload(files)
89
- .appendTo(options.filesContainer)
90
- .data('data', data);
91
- that._renderPreviews(files, data.context);
87
+ data.process(function () {
88
+ return $this.fileupload('process', data);
89
+ }).always(function () {
90
+ data.context = that._renderUpload(files).data('data', data);
91
+ that._renderPreviews(data);
92
+ options.filesContainer[
93
+ options.prependFiles ? 'prepend' : 'append'
94
+ ](data.context);
92
95
  that._forceReflow(data.context);
93
96
  that._transition(data.context).done(
94
97
  function () {
95
98
  if ((that._trigger('added', e, data) !== false) &&
96
99
  (options.autoUpload || data.autoUpload) &&
97
- data.autoUpload !== false && data.isValidated) {
100
+ data.autoUpload !== false && !data.files.error) {
98
101
  data.submit();
99
102
  }
100
103
  }
@@ -103,15 +106,8 @@
103
106
  },
104
107
  // Callback for the start of each file upload request:
105
108
  send: function (e, data) {
106
- var that = $(this).data('fileupload');
107
- if (!data.isValidated) {
108
- if (!data.isAdjusted) {
109
- that._adjustMaxNumberOfFiles(-data.files.length);
110
- }
111
- if (!that._validate(data.files)) {
112
- return false;
113
- }
114
- }
109
+ var that = $(this).data('blueimp-fileupload') ||
110
+ $(this).data('fileupload');
115
111
  if (data.context && data.dataType &&
116
112
  data.dataType.substr(0, 6) === 'iframe') {
117
113
  // Iframe Transport does not support progress events.
@@ -121,65 +117,73 @@
121
117
  .find('.progress').addClass(
122
118
  !$.support.transition && 'progress-animated'
123
119
  )
120
+ .attr('aria-valuenow', 100)
124
121
  .find('.bar').css(
125
122
  'width',
126
- parseInt(100, 10) + '%'
123
+ '100%'
127
124
  );
128
125
  }
129
126
  return that._trigger('sent', e, data);
130
127
  },
131
128
  // Callback for successful uploads:
132
129
  done: function (e, data) {
133
- var that = $(this).data('fileupload'),
130
+ var that = $(this).data('blueimp-fileupload') ||
131
+ $(this).data('fileupload'),
132
+ getFilesFromResponse = data.getFilesFromResponse ||
133
+ that.options.getFilesFromResponse,
134
+ files = getFilesFromResponse(data),
134
135
  template,
135
- preview;
136
-
136
+ deferred;
137
137
  if (data.context) {
138
138
  data.context.each(function (index) {
139
- var file = ($.isArray(data.result) &&
140
- data.result[index]) || {error: 'emptyResult'};
141
- if (file.error) {
142
- that._adjustMaxNumberOfFiles(1);
143
- }
139
+ var file = files[index] ||
140
+ {error: 'Empty file upload result'},
141
+ deferred = that._addFinishedDeferreds();
144
142
  that._transition($(this)).done(
145
143
  function () {
146
144
  var node = $(this);
147
145
  template = that._renderDownload([file])
148
- .css('height', node.height())
149
146
  .replaceAll(node);
150
147
  that._forceReflow(template);
151
148
  that._transition(template).done(
152
149
  function () {
153
150
  data.context = $(this);
154
151
  that._trigger('completed', e, data);
152
+ that._trigger('finished', e, data);
153
+ deferred.resolve();
155
154
  }
156
155
  );
157
156
  }
158
157
  );
159
158
  });
160
159
  } else {
161
- template = that._renderDownload(data.result)
160
+ template = that._renderDownload(files)
162
161
  .appendTo(that.options.filesContainer);
163
162
  that._forceReflow(template);
163
+ deferred = that._addFinishedDeferreds();
164
164
  that._transition(template).done(
165
165
  function () {
166
166
  data.context = $(this);
167
167
  that._trigger('completed', e, data);
168
+ that._trigger('finished', e, data);
169
+ deferred.resolve();
168
170
  }
169
171
  );
170
172
  }
171
173
  },
172
174
  // Callback for failed (abort or error) uploads:
173
175
  fail: function (e, data) {
174
- var that = $(this).data('fileupload'),
175
- template;
176
- that._adjustMaxNumberOfFiles(data.files.length);
176
+ var that = $(this).data('blueimp-fileupload') ||
177
+ $(this).data('fileupload'),
178
+ template,
179
+ deferred;
177
180
  if (data.context) {
178
181
  data.context.each(function (index) {
179
182
  if (data.errorThrown !== 'abort') {
180
183
  var file = data.files[index];
181
184
  file.error = file.error || data.errorThrown ||
182
185
  true;
186
+ deferred = that._addFinishedDeferreds();
183
187
  that._transition($(this)).done(
184
188
  function () {
185
189
  var node = $(this);
@@ -190,55 +194,83 @@
190
194
  function () {
191
195
  data.context = $(this);
192
196
  that._trigger('failed', e, data);
197
+ that._trigger('finished', e, data);
198
+ deferred.resolve();
193
199
  }
194
200
  );
195
201
  }
196
202
  );
197
203
  } else {
204
+ deferred = that._addFinishedDeferreds();
198
205
  that._transition($(this)).done(
199
206
  function () {
200
207
  $(this).remove();
201
208
  that._trigger('failed', e, data);
209
+ that._trigger('finished', e, data);
210
+ deferred.resolve();
202
211
  }
203
212
  );
204
213
  }
205
214
  });
206
215
  } else if (data.errorThrown !== 'abort') {
207
- that._adjustMaxNumberOfFiles(-data.files.length);
208
216
  data.context = that._renderUpload(data.files)
209
217
  .appendTo(that.options.filesContainer)
210
218
  .data('data', data);
211
219
  that._forceReflow(data.context);
220
+ deferred = that._addFinishedDeferreds();
212
221
  that._transition(data.context).done(
213
222
  function () {
214
223
  data.context = $(this);
215
224
  that._trigger('failed', e, data);
225
+ that._trigger('finished', e, data);
226
+ deferred.resolve();
216
227
  }
217
228
  );
218
229
  } else {
219
230
  that._trigger('failed', e, data);
231
+ that._trigger('finished', e, data);
232
+ that._addFinishedDeferreds().resolve();
220
233
  }
221
234
  },
222
235
  // Callback for upload progress events:
223
236
  progress: function (e, data) {
224
237
  if (data.context) {
225
- data.context.find('.progress .bar').css(
226
- 'width',
227
- parseInt(data.loaded / data.total * 100, 10) + '%'
228
- );
238
+ var progress = Math.floor(data.loaded / data.total * 100);
239
+ data.context.find('.progress')
240
+ .attr('aria-valuenow', progress)
241
+ .find('.bar').css(
242
+ 'width',
243
+ progress + '%'
244
+ );
229
245
  }
230
246
  },
231
247
  // Callback for global upload progress events:
232
248
  progressall: function (e, data) {
233
- $(this).find('.fileupload-buttonbar .progress .bar').css(
234
- 'width',
235
- parseInt(data.loaded / data.total * 100, 10) + '%'
236
- );
249
+ var $this = $(this),
250
+ progress = Math.floor(data.loaded / data.total * 100),
251
+ globalProgressNode = $this.find('.fileupload-progress'),
252
+ extendedProgressNode = globalProgressNode
253
+ .find('.progress-extended');
254
+ if (extendedProgressNode.length) {
255
+ extendedProgressNode.html(
256
+ ($this.data('blueimp-fileupload') || $this.data('fileupload'))
257
+ ._renderExtendedProgress(data)
258
+ );
259
+ }
260
+ globalProgressNode
261
+ .find('.progress')
262
+ .attr('aria-valuenow', progress)
263
+ .find('.bar').css(
264
+ 'width',
265
+ progress + '%'
266
+ );
237
267
  },
238
268
  // Callback for uploads start, equivalent to the global ajaxStart event:
239
269
  start: function (e) {
240
- var that = $(this).data('fileupload');
241
- that._transition($(this).find('.fileupload-buttonbar .progress')).done(
270
+ var that = $(this).data('blueimp-fileupload') ||
271
+ $(this).data('fileupload');
272
+ that._resetFinishedDeferreds();
273
+ that._transition($(this).find('.fileupload-progress')).done(
242
274
  function () {
243
275
  that._trigger('started', e);
244
276
  }
@@ -246,30 +278,62 @@
246
278
  },
247
279
  // Callback for uploads stop, equivalent to the global ajaxStop event:
248
280
  stop: function (e) {
249
- var that = $(this).data('fileupload');
250
- that._transition($(this).find('.fileupload-buttonbar .progress')).done(
251
- function () {
252
- $(this).find('.bar').css('width', '0%');
281
+ var that = $(this).data('blueimp-fileupload') ||
282
+ $(this).data('fileupload'),
283
+ deferred = that._addFinishedDeferreds();
284
+ $.when.apply($, that._getFinishedDeferreds())
285
+ .done(function () {
253
286
  that._trigger('stopped', e);
287
+ });
288
+ that._transition($(this).find('.fileupload-progress')).done(
289
+ function () {
290
+ $(this).find('.progress')
291
+ .attr('aria-valuenow', '0')
292
+ .find('.bar').css('width', '0%');
293
+ $(this).find('.progress-extended').html(' ');
294
+ deferred.resolve();
254
295
  }
255
296
  );
256
297
  },
298
+ processstart: function () {
299
+ $(this).addClass('fileupload-processing');
300
+ },
301
+ processstop: function () {
302
+ $(this).removeClass('fileupload-processing');
303
+ },
257
304
  // Callback for file deletion:
258
305
  destroy: function (e, data) {
259
- var that = $(this).data('fileupload');
306
+ var that = $(this).data('blueimp-fileupload') ||
307
+ $(this).data('fileupload');
260
308
  if (data.url) {
261
- $.ajax(data);
309
+ $.ajax(data).done(function () {
310
+ that._transition(data.context).done(
311
+ function () {
312
+ $(this).remove();
313
+ that._trigger('destroyed', e, data);
314
+ }
315
+ );
316
+ });
262
317
  }
263
- that._adjustMaxNumberOfFiles(1);
264
- that._transition(data.context).done(
265
- function () {
266
- $(this).remove();
267
- that._trigger('destroyed', e, data);
268
- }
269
- );
270
318
  }
271
319
  },
272
320
 
321
+ _resetFinishedDeferreds: function () {
322
+ this._finishedUploads = [];
323
+ },
324
+
325
+ _addFinishedDeferreds: function (deferred) {
326
+ if (!deferred) {
327
+ deferred = $.Deferred();
328
+ }
329
+ this._finishedUploads.push(deferred);
330
+ return deferred;
331
+ },
332
+
333
+ _getFinishedDeferreds: function () {
334
+ return this._finishedUploads;
335
+ },
336
+
273
337
  // Link handler, that allows to download files
274
338
  // by drag & drop of the links to the desktop:
275
339
  _enableDragToDesktop: function () {
@@ -283,21 +347,10 @@
283
347
  'DownloadURL',
284
348
  [type, name, url].join(':')
285
349
  );
286
- } catch (err) {}
350
+ } catch (ignore) {}
287
351
  });
288
352
  },
289
353
 
290
- _adjustMaxNumberOfFiles: function (operand) {
291
- if (typeof this.options.maxNumberOfFiles === 'number') {
292
- this.options.maxNumberOfFiles += operand;
293
- if (this.options.maxNumberOfFiles < 1) {
294
- this._disableFileInputButton();
295
- } else {
296
- this._enableFileInputButton();
297
- }
298
- }
299
- },
300
-
301
354
  _formatFileSize: function (bytes) {
302
355
  if (typeof bytes !== 'number') {
303
356
  return '';
@@ -311,44 +364,46 @@
311
364
  return (bytes / 1000).toFixed(2) + ' KB';
312
365
  },
313
366
 
314
- _hasError: function (file) {
315
- if (file.error) {
316
- return file.error;
317
- }
318
- // The number of added files is subtracted from
319
- // maxNumberOfFiles before validation, so we check if
320
- // maxNumberOfFiles is below 0 (instead of below 1):
321
- if (this.options.maxNumberOfFiles < 0) {
322
- return 'maxNumberOfFiles';
367
+ _formatBitrate: function (bits) {
368
+ if (typeof bits !== 'number') {
369
+ return '';
323
370
  }
324
- // Files are accepted if either the file type or the file name
325
- // matches against the acceptFileTypes regular expression, as
326
- // only browsers with support for the File API report the type:
327
- if (!(this.options.acceptFileTypes.test(file.type) ||
328
- this.options.acceptFileTypes.test(file.name))) {
329
- return 'acceptFileTypes';
371
+ if (bits >= 1000000000) {
372
+ return (bits / 1000000000).toFixed(2) + ' Gbit/s';
330
373
  }
331
- if (this.options.maxFileSize &&
332
- file.size > this.options.maxFileSize) {
333
- return 'maxFileSize';
374
+ if (bits >= 1000000) {
375
+ return (bits / 1000000).toFixed(2) + ' Mbit/s';
334
376
  }
335
- if (typeof file.size === 'number' &&
336
- file.size < this.options.minFileSize) {
337
- return 'minFileSize';
377
+ if (bits >= 1000) {
378
+ return (bits / 1000).toFixed(2) + ' kbit/s';
338
379
  }
339
- return null;
380
+ return bits.toFixed(2) + ' bit/s';
340
381
  },
341
382
 
342
- _validate: function (files) {
343
- var that = this,
344
- valid = !!files.length;
345
- $.each(files, function (index, file) {
346
- file.error = that._hasError(file);
347
- if (file.error) {
348
- valid = false;
349
- }
350
- });
351
- return valid;
383
+ _formatTime: function (seconds) {
384
+ var date = new Date(seconds * 1000),
385
+ days = Math.floor(seconds / 86400);
386
+ days = days ? days + 'd ' : '';
387
+ return days +
388
+ ('0' + date.getUTCHours()).slice(-2) + ':' +
389
+ ('0' + date.getUTCMinutes()).slice(-2) + ':' +
390
+ ('0' + date.getUTCSeconds()).slice(-2);
391
+ },
392
+
393
+ _formatPercentage: function (floatValue) {
394
+ return (floatValue * 100).toFixed(2) + ' %';
395
+ },
396
+
397
+ _renderExtendedProgress: function (data) {
398
+ return this._formatBitrate(data.bitrate) + ' | ' +
399
+ this._formatTime(
400
+ (data.total - data.loaded) * 8 / data.bitrate
401
+ ) + ' | ' +
402
+ this._formatPercentage(
403
+ data.loaded / data.total
404
+ ) + ' | ' +
405
+ this._formatFileSize(data.loaded) + ' / ' +
406
+ this._formatFileSize(data.total);
352
407
  },
353
408
 
354
409
  _renderTemplate: function (func, files) {
@@ -363,57 +418,13 @@
363
418
  if (result instanceof $) {
364
419
  return result;
365
420
  }
366
-
367
421
  return $(this.options.templatesContainer).html(result).children();
368
422
  },
369
423
 
370
- _renderPreview: function (file, node) {
371
- var that = this,
372
- options = this.options,
373
- deferred = $.Deferred();
374
- return ((loadImage && loadImage(
375
- file,
376
- function (img) {
377
- node.append(img);
378
- that._forceReflow(node);
379
- that._transition(node).done(function () {
380
- deferred.resolveWith(node);
381
- });
382
- if (!$.contains(document.body, node[0])) {
383
- // If the element is not part of the DOM,
384
- // transition events are not triggered,
385
- // so we have to resolve manually:
386
- deferred.resolveWith(node);
387
- }
388
- },
389
- {
390
- maxWidth: options.previewMaxWidth,
391
- maxHeight: options.previewMaxHeight,
392
- canvas: options.previewAsCanvas
393
- }
394
- )) || deferred.resolveWith(node)) && deferred;
395
- },
396
-
397
- _renderPreviews: function (files, nodes) {
398
- var that = this,
399
- options = this.options;
400
- nodes.find('.preview span').each(function (index, element) {
401
- var file = files[index];
402
- if (options.previewSourceFileTypes.test(file.type) &&
403
- ($.type(options.previewSourceMaxFileSize) !== 'number' ||
404
- file.size < options.previewSourceMaxFileSize)) {
405
- that._processingQueue = that._processingQueue.pipe(function () {
406
- var deferred = $.Deferred();
407
- that._renderPreview(file, $(element)).done(
408
- function () {
409
- deferred.resolveWith(that);
410
- }
411
- );
412
- return deferred.promise();
413
- });
414
- }
424
+ _renderPreviews: function (data) {
425
+ data.context.find('.preview').each(function (index, elm) {
426
+ $(elm).append(data.files[index].preview);
415
427
  });
416
- return this._processingQueue;
417
428
  },
418
429
 
419
430
  _renderUpload: function (files) {
@@ -432,7 +443,7 @@
432
443
 
433
444
  _startHandler: function (e) {
434
445
  e.preventDefault();
435
- var button = $(this),
446
+ var button = $(e.currentTarget),
436
447
  template = button.closest('.template-upload'),
437
448
  data = template.data('data');
438
449
  if (data && data.submit && !data.jqXHR && data.submit()) {
@@ -442,11 +453,11 @@
442
453
 
443
454
  _cancelHandler: function (e) {
444
455
  e.preventDefault();
445
- var template = $(this).closest('.template-upload'),
456
+ var template = $(e.currentTarget).closest('.template-upload'),
446
457
  data = template.data('data') || {};
447
458
  if (!data.jqXHR) {
448
459
  data.errorThrown = 'abort';
449
- e.data.fileupload._trigger('fail', e, data);
460
+ this._trigger('fail', e, data);
450
461
  } else {
451
462
  data.jqXHR.abort();
452
463
  }
@@ -454,24 +465,21 @@
454
465
 
455
466
  _deleteHandler: function (e) {
456
467
  e.preventDefault();
457
- var button = $(this);
458
- e.data.fileupload._trigger('destroy', e, {
468
+ var button = $(e.currentTarget);
469
+ this._trigger('destroy', e, $.extend({
459
470
  context: button.closest('.template-download'),
460
- url: button.attr('data-url'),
461
- type: button.attr('data-type') || 'DELETE',
462
- dataType: e.data.fileupload.options.dataType
463
- });
471
+ type: 'DELETE'
472
+ }, button.data()));
464
473
  },
465
474
 
466
475
  _forceReflow: function (node) {
467
- this._reflow = $.support.transition &&
468
- node.length && node[0].offsetWidth;
476
+ return $.support.transition && node.length &&
477
+ node[0].offsetWidth;
469
478
  },
470
479
 
471
480
  _transition: function (node) {
472
- var that = this,
473
- deferred = $.Deferred();
474
- if ($.support.transition && node.hasClass('fade')) {
481
+ var dfd = $.Deferred();
482
+ if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
475
483
  node.bind(
476
484
  $.support.transition.end,
477
485
  function (e) {
@@ -479,88 +487,78 @@
479
487
  // in the container element, e.g. from button elements:
480
488
  if (e.target === node[0]) {
481
489
  node.unbind($.support.transition.end);
482
- deferred.resolveWith(node);
490
+ dfd.resolveWith(node);
483
491
  }
484
492
  }
485
493
  ).toggleClass('in');
486
494
  } else {
487
495
  node.toggleClass('in');
488
- deferred.resolveWith(node);
496
+ dfd.resolveWith(node);
489
497
  }
490
- return deferred;
498
+ return dfd;
491
499
  },
492
500
 
493
501
  _initButtonBarEventHandlers: function () {
494
502
  var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
495
- filesList = this.options.filesContainer,
496
- ns = this.options.namespace;
497
- fileUploadButtonBar.find('.start')
498
- .bind('click.' + ns, function (e) {
503
+ filesList = this.options.filesContainer;
504
+ this._on(fileUploadButtonBar.find('.start'), {
505
+ click: function (e) {
499
506
  e.preventDefault();
500
- filesList.find('.start button').click();
501
- });
502
- fileUploadButtonBar.find('.cancel')
503
- .bind('click.' + ns, function (e) {
507
+ filesList.find('.start').click();
508
+ }
509
+ });
510
+ this._on(fileUploadButtonBar.find('.cancel'), {
511
+ click: function (e) {
504
512
  e.preventDefault();
505
- filesList.find('.cancel button').click();
506
- });
507
- fileUploadButtonBar.find('.delete')
508
- .bind('click.' + ns, function (e) {
513
+ filesList.find('.cancel').click();
514
+ }
515
+ });
516
+ this._on(fileUploadButtonBar.find('.delete'), {
517
+ click: function (e) {
509
518
  e.preventDefault();
510
- filesList.find('.delete input:checked')
511
- .siblings('button').click();
519
+ filesList.find('.toggle:checked')
520
+ .closest('.template-download')
521
+ .find('.delete').click();
512
522
  fileUploadButtonBar.find('.toggle')
513
523
  .prop('checked', false);
514
- });
515
- fileUploadButtonBar.find('.toggle')
516
- .bind('change.' + ns, function (e) {
517
- filesList.find('.delete input').prop(
524
+ }
525
+ });
526
+ this._on(fileUploadButtonBar.find('.toggle'), {
527
+ change: function (e) {
528
+ filesList.find('.toggle').prop(
518
529
  'checked',
519
- $(this).is(':checked')
530
+ $(e.currentTarget).is(':checked')
520
531
  );
521
- });
532
+ }
533
+ });
522
534
  },
523
535
 
524
536
  _destroyButtonBarEventHandlers: function () {
525
- this.element.find('.fileupload-buttonbar button')
526
- .unbind('click.' + this.options.namespace);
527
- this.element.find('.fileupload-buttonbar .toggle')
528
- .unbind('change.' + this.options.namespace);
537
+ this._off(
538
+ this.element.find('.fileupload-buttonbar')
539
+ .find('.start, .cancel, .delete'),
540
+ 'click'
541
+ );
542
+ this._off(
543
+ this.element.find('.fileupload-buttonbar .toggle'),
544
+ 'change.'
545
+ );
529
546
  },
530
547
 
531
548
  _initEventHandlers: function () {
532
- parentWidget.prototype._initEventHandlers.call(this);
533
- var eventData = {fileupload: this};
534
- this.options.filesContainer
535
- .delegate(
536
- '.start button',
537
- 'click.' + this.options.namespace,
538
- eventData,
539
- this._startHandler
540
- )
541
- .delegate(
542
- '.cancel a',
543
- 'click.' + this.options.namespace,
544
- eventData,
545
- this._cancelHandler
546
- )
547
- .delegate(
548
- '.delete a',
549
- 'click.' + this.options.namespace,
550
- eventData,
551
- this._deleteHandler
552
- );
549
+ this._super();
550
+ this._on(this.options.filesContainer, {
551
+ 'click .start': this._startHandler,
552
+ 'click .cancel': this._cancelHandler,
553
+ 'click .delete': this._deleteHandler
554
+ });
553
555
  this._initButtonBarEventHandlers();
554
556
  },
555
557
 
556
558
  _destroyEventHandlers: function () {
557
- var options = this.options;
558
559
  this._destroyButtonBarEventHandlers();
559
- options.filesContainer
560
- .undelegate('.start button', 'click.' + options.namespace)
561
- .undelegate('.cancel a', 'click.' + options.namespace)
562
- .undelegate('.delete a', 'click.' + options.namespace);
563
- parentWidget.prototype._destroyEventHandlers.call(this);
560
+ this._off(this.options.filesContainer, 'click');
561
+ this._super();
564
562
  },
565
563
 
566
564
  _enableFileInputButton: function () {
@@ -577,7 +575,7 @@
577
575
 
578
576
  _initTemplates: function () {
579
577
  var options = this.options;
580
- options.templatesContainer = document.createElement(
578
+ options.templatesContainer = this.document[0].createElement(
581
579
  options.filesContainer.prop('nodeName')
582
580
  );
583
581
  if (tmpl) {
@@ -600,36 +598,34 @@
600
598
  },
601
599
 
602
600
  _initSpecialOptions: function () {
603
- parentWidget.prototype._initSpecialOptions.call(this);
601
+ this._super();
604
602
  this._initFilesContainer();
605
603
  this._initTemplates();
606
604
  },
607
605
 
608
606
  _create: function () {
609
- parentWidget.prototype._create.call(this);
610
- this._refreshOptionsList.push(
611
- 'filesContainer',
612
- 'uploadTemplateId',
613
- 'downloadTemplateId'
614
- );
615
- if (!$.blueimpIP) {
616
- this._processingQueue = $.Deferred().resolveWith(this).promise();
617
- this.resize = function () {
618
- return this._processingQueue;
619
- };
620
- }
607
+ this._super();
608
+ this._resetFinishedDeferreds();
621
609
  },
622
610
 
623
611
  enable: function () {
624
- parentWidget.prototype.enable.call(this);
625
- this.element.find('input, button').prop('disabled', false);
626
- this._enableFileInputButton();
612
+ var wasDisabled = false;
613
+ if (this.options.disabled) {
614
+ wasDisabled = true;
615
+ }
616
+ this._super();
617
+ if (wasDisabled) {
618
+ this.element.find('input, button').prop('disabled', false);
619
+ this._enableFileInputButton();
620
+ }
627
621
  },
628
622
 
629
623
  disable: function () {
630
- this.element.find('input, button').prop('disabled', true);
631
- this._disableFileInputButton();
632
- parentWidget.prototype.disable.call(this);
624
+ if (!this.options.disabled) {
625
+ this.element.find('input, button').prop('disabled', true);
626
+ this._disableFileInputButton();
627
+ }
628
+ this._super();
633
629
  }
634
630
 
635
631
  });