condo 1.0.6 → 2.0.0
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 +4 -4
- data/README.textile +19 -32
- data/lib/condo.rb +124 -127
- data/lib/condo/configuration.rb +41 -76
- data/lib/condo/engine.rb +32 -39
- data/lib/condo/errors.rb +6 -8
- data/lib/condo/strata/amazon_s3.rb +246 -294
- data/lib/condo/strata/google_cloud_storage.rb +238 -272
- data/lib/condo/strata/open_stack_swift.rb +251 -0
- data/lib/condo/version.rb +1 -1
- metadata +31 -96
- data/app/assets/javascripts/condo.js +0 -9
- data/app/assets/javascripts/condo/amazon.js +0 -403
- data/app/assets/javascripts/condo/condo.js +0 -184
- data/app/assets/javascripts/condo/config.js +0 -69
- data/app/assets/javascripts/condo/google.js +0 -338
- data/app/assets/javascripts/condo/md5/hash.worker.emulator.js +0 -23
- data/app/assets/javascripts/condo/md5/hash.worker.js +0 -11
- data/app/assets/javascripts/condo/md5/hasher.js +0 -119
- data/app/assets/javascripts/condo/md5/spark-md5.js +0 -599
- data/app/assets/javascripts/condo/rackspace.js +0 -326
- data/app/assets/javascripts/condo/services/abstract-md5.js.erb +0 -86
- data/app/assets/javascripts/condo/services/base64.js +0 -184
- data/app/assets/javascripts/condo/services/broadcaster.js +0 -26
- data/app/assets/javascripts/condo/services/uploader.js +0 -302
- data/app/assets/javascripts/core/core.js +0 -4
- data/app/assets/javascripts/core/services/1-safe-apply.js +0 -17
- data/app/assets/javascripts/core/services/2-messaging.js +0 -171
- data/lib/condo/strata/rackspace_cloud_files.rb +0 -245
- data/test/condo_test.rb +0 -27
- data/test/dummy/README.rdoc +0 -261
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/assets/javascripts/application.js +0 -15
- data/test/dummy/app/assets/stylesheets/application.css +0 -13
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -59
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -25
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -37
- data/test/dummy/config/environments/production.rb +0 -67
- data/test/dummy/config/environments/test.rb +0 -37
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -15
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -58
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -25
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +0 -6
- data/test/integration/navigation_test.rb +0 -10
- data/test/test_helper.rb +0 -15
@@ -1,326 +0,0 @@
|
|
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);
|
@@ -1,86 +0,0 @@
|
|
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
|
-
|
@@ -1,184 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* Copyright (c) 2010 Nick Galbreath
|
3
|
-
* http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript
|
4
|
-
*
|
5
|
-
* Permission is hereby granted, free of charge, to any person
|
6
|
-
* obtaining a copy of this software and associated documentation
|
7
|
-
* files (the "Software"), to deal in the Software without
|
8
|
-
* restriction, including without limitation the rights to use,
|
9
|
-
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
-
* copies of the Software, and to permit persons to whom the
|
11
|
-
* Software is furnished to do so, subject to the following
|
12
|
-
* conditions:
|
13
|
-
*
|
14
|
-
* The above copyright notice and this permission notice shall be
|
15
|
-
* included in all copies or substantial portions of the Software.
|
16
|
-
*
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
-
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
-
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
-
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
-
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
-
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
-
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
-
* OTHER DEALINGS IN THE SOFTWARE.
|
25
|
-
*/
|
26
|
-
|
27
|
-
/* base64 encode/decode compatible with window.btoa/atob
|
28
|
-
*
|
29
|
-
* window.atob/btoa is a Firefox extension to convert binary data (the "b")
|
30
|
-
* to base64 (ascii, the "a").
|
31
|
-
*
|
32
|
-
* It is also found in Safari and Chrome. It is not available in IE.
|
33
|
-
*
|
34
|
-
* if (!window.btoa) window.btoa = base64.encode
|
35
|
-
* if (!window.atob) window.atob = base64.decode
|
36
|
-
*
|
37
|
-
* The original spec's for atob/btoa are a bit lacking
|
38
|
-
* https://developer.mozilla.org/en/DOM/window.atob
|
39
|
-
* https://developer.mozilla.org/en/DOM/window.btoa
|
40
|
-
*
|
41
|
-
* window.btoa and base64.encode takes a string where charCodeAt is [0,255]
|
42
|
-
* If any character is not [0,255], then an DOMException(5) is thrown.
|
43
|
-
*
|
44
|
-
* window.atob and base64.decode take a base64-encoded string
|
45
|
-
* If the input length is not a multiple of 4, or contains invalid characters
|
46
|
-
* then an DOMException(5) is thrown.
|
47
|
-
*/
|
48
|
-
|
49
|
-
|
50
|
-
window.base64 = (function(undefined) {
|
51
|
-
'use strict';
|
52
|
-
|
53
|
-
var base64 = {};
|
54
|
-
base64.PADCHAR = '=';
|
55
|
-
base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
56
|
-
|
57
|
-
base64.makeDOMException = function() {
|
58
|
-
// sadly in FF,Safari,Chrome you can't make a DOMException
|
59
|
-
var e, tmp;
|
60
|
-
|
61
|
-
try {
|
62
|
-
return new DOMException(DOMException.INVALID_CHARACTER_ERR);
|
63
|
-
} catch (tmp) {
|
64
|
-
// not available, just passback a duck-typed equiv
|
65
|
-
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error
|
66
|
-
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/prototype
|
67
|
-
var ex = new Error("DOM Exception 5");
|
68
|
-
|
69
|
-
// ex.number and ex.description is IE-specific.
|
70
|
-
ex.code = ex.number = 5;
|
71
|
-
ex.name = ex.description = "INVALID_CHARACTER_ERR";
|
72
|
-
|
73
|
-
// Safari/Chrome output format
|
74
|
-
ex.toString = function() { return 'Error: ' + ex.name + ': ' + ex.message; };
|
75
|
-
return ex;
|
76
|
-
}
|
77
|
-
}
|
78
|
-
|
79
|
-
base64.getbyte64 = function(s,i) {
|
80
|
-
// This is oddly fast, except on Chrome/V8.
|
81
|
-
// Minimal or no improvement in performance by using a
|
82
|
-
// object with properties mapping chars to value (eg. 'A': 0)
|
83
|
-
var idx = base64.ALPHA.indexOf(s.charAt(i));
|
84
|
-
if (idx === -1) {
|
85
|
-
throw base64.makeDOMException();
|
86
|
-
}
|
87
|
-
return idx;
|
88
|
-
}
|
89
|
-
|
90
|
-
base64.decode = function(s) {
|
91
|
-
// convert to string
|
92
|
-
s = '' + s;
|
93
|
-
var getbyte64 = base64.getbyte64;
|
94
|
-
var pads, i, b10;
|
95
|
-
var imax = s.length
|
96
|
-
if (imax === 0) {
|
97
|
-
return s;
|
98
|
-
}
|
99
|
-
|
100
|
-
if (imax % 4 !== 0) {
|
101
|
-
throw base64.makeDOMException();
|
102
|
-
}
|
103
|
-
|
104
|
-
pads = 0
|
105
|
-
if (s.charAt(imax - 1) === base64.PADCHAR) {
|
106
|
-
pads = 1;
|
107
|
-
if (s.charAt(imax - 2) === base64.PADCHAR) {
|
108
|
-
pads = 2;
|
109
|
-
}
|
110
|
-
// either way, we want to ignore this last block
|
111
|
-
imax -= 4;
|
112
|
-
}
|
113
|
-
|
114
|
-
var x = [];
|
115
|
-
for (i = 0; i < imax; i += 4) {
|
116
|
-
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
|
117
|
-
(getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
|
118
|
-
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
|
119
|
-
}
|
120
|
-
|
121
|
-
switch (pads) {
|
122
|
-
case 1:
|
123
|
-
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6);
|
124
|
-
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
|
125
|
-
break;
|
126
|
-
case 2:
|
127
|
-
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
|
128
|
-
x.push(String.fromCharCode(b10 >> 16));
|
129
|
-
break;
|
130
|
-
}
|
131
|
-
return x.join('');
|
132
|
-
}
|
133
|
-
|
134
|
-
base64.getbyte = function(s,i) {
|
135
|
-
var x = s.charCodeAt(i);
|
136
|
-
if (x > 255) {
|
137
|
-
throw base64.makeDOMException();
|
138
|
-
}
|
139
|
-
return x;
|
140
|
-
}
|
141
|
-
|
142
|
-
base64.encode = function(s) {
|
143
|
-
if (arguments.length !== 1) {
|
144
|
-
throw new SyntaxError("Not enough arguments");
|
145
|
-
}
|
146
|
-
var padchar = base64.PADCHAR;
|
147
|
-
var alpha = base64.ALPHA;
|
148
|
-
var getbyte = base64.getbyte;
|
149
|
-
|
150
|
-
var i, b10;
|
151
|
-
var x = [];
|
152
|
-
|
153
|
-
// convert to string
|
154
|
-
s = '' + s;
|
155
|
-
|
156
|
-
var imax = s.length - s.length % 3;
|
157
|
-
|
158
|
-
if (s.length === 0) {
|
159
|
-
return s;
|
160
|
-
}
|
161
|
-
for (i = 0; i < imax; i += 3) {
|
162
|
-
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
|
163
|
-
x.push(alpha.charAt(b10 >> 18));
|
164
|
-
x.push(alpha.charAt((b10 >> 12) & 0x3F));
|
165
|
-
x.push(alpha.charAt((b10 >> 6) & 0x3f));
|
166
|
-
x.push(alpha.charAt(b10 & 0x3f));
|
167
|
-
}
|
168
|
-
switch (s.length - imax) {
|
169
|
-
case 1:
|
170
|
-
b10 = getbyte(s,i) << 16;
|
171
|
-
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
|
172
|
-
padchar + padchar);
|
173
|
-
break;
|
174
|
-
case 2:
|
175
|
-
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
|
176
|
-
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
|
177
|
-
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
|
178
|
-
break;
|
179
|
-
}
|
180
|
-
return x.join('');
|
181
|
-
}
|
182
|
-
|
183
|
-
return base64;
|
184
|
-
})();
|