condo 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|