condo 1.0.4 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/README.textile +133 -133
  3. data/app/assets/javascripts/condo.js +9 -6
  4. data/app/assets/javascripts/condo/amazon.js +403 -406
  5. data/app/assets/javascripts/condo/condo.js +184 -0
  6. data/app/assets/javascripts/condo/config.js +69 -80
  7. data/app/assets/javascripts/condo/google.js +338 -255
  8. data/app/assets/javascripts/condo/md5/hash.worker.emulator.js +23 -23
  9. data/app/assets/javascripts/condo/md5/hash.worker.js +11 -11
  10. data/app/assets/javascripts/condo/md5/hasher.js +119 -100
  11. data/app/assets/javascripts/condo/md5/spark-md5.js +276 -161
  12. data/app/assets/javascripts/condo/rackspace.js +326 -329
  13. data/app/assets/javascripts/condo/{abstract-md5.js.erb → services/abstract-md5.js.erb} +86 -93
  14. data/app/assets/javascripts/condo/{base64.js → services/base64.js} +2 -10
  15. data/app/assets/javascripts/condo/services/broadcaster.js +26 -0
  16. data/app/assets/javascripts/condo/services/uploader.js +302 -0
  17. data/app/assets/javascripts/core/core.js +4 -0
  18. data/app/assets/javascripts/core/services/1-safe-apply.js +17 -0
  19. data/app/assets/javascripts/core/services/2-messaging.js +171 -0
  20. data/lib/condo.rb +269 -269
  21. data/lib/condo/configuration.rb +137 -139
  22. data/lib/condo/errors.rb +8 -8
  23. data/lib/condo/strata/amazon_s3.rb +301 -301
  24. data/lib/condo/strata/google_cloud_storage.rb +315 -314
  25. data/lib/condo/strata/rackspace_cloud_files.rb +245 -223
  26. data/lib/condo/version.rb +1 -1
  27. metadata +21 -44
  28. data/app/assets/javascripts/condo/broadcaster.js +0 -60
  29. data/app/assets/javascripts/condo/controller.js +0 -194
  30. data/app/assets/javascripts/condo/uploader.js +0 -310
  31. data/test/dummy/db/test.sqlite3 +0 -0
  32. data/test/dummy/log/test.log +0 -25
@@ -1,3 +1,3 @@
1
1
  module Condo
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.6"
3
3
  end
metadata CHANGED
@@ -1,62 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: condo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
5
- prerelease:
4
+ version: 1.0.6
6
5
  platform: ruby
7
6
  authors:
8
7
  - Stephen von Takach
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-06 00:00:00.000000000 Z
11
+ date: 2014-09-20 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rails
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
- version: 3.0.0
19
+ version: 4.0.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
- version: 3.0.0
26
+ version: 4.0.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: fog
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
46
- - !ruby/object:Gem::Dependency
47
- name: sqlite3
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ! '>='
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ! '>='
38
+ - - '>='
60
39
  - !ruby/object:Gem::Version
61
40
  version: '0'
62
41
  description: Provides signed upload signatures to your users browsers so they can
@@ -67,20 +46,23 @@ executables: []
67
46
  extensions: []
68
47
  extra_rdoc_files: []
69
48
  files:
70
- - app/assets/javascripts/condo/abstract-md5.js.erb
71
49
  - app/assets/javascripts/condo/amazon.js
72
- - app/assets/javascripts/condo/base64.js
73
- - app/assets/javascripts/condo/broadcaster.js
50
+ - app/assets/javascripts/condo/condo.js
74
51
  - app/assets/javascripts/condo/config.js
75
- - app/assets/javascripts/condo/controller.js
76
52
  - app/assets/javascripts/condo/google.js
77
53
  - app/assets/javascripts/condo/md5/hash.worker.emulator.js
78
54
  - app/assets/javascripts/condo/md5/hash.worker.js
79
55
  - app/assets/javascripts/condo/md5/hasher.js
80
56
  - app/assets/javascripts/condo/md5/spark-md5.js
81
57
  - app/assets/javascripts/condo/rackspace.js
82
- - app/assets/javascripts/condo/uploader.js
58
+ - app/assets/javascripts/condo/services/abstract-md5.js.erb
59
+ - app/assets/javascripts/condo/services/base64.js
60
+ - app/assets/javascripts/condo/services/broadcaster.js
61
+ - app/assets/javascripts/condo/services/uploader.js
83
62
  - app/assets/javascripts/condo.js
63
+ - app/assets/javascripts/core/core.js
64
+ - app/assets/javascripts/core/services/1-safe-apply.js
65
+ - app/assets/javascripts/core/services/2-messaging.js
84
66
  - lib/condo/configuration.rb
85
67
  - lib/condo/engine.rb
86
68
  - lib/condo/errors.rb
@@ -115,8 +97,6 @@ files:
115
97
  - test/dummy/config/locales/en.yml
116
98
  - test/dummy/config/routes.rb
117
99
  - test/dummy/config.ru
118
- - test/dummy/db/test.sqlite3
119
- - test/dummy/log/test.log
120
100
  - test/dummy/public/404.html
121
101
  - test/dummy/public/422.html
122
102
  - test/dummy/public/500.html
@@ -128,27 +108,26 @@ files:
128
108
  - test/test_helper.rb
129
109
  homepage: http://cotag.me/
130
110
  licenses: []
111
+ metadata: {}
131
112
  post_install_message:
132
113
  rdoc_options: []
133
114
  require_paths:
134
115
  - lib
135
116
  required_ruby_version: !ruby/object:Gem::Requirement
136
- none: false
137
117
  requirements:
138
- - - ! '>='
118
+ - - '>='
139
119
  - !ruby/object:Gem::Version
140
120
  version: '0'
141
121
  required_rubygems_version: !ruby/object:Gem::Requirement
142
- none: false
143
122
  requirements:
144
- - - ! '>='
123
+ - - '>='
145
124
  - !ruby/object:Gem::Version
146
125
  version: '0'
147
126
  requirements: []
148
127
  rubyforge_project:
149
- rubygems_version: 1.8.24
128
+ rubygems_version: 2.0.14
150
129
  signing_key:
151
- specification_version: 3
130
+ specification_version: 4
152
131
  summary: Direct Cloud Storage Uploader
153
132
  test_files:
154
133
  - test/condo_test.rb
@@ -173,8 +152,6 @@ test_files:
173
152
  - test/dummy/config/locales/en.yml
174
153
  - test/dummy/config/routes.rb
175
154
  - test/dummy/config.ru
176
- - test/dummy/db/test.sqlite3
177
- - test/dummy/log/test.log
178
155
  - test/dummy/public/404.html
179
156
  - test/dummy/public/422.html
180
157
  - test/dummy/public/500.html
@@ -1,60 +0,0 @@
1
- /**
2
- * CoTag Condo Amazon S3 Strategy
3
- * Direct to cloud resumable uploads for Google Cloud Storage
4
- *
5
- * Copyright (c) 2012 CoTag Media.
6
- *
7
- * @author Stephen von Takach <steve@cotag.me>
8
- * @copyright 2012 cotag.me
9
- *
10
- *
11
- * References:
12
- * * https://github.com/umdjs/umd
13
- * * https://github.com/addyosmani/jquery-plugin-patterns
14
- * *
15
- *
16
- **/
17
-
18
- (function (factory) {
19
- if (typeof define === 'function' && define.amd) {
20
- // AMD
21
- define('condo-broadcaster', factory);
22
- } else {
23
- // Browser globals
24
- factory();
25
- }
26
- }(function (undefined) {
27
- 'use strict';
28
-
29
-
30
- //
31
- //
32
- //
33
-
34
-
35
- angular.module('CondoBroadcaster', []).factory('Condo.Broadcast', ['$rootScope', function($rootScope) {
36
- // eventBroadcaster is the object created by the factory method.
37
- var eventBroadcaster = {};
38
-
39
- // The message is a string or object to carry data with the event.
40
- eventBroadcaster.message = {};
41
-
42
- // The event name is a string used to define event types.
43
- eventBroadcaster.eventName = '';
44
-
45
- // This method is called from within a controller to define an event and attach data to the eventBroadcaster object.
46
- eventBroadcaster.broadcast = function(evName, msg) {
47
- this.message = msg;
48
- this.eventName = evName;
49
- this.broadcastItem();
50
- };
51
-
52
- // This method broadcasts an event with the specified name.
53
- eventBroadcaster.broadcastItem = function() {
54
- $rootScope.$broadcast(this.eventName);
55
- };
56
-
57
- return eventBroadcaster;
58
- }]);
59
-
60
- }));
@@ -1,194 +0,0 @@
1
- /**
2
- * CoTag Condo
3
- * Direct to cloud resumable uploads
4
- *
5
- * Copyright (c) 2012 CoTag Media.
6
- *
7
- * @author Stephen von Takach <steve@cotag.me>
8
- * @copyright 2012 cotag.me
9
- *
10
- *
11
- * References:
12
- * * https://github.com/umdjs/umd
13
- * * https://github.com/addyosmani/jquery-plugin-patterns
14
- * * http://ericterpstra.com/2012/09/angular-cats-part-3-communicating-with-broadcast/
15
- * * http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch
16
- *
17
- **/
18
-
19
- (function (factory) {
20
- if (typeof define === 'function' && define.amd) {
21
- // AMD
22
- define('condo-controller', ['jquery', 'condo-uploader'], factory);
23
- } else {
24
- // Browser globals
25
- factory(jQuery, window.CondoUploader);
26
- }
27
- }(function ($, uploads, undefined) {
28
- 'use strict';
29
-
30
-
31
-
32
- //
33
- // Create a controller for managing the upload states
34
- //
35
- uploads.controller('Condo.Controller', ['$scope', 'Condo.Api', 'Condo.Broadcast', 'Condo.Config', function($scope, api, broadcaster, config) {
36
-
37
- $scope.uploads = [];
38
- $scope.upload_count = 0;
39
-
40
-
41
- //
42
- // See Condo.Config for configuration options
43
- //
44
- $scope.endpoint = config.endpoint;
45
- $scope.autostart = config.autostart;
46
- $scope.ignore_errors = config.ignore_errors; // Continue to autostart after an error?
47
- $scope.parallelism = config.parallelism; // number of uploads at once
48
-
49
-
50
- $scope.add = function(files) {
51
- var length = files.length,
52
- i = 0,
53
- ret = 0, // We only want to check for auto-start after the files have been added
54
- file;
55
-
56
- for (; i < length; i += 1) {
57
- file = files[i];
58
-
59
- if(file.size <= 0 || file.type == '')
60
- continue;
61
-
62
- //
63
- // check file size is acceptable
64
- //
65
- if(!config.file_checker(file) || (config.size_limit != undefined && file.size > config.size_limit)) {
66
- broadcaster.broadcast('coNotice', {
67
- type: 'warn',
68
- number: 0,
69
- file: file
70
- });
71
- continue;
72
- }
73
-
74
- $scope.upload_count += 1;
75
-
76
- api.check_provider($scope.endpoint, files[i]).then(function(upload){
77
- ret += 1;
78
- $scope.uploads.push(upload);
79
- if(ret == length)
80
- $scope.check_autostart();
81
- }, function(failure) {
82
-
83
- $scope.upload_count -= 1;
84
-
85
- ret += 1;
86
- if(ret == length)
87
- $scope.check_autostart();
88
-
89
- //
90
- // broadcast this so it can be handled by a directive
91
- //
92
- broadcaster.broadcast('coNotice', failure);
93
- });
94
- }
95
- };
96
-
97
-
98
- $scope.abort = function(upload) {
99
- upload.abort();
100
- $scope.check_autostart();
101
- };
102
-
103
-
104
- $scope.remove = function(upload) {
105
- //
106
- // Splice(upload, 1) was unreliable. This is better
107
- //
108
- for (var i = 0, length = $scope.uploads.length; i < length; i += 1) {
109
- if($scope.uploads[i] === upload) {
110
- $scope.uploads.splice(i, 1);
111
- $scope.upload_count -= 1;
112
- break;
113
- }
114
- }
115
- };
116
-
117
-
118
- $scope.playpause = function(upload) {
119
- if (upload.state == 3) // Uploading
120
- upload.pause();
121
- else
122
- upload.start();
123
- };
124
-
125
-
126
- //
127
- // Watch autostart and trigger a check when it is changed
128
- //
129
- $scope.$watch('autostart', function(newValue, oldValue) {
130
- if (newValue === true)
131
- $scope.check_autostart();
132
- });
133
-
134
-
135
- //
136
- // Autostart more uploads as this is bumped up
137
- //
138
- $scope.$watch('parallelism', function(newValue, oldValue) {
139
- if(newValue > oldValue)
140
- $scope.check_autostart();
141
- });
142
-
143
-
144
- $scope.check_autostart = function() {
145
- //
146
- // Check if any uploads have been started already
147
- // If there are no active uploads we'll auto-start
148
- //
149
- // PENDING = 0,
150
- // STARTED = 1,
151
- // PAUSED = 2,
152
- // UPLOADING = 3,
153
- // COMPLETED = 4,
154
- // ABORTED = 5
155
- //
156
- if ($scope.autostart) {
157
- var shouldStart = true,
158
- state, i, length, started = 0;
159
-
160
- for (i = 0, length = $scope.uploads.length; i < length; i += 1) {
161
- state = $scope.uploads[i].state;
162
-
163
- //
164
- // Count started uploads (that don't have errors if we are ignoring errors)
165
- // Up until we've reached our parallel limit, then stop
166
- //
167
- if (state > 0 && state < 4 && !($scope.uploads[i].error && $scope.ignore_errors)) {
168
- started += 1;
169
- if(started >= $scope.parallelism) {
170
- shouldStart = false;
171
- break;
172
- }
173
- }
174
- }
175
-
176
- if (shouldStart) {
177
- started = $scope.parallelism - started; // How many can we start
178
-
179
- for (i = 0; i < length; i += 1) {
180
- if ($scope.uploads[i].state == 0) {
181
- $scope.uploads[i].start();
182
-
183
- started -= 1;
184
- if(started <= 0) // Break if we can't start anymore
185
- break;
186
- }
187
- }
188
- }
189
- }
190
- };
191
-
192
- }]);
193
-
194
- }));
@@ -1,310 +0,0 @@
1
- /**
2
- * CoTag Condo
3
- * Direct to cloud resumable uploads
4
- *
5
- * Copyright (c) 2012 CoTag Media.
6
- *
7
- * @author Stephen von Takach <steve@cotag.me>
8
- * @copyright 2012 cotag.me
9
- *
10
- *
11
- * References:
12
- * * https://github.com/umdjs/umd
13
- * * https://github.com/addyosmani/jquery-plugin-patterns
14
- * * http://docs.angularjs.org/api/ng.$http
15
- * * http://docs.angularjs.org/api/ng.$q
16
- *
17
- **/
18
-
19
- (function (factory) {
20
- if (typeof define === 'function' && define.amd) {
21
- // AMD
22
- define('condo-uploader', ['jquery', 'condo-broadcaster'], factory);
23
- } else {
24
- // Browser globals
25
- window.CondoUploader = factory(jQuery);
26
- }
27
- }(function ($) {
28
- 'use strict';
29
-
30
- var uploads = angular.module('CondoUploader', ['CondoBroadcaster']),
31
- residencies = {};
32
-
33
-
34
- //
35
- // Implements the Condo API
36
- //
37
- uploads.factory('Condo.Api', ['$http', '$rootScope', '$q', function($http, $rootScope, $q) {
38
-
39
-
40
- var token = $('meta[name="csrf-token"]').attr('content'),
41
-
42
-
43
- condoConnection = function(api_endpoint, params) {
44
- this.endpoint = api_endpoint; // The API mounting point
45
- this.params = params; // Custom API parameters
46
-
47
- this.upload_id = null; // The current upload ID
48
- this.aborting = false; // Has the user has requested an abort?
49
- this.xhr = null; // Any active cloud file xhr requests
50
- };
51
-
52
-
53
- $http.defaults.headers = {};
54
- $http.defaults.headers['common'] = {'X-Requested-With': 'XMLHttpRequest'};
55
- $http.defaults.headers['post'] = {'X-CSRF-Token': token};
56
- $http.defaults.headers['put'] = {'X-CSRF-Token': token};
57
- $http.defaults.headers['delete'] = {'X-CSRF-Token': token};
58
-
59
- condoConnection.prototype = {
60
-
61
-
62
- //
63
- // Creates an entry in the database for the requested file and returns the upload signature
64
- // If an entry already exists it returns a parts request signature for resumable uploads
65
- //
66
- create: function(options) { // file_id: 123, options: {}
67
- var self = this;
68
- options = options || {};
69
- this.aborting = false;
70
-
71
- if(!!options['file_id'])
72
- this.params['file_id'] = options['file_id'];
73
-
74
- if(!!options['parameters'])
75
- this.params['parameters'] = options['parameters']; // We may be requesting the next set of parts
76
-
77
- return $http({
78
- method: 'POST',
79
- url: this.endpoint,
80
- params: this.params
81
- }).then(function(result){
82
- result = result.data;
83
- self.upload_id = result.upload_id; // Extract the upload id from the results
84
-
85
- if (!self.aborting)
86
- return result;
87
- else
88
- return $q.reject(undefined);
89
- }, function(reason) {
90
- return $q.reject('upload error');
91
- });
92
- },
93
-
94
-
95
- //
96
- // This requests a chunk signature
97
- // Only used for resumable uploads
98
- //
99
- edit: function(part_number, part_id) {
100
- var self = this;
101
- this.aborting = false;
102
-
103
- return $http({
104
- method: 'GET',
105
- url: this.endpoint + '/' + this.upload_id + '/edit',
106
- params: {
107
- part: part_number,
108
- file_id: part_id
109
- }
110
- }).then(function(result){
111
- if (!self.aborting)
112
- return result.data;
113
- else
114
- return $q.reject(undefined);
115
- }, function(reason) {
116
- return $q.reject('upload error');
117
- });
118
- },
119
-
120
-
121
- //
122
- // If resumable id is present the upload is updated
123
- // Otherwise the upload deemed complete
124
- //
125
- update: function(params) { // optional parameters (resumable_id, file_id and part)
126
- var self = this;
127
-
128
- this.aborting = false;
129
- params = params || {};
130
-
131
- return $http({
132
- method: 'PUT',
133
- url: this.endpoint + '/' + this.upload_id,
134
- params: params
135
- }).then(function(result){
136
- if (!self.aborting)
137
- return result.data;
138
- else
139
- return $q.reject(undefined);
140
- }, function(reason) {
141
- if (reason.status == 401 && params.resumable_id == undefined) {
142
- return ''; // User may have paused upload as put was being sent. We should let this through just to update the UI
143
- } else
144
- return $q.reject('upload error');
145
- });
146
- },
147
-
148
-
149
- //
150
- // Cancels a resumable upload
151
- // The actual destruction of the file is handled on the server side as we can't trust the client to do this
152
- // We don't care if this succeeds as the back-end will destroy the file eventually anyway.
153
- //
154
- destroy: function() {
155
- return $http({
156
- method: 'DELETE',
157
- url: this.endpoint + '/' + this.upload_id
158
- });
159
- },
160
-
161
-
162
-
163
- //
164
- // Provides a promise for any request this is what communicated with the cloud storage servers
165
- //
166
- process_request: function(signature, progress_callback) {
167
- var self = this,
168
- result = $q.defer(),
169
- params = {
170
- url: signature.signature.url,
171
- type: signature.signature.verb,
172
- headers: signature.signature.headers,
173
- processData: false,
174
- success: function(response, textStatus, jqXHR) {
175
- self.xhr = null;
176
- result.resolve(response);
177
- },
178
- error: function(jqXHR, textStatus, errorThrown) {
179
- self.xhr = null;
180
- if (!self.aborting)
181
- result.reject('upload error');
182
- else
183
- result.reject(undefined);
184
- },
185
- complete: function(jqXHR, textStatus) {
186
- if(!$rootScope.$$phase) {
187
- $rootScope.$apply(); // This triggers the promise response
188
- }
189
- }
190
- };
191
-
192
- this.aborting = false;
193
-
194
- if (!!self.xhr) {
195
- result.reject('request in progress'); // This is awesome
196
- return result.promise;
197
- }
198
-
199
- if(!!signature.data){
200
- params['data'] = signature.data;
201
- }
202
-
203
- if(!!progress_callback) {
204
- params['xhr'] = function() {
205
- var xhr = $.ajaxSettings.xhr();
206
- if(!!xhr.upload){
207
- xhr.upload.addEventListener('progress', function(e) {
208
- if (e.lengthComputable) {
209
- var phase = $rootScope.$$phase;
210
- if(phase == '$apply' || phase == '$digest') {
211
- progress_callback(e.loaded);
212
- } else {
213
- $rootScope.$apply(function(){
214
- progress_callback(e.loaded);
215
- });
216
- }
217
- }
218
- }, false);
219
- }
220
- return xhr;
221
- };
222
- }
223
-
224
- this.xhr = $.ajax(params);
225
-
226
- return result.promise;
227
- },
228
-
229
-
230
- //
231
- // Will trigger the error call-back of the xhr object
232
- //
233
- abort: function() {
234
- this.aborting = true;
235
- if(!!this.xhr) {
236
- this.xhr.abort();
237
- }
238
- }
239
- };
240
-
241
- return {
242
- //
243
- // Used to determine what upload strategy to use (Amazon, Google, etc)
244
- //
245
- check_provider: function(api_endpoint, the_file, params) {
246
- params = params || {};
247
- params['file_size'] = the_file.size;
248
- params['file_name'] = the_file.name;
249
-
250
- if(!!the_file.dir_path)
251
- params['file_path'] = the_file.dir_path;
252
-
253
- return $http({
254
- method: 'GET',
255
- url: api_endpoint + '/new',
256
- params: params
257
- }).then(function(result){
258
- if(!!residencies[result.data.residence]) {
259
-
260
- var api = new condoConnection(api_endpoint, params);
261
-
262
- //
263
- // TODO:: Check if a file is already in the list and reject if it is
264
- //
265
- return residencies[result.data.residence].new_upload(api, the_file); // return the instantiated provider
266
-
267
- } else {
268
- return $q.reject({
269
- type: 'error',
270
- number: 0,
271
- file: the_file
272
- });
273
- }
274
- }, function(reason) {
275
- if(reason.status == 406) {
276
- return $q.reject({
277
- type: 'warn',
278
- number: 0,
279
- details: reason.data,
280
- file: the_file
281
- });
282
- } else {
283
- return $q.reject({
284
- type: 'warn',
285
- number: 1,
286
- file: the_file
287
- });
288
- }
289
- });
290
- }
291
- };
292
- }]).factory('Condo.Registrar', function(){
293
- return {
294
- //
295
- // Simple dependency injection allows us to load only the providers we need
296
- //
297
- register: function(provider_name, iface) {
298
- residencies[provider_name] = iface;
299
- }
300
- };
301
- });
302
-
303
-
304
-
305
- //
306
- // Anonymous function return
307
- //
308
- return uploads;
309
-
310
- }));