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
@@ -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);