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.
@@ -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 and IE10.
36
- * Tested in Firefox 4, Safari 6 and Chromes latest stable
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
- #* @<div class="uploads-container" data-ng-app="CondoUploader"><%= render "condo_interface/upload" %></div>@
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
 
@@ -1,7 +1,5 @@
1
- //= require condo/spark-md5
1
+ //= require condo/abstract-md5
2
2
  //= require condo/base64
3
+ //= require condo/broadcaster
3
4
  //= require condo/uploader
4
- //= require condo/amazon
5
- //= require condo/rackspace
6
- //= require condo/google
7
5
  //= require condo/controller
@@ -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', 'spark-md5', 'base64', 'condo_uploader'], factory);
21
+ define(['jquery', 'base64', 'condo-uploader'], factory);
22
22
  } else {
23
23
  // Browser globals
24
- factory(jQuery, window.SparkMD5, window.base64, window.CondoUploader);
24
+ factory(jQuery, window.base64);
25
25
  }
26
- }(function ($, MD5, base64, uploads, undefined) {
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 result = $q.defer(),
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
- reader.onload = function(e) {
106
- result.resolve({
93
+ return md5.hash(current_part).then(function(val) {
94
+ return {
107
95
  data: current_part,
108
- data_id: MD5.hashBinary(e.target.result),
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
- reader.onerror = fail;
118
- reader.onabort = fail;
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
- return {
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('condo_controller', ['jquery', 'condo_uploader'], factory);
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.factory('Condo.Broadcast', ['$rootScope', function($rootScope) {
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', 'spark-md5', 'base64', 'condo_uploader'], factory);
21
+ define(['jquery', 'base64', 'condo-uploader'], factory);
22
22
  } else {
23
23
  // Browser globals
24
- factory(jQuery, window.SparkMD5, window.base64, window.CondoUploader);
24
+ factory(jQuery, window.base64);
25
25
  }
26
- }(function ($, MD5, base64, uploads, undefined) {
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(part_number, hash) {
88
- var result = $q.defer(),
89
- reader = new FileReader(),
90
- fail = function(){
91
- result.reject('file read failed');
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(1).then(function(result) {
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
- return {
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,11 @@
1
+ //= require condo/md5/spark-md5
2
+ //= require condo/md5/hasher
3
+
4
+
5
+ var hasher = new CondoMD5Hasher(postMessage); // Accepts the callback as the parameter
6
+
7
+
8
+ // Hook-up worker input
9
+ onmessage = function (e) {
10
+ hasher.hash(e.data);
11
+ }
@@ -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
- /*jslint bitwise: true, nomen: true */
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.mozSlice || File.prototype.webkitSlice || File.prototype.slice,
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
- if (typeof exports === 'object') {
85
- // Node/CommonJS
86
- exports.SparkMD5 = factory();
87
- } else if (typeof define === 'function' && define.amd) {
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
- window.SparkMD5 = factory();
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
- "use strict";
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
- /* this function is much faster,
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 cmn(q, a, b, x, s, t) {
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 ff(a, b, c, d, x, s, t) {
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 gg(a, b, c, d, x, s, t) {
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 hh(a, b, c, d, x, s, t) {
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 ii(a, b, c, d, x, s, t) {
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 md5cycle(x, k) {
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.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
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 md51(s) {
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.substring(i - 64, i)));
264
+ md5cycle(state, md5blk(s.subarray(i - 64, i)));
251
265
  }
252
- s = s.substring(i - 64);
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.charCodeAt(i) << ((i % 4) << 3);
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
- tail[14] = n * 8;
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
- /*jslint vars: true*/
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 rhex(n) {
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 hex(x) {
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 md5(s) {
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
- /*jslint vars: true*/
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
- * A conversion will be applied if an utf8 string is detected.
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 {String} contents The binary string to be appended
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
- // add to the buffer and increment string total length
347
- var offset = 64 - this._buff.length,
348
- sub = this._buff + contents.substr(0, offset),
349
- length = contents.length,
350
- total;
351
-
352
- this._length += length;
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.charCodeAt(i) << ((i % 4) << 3);
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
- tail[14] = this._length * 8;
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
- // converts the string to utf8 bytes if necessary
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 binary string.
470
+ * Performs the md5 hash on a typed array.
457
471
  *
458
- * @param {String} content The binary string
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 Amazon S3
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', 'spark-md5', 'condo_uploader'], factory);
21
+ define(['jquery', 'condo-uploader'], factory);
22
22
  } else {
23
23
  // Browser globals
24
- factory(jQuery, window.SparkMD5, window.CondoUploader);
24
+ factory(jQuery);
25
25
  }
26
- }(function ($, MD5, uploads, undefined) {
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 result = $q.defer(),
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
- reader.onload = function(e) {
89
- result.resolve({
76
+ return md5.hash(current_part).then(function(val) {
77
+ return {
90
78
  data: current_part,
91
- data_id: MD5.hashBinary(e.target.result),
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
- reader.onerror = fail;
101
- reader.onabort = fail;
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 AMAZON
315
+ }; // END RACKSPACE
336
316
 
337
317
 
338
- return {
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('condo_uploader', ['jquery'], factory);
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 ($, undefined) {
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', 'Condo.AmazonS3', 'Condo.RackspaceCloudFiles', 'Condo.GoogleCloudStorage', function($http, $rootScope, $q, AmazonS3Condo, RackspaceFilesCondo, GoogleStorageCondo) {
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
 
@@ -1,3 +1,3 @@
1
1
  module Condo
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
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.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-08 00:00:00.000000000 Z
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