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,93 +1,86 @@
1
- (function (factory) {
2
- if (typeof define === 'function' && define.amd) {
3
- // AMD
4
- define('condo-abstract-md5', ['jquery', 'condo-broadcaster'], factory);
5
- } else {
6
- // Browser global
7
- factory(jQuery);
8
- }
9
- }(function ($, uploads, undefined) {
10
- 'use strict';
11
-
12
- angular.module('CondoAbstractMd5', ['CondoBroadcaster'])
13
- .factory('Condo.Md5', ['$rootScope', '$q', 'Condo.Broadcast', function($rootScope, $q, broadcaster) {
14
-
15
- var hasher,
16
- queue = [],
17
- ready = false,
18
- processing = undefined,
19
-
20
- //
21
- // Resolves the hashing promise
22
- //
23
- recievedMessage = function(e) {
24
- if(e.data.success) {
25
- processing.result.resolve(e.data.result);
26
- } else {
27
- processing.result.reject(e.data.result);
28
- }
29
-
30
- processing = undefined;
31
- processNext();
32
-
33
- if(!$rootScope.$$phase) {
34
- $rootScope.$apply(); // This triggers the promise response
35
- }
36
- },
37
-
38
- //
39
- // starts processing the next item if the queue is not empty
40
- //
41
- processNext = function() {
42
- if(ready && processing === undefined && queue.length > 0) {
43
- processing = queue.pop();
44
- hasher.postMessage(processing.blob);
45
- }
46
- };
47
-
48
-
49
- if(!!window.Worker) {
50
- hasher = new Worker('<%= asset_path("condo/md5/hash.worker.js") %>');
51
- hasher.onmessage = recievedMessage;
52
- hasher.onerror = function(e) {
53
- ready = false;
54
- broadcaster.broadcast('coNotice', {
55
- type: 'error',
56
- number: 1
57
- });
58
- };
59
- ready = true;
60
- } else {
61
- $.getScript('<%= asset_path("condo/md5/hash.worker.emulator.js") %>', function() {
62
- hasher = new CondoHashWorkerEmulator(recievedMessage);
63
- ready = true;
64
- processNext(); // It is possible
65
- }).fail(function(jqxhr, settings, exception) {
66
- broadcaster.broadcast('coNotice', {
67
- type: 'error',
68
- number: 1
69
- });
70
- });
71
- }
72
-
73
-
74
-
75
- return {
76
- //
77
- // Will queue a start message and return the hash result
78
- //
79
- hash: function(blob) {
80
- var result = $q.defer();
81
-
82
- queue.push({
83
- blob: blob,
84
- result: result
85
- });
86
- processNext();
87
-
88
- return result.promise;
89
- }
90
- };
91
- }]);
92
- }));
93
-
1
+ (function(angular, undefined) {
2
+ 'use strict';
3
+
4
+ angular.module('Condo').
5
+
6
+ factory('Condo.Md5', ['$rootScope', '$q', 'Condo.Broadcast', function($rootScope, $q, broadcaster) {
7
+
8
+ var hasher,
9
+ queue = [],
10
+ ready = false,
11
+ processing = undefined,
12
+
13
+ //
14
+ // Resolves the hashing promise
15
+ //
16
+ recievedMessage = function(e) {
17
+ if(e.data.success) {
18
+ processing.result.resolve(e.data.result);
19
+ } else {
20
+ processing.result.reject(e.data.result);
21
+ }
22
+
23
+ processing = undefined;
24
+ processNext();
25
+
26
+ if(!$rootScope.$$phase) {
27
+ $rootScope.$apply(); // This triggers the promise response
28
+ }
29
+ },
30
+
31
+ //
32
+ // starts processing the next item if the queue is not empty
33
+ //
34
+ processNext = function() {
35
+ if(ready && processing === undefined && queue.length > 0) {
36
+ processing = queue.pop();
37
+ hasher.postMessage(processing.blob);
38
+ }
39
+ };
40
+
41
+
42
+ if(!!window.Worker) {
43
+ hasher = new Worker('<%= asset_path("condo/md5/hash.worker.js") %>');
44
+ hasher.onmessage = recievedMessage;
45
+ hasher.onerror = function(e) {
46
+ ready = false;
47
+ broadcaster.publish('coNotice', {
48
+ type: 'error',
49
+ number: 1
50
+ });
51
+ };
52
+ ready = true;
53
+ } else {
54
+ $.getScript('<%= asset_path("condo/md5/hash.worker.emulator.js") %>', function() {
55
+ hasher = new CondoHashWorkerEmulator(recievedMessage);
56
+ ready = true;
57
+ processNext(); // It is possible
58
+ }).fail(function(jqxhr, settings, exception) {
59
+ broadcaster.publish('coNotice', {
60
+ type: 'error',
61
+ number: 1
62
+ });
63
+ });
64
+ }
65
+
66
+
67
+
68
+ return {
69
+ //
70
+ // Will queue a start message and return the hash result
71
+ //
72
+ hash: function(blob) {
73
+ var result = $q.defer();
74
+
75
+ queue.push({
76
+ blob: blob,
77
+ result: result
78
+ });
79
+ processNext();
80
+
81
+ return result.promise;
82
+ }
83
+ };
84
+ }]);
85
+ })(angular);
86
+
@@ -47,15 +47,7 @@
47
47
  */
48
48
 
49
49
 
50
- (function (factory) {
51
- if (typeof define === 'function' && define.amd) {
52
- // AMD
53
- define('base64', factory);
54
- } else {
55
- // Browser globals
56
- window.base64 = factory();
57
- }
58
- }(function (undefined) {
50
+ window.base64 = (function(undefined) {
59
51
  'use strict';
60
52
 
61
53
  var base64 = {};
@@ -189,4 +181,4 @@
189
181
  }
190
182
 
191
183
  return base64;
192
- }));
184
+ })();
@@ -0,0 +1,26 @@
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
+ * *
13
+ *
14
+ **/
15
+
16
+
17
+ (function(angular, undefined) {
18
+ 'use strict';
19
+
20
+ angular.module('Condo').
21
+
22
+ factory('Condo.Broadcast', ['$channel', function($channel) {
23
+ return $channel.openChannel('once unique');
24
+ }]);
25
+
26
+ })(angular);
@@ -0,0 +1,302 @@
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
+ * * http://docs.angularjs.org/api/ng.$http
13
+ * * http://docs.angularjs.org/api/ng.$q
14
+ *
15
+ **/
16
+
17
+
18
+ (function(jQuery, angular, undefined) { // jQuery required for progress event
19
+ 'use strict';
20
+
21
+ angular.module('Condo').
22
+
23
+ //
24
+ // Implements the Condo API
25
+ //
26
+ provider('Condo.Api', function() {
27
+
28
+ var residencies = {};
29
+
30
+ this.register = function(provider_name, dependency) {
31
+ residencies[provider_name] = dependency;
32
+ };
33
+
34
+
35
+ this.$get = ['$http', '$rootScope', '$q', '$injector', function($http, $rootScope, $q, $injector) {
36
+ var token = $('meta[name="csrf-token"]').attr('content'),
37
+
38
+
39
+ condoConnection = function(api_endpoint, params) {
40
+ this.endpoint = api_endpoint; // The API mounting point
41
+ this.params = params; // Custom API parameters
42
+
43
+ this.upload_id = null; // The current upload ID
44
+ this.aborting = false; // Has the user has requested an abort?
45
+ this.xhr = null; // Any active cloud file xhr requests
46
+ };
47
+
48
+
49
+ // Inject the handlers
50
+ angular.forEach(residencies, function(value, key) {
51
+ residencies[key] = $injector.get(value);
52
+ });
53
+
54
+
55
+ $http.defaults.headers = {};
56
+ $http.defaults.headers['common'] = {'X-Requested-With': 'XMLHttpRequest'};
57
+ $http.defaults.headers['post'] = {'X-CSRF-Token': token};
58
+ $http.defaults.headers['put'] = {'X-CSRF-Token': token};
59
+ $http.defaults.headers['delete'] = {'X-CSRF-Token': token};
60
+
61
+ condoConnection.prototype = {
62
+
63
+
64
+ //
65
+ // Creates an entry in the database for the requested file and returns the upload signature
66
+ // If an entry already exists it returns a parts request signature for resumable uploads
67
+ //
68
+ create: function(options) { // file_id: 123, options: {}
69
+ var self = this;
70
+ options = options || {};
71
+ this.aborting = false;
72
+
73
+ if(!!options['file_id'])
74
+ this.params['file_id'] = options['file_id'];
75
+
76
+ if(!!options['parameters'])
77
+ this.params['parameters'] = options['parameters']; // We may be requesting the next set of parts
78
+
79
+ return $http({
80
+ method: 'POST',
81
+ url: this.endpoint,
82
+ params: this.params
83
+ }).then(function(result){
84
+ result = result.data;
85
+ self.upload_id = result.upload_id; // Extract the upload id from the results
86
+
87
+ if (!self.aborting)
88
+ return result;
89
+ else
90
+ return $q.reject(undefined);
91
+ }, function(reason) {
92
+ return $q.reject('upload error');
93
+ });
94
+ },
95
+
96
+
97
+ //
98
+ // This requests a chunk signature
99
+ // Only used for resumable uploads
100
+ //
101
+ edit: function(part_number, part_id) {
102
+ var self = this;
103
+ this.aborting = false;
104
+
105
+ return $http({
106
+ method: 'GET',
107
+ url: this.endpoint + '/' + this.upload_id + '/edit',
108
+ params: {
109
+ part: part_number,
110
+ file_id: part_id
111
+ }
112
+ }).then(function(result){
113
+ if (!self.aborting)
114
+ return result.data;
115
+ else
116
+ return $q.reject(undefined);
117
+ }, function(reason) {
118
+ return $q.reject('upload error');
119
+ });
120
+ },
121
+
122
+
123
+ //
124
+ // If resumable id is present the upload is updated
125
+ // Otherwise the upload deemed complete
126
+ //
127
+ update: function(params) { // optional parameters (resumable_id, file_id and part)
128
+ var self = this;
129
+
130
+ this.aborting = false;
131
+ params = params || {};
132
+
133
+ return $http({
134
+ method: 'PUT',
135
+ url: this.endpoint + '/' + this.upload_id,
136
+ params: params
137
+ }).then(function(result){
138
+ if (!self.aborting)
139
+ return result.data;
140
+ else
141
+ return $q.reject(undefined);
142
+ }, function(reason) {
143
+ if (reason.status == 401 && params.resumable_id == undefined) {
144
+ return ''; // User may have paused upload as put was being sent. We should let this through just to update the UI
145
+ } else
146
+ return $q.reject('upload error');
147
+ });
148
+ },
149
+
150
+
151
+ //
152
+ // Cancels a resumable upload
153
+ // The actual destruction of the file is handled on the server side as we can't trust the client to do this
154
+ // We don't care if this succeeds as the back-end will destroy the file eventually anyway.
155
+ //
156
+ destroy: function() {
157
+ return $http({
158
+ method: 'DELETE',
159
+ url: this.endpoint + '/' + this.upload_id
160
+ });
161
+ },
162
+
163
+
164
+
165
+ //
166
+ // Provides a promise for any request this is what communicated with the cloud storage servers
167
+ //
168
+ process_request: function(signature, progress_callback) {
169
+ var self = this,
170
+ result = $q.defer(),
171
+ params = {
172
+ url: signature.signature.url,
173
+ type: signature.signature.verb,
174
+ headers: signature.signature.headers,
175
+ processData: false,
176
+ success: function(response, textStatus, jqXHR) {
177
+ self.xhr = null;
178
+ result.resolve([response, jqXHR]);
179
+ },
180
+ error: function(jqXHR, textStatus, errorThrown) {
181
+ if(jqXHR.status === signature.expected) {
182
+ self.xhr = null;
183
+ result.resolve([errorThrown, jqXHR]);
184
+ } else {
185
+ self.xhr = null;
186
+ if (!self.aborting)
187
+ result.reject('upload error');
188
+ else
189
+ result.reject(undefined);
190
+ }
191
+ },
192
+ complete: function(jqXHR, textStatus) {
193
+ if(!$rootScope.$$phase) {
194
+ $rootScope.$apply(); // This triggers the promise response
195
+ }
196
+ }
197
+ };
198
+
199
+ this.aborting = false;
200
+
201
+ if (!!self.xhr) {
202
+ result.reject('request in progress'); // This is awesome
203
+ return result.promise;
204
+ }
205
+
206
+ if(!!signature.data){
207
+ params['data'] = signature.data;
208
+ }
209
+
210
+ if(!!progress_callback) {
211
+ params['xhr'] = function() {
212
+ var xhr = jQuery.ajaxSettings.xhr();
213
+ if(!!xhr.upload){
214
+ xhr.upload.addEventListener('progress', function(e) {
215
+ if (e.lengthComputable) {
216
+ var phase = $rootScope.$$phase;
217
+ if(phase == '$apply' || phase == '$digest') {
218
+ progress_callback(e.loaded);
219
+ } else {
220
+ $rootScope.$apply(function(){
221
+ progress_callback(e.loaded);
222
+ });
223
+ }
224
+ }
225
+ }, false);
226
+ }
227
+ return xhr;
228
+ };
229
+ }
230
+
231
+ this.xhr = jQuery.ajax(params);
232
+
233
+ return result.promise;
234
+ },
235
+
236
+
237
+ //
238
+ // Will trigger the error call-back of the xhr object
239
+ //
240
+ abort: function() {
241
+ this.aborting = true;
242
+ if(!!this.xhr) {
243
+ this.xhr.abort();
244
+ }
245
+ }
246
+ };
247
+
248
+ return {
249
+ //
250
+ // Used to determine what upload strategy to use (Amazon, Google, etc)
251
+ //
252
+ check_provider: function(api_endpoint, the_file, params) {
253
+ params = params || {};
254
+ params['file_size'] = the_file.size;
255
+ params['file_name'] = the_file.name;
256
+
257
+ if(!!the_file.dir_path)
258
+ params['file_path'] = the_file.dir_path;
259
+
260
+ return $http({
261
+ method: 'GET',
262
+ url: api_endpoint + '/new',
263
+ params: params
264
+ }).then(function(result){
265
+ if(!!residencies[result.data.residence]) {
266
+
267
+ var api = new condoConnection(api_endpoint, params);
268
+
269
+ //
270
+ // TODO:: Check if a file is already in the list and reject if it is
271
+ //
272
+ return residencies[result.data.residence].new_upload(api, the_file); // return the instantiated provider
273
+
274
+ } else {
275
+ return $q.reject({
276
+ type: 'error',
277
+ number: 0,
278
+ file: the_file
279
+ });
280
+ }
281
+ }, function(reason) {
282
+ if(reason.status == 406) {
283
+ return $q.reject({
284
+ type: 'warn',
285
+ number: 0,
286
+ details: reason.data,
287
+ file: the_file
288
+ });
289
+ } else {
290
+ return $q.reject({
291
+ type: 'warn',
292
+ number: 1,
293
+ file: the_file
294
+ });
295
+ }
296
+ });
297
+ }
298
+ };
299
+ }];
300
+ });
301
+
302
+ })(jQuery, angular);