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.
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
@@ -0,0 +1,184 @@
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://ericterpstra.com/2012/09/angular-cats-part-3-communicating-with-broadcast/
13
+ * * http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch
14
+ *
15
+ **/
16
+
17
+
18
+ (function (angular, undefined) {
19
+ 'use strict';
20
+
21
+ //
22
+ // Create a controller for managing the upload states
23
+ //
24
+ angular.module('Condo', ['Core']).
25
+ controller('Condo.Controller', ['$scope', 'Condo.Api', 'Condo.Broadcast', 'Condo.Config', function($scope, api, broadcaster, config) {
26
+
27
+ $scope.uploads = [];
28
+ $scope.upload_count = 0;
29
+
30
+
31
+ //
32
+ // See Condo.Config for configuration options
33
+ //
34
+ $scope.endpoint = config.endpoint;
35
+ $scope.autostart = config.autostart;
36
+ $scope.ignore_errors = config.ignore_errors; // Continue to autostart after an error?
37
+ $scope.parallelism = config.parallelism; // number of uploads at once
38
+
39
+
40
+ $scope.add = function(files) {
41
+ var length = files.length,
42
+ i = 0,
43
+ ret = 0, // We only want to check for auto-start after the files have been added
44
+ file;
45
+
46
+ for (; i < length; i += 1) {
47
+ file = files[i];
48
+
49
+ if(file.size <= 0 || file.type == '')
50
+ continue;
51
+
52
+ //
53
+ // check file size is acceptable
54
+ //
55
+ if(!config.file_checker(file) || (config.size_limit != undefined && file.size > config.size_limit)) {
56
+ broadcaster.publish('coNotice', {
57
+ type: 'warn',
58
+ number: 0,
59
+ file: file
60
+ });
61
+ continue;
62
+ }
63
+
64
+ $scope.upload_count += 1;
65
+
66
+ api.check_provider($scope.endpoint, files[i]).then(function(upload){
67
+ ret += 1;
68
+ $scope.uploads.push(upload);
69
+ if(ret == length)
70
+ $scope.check_autostart();
71
+ }, function(failure) {
72
+
73
+ $scope.upload_count -= 1;
74
+
75
+ ret += 1;
76
+ if(ret == length)
77
+ $scope.check_autostart();
78
+
79
+ //
80
+ // broadcast this so it can be handled by a directive
81
+ //
82
+ broadcaster.publish('coNotice', failure);
83
+ });
84
+ }
85
+ };
86
+
87
+
88
+ $scope.abort = function(upload) {
89
+ upload.abort();
90
+ $scope.check_autostart();
91
+ };
92
+
93
+
94
+ $scope.remove = function(upload) {
95
+ //
96
+ // Splice(upload, 1) was unreliable. This is better
97
+ //
98
+ for (var i = 0, length = $scope.uploads.length; i < length; i += 1) {
99
+ if($scope.uploads[i] === upload) {
100
+ $scope.uploads.splice(i, 1);
101
+ $scope.upload_count -= 1;
102
+ break;
103
+ }
104
+ }
105
+ };
106
+
107
+
108
+ $scope.playpause = function(upload) {
109
+ if (upload.state == 3) // Uploading
110
+ upload.pause();
111
+ else
112
+ upload.start();
113
+ };
114
+
115
+
116
+ //
117
+ // Watch autostart and trigger a check when it is changed
118
+ //
119
+ $scope.$watch('autostart', function(newValue, oldValue) {
120
+ if (newValue === true)
121
+ $scope.check_autostart();
122
+ });
123
+
124
+
125
+ //
126
+ // Autostart more uploads as this is bumped up
127
+ //
128
+ $scope.$watch('parallelism', function(newValue, oldValue) {
129
+ if(newValue > oldValue)
130
+ $scope.check_autostart();
131
+ });
132
+
133
+
134
+ $scope.check_autostart = function() {
135
+ //
136
+ // Check if any uploads have been started already
137
+ // If there are no active uploads we'll auto-start
138
+ //
139
+ // PENDING = 0,
140
+ // STARTED = 1,
141
+ // PAUSED = 2,
142
+ // UPLOADING = 3,
143
+ // COMPLETED = 4,
144
+ // ABORTED = 5
145
+ //
146
+ if ($scope.autostart) {
147
+ var shouldStart = true,
148
+ state, i, length, started = 0;
149
+
150
+ for (i = 0, length = $scope.uploads.length; i < length; i += 1) {
151
+ state = $scope.uploads[i].state;
152
+
153
+ //
154
+ // Count started uploads (that don't have errors if we are ignoring errors)
155
+ // Up until we've reached our parallel limit, then stop
156
+ //
157
+ if (state > 0 && state < 4 && !($scope.uploads[i].error && $scope.ignore_errors)) {
158
+ started += 1;
159
+ if(started >= $scope.parallelism) {
160
+ shouldStart = false;
161
+ break;
162
+ }
163
+ }
164
+ }
165
+
166
+ if (shouldStart) {
167
+ started = $scope.parallelism - started; // How many can we start
168
+
169
+ for (i = 0; i < length; i += 1) {
170
+ if ($scope.uploads[i].state == 0) {
171
+ $scope.uploads[i].start();
172
+
173
+ started -= 1;
174
+ if(started <= 0) // Break if we can't start anymore
175
+ break;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ };
181
+
182
+ }]);
183
+
184
+ })(angular);
@@ -1,80 +1,69 @@
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/AUTO.$provide
15
- * * http://jsfiddle.net/pkozlowski_opensource/PxdSP/14/
16
- *
17
- **/
18
-
19
- (function (factory) {
20
- if (typeof define === 'function' && define.amd) {
21
- // AMD
22
- define('condo-config', ['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 provider for defining the configuration
34
- //
35
- uploads.provider('Condo.Config', function() {
36
-
37
- //
38
- // Controller options
39
- //
40
- this.endpoint = '/uploads'; // Default endpoint path
41
- this.autostart = true; // Start uploading as soon as the file is added?
42
- this.ignore_errors = true; // Continue to autostart after an error?
43
- this.parallelism = 1; // number of autostarted uploads at once
44
- this.size_limit = undefined; // defaults to unlimited
45
- this.file_checker = function(file) { // client side filtering of files
46
- return true;
47
- };
48
-
49
- //
50
- // Directive options (specifically for the condo default interface)
51
- //
52
- this.delegate = undefined; // defaults to the condo interface container
53
- this.drop_targets = undefined; // defaults to the condo interface container
54
- this.hover_class = 'drag-hover'; // for styling the interface
55
- this.supress_notifications = false; // this prevents js alerts about warnings and errors if you are observing these yourself (Condo.Broadcast)
56
-
57
-
58
-
59
- this.$get = function() {
60
- var self = this;
61
-
62
- return {
63
- endpoint: self.endpoint,
64
- autostart: self.autostart,
65
- ignore_errors: self.ignore_errors,
66
- parallelism: self.parallelism,
67
- file_checker: self.file_checker,
68
- size_limit: self.size_limit,
69
-
70
- delegate: self.delegate,
71
- drop_targets: self.drop_targets,
72
- hover_class: self.hover_class,
73
- supress_notifications: self.supress_notifications
74
- };
75
- };
76
- });
77
-
78
-
79
-
80
- }));
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/AUTO.$provide
13
+ * * http://jsfiddle.net/pkozlowski_opensource/PxdSP/14/
14
+ *
15
+ **/
16
+
17
+
18
+ (function (angular, undefined) {
19
+ 'use strict';
20
+
21
+
22
+ //
23
+ // Create a provider for defining the configuration
24
+ //
25
+ angular.module('Condo').
26
+ provider('Condo.Config', function() {
27
+
28
+ //
29
+ // Controller options
30
+ //
31
+ this.endpoint = '/uploads'; // Default endpoint path
32
+ this.autostart = true; // Start uploading as soon as the file is added?
33
+ this.ignore_errors = true; // Continue to autostart after an error?
34
+ this.parallelism = 1; // number of autostarted uploads at once
35
+ this.size_limit = undefined; // defaults to unlimited
36
+ this.file_checker = function(file) { // client side filtering of files
37
+ return true;
38
+ };
39
+
40
+ //
41
+ // Directive options (specifically for the condo default interface)
42
+ //
43
+ this.delegate = undefined; // defaults to the condo interface container
44
+ this.drop_targets = undefined; // defaults to the condo interface container
45
+ this.hover_class = 'drag-hover'; // for styling the interface
46
+ this.supress_notifications = false; // this prevents js alerts about warnings and errors if you are observing these yourself (Condo.Broadcast)
47
+
48
+
49
+
50
+ this.$get = function() {
51
+ var self = this;
52
+
53
+ return {
54
+ endpoint: self.endpoint,
55
+ autostart: self.autostart,
56
+ ignore_errors: self.ignore_errors,
57
+ parallelism: self.parallelism,
58
+ file_checker: self.file_checker,
59
+ size_limit: self.size_limit,
60
+
61
+ delegate: self.delegate,
62
+ drop_targets: self.drop_targets,
63
+ hover_class: self.hover_class,
64
+ supress_notifications: self.supress_notifications
65
+ };
66
+ };
67
+ });
68
+
69
+ })(angular);
@@ -1,255 +1,338 @@
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(['jquery', 'base64', 'condo-uploader'], factory);
22
- } else {
23
- // Browser globals
24
- factory(jQuery, window.base64);
25
- }
26
- }(function ($, base64) {
27
- 'use strict';
28
-
29
- angular.module('CondoGoogleProvider', ['CondoUploader', 'CondoAbstractMd5']).run(['$q', 'Condo.Registrar', 'Condo.Md5', function($q, registrar, md5) {
30
- var PENDING = 0,
31
- STARTED = 1,
32
- PAUSED = 2,
33
- UPLOADING = 3,
34
- COMPLETED = 4,
35
- ABORTED = 5,
36
-
37
-
38
-
39
- hexToBin = function(input) {
40
- var result = "";
41
-
42
- if ((input.length % 2) > 0) {
43
- input = '0' + input;
44
- }
45
-
46
- for (var i = 0, length = input.length; i < length; i += 2) {
47
- result += String.fromCharCode(parseInt(input.slice(i, i + 2), 16));
48
- }
49
-
50
- return result;
51
- },
52
-
53
-
54
- GoogleCloudStorage = function (api, file) {
55
- var self = this,
56
- strategy = null,
57
- part_size = 1048576, // This is the amount of the file we read into memory as we are building the hash (1mb)
58
- pausing = false,
59
- defaultError = function(reason) {
60
- self.error = !pausing;
61
- pausing = false;
62
- self.pause(reason);
63
- },
64
-
65
- restart = function() {
66
- strategy = null;
67
- },
68
-
69
-
70
- completeUpload = function() {
71
- api.update().then(function(data) {
72
- self.state = COMPLETED;
73
- }, defaultError);
74
- },
75
-
76
-
77
- //
78
- // We need to sign our uploads so Google can confirm they are valid for us
79
- //
80
- build_request = function() {
81
- return md5.hash(file).then(function(val) {
82
- return {
83
- data: file,
84
- data_id: base64.encode(hexToBin(val))
85
- }
86
- }, function(reason){
87
- return $q.reject(reason);
88
- });
89
- },
90
-
91
- //
92
- // Direct file upload strategy
93
- //
94
- GoogleDirect = function(data) {
95
- //
96
- // resume
97
- // abort
98
- // pause
99
- //
100
- var $this = this,
101
- finalising = false;
102
-
103
- //
104
- // Update the parent
105
- //
106
- self.state = UPLOADING;
107
-
108
-
109
- //
110
- // This will only be called when the upload has finished and we need to inform the application
111
- //
112
- this.resume = function() {
113
- self.state = UPLOADING;
114
- completeUpload();
115
- }
116
-
117
- this.pause = function() {
118
- api.abort();
119
-
120
- if(!finalising) {
121
- restart(); // Should occur before events triggered
122
- self.progress = 0;
123
- }
124
- };
125
-
126
-
127
- //
128
- // AJAX for upload goes here
129
- //
130
- data['data'] = file;
131
- api.process_request(data, function(progress) {
132
- self.progress = progress;
133
- }).then(function(result) {
134
- finalising = true;
135
- $this.resume(); // Resume informs the application that the upload is complete
136
- }, function(reason) {
137
- self.progress = 0;
138
- defaultError(reason);
139
- });
140
- }, // END DIRECT
141
-
142
-
143
- //
144
- // Resumable upload strategy--------------------------------------------------
145
- //
146
- GoogleResumable = function (data, first_chunk) {
147
-
148
- }; // END RESUMABLE
149
-
150
-
151
- //
152
- // Variables required for all drivers
153
- //
154
- this.state = PENDING;
155
- this.progress = 0;
156
- this.message = 'pending';
157
- this.name = file.name;
158
- this.size = file.size;
159
- this.error = false;
160
-
161
-
162
- //
163
- // Support file slicing
164
- //
165
- if (typeof(file.slice) != 'function')
166
- file.slice = file.webkitSlice || file.mozSlice;
167
-
168
-
169
- this.start = function(){
170
- if(strategy == null) { // We need to create the upload
171
-
172
- this.error = false;
173
- pausing = false;
174
- this.message = null;
175
- this.state = STARTED;
176
- strategy = {}; // This function shouldn't be called twice so we need a state
177
-
178
- build_request().then(function(result) {
179
- if (self.state != STARTED)
180
- return; // upload was paused or aborted as we were reading the file
181
-
182
- api.create({file_id: result.data_id}).
183
- then(function(data) {
184
- if(data.type == 'direct_upload') {
185
- strategy = new GoogleDirect(data);
186
- } else {
187
- strategy = new GoogleResumable(data, result);
188
- }
189
- }, defaultError);
190
-
191
- }, defaultError); // END BUILD_REQUEST
192
-
193
-
194
- } else if (this.state == PAUSED) { // We need to resume the upload if it is paused
195
- this.error = false;
196
- pausing = false;
197
- this.message = null;
198
- strategy.resume();
199
- }
200
- };
201
-
202
- this.pause = function(reason) {
203
- if(strategy != null && this.state == UPLOADING) { // Check if the upload is uploading
204
- this.state = PAUSED;
205
- pausing = true;
206
- strategy.pause();
207
- } else if (this.state <= STARTED) {
208
- this.state = PAUSED;
209
- restart();
210
- }
211
- if(this.state == PAUSED)
212
- this.message = reason;
213
- };
214
-
215
- this.abort = function(reason) {
216
- if(strategy != null && this.state < COMPLETED) { // Check the upload has not finished
217
- var old_state = this.state;
218
-
219
- this.state = ABORTED;
220
- api.abort();
221
-
222
-
223
- //
224
- // As we may not have successfully deleted the upload
225
- // or we aborted before we received a response from create
226
- //
227
- restart(); // nullifies strategy
228
-
229
-
230
- //
231
- // if we have an upload_id then we should destroy the upload
232
- // we won't worry if this fails as it should be automatically cleaned up by the back end
233
- //
234
- if(old_state > STARTED) {
235
- api.destroy();
236
- }
237
-
238
- this.message = reason;
239
- }
240
- };
241
- }; // END GOOGLE
242
-
243
-
244
- //
245
- // Register the residence with the API
246
- // Dependency injection succeeded
247
- //
248
- registrar.register('GoogleCloudStorage', {
249
- new_upload: function(api, file) {
250
- return new GoogleCloudStorage(api, file);
251
- }
252
- });
253
- }]);
254
-
255
- }));
1
+ /**
2
+ * CoTag Condo Google 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, base64, undefined) {
18
+ 'use strict';
19
+
20
+ angular.module('Condo').
21
+
22
+ factory('Condo.Google', ['$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
+
32
+ hexToBin = function(input) {
33
+ var result = "", i, length;
34
+
35
+ if ((input.length % 2) > 0) {
36
+ input = '0' + input;
37
+ }
38
+
39
+ for (i = 0, length = input.length; i < length; i += 2) {
40
+ result += String.fromCharCode(parseInt(input.slice(i, i + 2), 16));
41
+ }
42
+
43
+ return result;
44
+ },
45
+
46
+
47
+ GoogleCloudStorage = function (api, file) {
48
+ var self = this,
49
+ strategy = null,
50
+ pausing = false,
51
+ defaultError = function(reason) {
52
+ self.error = !pausing;
53
+ pausing = false;
54
+ self.pause(reason);
55
+ },
56
+
57
+ restart = function() {
58
+ strategy = null;
59
+ },
60
+
61
+
62
+ completeUpload = function() {
63
+ api.update().then(function() {
64
+ self.progress = self.size; // Update to 100%
65
+ self.state = COMPLETED;
66
+ }, defaultError);
67
+ },
68
+
69
+
70
+ //
71
+ // We need to sign our uploads so Google can confirm they are valid for us
72
+ //
73
+ build_request = function(chunk) {
74
+ return md5.hash(chunk).then(function(val) {
75
+ return {
76
+ data: chunk,
77
+ data_id: base64.encode(hexToBin(val))
78
+ };
79
+ }, function(reason){
80
+ return $q.reject(reason);
81
+ });
82
+ },
83
+
84
+ //
85
+ // Direct file upload strategy
86
+ //
87
+ GoogleDirect = function(data) {
88
+ //
89
+ // resume
90
+ // abort
91
+ // pause
92
+ //
93
+ var $this = this,
94
+ finalising = false;
95
+
96
+ //
97
+ // Update the parent
98
+ //
99
+ self.state = UPLOADING;
100
+
101
+
102
+ //
103
+ // This will only be called when the upload has finished and we need to inform the application
104
+ //
105
+ this.resume = function() {
106
+ self.state = UPLOADING;
107
+ completeUpload();
108
+ };
109
+
110
+ this.pause = function() {
111
+ api.abort();
112
+
113
+ if(!finalising) {
114
+ restart(); // Should occur before events triggered
115
+ self.progress = 0;
116
+ }
117
+ };
118
+
119
+
120
+ //
121
+ // AJAX for upload goes here
122
+ //
123
+ data['data'] = file;
124
+ api.process_request(data, function(progress) {
125
+ self.progress = progress;
126
+ }).then(function() {
127
+ finalising = true;
128
+ $this.resume(); // Resume informs the application that the upload is complete
129
+ }, function(reason) {
130
+ self.progress = 0;
131
+ defaultError(reason);
132
+ });
133
+ }, // END DIRECT
134
+
135
+
136
+ //
137
+ // Resumable upload strategy--------------------------------------------------
138
+ //
139
+ GoogleResumable = function (data, file_hash, finalising) {
140
+ var getQueryParams = function(qs) {
141
+ qs = qs.split("+").join(" ");
142
+
143
+ var params = {}, tokens,
144
+ re = /[?&]?([^=]+)=([^&]*)/g;
145
+
146
+ while (tokens = re.exec(qs)) { // NOTE:: we expect the assignment here
147
+ params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
148
+ }
149
+
150
+ return params;
151
+ },
152
+
153
+
154
+ resume_upload = function(request, file_hash, range_start) {
155
+ request.data = file_hash.data;
156
+ api.process_request(request, function(progress) {
157
+ self.progress = range_start + progress;
158
+ }).then(function(result) {
159
+ finalising = true;
160
+ completeUpload();
161
+ }, function(reason) {
162
+ defaultError(reason);
163
+ });
164
+ };
165
+
166
+
167
+
168
+
169
+ self.state = UPLOADING;
170
+
171
+ this.resume = function() {
172
+ self.state = UPLOADING;
173
+ if (finalising == true) {
174
+ completeUpload();
175
+ } else {
176
+ api.create({file_id: file_hash.data_id}).
177
+ then(function(data) {
178
+ if(data.type == 'direct_upload') {
179
+ strategy = new GoogleDirect(data);
180
+ } else {
181
+ strategy = new GoogleResumable(data, file_hash);
182
+ }
183
+ }, defaultError);
184
+ }
185
+ };
186
+
187
+ this.pause = function() {
188
+ api.abort();
189
+ };
190
+
191
+
192
+
193
+ api.process_request(data).then(function(response) {
194
+ //
195
+ // Check if we were grabbing a parts list or creating an upload
196
+ //
197
+ if(data.type == 'status') { // the request was for the byte we are up to
198
+ // Get the byte we were up to here and update the application
199
+ var range_start = parseInt(response[1].getResponseHeader('Range').split('-')[1], 10) + 1;
200
+
201
+ build_request(file.slice(range_start)).then(function(result) {
202
+ if (self.state != UPLOADING) {
203
+ return; // upload was paused or aborted as we were reading the file
204
+ }
205
+
206
+ api.edit(range_start, result.data_id).
207
+ then(function(data) {
208
+ resume_upload(data, result, range_start);
209
+ }, defaultError);
210
+
211
+ }, defaultError); // END BUILD_REQUEST
212
+ } else {
213
+ //
214
+ // We've created the upload - we need to update our application with the upload id.
215
+ // This will also return the request for uploading the file which we've already prepared
216
+ //
217
+ api.update({
218
+ resumable_id: getQueryParams(response[1].getResponseHeader('Location').split('?')[1]).upload_id, // grab the upload_id from the Location header
219
+ file_id: file_hash.data_id,
220
+ part: 0 // part for google === the byte we are up to
221
+ }).then(function(data) {
222
+ resume_upload(data, file_hash, 0); // As this is the first upload attempt we want to upload from byte 0
223
+ }, function(reason) {
224
+ defaultError(reason);
225
+ restart(); // Easier to start from the beginning
226
+ });
227
+ }
228
+ }, function(reason) {
229
+ defaultError(reason);
230
+ restart(); // We need to get a new request signature
231
+ });
232
+ }; // END RESUMABLE
233
+
234
+
235
+ //
236
+ // Variables required for all drivers
237
+ //
238
+ this.state = PENDING;
239
+ this.progress = 0;
240
+ this.message = 'pending';
241
+ this.name = file.name;
242
+ this.size = file.size;
243
+ this.error = false;
244
+
245
+
246
+ //
247
+ // Support file slicing
248
+ //
249
+ if (typeof(file.slice) != 'function') {
250
+ file.slice = file.webkitSlice || file.mozSlice;
251
+ }
252
+
253
+
254
+ this.start = function(){
255
+ if(strategy == null) { // We need to create the upload
256
+
257
+ this.error = false;
258
+ pausing = false;
259
+ this.message = null;
260
+ this.state = STARTED;
261
+ strategy = {}; // This function shouldn't be called twice so we need a state
262
+
263
+ build_request(file).then(function(result) {
264
+ if (self.state != STARTED) { return; } // upload was paused or aborted as we were reading the file
265
+
266
+ api.create({file_id: result.data_id}).
267
+ then(function(data) {
268
+ if(data.type == 'direct_upload') {
269
+ strategy = new GoogleDirect(data);
270
+ } else {
271
+ strategy = new GoogleResumable(data, result);
272
+ }
273
+ }, defaultError);
274
+
275
+ }, defaultError); // END BUILD_REQUEST
276
+
277
+
278
+ } else if (this.state == PAUSED) { // We need to resume the upload if it is paused
279
+ this.error = false;
280
+ pausing = false;
281
+ this.message = null;
282
+ strategy.resume();
283
+ }
284
+ };
285
+
286
+ this.pause = function(reason) {
287
+ if(strategy != null && this.state == UPLOADING) { // Check if the upload is uploading
288
+ this.state = PAUSED;
289
+ pausing = true;
290
+ strategy.pause();
291
+ } else if (this.state <= STARTED) {
292
+ this.state = PAUSED;
293
+ restart();
294
+ }
295
+ if(this.state == PAUSED) { this.message = reason; }
296
+ };
297
+
298
+ this.abort = function(reason) {
299
+ if(strategy != null && this.state < COMPLETED) { // Check the upload has not finished
300
+ var old_state = this.state;
301
+
302
+ this.state = ABORTED;
303
+ api.abort();
304
+
305
+
306
+ //
307
+ // As we may not have successfully deleted the upload
308
+ // or we aborted before we received a response from create
309
+ //
310
+ restart(); // nullifies strategy
311
+
312
+
313
+ //
314
+ // if we have an upload_id then we should destroy the upload
315
+ // we won't worry if this fails as it should be automatically cleaned up by the back end
316
+ //
317
+ if(old_state > STARTED) {
318
+ api.destroy();
319
+ }
320
+
321
+ this.message = reason;
322
+ }
323
+ };
324
+ }; // END GOOGLE
325
+
326
+
327
+ return {
328
+ new_upload: function(api, file) {
329
+ return new GoogleCloudStorage(api, file);
330
+ }
331
+ };
332
+ }]).
333
+
334
+ config(['Condo.ApiProvider', function (ApiProvider) {
335
+ ApiProvider.register('GoogleCloudStorage', 'Condo.Google');
336
+ }]);
337
+
338
+ })(angular, window.base64);