condo 1.0.0 → 1.0.1
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.
- data/README.textile +4 -3
- data/app/assets/javascripts/condo.js +2 -4
- data/app/assets/javascripts/condo/abstract-md5.js.erb +87 -0
- data/app/assets/javascripts/condo/amazon.js +17 -34
- data/app/assets/javascripts/condo/broadcaster.js +54 -0
- data/app/assets/javascripts/condo/controller.js +2 -28
- data/app/assets/javascripts/condo/google.js +17 -61
- data/app/assets/javascripts/condo/md5/hash.worker.emulator.js +23 -0
- data/app/assets/javascripts/condo/md5/hash.worker.js +11 -0
- data/app/assets/javascripts/condo/md5/hasher.js +66 -0
- data/app/assets/javascripts/condo/{spark-md5.js → md5/spark-md5.js} +140 -126
- data/app/assets/javascripts/condo/rackspace.js +19 -36
- data/app/assets/javascripts/condo/uploader.js +15 -10
- data/lib/condo/version.rb +1 -1
- metadata +8 -3
data/README.textile
CHANGED
@@ -32,8 +32,8 @@ This has numerous advantages over traditional Form Data style post uploads too.
|
|
32
32
|
* Resumability when uploading large files
|
33
33
|
|
34
34
|
|
35
|
-
Support for all major browsers
|
36
|
-
* Tested in Firefox 4, Safari 6
|
35
|
+
Support for all major browsers
|
36
|
+
* Tested in Firefox 4, Safari 6, Chromes latest stable and IE10 (IE10 on Win7)
|
37
37
|
|
38
38
|
|
39
39
|
h2. Usage
|
@@ -73,7 +73,8 @@ See the "example application":https://github.com/cotag/condo_example which imple
|
|
73
73
|
If you are using "Condo Interface":https://github.com/cotag/condo_interface then you may want to do the following:
|
74
74
|
# Create an index for your controller @def index; end@
|
75
75
|
# Create an index.html.erb in your view with:
|
76
|
-
|
76
|
+
# Make sure your AngularJS app includes: @angular.module('YourApp', ['CondoUploader', 'CondoInterface']);@
|
77
|
+
#* @<div data-ng-app="YourApp"><%= render "condo_interface/upload" %></div>@
|
77
78
|
|
78
79
|
Alternative you could load an AngularJS template linking to <%= asset_path('templates/_upload.html') %>
|
79
80
|
|
@@ -0,0 +1,87 @@
|
|
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('coComponentLoadFailed', ['MD5', 'file fingerprinting']); // Critical error
|
55
|
+
};
|
56
|
+
ready = true;
|
57
|
+
} else {
|
58
|
+
$.getScript('<%= asset_path("condo/md5/hash.worker.emulator.js") %>', function() {
|
59
|
+
hasher = new CondoHashWorkerEmulator(recievedMessage);
|
60
|
+
ready = true;
|
61
|
+
processNext(); // It is possible
|
62
|
+
}).fail(function(jqxhr, settings, exception) {
|
63
|
+
broadcaster.broadcast('coComponentLoadFailed', ['MD5', 'file fingerprinting']);
|
64
|
+
});
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
return {
|
70
|
+
//
|
71
|
+
// Will queue a start message and return the hash result
|
72
|
+
//
|
73
|
+
hash: function(blob) {
|
74
|
+
var result = $q.defer();
|
75
|
+
|
76
|
+
queue.push({
|
77
|
+
blob: blob,
|
78
|
+
result: result
|
79
|
+
});
|
80
|
+
processNext();
|
81
|
+
|
82
|
+
return result.promise;
|
83
|
+
}
|
84
|
+
};
|
85
|
+
}]);
|
86
|
+
}));
|
87
|
+
|
@@ -18,20 +18,15 @@
|
|
18
18
|
(function (factory) {
|
19
19
|
if (typeof define === 'function' && define.amd) {
|
20
20
|
// AMD
|
21
|
-
define(['jquery', '
|
21
|
+
define(['jquery', 'base64', 'condo-uploader'], factory);
|
22
22
|
} else {
|
23
23
|
// Browser globals
|
24
|
-
factory(jQuery, window.
|
24
|
+
factory(jQuery, window.base64);
|
25
25
|
}
|
26
|
-
}(function ($,
|
26
|
+
}(function ($, base64) {
|
27
27
|
'use strict';
|
28
28
|
|
29
|
-
|
30
|
-
// TODO:: Create an Amazon, google factory etc
|
31
|
-
// We should split all these into different files too (controller and factories separate from directives and views)
|
32
|
-
// So we can have different views for the same controller
|
33
|
-
//
|
34
|
-
uploads.factory('Condo.AmazonS3', ['$rootScope', '$q', function($rootScope, $q) {
|
29
|
+
angular.module('CondoAmazonProvider', ['CondoUploader', 'CondoAbstractMd5']).run(['$q', 'Condo.Registrar', 'Condo.Md5', function($q, registrar, md5) {
|
35
30
|
var PENDING = 0,
|
36
31
|
STARTED = 1,
|
37
32
|
PAUSED = 2,
|
@@ -82,16 +77,9 @@
|
|
82
77
|
//
|
83
78
|
// We need to sign our uploads so amazon can confirm they are valid for us
|
84
79
|
// Part numbers can be any number from 1 to 10,000 - inclusive
|
85
|
-
// TODO:: use http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast
|
86
|
-
// where available :)
|
87
80
|
//
|
88
81
|
build_request = function(part_number) {
|
89
|
-
var
|
90
|
-
reader = new FileReader(),
|
91
|
-
fail = function(){
|
92
|
-
result.reject('file read failed');
|
93
|
-
},
|
94
|
-
current_part;
|
82
|
+
var current_part;
|
95
83
|
|
96
84
|
if (file.size > part_size) { // If file bigger then 5mb we expect a chunked upload
|
97
85
|
var endbyte = part_number * part_size;
|
@@ -102,23 +90,15 @@
|
|
102
90
|
current_part = file;
|
103
91
|
}
|
104
92
|
|
105
|
-
|
106
|
-
|
93
|
+
return md5.hash(current_part).then(function(val) {
|
94
|
+
return {
|
107
95
|
data: current_part,
|
108
|
-
data_id:
|
96
|
+
data_id: val,
|
109
97
|
part_number: part_number
|
110
|
-
});
|
111
|
-
|
112
|
-
|
113
|
-
if(!$rootScope.$$phase) {
|
114
|
-
$rootScope.$apply(); // This triggers the promise response if required
|
115
98
|
}
|
116
|
-
}
|
117
|
-
|
118
|
-
|
119
|
-
reader.readAsBinaryString(current_part);
|
120
|
-
|
121
|
-
return result.promise;
|
99
|
+
}, function(reason){
|
100
|
+
return $q.reject(reason);
|
101
|
+
});
|
122
102
|
},
|
123
103
|
|
124
104
|
//
|
@@ -412,12 +392,15 @@
|
|
412
392
|
}; // END AMAZON
|
413
393
|
|
414
394
|
|
415
|
-
|
395
|
+
//
|
396
|
+
// Register the residence with the API
|
397
|
+
// Dependency injection succeeded
|
398
|
+
//
|
399
|
+
registrar.register('AmazonS3', {
|
416
400
|
new_upload: function(api, file) {
|
417
401
|
return new Amazon(api, file);
|
418
402
|
}
|
419
|
-
};
|
420
|
-
|
403
|
+
});
|
421
404
|
}]);
|
422
405
|
|
423
406
|
}));
|
@@ -0,0 +1,54 @@
|
|
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('condo-broadcaster', factory);
|
22
|
+
} else {
|
23
|
+
// Browser globals
|
24
|
+
factory();
|
25
|
+
}
|
26
|
+
}(function (undefined) {
|
27
|
+
'use strict';
|
28
|
+
|
29
|
+
angular.module('CondoBroadcaster', []).factory('Condo.Broadcast', ['$rootScope', function($rootScope) {
|
30
|
+
// eventBroadcaster is the object created by the factory method.
|
31
|
+
var eventBroadcaster = {};
|
32
|
+
|
33
|
+
// The message is a string or object to carry data with the event.
|
34
|
+
eventBroadcaster.message = '';
|
35
|
+
|
36
|
+
// The event name is a string used to define event types.
|
37
|
+
eventBroadcaster.eventName = '';
|
38
|
+
|
39
|
+
// This method is called from within a controller to define an event and attach data to the eventBroadcaster object.
|
40
|
+
eventBroadcaster.broadcast = function(evName, msg) {
|
41
|
+
this.message = msg;
|
42
|
+
this.eventName = evName;
|
43
|
+
this.broadcastItem();
|
44
|
+
};
|
45
|
+
|
46
|
+
// This method broadcasts an event with the specified name.
|
47
|
+
eventBroadcaster.broadcastItem = function() {
|
48
|
+
$rootScope.$broadcast(this.eventName);
|
49
|
+
};
|
50
|
+
|
51
|
+
return eventBroadcaster;
|
52
|
+
}]);
|
53
|
+
|
54
|
+
}));
|
@@ -19,7 +19,7 @@
|
|
19
19
|
(function (factory) {
|
20
20
|
if (typeof define === 'function' && define.amd) {
|
21
21
|
// AMD
|
22
|
-
define('
|
22
|
+
define('condo-controller', ['jquery', 'condo-uploader'], factory);
|
23
23
|
} else {
|
24
24
|
// Browser globals
|
25
25
|
window.CondoController = factory(jQuery, window.CondoUploader);
|
@@ -32,33 +32,7 @@
|
|
32
32
|
//
|
33
33
|
// Create a controller for managing the upload states
|
34
34
|
//
|
35
|
-
uploads.
|
36
|
-
// eventBroadcaster is the object created by the factory method.
|
37
|
-
var eventBroadcaster = {};
|
38
|
-
|
39
|
-
// The message is a string or object to carry data with the event.
|
40
|
-
eventBroadcaster.message = '';
|
41
|
-
|
42
|
-
// The event name is a string used to define event types.
|
43
|
-
eventBroadcaster.eventName = '';
|
44
|
-
|
45
|
-
// This method is called from within a controller to define an event and attach data to the eventBroadcaster object.
|
46
|
-
eventBroadcaster.broadcast = function(evName, msg) {
|
47
|
-
this.message = msg;
|
48
|
-
this.eventName = evName;
|
49
|
-
this.broadcastItem();
|
50
|
-
};
|
51
|
-
|
52
|
-
// This method broadcasts an event with the specified name.
|
53
|
-
eventBroadcaster.broadcastItem = function() {
|
54
|
-
$rootScope.$broadcast(this.eventName);
|
55
|
-
};
|
56
|
-
|
57
|
-
return eventBroadcaster;
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
}]).controller('Condo.Controller', ['$scope', 'Condo.Api', 'Condo.Broadcast', function($scope, api, broadcaster) {
|
35
|
+
uploads.controller('Condo.Controller', ['$scope', 'Condo.Api', 'Condo.Broadcast', function($scope, api, broadcaster) {
|
62
36
|
|
63
37
|
$scope.uploads = [];
|
64
38
|
$scope.upload_count = 0;
|
@@ -18,20 +18,15 @@
|
|
18
18
|
(function (factory) {
|
19
19
|
if (typeof define === 'function' && define.amd) {
|
20
20
|
// AMD
|
21
|
-
define(['jquery', '
|
21
|
+
define(['jquery', 'base64', 'condo-uploader'], factory);
|
22
22
|
} else {
|
23
23
|
// Browser globals
|
24
|
-
factory(jQuery, window.
|
24
|
+
factory(jQuery, window.base64);
|
25
25
|
}
|
26
|
-
}(function ($,
|
26
|
+
}(function ($, base64) {
|
27
27
|
'use strict';
|
28
28
|
|
29
|
-
|
30
|
-
// TODO:: Create an Amazon, google factory etc
|
31
|
-
// We should split all these into different files too (controller and factories separate from directives and views)
|
32
|
-
// So we can have different views for the same controller
|
33
|
-
//
|
34
|
-
uploads.factory('Condo.GoogleCloudStorage', ['$rootScope', '$q', function($rootScope, $q) {
|
29
|
+
angular.module('CondoGoogleProvider', ['CondoUploader', 'CondoAbstractMd5']).run(['$q', 'Condo.Registrar', 'Condo.Md5', function($q, registrar, md5) {
|
35
30
|
var PENDING = 0,
|
36
31
|
STARTED = 1,
|
37
32
|
PAUSED = 2,
|
@@ -81,57 +76,15 @@
|
|
81
76
|
|
82
77
|
//
|
83
78
|
// We need to sign our uploads so Google can confirm they are valid for us
|
84
|
-
// TODO:: use http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast
|
85
|
-
// where available :) - especially important since we have to hash the entire file
|
86
79
|
//
|
87
|
-
build_request = function(
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
},
|
93
|
-
current_part;
|
94
|
-
|
95
|
-
if (part_number == 1) {
|
96
|
-
hash = new MD5();
|
97
|
-
}
|
98
|
-
|
99
|
-
if (file.size > part_size) { // If file bigger then 5mb we expect a chunked upload
|
100
|
-
var endbyte = part_number * part_size;
|
101
|
-
if (endbyte > file.size)
|
102
|
-
endbyte = file.size;
|
103
|
-
current_part = file.slice((part_number - 1) * part_size, endbyte);
|
104
|
-
} else {
|
105
|
-
current_part = file;
|
106
|
-
}
|
107
|
-
|
108
|
-
reader.onload = function(e) {
|
109
|
-
hash.appendBinary(e.target.result);
|
110
|
-
result.resolve(hash);
|
111
|
-
|
112
|
-
|
113
|
-
if(!$rootScope.$$phase) {
|
114
|
-
$rootScope.$apply(); // This triggers the promise response if required
|
115
|
-
}
|
116
|
-
};
|
117
|
-
reader.onerror = fail;
|
118
|
-
reader.onabort = fail;
|
119
|
-
reader.readAsBinaryString(current_part);
|
120
|
-
|
121
|
-
//
|
122
|
-
// Chaining promises means the UI will have a chance to update
|
123
|
-
//
|
124
|
-
return result.promise.then(function(val){
|
125
|
-
if ((part_number * part_size) < file.size) {
|
126
|
-
return build_request(part_number + 1, val);
|
127
|
-
} else {
|
128
|
-
return {
|
129
|
-
data: file,
|
130
|
-
data_id: base64.encode(hexToBin(val.end()))
|
131
|
-
}
|
80
|
+
build_request = function() {
|
81
|
+
return md5.hash(file).then(function(val) {
|
82
|
+
return {
|
83
|
+
data: file,
|
84
|
+
data_id: base64.encode(hexToBin(val))
|
132
85
|
}
|
133
86
|
}, function(reason){
|
134
|
-
$q.reject(reason);
|
87
|
+
return $q.reject(reason);
|
135
88
|
});
|
136
89
|
},
|
137
90
|
|
@@ -222,7 +175,7 @@
|
|
222
175
|
this.state = STARTED;
|
223
176
|
strategy = {}; // This function shouldn't be called twice so we need a state
|
224
177
|
|
225
|
-
build_request(
|
178
|
+
build_request().then(function(result) {
|
226
179
|
if (self.state != STARTED)
|
227
180
|
return; // upload was paused or aborted as we were reading the file
|
228
181
|
|
@@ -288,12 +241,15 @@
|
|
288
241
|
}; // END GOOGLE
|
289
242
|
|
290
243
|
|
291
|
-
|
244
|
+
//
|
245
|
+
// Register the residence with the API
|
246
|
+
// Dependency injection succeeded
|
247
|
+
//
|
248
|
+
registrar.register('GoogleCloudStorage', {
|
292
249
|
new_upload: function(api, file) {
|
293
250
|
return new GoogleCloudStorage(api, file);
|
294
251
|
}
|
295
|
-
};
|
296
|
-
|
252
|
+
});
|
297
253
|
}]);
|
298
254
|
|
299
255
|
}));
|
@@ -0,0 +1,23 @@
|
|
1
|
+
//= require condo/md5/spark-md5
|
2
|
+
//= require condo/md5/hasher
|
3
|
+
|
4
|
+
|
5
|
+
function CondoHashWorkerEmulator(callback) {
|
6
|
+
|
7
|
+
// Create an API that looks like postMessage
|
8
|
+
this.postMessage = function (data, portArray) {
|
9
|
+
hasher.hash(data); // Clone the data if required JSON.parse(JSON.stringify(message)); // - Don't think it is required
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
this.terminate = function () {
|
14
|
+
// No special clean-up needed.
|
15
|
+
}
|
16
|
+
|
17
|
+
function messageEvtEmulator(rawMessage) {
|
18
|
+
callback({ data: rawMessage });
|
19
|
+
}
|
20
|
+
|
21
|
+
// Create an instance of downloader.
|
22
|
+
var hasher = new CondoMD5Hasher(messageEvtEmulator);
|
23
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
var CondoMD5Hasher = (function(global) {
|
4
|
+
|
5
|
+
var part_size = 1048576; // This is the amount of the file we read into memory as we are building the hash (1mb)
|
6
|
+
|
7
|
+
return function(callback) {
|
8
|
+
|
9
|
+
|
10
|
+
//
|
11
|
+
// responds with: {success: true|false, result: <Object>}
|
12
|
+
//
|
13
|
+
this.hash = function(blob) {
|
14
|
+
|
15
|
+
var current_part,
|
16
|
+
md5 = new global.SparkMD5(),
|
17
|
+
reader = new FileReader(),
|
18
|
+
part_number = 0,
|
19
|
+
length = Math.ceil(blob.size / part_size),
|
20
|
+
fail = function() {
|
21
|
+
callback({
|
22
|
+
success: false,
|
23
|
+
result: 'file read failed'
|
24
|
+
});
|
25
|
+
},
|
26
|
+
hashData = function(e) {
|
27
|
+
md5.appendBinary(e.target.result);
|
28
|
+
if(part_number * part_size >= blob.size) {
|
29
|
+
callback({
|
30
|
+
success: true,
|
31
|
+
result: md5.end()
|
32
|
+
});
|
33
|
+
} else {
|
34
|
+
processPart();
|
35
|
+
}
|
36
|
+
},
|
37
|
+
processPart = function() {
|
38
|
+
var endbyte = 0;
|
39
|
+
|
40
|
+
part_number += 1;
|
41
|
+
|
42
|
+
if (blob.size > part_size) { // If blob bigger then part_size we will slice it up
|
43
|
+
endbyte = part_number * part_size;
|
44
|
+
if (endbyte > blob.size)
|
45
|
+
endbyte = blob.size;
|
46
|
+
|
47
|
+
current_part = blob.slice((part_number - 1) * part_size, endbyte);
|
48
|
+
} else {
|
49
|
+
current_part = blob;
|
50
|
+
}
|
51
|
+
|
52
|
+
reader.readAsArrayBuffer(current_part);
|
53
|
+
};
|
54
|
+
|
55
|
+
|
56
|
+
reader.onload = hashData;
|
57
|
+
reader.onerror = fail;
|
58
|
+
reader.onabort = fail;
|
59
|
+
|
60
|
+
|
61
|
+
processPart();
|
62
|
+
};
|
63
|
+
};
|
64
|
+
|
65
|
+
})(this);
|
66
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/*
|
1
|
+
/*jshint bitwise:false*/
|
2
2
|
/*global unescape*/
|
3
3
|
|
4
4
|
/**
|
@@ -45,7 +45,7 @@
|
|
45
45
|
* document.getElementById("file").addEventListener("change", function() {
|
46
46
|
*
|
47
47
|
* var fileReader = new FileReader(),
|
48
|
-
* blobSlice = File.prototype.
|
48
|
+
* blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
|
49
49
|
* file = document.getElementById("file").files[0],
|
50
50
|
* chunkSize = 2097152, // read in chunks of 2MB
|
51
51
|
* chunks = Math.ceil(file.size / chunkSize),
|
@@ -76,24 +76,55 @@
|
|
76
76
|
* loadNext();
|
77
77
|
* });
|
78
78
|
*
|
79
|
-
* @TODO: Add support for byteArrays.
|
80
|
-
* @TODO: Add support for HMAC.
|
81
79
|
* @TODO: Add native support for reading files? Maybe add it as an extension?
|
82
80
|
*/
|
83
81
|
(function (factory) {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
if (typeof exports === 'object') {
|
83
|
+
// Node/CommonJS
|
84
|
+
module.exports = factory();
|
85
|
+
} else if (typeof define === 'function' && define.amd) {
|
88
86
|
// AMD
|
89
87
|
define('spark-md5', factory);
|
90
88
|
} else {
|
91
|
-
// Browser globals
|
92
|
-
|
89
|
+
// Browser globals (with support for web workers)
|
90
|
+
var glob;
|
91
|
+
try {
|
92
|
+
glob = window;
|
93
|
+
} catch (e) {
|
94
|
+
glob = self;
|
95
|
+
}
|
96
|
+
|
97
|
+
glob.SparkMD5 = factory();
|
93
98
|
}
|
94
99
|
}(function (undefined) {
|
95
100
|
|
96
|
-
|
101
|
+
'use strict';
|
102
|
+
|
103
|
+
|
104
|
+
/* converts strings to array buffers
|
105
|
+
* From: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
|
106
|
+
*/
|
107
|
+
var str2ab = function (str) {
|
108
|
+
var buf = new ArrayBuffer(str.length * 2), // 2 bytes for each char
|
109
|
+
bufView = new Uint16Array(buf);
|
110
|
+
for (var i = 0, strLen = str.length; i < strLen; i++) {
|
111
|
+
bufView[i] = str.charCodeAt(i);
|
112
|
+
}
|
113
|
+
return buf;
|
114
|
+
},
|
115
|
+
|
116
|
+
|
117
|
+
abConcat = function(first, second)
|
118
|
+
{
|
119
|
+
var firstLength = first.length,
|
120
|
+
result = new Uint8Array(firstLength + second.byteLength);
|
121
|
+
|
122
|
+
result.set(first);
|
123
|
+
result.set(new Uint8Array(second), firstLength);
|
124
|
+
|
125
|
+
return result;
|
126
|
+
},
|
127
|
+
|
97
128
|
|
98
129
|
////////////////////////////////////////////////////////////////////////////
|
99
130
|
|
@@ -104,38 +135,33 @@
|
|
104
135
|
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
|
105
136
|
* @see http://jsperf.com/md5-shootout/7
|
106
137
|
*/
|
107
|
-
|
108
|
-
|
109
|
-
so if possible we use it. Some IEs
|
110
|
-
are the only ones I know of that
|
111
|
-
need the idiotic second function,
|
112
|
-
generated by an if clause. */
|
113
|
-
var add32 = function (a, b) {
|
138
|
+
|
139
|
+
add32 = function (a, b) {
|
114
140
|
return (a + b) & 0xFFFFFFFF;
|
115
|
-
}
|
141
|
+
},
|
116
142
|
|
117
|
-
function
|
143
|
+
cmn = function (q, a, b, x, s, t) {
|
118
144
|
a = add32(add32(a, q), add32(x, t));
|
119
145
|
return add32((a << s) | (a >>> (32 - s)), b);
|
120
|
-
}
|
146
|
+
},
|
121
147
|
|
122
|
-
function
|
148
|
+
ff = function(a, b, c, d, x, s, t) {
|
123
149
|
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
124
|
-
}
|
150
|
+
},
|
125
151
|
|
126
|
-
function
|
152
|
+
gg = function(a, b, c, d, x, s, t) {
|
127
153
|
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
128
|
-
}
|
154
|
+
},
|
129
155
|
|
130
|
-
function
|
156
|
+
hh = function(a, b, c, d, x, s, t) {
|
131
157
|
return cmn(b ^ c ^ d, a, b, x, s, t);
|
132
|
-
}
|
158
|
+
},
|
133
159
|
|
134
|
-
function
|
160
|
+
ii = function(a, b, c, d, x, s, t) {
|
135
161
|
return cmn(c ^ (b | (~d)), a, b, x, s, t);
|
136
|
-
}
|
162
|
+
},
|
137
163
|
|
138
|
-
function
|
164
|
+
md5cycle = function(x, k) {
|
139
165
|
var a = x[0],
|
140
166
|
b = x[1],
|
141
167
|
c = x[2],
|
@@ -213,47 +239,46 @@
|
|
213
239
|
x[1] = add32(b, x[1]);
|
214
240
|
x[2] = add32(c, x[2]);
|
215
241
|
x[3] = add32(d, x[3]);
|
242
|
+
},
|
216
243
|
|
217
|
-
|
218
|
-
|
219
|
-
/* there needs to be support for Unicode here,
|
220
|
-
* unless we pretend that we can redefine the MD-5
|
221
|
-
* algorithm for multi-byte characters (perhaps
|
222
|
-
* by adding every four 16-bit characters and
|
223
|
-
* shortening the sum to 32 bits). Otherwise
|
224
|
-
* I suggest performing MD-5 as if every character
|
225
|
-
* was two bytes--e.g., 0040 0025 = @%--but then
|
226
|
-
* how will an ordinary MD-5 sum be matched?
|
227
|
-
* There is no way to standardize text to something
|
228
|
-
* like UTF-8 before transformation; speed cost is
|
229
|
-
* utterly prohibitive. The JavaScript standard
|
230
|
-
* itself needs to look at this: it should start
|
231
|
-
* providing access to strings as preformed UTF-8
|
232
|
-
* 8-bit unsigned value arrays.
|
233
|
-
*/
|
234
|
-
function md5blk(s) { /* I figured global was faster. */
|
244
|
+
md5blk = function(s) { /* I figured global was faster. */
|
235
245
|
var md5blks = [],
|
236
246
|
i; /* Andy King said do it this way. */
|
237
247
|
for (i = 0; i < 64; i += 4) {
|
238
|
-
md5blks[i >> 2] = s
|
248
|
+
md5blks[i >> 2] = s[i] + (s[i + 1] << 8) + (s[i + 2] << 16) + (s[i + 3] << 24);
|
239
249
|
}
|
240
250
|
return md5blks;
|
241
|
-
}
|
251
|
+
},
|
242
252
|
|
243
|
-
function
|
253
|
+
md51 = function(s) {
|
244
254
|
var n = s.length,
|
245
255
|
state = [1732584193, -271733879, -1732584194, 271733878],
|
246
256
|
i,
|
247
257
|
length,
|
248
|
-
tail
|
258
|
+
tail,
|
259
|
+
tmp,
|
260
|
+
lo,
|
261
|
+
hi;
|
262
|
+
|
249
263
|
for (i = 64; i <= n; i += 64) {
|
250
|
-
md5cycle(state, md5blk(s.
|
264
|
+
md5cycle(state, md5blk(s.subarray(i - 64, i)));
|
251
265
|
}
|
252
|
-
|
266
|
+
|
267
|
+
//
|
268
|
+
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
|
269
|
+
// containing the last element of the parent array if the sub array specified starts
|
270
|
+
// beyond the length of the parent array - weird.
|
271
|
+
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
|
272
|
+
//
|
273
|
+
if((i - 64) < n)
|
274
|
+
s = s.subarray(i - 64);
|
275
|
+
else
|
276
|
+
s = new Uint8Array(0);
|
277
|
+
|
253
278
|
length = s.length;
|
254
279
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
255
280
|
for (i = 0; i < length; i += 1) {
|
256
|
-
tail[i >> 2] |= s
|
281
|
+
tail[i >> 2] |= s[i] << ((i % 4) << 3);
|
257
282
|
}
|
258
283
|
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
259
284
|
if (i > 55) {
|
@@ -262,43 +287,42 @@
|
|
262
287
|
tail[i] = 0;
|
263
288
|
}
|
264
289
|
}
|
265
|
-
|
290
|
+
|
291
|
+
// Beware that the final length might not fit in 32 bits so we take care of that
|
292
|
+
tmp = n * 8;
|
293
|
+
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
294
|
+
lo = parseInt(tmp[2], 16);
|
295
|
+
hi = parseInt(tmp[1], 16) || 0;
|
296
|
+
|
297
|
+
tail[14] = lo;
|
298
|
+
tail[15] = hi;
|
299
|
+
|
266
300
|
md5cycle(state, tail);
|
267
301
|
return state;
|
268
|
-
}
|
302
|
+
},
|
269
303
|
|
270
|
-
|
271
|
-
var hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
272
|
-
/*jslint vars: false*/
|
304
|
+
hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],
|
273
305
|
|
274
|
-
function
|
306
|
+
rhex = function(n) {
|
275
307
|
var s = '',
|
276
308
|
j;
|
277
309
|
for (j = 0; j < 4; j += 1) {
|
278
310
|
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
|
279
311
|
}
|
280
312
|
return s;
|
281
|
-
}
|
313
|
+
},
|
282
314
|
|
283
|
-
function
|
315
|
+
hex = function(x) {
|
284
316
|
var i;
|
285
317
|
for (i = 0; i < x.length; i += 1) {
|
286
318
|
x[i] = rhex(x[i]);
|
287
319
|
}
|
288
320
|
return x.join('');
|
289
|
-
}
|
321
|
+
},
|
290
322
|
|
291
|
-
function
|
323
|
+
md5 = function(s) {
|
292
324
|
return hex(md51(s));
|
293
|
-
}
|
294
|
-
|
295
|
-
if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {
|
296
|
-
add32 = function (x, y) {
|
297
|
-
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
|
298
|
-
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
299
|
-
return (msw << 16) | (lsw & 0xFFFF);
|
300
|
-
};
|
301
|
-
}
|
325
|
+
},
|
302
326
|
|
303
327
|
////////////////////////////////////////////////////////////////////////////
|
304
328
|
|
@@ -308,29 +332,24 @@
|
|
308
332
|
* Use this class to perform an incremental md5, otherwise use the
|
309
333
|
* static methods instead.
|
310
334
|
*/
|
311
|
-
|
312
|
-
var SparkMD5 = function () {
|
313
|
-
/*jslint vars: false*/
|
335
|
+
SparkMD5 = function () {
|
314
336
|
// call reset to init the instance
|
315
337
|
this.reset();
|
316
|
-
};
|
338
|
+
};
|
339
|
+
|
317
340
|
|
318
341
|
/**
|
319
342
|
* Appends a string.
|
320
|
-
*
|
343
|
+
* Converts to a byte array.
|
321
344
|
*
|
322
345
|
* @param {String} str The string to be appended
|
323
346
|
*
|
324
347
|
* @return {SparkMD5} The instance itself
|
325
348
|
*/
|
326
349
|
SparkMD5.prototype.append = function (str) {
|
327
|
-
// converts the string to utf8 bytes if necessary
|
328
|
-
if (/[\u0080-\uFFFF]/.test(str)) {
|
329
|
-
str = unescape(encodeURIComponent(str));
|
330
|
-
}
|
331
350
|
|
332
|
-
// then append as binary
|
333
|
-
this.appendBinary(str);
|
351
|
+
// convert to array then append as binary
|
352
|
+
this.appendBinary(str2ab(str));
|
334
353
|
|
335
354
|
return this;
|
336
355
|
};
|
@@ -338,38 +357,27 @@
|
|
338
357
|
/**
|
339
358
|
* Appends a binary string.
|
340
359
|
*
|
341
|
-
* @param {
|
360
|
+
* @param {ArrayBuffer} contents The binary string to be appended
|
342
361
|
*
|
343
362
|
* @return {SparkMD5} The instance itself
|
344
363
|
*/
|
345
364
|
SparkMD5.prototype.appendBinary = function (contents) {
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
if (sub.length >= 64) { // if there is 64 bytes accumulated
|
355
|
-
|
356
|
-
md5cycle(this._state, md5blk(sub));
|
357
|
-
|
358
|
-
total = contents.length - 64;
|
359
|
-
|
360
|
-
// while we got bytes to process
|
361
|
-
while (offset <= total) {
|
362
|
-
sub = contents.substr(offset, 64);
|
363
|
-
md5cycle(this._state, md5blk(sub));
|
364
|
-
offset += 64;
|
365
|
-
}
|
366
|
-
|
367
|
-
this._buff = contents.substr(offset, 64);
|
368
|
-
|
369
|
-
} else {
|
370
|
-
this._buff = sub;
|
365
|
+
var buff = abConcat(this._buff, contents),
|
366
|
+
length = buff.length,
|
367
|
+
i;
|
368
|
+
|
369
|
+
this._length += contents.byteLength;
|
370
|
+
|
371
|
+
for (i = 64; i <= length; i += 64) {
|
372
|
+
md5cycle(this._state, md5blk(buff.subarray(i - 64, i)));
|
371
373
|
}
|
372
374
|
|
375
|
+
// Avoids IE10 weirdness
|
376
|
+
if((i - 64) < length)
|
377
|
+
this._buff = buff.subarray(i - 64);
|
378
|
+
else
|
379
|
+
this._buff = new Uint8Array(0);
|
380
|
+
|
373
381
|
return this;
|
374
382
|
};
|
375
383
|
|
@@ -387,10 +395,13 @@
|
|
387
395
|
length = buff.length,
|
388
396
|
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
389
397
|
i,
|
390
|
-
ret
|
398
|
+
ret,
|
399
|
+
tmp,
|
400
|
+
lo,
|
401
|
+
hi;
|
391
402
|
|
392
403
|
for (i = 0; i < length; i += 1) {
|
393
|
-
tail[i >> 2] |= buff
|
404
|
+
tail[i >> 2] |= buff[i] << ((i % 4) << 3);
|
394
405
|
}
|
395
406
|
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
|
396
407
|
if (i > 55) {
|
@@ -399,7 +410,16 @@
|
|
399
410
|
tail[i] = 0;
|
400
411
|
}
|
401
412
|
}
|
402
|
-
|
413
|
+
|
414
|
+
// Do the final computation based on the tail and length
|
415
|
+
// Beware that the final length may not fit in 32 bits so we take care of that
|
416
|
+
tmp = this._length * 8;
|
417
|
+
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
|
418
|
+
lo = parseInt(tmp[2], 16);
|
419
|
+
hi = parseInt(tmp[1], 16) || 0;
|
420
|
+
|
421
|
+
tail[14] = lo;
|
422
|
+
tail[15] = hi;
|
403
423
|
md5cycle(this._state, tail);
|
404
424
|
|
405
425
|
ret = !!raw ? this._state : hex(this._state);
|
@@ -415,7 +435,7 @@
|
|
415
435
|
* @return {SparkMD5} The instance itself
|
416
436
|
*/
|
417
437
|
SparkMD5.prototype.reset = function () {
|
418
|
-
this._buff =
|
438
|
+
this._buff = new Uint8Array(0);
|
419
439
|
this._length = 0;
|
420
440
|
this._state = [1732584193, -271733879, -1732584194, 271733878];
|
421
441
|
|
@@ -433,8 +453,7 @@
|
|
433
453
|
};
|
434
454
|
|
435
455
|
/**
|
436
|
-
* Performs the md5 hash on a string
|
437
|
-
* A conversion will be applied if utf8 string is detected.
|
456
|
+
* Performs the md5 hash on a string as utf16
|
438
457
|
*
|
439
458
|
* @param {String} str The string
|
440
459
|
* @param {Boolean} raw True to get the raw result, false to get the hex result
|
@@ -442,26 +461,21 @@
|
|
442
461
|
* @return {String|Array} The result
|
443
462
|
*/
|
444
463
|
SparkMD5.hash = function (str, raw) {
|
445
|
-
|
446
|
-
if (/[\u0080-\uFFFF]/.test(str)) {
|
447
|
-
str = unescape(encodeURIComponent(str));
|
448
|
-
}
|
449
|
-
|
450
|
-
var hash = md51(str);
|
464
|
+
var hash = md51(new Uint8Array(str2ab(str)));
|
451
465
|
|
452
466
|
return !!raw ? hash : hex(hash);
|
453
467
|
};
|
454
468
|
|
455
469
|
/**
|
456
|
-
* Performs the md5 hash on a
|
470
|
+
* Performs the md5 hash on a typed array.
|
457
471
|
*
|
458
|
-
* @param {
|
472
|
+
* @param {ArrayBuffer} content The binary array
|
459
473
|
* @param {Boolean} raw True to get the raw result, false to get the hex result
|
460
474
|
*
|
461
475
|
* @return {String|Array} The result
|
462
476
|
*/
|
463
477
|
SparkMD5.hashBinary = function (content, raw) {
|
464
|
-
var hash = md51(content);
|
478
|
+
var hash = md51(new Uint8Array(content));
|
465
479
|
|
466
480
|
return !!raw ? hash : hex(hash);
|
467
481
|
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* CoTag Condo Rackspace Cloud Files Strategy
|
3
|
-
* Direct to cloud resumable uploads for
|
3
|
+
* Direct to cloud resumable uploads for Rackspace Cloud Files
|
4
4
|
*
|
5
5
|
* Copyright (c) 2012 CoTag Media.
|
6
6
|
*
|
@@ -18,20 +18,15 @@
|
|
18
18
|
(function (factory) {
|
19
19
|
if (typeof define === 'function' && define.amd) {
|
20
20
|
// AMD
|
21
|
-
define(['jquery', '
|
21
|
+
define(['jquery', 'condo-uploader'], factory);
|
22
22
|
} else {
|
23
23
|
// Browser globals
|
24
|
-
factory(jQuery
|
24
|
+
factory(jQuery);
|
25
25
|
}
|
26
|
-
}(function (
|
26
|
+
}(function ($) {
|
27
27
|
'use strict';
|
28
28
|
|
29
|
-
|
30
|
-
// TODO:: Create an Amazon, google factory etc
|
31
|
-
// We should split all these into different files too (controller and factories separate from directives and views)
|
32
|
-
// So we can have different views for the same controller
|
33
|
-
//
|
34
|
-
uploads.factory('Condo.RackspaceCloudFiles', ['$rootScope', '$q', function($rootScope, $q) {
|
29
|
+
angular.module('CondoRackspaceProvider', ['CondoUploader', 'CondoAbstractMd5']).run(['$q', 'Condo.Registrar', 'Condo.Md5', function($q, registrar, md5) {
|
35
30
|
var PENDING = 0,
|
36
31
|
STARTED = 1,
|
37
32
|
PAUSED = 2,
|
@@ -65,16 +60,9 @@
|
|
65
60
|
|
66
61
|
//
|
67
62
|
// We need to sign our uploads so rackspace can confirm they are valid for us
|
68
|
-
// TODO:: use http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast
|
69
|
-
// where available :)
|
70
63
|
//
|
71
64
|
build_request = function(part_number) {
|
72
|
-
var
|
73
|
-
reader = new FileReader(),
|
74
|
-
fail = function(){
|
75
|
-
result.reject('file read failed');
|
76
|
-
},
|
77
|
-
current_part;
|
65
|
+
var current_part;
|
78
66
|
|
79
67
|
if (file.size > part_size) { // If file bigger then 5mb we expect a chunked upload
|
80
68
|
var endbyte = part_number * part_size;
|
@@ -85,23 +73,15 @@
|
|
85
73
|
current_part = file;
|
86
74
|
}
|
87
75
|
|
88
|
-
|
89
|
-
|
76
|
+
return md5.hash(current_part).then(function(val) {
|
77
|
+
return {
|
90
78
|
data: current_part,
|
91
|
-
data_id:
|
79
|
+
data_id: val,
|
92
80
|
part_number: part_number
|
93
|
-
});
|
94
|
-
|
95
|
-
|
96
|
-
if(!$rootScope.$$phase) {
|
97
|
-
$rootScope.$apply(); // This triggers the promise response if required
|
98
81
|
}
|
99
|
-
}
|
100
|
-
|
101
|
-
|
102
|
-
reader.readAsBinaryString(current_part);
|
103
|
-
|
104
|
-
return result.promise;
|
82
|
+
}, function(reason){
|
83
|
+
return $q.reject(reason);
|
84
|
+
});
|
105
85
|
},
|
106
86
|
|
107
87
|
//
|
@@ -332,15 +312,18 @@
|
|
332
312
|
this.message = reason;
|
333
313
|
}
|
334
314
|
};
|
335
|
-
}; // END
|
315
|
+
}; // END RACKSPACE
|
336
316
|
|
337
317
|
|
338
|
-
|
318
|
+
//
|
319
|
+
// Register the residence with the API
|
320
|
+
// Dependency injection succeeded
|
321
|
+
//
|
322
|
+
registrar.register('RackspaceCloudFiles', {
|
339
323
|
new_upload: function(api, file) {
|
340
324
|
return new Rackspace(api, file);
|
341
325
|
}
|
342
|
-
};
|
343
|
-
|
326
|
+
});
|
344
327
|
}]);
|
345
328
|
|
346
329
|
}));
|
@@ -19,29 +19,25 @@
|
|
19
19
|
(function (factory) {
|
20
20
|
if (typeof define === 'function' && define.amd) {
|
21
21
|
// AMD
|
22
|
-
define('
|
22
|
+
define('condo-uploader', ['jquery', 'condo-broadcaster'], factory);
|
23
23
|
} else {
|
24
24
|
// Browser globals
|
25
25
|
window.CondoUploader = factory(jQuery);
|
26
26
|
}
|
27
|
-
}(function (
|
27
|
+
}(function ($) {
|
28
28
|
'use strict';
|
29
29
|
|
30
|
-
var uploads = angular.module('CondoUploader', [])
|
30
|
+
var uploads = angular.module('CondoUploader', ['CondoBroadcaster']),
|
31
|
+
residencies = {};
|
31
32
|
|
32
33
|
|
33
34
|
//
|
34
35
|
// Implements the Condo API
|
35
36
|
//
|
36
|
-
uploads.factory('Condo.Api', ['$http', '$rootScope', '$q',
|
37
|
+
uploads.factory('Condo.Api', ['$http', '$rootScope', '$q', function($http, $rootScope, $q) {
|
37
38
|
|
38
39
|
|
39
40
|
var token = $('meta[name="csrf-token"]').attr('content'),
|
40
|
-
residencies = {
|
41
|
-
AmazonS3: AmazonS3Condo,
|
42
|
-
RackspaceCloudFiles: RackspaceFilesCondo,
|
43
|
-
GoogleCloudStorage: GoogleStorageCondo
|
44
|
-
},
|
45
41
|
|
46
42
|
|
47
43
|
condoConnection = function(api_endpoint, params) {
|
@@ -289,7 +285,16 @@
|
|
289
285
|
});
|
290
286
|
}
|
291
287
|
};
|
292
|
-
}])
|
288
|
+
}]).factory('Condo.Registrar', function(){
|
289
|
+
return {
|
290
|
+
//
|
291
|
+
// Simple dependency injection allows us to load only the providers we need
|
292
|
+
//
|
293
|
+
register: function(provider_name, iface) {
|
294
|
+
residencies[provider_name] = iface;
|
295
|
+
}
|
296
|
+
};
|
297
|
+
});
|
293
298
|
|
294
299
|
|
295
300
|
|
data/lib/condo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: condo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -67,12 +67,17 @@ executables: []
|
|
67
67
|
extensions: []
|
68
68
|
extra_rdoc_files: []
|
69
69
|
files:
|
70
|
+
- app/assets/javascripts/condo/abstract-md5.js.erb
|
70
71
|
- app/assets/javascripts/condo/amazon.js
|
71
72
|
- app/assets/javascripts/condo/base64.js
|
73
|
+
- app/assets/javascripts/condo/broadcaster.js
|
72
74
|
- app/assets/javascripts/condo/controller.js
|
73
75
|
- app/assets/javascripts/condo/google.js
|
76
|
+
- app/assets/javascripts/condo/md5/hash.worker.emulator.js
|
77
|
+
- app/assets/javascripts/condo/md5/hash.worker.js
|
78
|
+
- app/assets/javascripts/condo/md5/hasher.js
|
79
|
+
- app/assets/javascripts/condo/md5/spark-md5.js
|
74
80
|
- app/assets/javascripts/condo/rackspace.js
|
75
|
-
- app/assets/javascripts/condo/spark-md5.js
|
76
81
|
- app/assets/javascripts/condo/uploader.js
|
77
82
|
- app/assets/javascripts/condo.js
|
78
83
|
- lib/condo/configuration.rb
|