condo 1.0.4 → 1.0.6
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.
- checksums.yaml +7 -0
- data/README.textile +133 -133
- data/app/assets/javascripts/condo.js +9 -6
- data/app/assets/javascripts/condo/amazon.js +403 -406
- data/app/assets/javascripts/condo/condo.js +184 -0
- data/app/assets/javascripts/condo/config.js +69 -80
- data/app/assets/javascripts/condo/google.js +338 -255
- data/app/assets/javascripts/condo/md5/hash.worker.emulator.js +23 -23
- data/app/assets/javascripts/condo/md5/hash.worker.js +11 -11
- data/app/assets/javascripts/condo/md5/hasher.js +119 -100
- data/app/assets/javascripts/condo/md5/spark-md5.js +276 -161
- data/app/assets/javascripts/condo/rackspace.js +326 -329
- data/app/assets/javascripts/condo/{abstract-md5.js.erb → services/abstract-md5.js.erb} +86 -93
- data/app/assets/javascripts/condo/{base64.js → services/base64.js} +2 -10
- data/app/assets/javascripts/condo/services/broadcaster.js +26 -0
- data/app/assets/javascripts/condo/services/uploader.js +302 -0
- data/app/assets/javascripts/core/core.js +4 -0
- data/app/assets/javascripts/core/services/1-safe-apply.js +17 -0
- data/app/assets/javascripts/core/services/2-messaging.js +171 -0
- data/lib/condo.rb +269 -269
- data/lib/condo/configuration.rb +137 -139
- data/lib/condo/errors.rb +8 -8
- data/lib/condo/strata/amazon_s3.rb +301 -301
- data/lib/condo/strata/google_cloud_storage.rb +315 -314
- data/lib/condo/strata/rackspace_cloud_files.rb +245 -223
- data/lib/condo/version.rb +1 -1
- metadata +21 -44
- data/app/assets/javascripts/condo/broadcaster.js +0 -60
- data/app/assets/javascripts/condo/controller.js +0 -194
- data/app/assets/javascripts/condo/uploader.js +0 -310
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -25
@@ -1,329 +1,326 @@
|
|
1
|
-
/**
|
2
|
-
* CoTag Condo Rackspace Cloud Files Strategy
|
3
|
-
* Direct to cloud resumable uploads for Rackspace Cloud Files
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
return
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
//
|
94
|
-
//
|
95
|
-
//
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
//
|
100
|
-
//
|
101
|
-
//
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
//
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
})
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
//
|
145
|
-
//
|
146
|
-
//
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
}
|
192
|
-
|
193
|
-
|
194
|
-
}
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
this.
|
228
|
-
this.
|
229
|
-
this.
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
}
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
//
|
298
|
-
//
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
//
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
}]);
|
328
|
-
|
329
|
-
}));
|
1
|
+
/**
|
2
|
+
* CoTag Condo Rackspace Cloud Files Strategy
|
3
|
+
* Direct to cloud resumable uploads for Rackspace Cloud Files
|
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.Rackspace', ['$q', 'Condo.Md5', function($q, md5) {
|
23
|
+
var PENDING = 0,
|
24
|
+
STARTED = 1,
|
25
|
+
PAUSED = 2,
|
26
|
+
UPLOADING = 3,
|
27
|
+
COMPLETED = 4,
|
28
|
+
ABORTED = 5,
|
29
|
+
|
30
|
+
|
31
|
+
Rackspace = function (api, file) {
|
32
|
+
var self = this,
|
33
|
+
strategy = null,
|
34
|
+
part_size = 2097152, // Multi-part uploads should be bigger then this
|
35
|
+
pausing = false,
|
36
|
+
defaultError = function(reason) {
|
37
|
+
self.error = !pausing;
|
38
|
+
pausing = false;
|
39
|
+
self.pause(reason);
|
40
|
+
},
|
41
|
+
|
42
|
+
restart = function() {
|
43
|
+
strategy = null;
|
44
|
+
},
|
45
|
+
|
46
|
+
|
47
|
+
completeUpload = function() {
|
48
|
+
api.update().then(function(data) {
|
49
|
+
self.progress = self.size; // Update to 100%
|
50
|
+
self.state = COMPLETED;
|
51
|
+
}, defaultError);
|
52
|
+
},
|
53
|
+
|
54
|
+
|
55
|
+
//
|
56
|
+
// We need to sign our uploads so rackspace can confirm they are valid for us
|
57
|
+
//
|
58
|
+
build_request = function(part_number) {
|
59
|
+
var current_part;
|
60
|
+
|
61
|
+
if (file.size > part_size) { // If file bigger then 5mb we expect a chunked upload
|
62
|
+
var endbyte = part_number * part_size;
|
63
|
+
if (endbyte > file.size)
|
64
|
+
endbyte = file.size;
|
65
|
+
current_part = file.slice((part_number - 1) * part_size, endbyte);
|
66
|
+
} else {
|
67
|
+
current_part = file;
|
68
|
+
}
|
69
|
+
|
70
|
+
return md5.hash(current_part).then(function(val) {
|
71
|
+
return {
|
72
|
+
data: current_part,
|
73
|
+
data_id: val,
|
74
|
+
part_number: part_number
|
75
|
+
}
|
76
|
+
}, function(reason){
|
77
|
+
return $q.reject(reason);
|
78
|
+
});
|
79
|
+
},
|
80
|
+
|
81
|
+
//
|
82
|
+
// Direct file upload strategy
|
83
|
+
//
|
84
|
+
RackspaceDirect = function(data) {
|
85
|
+
//
|
86
|
+
// resume
|
87
|
+
// abort
|
88
|
+
// pause
|
89
|
+
//
|
90
|
+
var $this = this,
|
91
|
+
finalising = false;
|
92
|
+
|
93
|
+
//
|
94
|
+
// Update the parent
|
95
|
+
//
|
96
|
+
self.state = UPLOADING;
|
97
|
+
|
98
|
+
|
99
|
+
//
|
100
|
+
// This will only be called when the upload has finished and we need to inform the application
|
101
|
+
//
|
102
|
+
this.resume = function() {
|
103
|
+
self.state = UPLOADING;
|
104
|
+
completeUpload();
|
105
|
+
}
|
106
|
+
|
107
|
+
this.pause = function() {
|
108
|
+
api.abort();
|
109
|
+
|
110
|
+
if(!finalising) {
|
111
|
+
restart(); // Should occur before events triggered
|
112
|
+
self.progress = 0;
|
113
|
+
}
|
114
|
+
};
|
115
|
+
|
116
|
+
|
117
|
+
//
|
118
|
+
// AJAX for upload goes here
|
119
|
+
//
|
120
|
+
data['data'] = file;
|
121
|
+
api.process_request(data, function(progress) {
|
122
|
+
self.progress = progress;
|
123
|
+
}).then(function(result) {
|
124
|
+
finalising = true;
|
125
|
+
$this.resume(); // Resume informs the application that the upload is complete
|
126
|
+
}, function(reason) {
|
127
|
+
self.progress = 0;
|
128
|
+
defaultError(reason);
|
129
|
+
});
|
130
|
+
}, // END DIRECT
|
131
|
+
|
132
|
+
|
133
|
+
//
|
134
|
+
// Chunked upload strategy--------------------------------------------------
|
135
|
+
//
|
136
|
+
RackspaceChunked = function (data, first_chunk) {
|
137
|
+
//
|
138
|
+
// resume
|
139
|
+
// abort
|
140
|
+
// pause
|
141
|
+
//
|
142
|
+
var last_part = 0,
|
143
|
+
|
144
|
+
//
|
145
|
+
// Get the next part signature
|
146
|
+
//
|
147
|
+
next_part = function(part_number) {
|
148
|
+
//
|
149
|
+
// Check if we are past the end of the file
|
150
|
+
//
|
151
|
+
if ((part_number - 1) * part_size < file.size) {
|
152
|
+
|
153
|
+
self.progress = (part_number - 1) * part_size; // Update the progress
|
154
|
+
|
155
|
+
build_request(part_number).then(function(result) {
|
156
|
+
if (self.state != UPLOADING)
|
157
|
+
return; // upload was paused or aborted as we were reading the file
|
158
|
+
|
159
|
+
api.update({
|
160
|
+
resumable_id: part_number,
|
161
|
+
file_id: result.data_id,
|
162
|
+
part: part_number
|
163
|
+
}).then(function(data) {
|
164
|
+
set_part(data, result);
|
165
|
+
}, defaultError);
|
166
|
+
|
167
|
+
}, defaultError); // END BUILD_REQUEST
|
168
|
+
|
169
|
+
} else {
|
170
|
+
//
|
171
|
+
// We're after the final commit
|
172
|
+
//
|
173
|
+
api.edit('finish').
|
174
|
+
then(function(request) {
|
175
|
+
api.process_request(request).then(completeUpload, defaultError);
|
176
|
+
}, defaultError);
|
177
|
+
}
|
178
|
+
},
|
179
|
+
|
180
|
+
|
181
|
+
//
|
182
|
+
// Send a part to rackspace
|
183
|
+
//
|
184
|
+
set_part = function(request, part_info) {
|
185
|
+
request['data'] = part_info.data;
|
186
|
+
api.process_request(request, function(progress) {
|
187
|
+
self.progress = (part_info.part_number - 1) * part_size + progress;
|
188
|
+
}).then(function(result) {
|
189
|
+
last_part = part_info.part_number;
|
190
|
+
next_part(last_part + 1);
|
191
|
+
}, function(reason) {
|
192
|
+
self.progress = (part_info.part_number - 1) * part_size;
|
193
|
+
defaultError(reason);
|
194
|
+
});
|
195
|
+
};
|
196
|
+
|
197
|
+
|
198
|
+
self.state = UPLOADING;
|
199
|
+
|
200
|
+
this.resume = function() {
|
201
|
+
self.state = UPLOADING;
|
202
|
+
next_part(last_part + 1);
|
203
|
+
};
|
204
|
+
|
205
|
+
this.pause = function() {
|
206
|
+
api.abort();
|
207
|
+
};
|
208
|
+
|
209
|
+
|
210
|
+
//
|
211
|
+
// We need to check if we are resuming or starting an upload
|
212
|
+
//
|
213
|
+
if(data.type == 'parts') {
|
214
|
+
next_part(data.current_part);
|
215
|
+
} else {
|
216
|
+
set_part(data, first_chunk);
|
217
|
+
}
|
218
|
+
}; // END CHUNKED
|
219
|
+
|
220
|
+
|
221
|
+
//
|
222
|
+
// Variables required for all drivers
|
223
|
+
//
|
224
|
+
this.state = PENDING;
|
225
|
+
this.progress = 0;
|
226
|
+
this.message = 'pending';
|
227
|
+
this.name = file.name;
|
228
|
+
this.size = file.size;
|
229
|
+
this.error = false;
|
230
|
+
|
231
|
+
|
232
|
+
//
|
233
|
+
// Support file slicing
|
234
|
+
//
|
235
|
+
if (typeof(file.slice) != 'function')
|
236
|
+
file.slice = file.webkitSlice || file.mozSlice;
|
237
|
+
|
238
|
+
|
239
|
+
this.start = function(){
|
240
|
+
if(strategy == null) { // We need to create the upload
|
241
|
+
|
242
|
+
pausing = false;
|
243
|
+
this.error = false;
|
244
|
+
this.message = null;
|
245
|
+
this.state = STARTED;
|
246
|
+
strategy = {}; // This function shouldn't be called twice so we need a state (TODO:: fix this)
|
247
|
+
|
248
|
+
build_request(1).then(function(result) {
|
249
|
+
if (self.state != STARTED)
|
250
|
+
return; // upload was paused or aborted as we were reading the file
|
251
|
+
|
252
|
+
api.create({file_id: result.data_id}).
|
253
|
+
then(function(data) {
|
254
|
+
if(data.type == 'direct_upload') {
|
255
|
+
strategy = new RackspaceDirect(data);
|
256
|
+
} else {
|
257
|
+
strategy = new RackspaceChunked(data, result);
|
258
|
+
}
|
259
|
+
}, defaultError);
|
260
|
+
|
261
|
+
}, defaultError); // END BUILD_REQUEST
|
262
|
+
|
263
|
+
|
264
|
+
} else if (this.state == PAUSED) { // We need to resume the upload if it is paused
|
265
|
+
|
266
|
+
pausing = false;
|
267
|
+
this.error = false;
|
268
|
+
this.message = null;
|
269
|
+
strategy.resume();
|
270
|
+
}
|
271
|
+
};
|
272
|
+
|
273
|
+
this.pause = function(reason) {
|
274
|
+
if(strategy != null && this.state == UPLOADING) { // Check if the upload is uploading
|
275
|
+
this.state = PAUSED;
|
276
|
+
pausing = true;
|
277
|
+
strategy.pause();
|
278
|
+
} else if (this.state <= STARTED) {
|
279
|
+
this.state = PAUSED;
|
280
|
+
restart();
|
281
|
+
}
|
282
|
+
if(this.state == PAUSED)
|
283
|
+
this.message = reason;
|
284
|
+
};
|
285
|
+
|
286
|
+
this.abort = function(reason) {
|
287
|
+
if(strategy != null && this.state < COMPLETED) { // Check the upload has not finished
|
288
|
+
var old_state = this.state;
|
289
|
+
|
290
|
+
this.state = ABORTED;
|
291
|
+
api.abort();
|
292
|
+
|
293
|
+
|
294
|
+
//
|
295
|
+
// As we may not have successfully deleted the upload
|
296
|
+
// or we aborted before we received a response from create
|
297
|
+
//
|
298
|
+
restart(); // nullifies strategy
|
299
|
+
|
300
|
+
|
301
|
+
//
|
302
|
+
// if we have an upload_id then we should destroy the upload
|
303
|
+
// we won't worry if this fails as it should be automatically cleaned up by the back end
|
304
|
+
//
|
305
|
+
if(old_state > STARTED) {
|
306
|
+
api.destroy();
|
307
|
+
}
|
308
|
+
|
309
|
+
this.message = reason;
|
310
|
+
}
|
311
|
+
};
|
312
|
+
}; // END RACKSPACE
|
313
|
+
|
314
|
+
|
315
|
+
return {
|
316
|
+
new_upload: function(api, file) {
|
317
|
+
return new Rackspace(api, file);
|
318
|
+
}
|
319
|
+
};
|
320
|
+
}]).
|
321
|
+
|
322
|
+
config(['Condo.ApiProvider', function (ApiProvider) {
|
323
|
+
ApiProvider.register('RackspaceCloudFiles', 'Condo.Rackspace');
|
324
|
+
}]);
|
325
|
+
|
326
|
+
})(angular);
|