glebtv-rails-uploader 0.1.3 → 0.2.0

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.
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
  });