Skip to content

Loading…

0.3.5 #8

Merged
merged 9 commits into from
Showing with 473 additions and 62 deletions.
  1. +3 −3 README.md
  2. +1 −0 build-browser.js
  3. +197 −29 dist/imbo.browser.js
  4. +1 −1 dist/imbo.browser.min.js
  5. +12 −0 lib/browser.js
  6. +67 −16 lib/client.js
  7. +12 −3 lib/compat.js
  8. +1 −0 lib/imbo.js
  9. +90 −0 lib/query.js
  10. +16 −8 lib/url.js
  11. +1 −1 package.json
  12. +53 −1 test/client.js
  13. +19 −0 test/url.js
View
6 README.md
@@ -1,7 +1,7 @@
-# Node.js client for imbo
-A [node.js](http://nodejs.org/) client for [imbo](https://github.com/imbo/imbo).
+# Javascript client for imbo
+A javascript (browser/[node.js](http://nodejs.org/)) client for [imbo](https://github.com/imbo/imbo).
-## Basic usage
+## Basic usage (node.js)
```javascript
var Imbo = require('imbo');
View
1 build-browser.js
@@ -10,6 +10,7 @@ var files = [
'lib/browser.js',
'lib/compat.js',
'lib/url.js',
+ 'lib/query.js',
'lib/client.js'
];
View
226 dist/imbo.browser.js
@@ -1,5 +1,5 @@
// Set up a global Imbo-namespace and signify that we're not in Node
-Imbo = { Node: false, Version: '0.3.1' };
+Imbo = { Node: false, Version: '0.3.5' };
(function(Imbo, undef) {
@@ -108,6 +108,18 @@ Imbo = { Node: false, Version: '0.3.1' };
xhr.send(null);
};
+ Imbo.Browser.EtagCache = {
+ cache: {},
+
+ get: function(url) {
+ return Imbo.Browser.EtagCache[url];
+ },
+
+ put: function(url, etag) {
+ Imbo.Browser.EtagCache[url] = etag;
+ }
+ };
+
})(Imbo);
// Compatability layer for browsers
@@ -161,19 +173,26 @@ Imbo = { Node: false, Version: '0.3.1' };
data = JSON.stringify(data);
}
- if (Imbo.Node) {
-
- }
+
// Browser environment
- var xhr = new XMLHttpRequest();
+ var xhr = new XMLHttpRequest(), etag;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status !== 0 && onComplete) {
+ etag = xhr.getResponseHeader('etag');
+ etag && Imbo.Browser.EtagCache.put(uri, etag);
+
onComplete(undef, Imbo.Compat.requestDone(xhr, jsonRequest));
}
};
xhr.open(method, uri, true);
xhr.setRequestHeader('Accept', headers.Accept);
+
+ var etag = Imbo.Browser.EtagCache.get(uri);
+ if (method == 'GET' && etag) {
+ xhr.setRequestHeader('If-None-Match', etag);
+ }
+
xhr.onerror = function() {
onComplete('XMLHttpRequest failed - CORS disabled?');
};
@@ -262,14 +281,15 @@ Imbo = { Node: false, Version: '0.3.1' };
/**
* Imbo URL helper
*/
- function ImboUrl(baseUrl, publicKey, privateKey, imageIdentifier, path) {
+ var ImboUrl = function(baseUrl, publicKey, privateKey, imageIdentifier, path, queryString) {
this.transformations = [];
this.baseUrl = baseUrl;
this.publicKey = publicKey;
- this.imageIdentifier = imageIdentifier;
this.privateKey = privateKey;
+ this.imageIdentifier = imageIdentifier || '';
this.path = path || '';
- }
+ this.queryString = queryString;
+ };
ImboUrl.prototype.border = function(color, width, height) {
color = (color || '000000').replace(/^#/, '');
@@ -386,11 +406,12 @@ Imbo = { Node: false, Version: '0.3.1' };
};
ImboUrl.prototype.getQueryString = function() {
- var query = '';
+ var query = this.queryString || '';
if (this.transformations.length) {
// We have some transformations. Generate a transformation key that will be sent to the
// server so the server can verify if the transformations are valid or not.
- query = 't[]=' + this.transformations.reduce(function(query, element) {
+ query += query.length ? '&' : '';
+ query += 't[]=' + this.transformations.reduce(function(query, element) {
return query + '&t[]=' + element;
});
}
@@ -399,15 +420,21 @@ Imbo = { Node: false, Version: '0.3.1' };
};
ImboUrl.prototype.getUrl = function() {
- var url = this.baseUrl + '/users/' + this.publicKey + '/images/' + this.imageIdentifier + this.path;
- var qs = this.getQueryString();
+ var url = this.baseUrl + '/users/' + this.publicKey;
+ if (this.imageIdentifier || this.path) {
+ url = url + '/images/' + this.imageIdentifier + this.path;
+ }
+
+ url = url.replace(/\/+$/, '');
+
+ var qs = this.getQueryString();
if (qs.length) {
url += '?' + qs;
}
var token = this.getAccessToken(url, this.privateKey);
- return url + (qs.length ? '&' : '?') + 'accessToken=' + token;
+ return url + (url.indexOf('?') > -1 ? '&' : '?') + 'accessToken=' + token;
};
ImboUrl.prototype.toString = function() {
@@ -421,6 +448,96 @@ Imbo = { Node: false, Version: '0.3.1' };
Imbo.Url = ImboUrl;
return ImboUrl;
})(typeof Imbo !== 'undefined' ? Imbo : {});
+(function(Imbo, undef) {
+
+ var ImboQuery = function() {
+ this.values = {
+ page: 1,
+ num: 20,
+ metadata: false,
+ query: null,
+ from: null,
+ to: null
+ };
+ };
+
+ ImboQuery.prototype.page = function(val) {
+ if (!val) { return this.values.page; }
+ this.values.page = val;
+ return this;
+ };
+
+ ImboQuery.prototype.num = function(val) {
+ if (!val) { return this.values.num; }
+ this.values.num = val;
+ return this;
+ };
+
+ ImboQuery.prototype.limit = ImboQuery.prototype.num;
+
+ ImboQuery.prototype.metadata = function(val) {
+ if (typeof val === 'undefined') { return this.values.metadata; }
+ this.values.metadata = !!val;
+ return this;
+ };
+
+ ImboQuery.prototype.query = function(val) {
+ if (!val) { return this.values.query; }
+ this.values.query = val;
+ return this;
+ };
+
+ ImboQuery.prototype.from = function(val) {
+ if (!val) { return this.values.from; }
+ this.values.from = val;
+ return this;
+ };
+
+ ImboQuery.prototype.to = function(val) {
+ if (!val) { return this.values.to; }
+ this.values.to = val;
+ return this;
+ };
+
+ ImboQuery.prototype.toQueryString = function() {
+ // Retrieve query parameters, reduce params down to non-empty values
+ var params = {}, keys = ['page', 'num', 'metadata', 'query', 'from', 'to'];
+ for (var i = 0; i < keys.length; i++) {
+ if (!!this.values[keys[i]]) {
+ params[keys[i]] = this.values[keys[i]];
+ }
+ }
+
+ // JSON-encode metadata query, if present
+ if (params.query) {
+ params.query = JSON.stringify(params.query);
+ }
+
+ // Get timestamps from dates
+ if (params.from) {
+ params.from = Math.floor(params.from.getTime() / 1000);
+ }
+ if (params.to) {
+ params.to = Math.floor(params.to.getTime() / 1000);
+ }
+
+ // Build query string
+ var parts = [], key;
+ for (key in params) {
+ parts.push(key + '=' + encodeURIComponent(params[key]));
+ }
+ return parts.join('&');
+ };
+
+ ImboQuery.prototype.toString = ImboQuery.prototype.toQueryString;
+
+ if (typeof module !== 'undefined') {
+ module.exports = ImboQuery;
+ }
+
+ Imbo.Query = ImboQuery;
+ return ImboQuery;
+})(typeof Imbo !== 'undefined' ? Imbo : {});
(function(Imbo, undef) {
@@ -461,8 +578,23 @@ Imbo = { Node: false, Version: '0.3.1' };
return new Imbo.Url(host, this.options.publicKey, this.options.privateKey, imageIdentifier);
};
- ImboClient.prototype.getResourceUrl = function(resourceIdentifier, path) {
- return new Imbo.Url(this.options.hosts[0], this.options.publicKey, this.options.privateKey, resourceIdentifier, path);
+ ImboClient.prototype.getImagesUrl = function(query) {
+ return this.getResourceUrl('', '/', query ? query.toString() : null);
+ };
+
+ ImboClient.prototype.getUserUrl = function() {
+ return this.getResourceUrl();
+ };
+
+ ImboClient.prototype.getResourceUrl = function(resourceIdentifier, path, query) {
+ return new Imbo.Url(
+ this.options.hosts[0],
+ this.options.publicKey,
+ this.options.privateKey,
+ resourceIdentifier,
+ path,
+ query
+ );
};
ImboClient.prototype.getSignedResourceUrl = function(method, url, date) {
@@ -511,7 +643,8 @@ Imbo = { Node: false, Version: '0.3.1' };
/**
* Image operations
*/
- ImboClient.prototype.headImage = function(imageIdentifier, callback) {
+ ImboClient.prototype.headImage = function(imageIdentifier, cb) {
+ var callback = cb || function() {};
var url = this.getResourceUrl(imageIdentifier), undef;
Imbo.Compat.request('HEAD', url.toString(), function(err, res) {
@@ -524,8 +657,8 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.deleteImage = function(imgPath, callback) {
- var self = this;
+ ImboClient.prototype.deleteImage = function(imgPath, cb) {
+ var self = this, callback = cb || function() {};
self.getImageIdentifier(imgPath, function(err, imageIdentifier) {
if (err) {
return callback(err);
@@ -535,7 +668,8 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.deleteImageByIdentifier = function(imageIdentifier, callback) {
+ ImboClient.prototype.deleteImageByIdentifier = function(imageIdentifier, cb) {
+ var callback = cb || function() {};
var url = this.getSignedResourceUrl('DELETE', this.getResourceUrl(imageIdentifier));
Imbo.Compat.request('DELETE', url, function(err, res) {
@@ -572,7 +706,8 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.addImageFromBlob = function(blob, callback) {
+ ImboClient.prototype.addImageFromBlob = function(blob, cb) {
+ var callback = cb || function() {};
var self = this, onComplete = callback.complete || callback;
var start = Date.now();
self.getImageIdentifierFromString(blob, function(err, imageIdentifier) {
@@ -603,11 +738,11 @@ Imbo = { Node: false, Version: '0.3.1' };
/**
* Add a new image to the server (from filesystem)
*
- * @param {string|File} path Path to the local image, or an instance of File
- * @param {Function} callback Function to call when image has been uploaded
+ * @param {string|File} path Path to the local image, or an instance of File
+ * @param {Function} cb Function to call when image has been uploaded
*/
- ImboClient.prototype.addImage = function(path, callback) {
- var self = this;
+ ImboClient.prototype.addImage = function(path, cb) {
+ var self = this, callback = cb || function() {};
Imbo.Compat.getContents(path, function(err, data) {
if (err) {
return callback(err);
@@ -617,8 +752,8 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.addImageFromUrl = function(url, callback) {
- var self = this;
+ ImboClient.prototype.addImageFromUrl = function(url, cb) {
+ var self = this, callback = cb || function() {};
Imbo.Compat.getContentsFromUrl(url, function(err, data) {
if (err) {
return callback(err);
@@ -629,10 +764,41 @@ Imbo = { Node: false, Version: '0.3.1' };
};
/**
+ * Fetch information for a given user/public key
+ */
+ ImboClient.prototype.getUserInfo = function(callback) {
+ Imbo.Compat.request('GET', this.getUserUrl().toString(), function(err, res) {
+ if (err || (res && res.statusCode != 200)) {
+ return callback(err || getErrorMessage(res), null, res);
+ }
+
+ callback(undef, res.body, res);
+ });
+ };
+
+ /**
+ * Fetch images
+ */
+ ImboClient.prototype.getImages = function(callback, query) {
+ // Build the complete URL
+ var url = this.getImagesUrl(query);
+
+ // Fetch the response
+ Imbo.Compat.request('GET', url.toString(), function(err, res) {
+ if (err || (res && res.statusCode != 200)) {
+ return callback(err || getErrorMessage(res), null, res);
+ }
+
+ callback(undef, res.body, res);
+ });
+ };
+
+ /**
* Metadata methods
*/
- ImboClient.prototype.getMetadata = function(imageIdentifier, callback) {
+ ImboClient.prototype.getMetadata = function(imageIdentifier, cb) {
var url = this.getResourceUrl(imageIdentifier, '/meta');
+ var callback = cb || function() {};
Imbo.Compat.request('GET', url.toString(), function(err, res) {
if (err || (res && res.statusCode != 200)) {
return callback(err || getErrorMessage(res), null, res);
@@ -642,8 +808,9 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.deleteMetadata = function(imageIdentifier, callback) {
+ ImboClient.prototype.deleteMetadata = function(imageIdentifier, cb) {
var url = this.getSignedResourceUrl('DELETE', this.getResourceUrl(imageIdentifier, '/meta'));
+ var callback = cb || function() {};
Imbo.Compat.request('DELETE', url, function(err, res) {
if (err || (res && res.statusCode != 200)) {
return callback(err || getErrorMessage(res), res);
@@ -653,8 +820,9 @@ Imbo = { Node: false, Version: '0.3.1' };
});
};
- ImboClient.prototype.editMetadata = function(imageIdentifier, data, callback) {
+ ImboClient.prototype.editMetadata = function(imageIdentifier, data, cb) {
var url = this.getSignedResourceUrl('POST', this.getResourceUrl(imageIdentifier, '/meta'));
+ var callback = cb || function() {};
Imbo.Compat.request('POST', url, data, function(err, res) {
if (err || (res && res.statusCode != 200 && res.statusCode != 201)) {
return callback(err || getErrorMessage(res), res);
View
2 dist/imbo.browser.min.js
@@ -1 +1 @@
-Imbo={Node:false,Version:"0.3.1"};(function(Imbo,undef){var faultylabs={};faultylabs.MD5=function(h){function c(s){var r=(s>>>0).toString(16);return"00000000".substr(0,8-r.length)+r}function b(t){var r=[];for(var s=0;s<t.length;s++){r=r.concat(o(t[s]))}return r}function q(s){var r=[];for(var t=0;t<8;t++){r.push(s&255);s=s>>>8}return r}function k(s,r){return s<<r&4294967295|s>>>32-r}function l(r,t,s){return r&t|~r&s}function i(r,t,s){return s&r|~s&t}function e(r,t,s){return r^t^s}function d(r,t,s){return t^(r|~s)}function n(r,s){return r[s+3]<<24|r[s+2]<<16|r[s+1]<<8|r[s]}function o(v){var r=[];for(var u=0;u<v.length;u++){if(v.charCodeAt(u)<=127){r.push(v.charCodeAt(u))}else{var t=encodeURIComponent(v.charAt(u)).substr(1).split("%");for(var s=0;s<t.length;s++){r.push(parseInt(t[s],16))}}}return r}function p(s,r,z,y){var x="";var w=0;var u=0;for(var v=3;v>=0;v--){u=arguments[v];w=u&255;u=u>>>8;w=w<<8;w=w|u&255;u=u>>>8;w=w<<8;w=w|u&255;u=u>>>8;w=w<<8;w=w|u;x=x+c(w)}return x}function j(t){var r=new Array(t.length);for(var s=0;s<t.length;s++){r[s]=t[s]}return r}var g=null;var m=null;if(typeof h=="string"){g=o(h)}else{if(h.constructor==Array){if(h.length===0){g=h}else{if(typeof h[0]=="string"){g=b(h)}else{if(typeof h[0]=="number"){g=h}else{m=typeof h[0]}}}}else{if(typeof ArrayBuffer!="undefined"){if(h instanceof ArrayBuffer){g=j(new Uint8Array(h))}else{if(h instanceof Uint8Array||h instanceof Int8Array){g=j(h)}else{if(h instanceof Uint32Array||h instanceof Int32Array||h instanceof Uint16Array||h instanceof Int16Array||h instanceof Float32Array||h instanceof Float64Array){g=j(new Uint8Array(h.buffer))}else{m=typeof h}}}}else{m=typeof h}}}if(m){alert("MD5 type mismatch, cannot process "+m)}function a(s,r){return 4294967295&s+r}return f();function f(){function u(H,I,F,E){var G=x;x=z;z=B;B=a(B,k(a(D,a(H,a(I,F))),E));D=G}var s=g.length;g.push(128);var v=g.length%64;if(v>56){for(var t=0;t<64-v;t++){g.push(0)}v=g.length%64}for(t=0;t<56-v;t++){g.push(0)}g=g.concat(q(s*8));var C=1732584193;var A=4023233417;var y=2562383102;var w=271733878;var D=0,B=0,z=0,x=0;for(t=0;t<g.length/64;t++){D=C;B=A;z=y;x=w;var r=t*64;u(l(B,z,x),3614090360,n(g,r),7);u(l(B,z,x),3905402710,n(g,r+4),12);u(l(B,z,x),606105819,n(g,r+8),17);u(l(B,z,x),3250441966,n(g,r+12),22);u(l(B,z,x),4118548399,n(g,r+16),7);u(l(B,z,x),1200080426,n(g,r+20),12);u(l(B,z,x),2821735955,n(g,r+24),17);u(l(B,z,x),4249261313,n(g,r+28),22);u(l(B,z,x),1770035416,n(g,r+32),7);u(l(B,z,x),2336552879,n(g,r+36),12);u(l(B,z,x),4294925233,n(g,r+40),17);u(l(B,z,x),2304563134,n(g,r+44),22);u(l(B,z,x),1804603682,n(g,r+48),7);u(l(B,z,x),4254626195,n(g,r+52),12);u(l(B,z,x),2792965006,n(g,r+56),17);u(l(B,z,x),1236535329,n(g,r+60),22);u(i(B,z,x),4129170786,n(g,r+4),5);u(i(B,z,x),3225465664,n(g,r+24),9);u(i(B,z,x),643717713,n(g,r+44),14);u(i(B,z,x),3921069994,n(g,r),20);u(i(B,z,x),3593408605,n(g,r+20),5);u(i(B,z,x),38016083,n(g,r+40),9);u(i(B,z,x),3634488961,n(g,r+60),14);u(i(B,z,x),3889429448,n(g,r+16),20);u(i(B,z,x),568446438,n(g,r+36),5);u(i(B,z,x),3275163606,n(g,r+56),9);u(i(B,z,x),4107603335,n(g,r+12),14);u(i(B,z,x),1163531501,n(g,r+32),20);u(i(B,z,x),2850285829,n(g,r+52),5);u(i(B,z,x),4243563512,n(g,r+8),9);u(i(B,z,x),1735328473,n(g,r+28),14);u(i(B,z,x),2368359562,n(g,r+48),20);u(e(B,z,x),4294588738,n(g,r+20),4);u(e(B,z,x),2272392833,n(g,r+32),11);u(e(B,z,x),1839030562,n(g,r+44),16);u(e(B,z,x),4259657740,n(g,r+56),23);u(e(B,z,x),2763975236,n(g,r+4),4);u(e(B,z,x),1272893353,n(g,r+16),11);u(e(B,z,x),4139469664,n(g,r+28),16);u(e(B,z,x),3200236656,n(g,r+40),23);u(e(B,z,x),681279174,n(g,r+52),4);u(e(B,z,x),3936430074,n(g,r),11);u(e(B,z,x),3572445317,n(g,r+12),16);u(e(B,z,x),76029189,n(g,r+24),23);u(e(B,z,x),3654602809,n(g,r+36),4);u(e(B,z,x),3873151461,n(g,r+48),11);u(e(B,z,x),530742520,n(g,r+60),16);u(e(B,z,x),3299628645,n(g,r+8),23);u(d(B,z,x),4096336452,n(g,r),6);u(d(B,z,x),1126891415,n(g,r+28),10);u(d(B,z,x),2878612391,n(g,r+56),15);u(d(B,z,x),4237533241,n(g,r+20),21);u(d(B,z,x),1700485571,n(g,r+48),6);u(d(B,z,x),2399980690,n(g,r+12),10);u(d(B,z,x),4293915773,n(g,r+40),15);u(d(B,z,x),2240044497,n(g,r+4),21);u(d(B,z,x),1873313359,n(g,r+32),6);u(d(B,z,x),4264355552,n(g,r+60),10);u(d(B,z,x),2734768916,n(g,r+24),15);u(d(B,z,x),1309151649,n(g,r+52),21);u(d(B,z,x),4149444226,n(g,r+16),6);u(d(B,z,x),3174756917,n(g,r+44),10);u(d(B,z,x),718787259,n(g,r+8),15);u(d(B,z,x),3951481745,n(g,r+36),21);C=a(C,D);A=a(A,B);y=a(y,z);w=a(w,x)}return p(w,y,A,C).toUpperCase()}};var CryptoJS=function(h,i){var e={},f=e.lib={},l=f.Base=function(){function a(){}return{extend:function(j){a.prototype=this;var d=new a;j&&d.mixIn(j);d.$super=this;return d},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var d in a)a.hasOwnProperty(d)&&(this[d]=a[d]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.$super.extend(this)}}}(),k=f.WordArray=l.extend({init:function(a,j){a=this.words=a||[];this.sigBytes=j!=i?j:4*a.length},toString:function(a){return(a||m).stringify(this)},concat:function(a){var j=this.words,d=a.words,c=this.sigBytes,a=a.sigBytes;this.clamp();if(c%4)for(var b=0;b<a;b++)j[c+b>>>2]|=(d[b>>>2]>>>24-8*(b%4)&255)<<24-8*((c+b)%4);else if(65535<d.length)for(b=0;b<a;b+=4)j[c+b>>>2]=d[b>>>2];else j.push.apply(j,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=l.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return k.create(b,a)}}),o=e.enc={},m=o.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,2),16)<<24-4*(c%8);return k.create(d,b/2)}},q=o.Latin1={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return k.create(d,b)}},r=o.Utf8={stringify:function(a){try{return decodeURIComponent(escape(q.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data")}},parse:function(a){return q.parse(unescape(encodeURIComponent(a)))}},b=f.BufferedBlockAlgorithm=l.extend({reset:function(){this._data=k.create();this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,g=c/(4*e),g=a?h.ceil(g):h.max((g|0)-this._minBufferSize,0),a=g*e,c=h.min(4*a,c);if(a){for(var f=0;f<a;f+=e)this._doProcessBlock(d,f);f=d.splice(0,a);b.sigBytes-=c}return k.create(f,c)},clone:function(){var a=l.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});f.Hasher=b.extend({init:function(){this.reset()},reset:function(){b.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);this._doFinalize();return this._hash},clone:function(){var a=b.clone.call(this);a._hash=this._hash.clone();return a},blockSize:16,_createHelper:function(a){return function(b,d){return a.create(d).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return g.HMAC.create(a,d).finalize(b)}}});var g=e.algo={};return e}(Math);(function(h){var i=CryptoJS,e=i.lib,f=e.WordArray,e=e.Hasher,l=i.algo,k=[],o=[];(function(){function e(a){for(var b=h.sqrt(a),d=2;d<=b;d++)if(!(a%d))return!1;return!0}function f(a){return 4294967296*(a-(a|0))|0}for(var b=2,g=0;64>g;)e(b)&&(8>g&&(k[g]=f(h.pow(b,.5))),o[g]=f(h.pow(b,1/3)),g++),b++})();var m=[],l=l.SHA256=e.extend({_doReset:function(){this._hash=f.create(k.slice(0))},_doProcessBlock:function(e,f){for(var b=this._hash.words,g=b[0],a=b[1],j=b[2],d=b[3],c=b[4],h=b[5],l=b[6],k=b[7],n=0;64>n;n++){if(16>n)m[n]=e[f+n]|0;else{var i=m[n-15],p=m[n-2];m[n]=((i<<25|i>>>7)^(i<<14|i>>>18)^i>>>3)+m[n-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+m[n-16]}i=k+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&h^~c&l)+o[n]+m[n];p=((g<<30|g>>>2)^(g<<19|g>>>13)^(g<<10|g>>>22))+(g&a^g&j^a&j);k=l;l=h;h=c;c=d+i|0;d=j;j=a;a=g;g=i+p|0}b[0]=b[0]+g|0;b[1]=b[1]+a|0;b[2]=b[2]+j|0;b[3]=b[3]+d|0;b[4]=b[4]+c|0;b[5]=b[5]+h|0;b[6]=b[6]+l|0;b[7]=b[7]+k|0},_doFinalize:function(){var e=this._data,f=e.words,b=8*this._nDataBytes,g=8*e.sigBytes;f[g>>>5]|=128<<24-g%32;f[(g+64>>>9<<4)+15]=b;e.sigBytes=4*f.length;this._process()}});i.SHA256=e._createHelper(l);i.HmacSHA256=e._createHmacHelper(l)})(Math);(function(){var h=CryptoJS,i=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(e,f){e=this._hasher=e.create();"string"==typeof f&&(f=i.parse(f));var h=e.blockSize,k=4*h;f.sigBytes>k&&(f=e.finalize(f));for(var o=this._oKey=f.clone(),m=this._iKey=f.clone(),q=o.words,r=m.words,b=0;b<h;b++)q[b]^=1549556828,r[b]^=909522486;o.sigBytes=m.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var f=this._hasher,e=f.finalize(e);f.reset();return f.finalize(this._oKey.clone().concat(e))}})})();if(!window.BlobBuilder){window.BlobBuilder=window.WebKitBlobBuilder||window.MSBlobBuilder||window.MozBlobBuilder}if(typeof XMLHttpRequest.prototype.sendAsBinary!=="function"){XMLHttpRequest.prototype.sendAsBinary=function(text){var buffer=Imbo.Browser.getArrayBuffer(text);var data=null;try{if(typeof Blob!=="undefined"&&typeof DataView!=="undefined"){var dataView=new DataView(buffer);data=new Blob([dataView])}else if(typeof Blob!=="undefined"){data=new Blob([buffer])}}catch(e){data=null}if(!data){var bb=new BlobBuilder;bb.append(buffer);data=bb.getBlob()}this.send(data)}}Imbo.Browser={};Imbo.Browser.getArrayBuffer=function(text){var data=new ArrayBuffer(text.length);var ui8a=new Uint8Array(data,0);for(var i=0;i<text.length;i++)ui8a[i]=text.charCodeAt(i)&255;return data};Imbo.Browser.md5=function(buffer){if(typeof buffer!=="object"){buffer=Imbo.Browser.getArrayBuffer(buffer)}return faultylabs.MD5(buffer).toLowerCase()};Imbo.Browser.sha256=function(key,data){return CryptoJS.HmacSHA256(data,key).toString()};Imbo.Browser.getImageBlob=function(input,callback){if(input.constructor.name=="File"){return Imbo.Browser.getContentsFromFile(input,callback)}return callback(undef,input)};Imbo.Browser.getContentsFromFile=function(file,callback){var reader=new FileReader;reader.onload=function(e){return callback(undef,e.target.result)};reader.readAsBinaryString(file)};Imbo.Browser.getContentsFromUrl=function(url,callback){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.overrideMimeType("text/plain; charset=x-user-defined");xhr.onreadystatechange=function(){if(xhr.readyState===4){callback(undef,xhr.responseText)}};xhr.send(null)}})(Imbo);(function(env,undef){var headers={Accept:"application/json,image/*","User-Agent":"imboclient-js "+Imbo.Version+" ("+(env||navigator.userAgent)+")"};Imbo.Compat={sha256:function(key,data){if(Imbo.Node){return crypto.createHmac("sha256",key).update(data).digest("hex")}return Imbo.Browser.sha256(key,data)},md5:function(buffer,callback,isString){if(!Imbo.Node){if(buffer instanceof File){return Imbo.Browser.getContentsFromFile(buffer,function(err,data){Imbo.Compat.md5(data,callback)})}return callback(undef,Imbo.Browser.md5(buffer),{size:buffer.length})}},request:function(method,uri,data,callback){method=method.toUpperCase();if(!callback&&(data.complete||typeof data==="function")){callback=data;data=null}var onComplete=callback.complete||callback;var jsonRequest=data&&data.constructor&&data.constructor.name=="Object";var reqHeaders=method=="PUT"?Imbo.Compat.getPutHeaders(data?data.length:0):headers;if(jsonRequest){reqHeaders["Content-Type"]="application/json";data=JSON.stringify(data)}if(Imbo.Node){}var xhr=new XMLHttpRequest;xhr.onreadystatechange=function(){if(xhr.readyState===4&&xhr.status!==0&&onComplete){onComplete(undef,Imbo.Compat.requestDone(xhr,jsonRequest))}};xhr.open(method,uri,true);xhr.setRequestHeader("Accept",headers.Accept);xhr.onerror=function(){onComplete("XMLHttpRequest failed - CORS disabled?")};if(callback.progress){xhr.upload.addEventListener("progress",callback.progress,false)}if(callback.uploadComplete){xhr.upload.addEventListener("load",callback.uploadComplete,false)}if(method=="PUT"){xhr.sendAsBinary(data)}else{xhr.send(data)}},requestDone:function(xhr,json){var resHeaders={"Content-Type":xhr.getResponseHeader("Content-Type"),"Last-Modified":xhr.getResponseHeader("Last-Modified"),"X-Imbo-Error-Internalcode":xhr.getResponseHeader("X-Imbo-Error-Internalcode")};for(var key in resHeaders){if(!resHeaders[key]){delete resHeaders[key]}}if(resHeaders["Content-Type"]=="application/json"){json=true}return{statusCode:xhr.status,body:json?JSON.parse(xhr.responseText):xhr.responseText,headers:resHeaders}},getPutHeaders:function(length){var putHeaders={};for(var key in headers){putHeaders[key]=headers[key]}if(length){putHeaders["Content-Length"]=length}return putHeaders},getContentsFromUrl:function(url,callback){if(!Imbo.Node){return Imbo.Browser.getContentsFromUrl(url,callback)}},getContents:function(file,callback){if(typeof file=="string"&&Imbo.Node){return fs.readFile(file,"binary",function(err,data){if(err&&err.code=="ENOENT"){return callback("File does not exist ("+err.path+")")}callback(err,data)})}else if(!Imbo.Node){return Imbo.Browser.getContentsFromFile(file,callback)}return callback("getContents() - not sure what to do with the passed file")}}})(typeof env==="undefined"?null:env);(function(Imbo,undef){Imbo.Compat=Imbo.Compat||require("./compat");function ImboUrl(baseUrl,publicKey,privateKey,imageIdentifier,path){this.transformations=[];this.baseUrl=baseUrl;this.publicKey=publicKey;this.imageIdentifier=imageIdentifier;this.privateKey=privateKey;this.path=path||""}ImboUrl.prototype.border=function(color,width,height){color=(color||"000000").replace(/^#/,"");width=parseInt(isNaN(width)?1:width,10);height=parseInt(isNaN(height)?1:height,10);return this.append("border:color="+color+",width="+width+",height="+height)};ImboUrl.prototype.compress=function(quality){quality=parseInt(quality,10);return this.append("compress:quality="+(quality?quality:75))};ImboUrl.prototype.convert=function(type){this.imageIdentifier=this.imageIdentifier.substr(0,32)+"."+type;return this};ImboUrl.prototype.gif=function(){return this.convert("gif")};ImboUrl.prototype.jpg=function(){return this.convert("jpg")};ImboUrl.prototype.png=function(){return this.convert("png")};ImboUrl.prototype.crop=function(x,y,width,height){return this.append("crop:x="+x+",y="+y+",width="+width+",height="+height)};ImboUrl.prototype.desaturate=function(){return this.append("desaturate")};ImboUrl.prototype.flipHorizontally=function(){return this.append("flipHorizontally")};ImboUrl.prototype.flipVertically=function(){return this.append("flipVertically")};ImboUrl.prototype.maxSize=function(width,height){var params=[];if(width){params.push("width="+parseInt(width,10))}if(height){params.push("height="+parseInt(height,10))}return this.append("maxSize:"+params.join(","))};ImboUrl.prototype.resize=function(width,height){var params=[];if(width){params.push("width="+parseInt(width,10))}if(height){params.push("height="+parseInt(height,10))}return this.append("resize:"+params.join(","))};ImboUrl.prototype.rotate=function(angle,bg){if(isNaN(angle)){return this}bg=(bg||"000000").replace(/^#/,"");return this.append("rotate:angle="+angle+",bg="+bg)};ImboUrl.prototype.sepia=function(threshold){threshold=parseInt(threshold,10);return this.append("sepia:threshold="+(threshold?threshold:80))};ImboUrl.prototype.thumbnail=function(width,height,fit){return this.append("thumbnail:width="+(width||50)+",height="+(height||50)+",fit="+(fit||"outbound"))};ImboUrl.prototype.transpose=function(){return this.append("transpose")};ImboUrl.prototype.transverse=function(){return this.append("transverse")};ImboUrl.prototype.reset=function(){this.imageIdentifier=this.imageIdentifier.substr(0,32);this.transformations=[];return this};ImboUrl.prototype.append=function(part){this.transformations.push(encodeURIComponent(part));return this};ImboUrl.prototype.getAccessToken=function(url){return Imbo.Compat.sha256(this.privateKey,url)};ImboUrl.prototype.getQueryString=function(){var query="";if(this.transformations.length){query="t[]="+this.transformations.reduce(function(query,element){return query+"&t[]="+element})}return query};ImboUrl.prototype.getUrl=function(){var url=this.baseUrl+"/users/"+this.publicKey+"/images/"+this.imageIdentifier+this.path;var qs=this.getQueryString();if(qs.length){url+="?"+qs}var token=this.getAccessToken(url,this.privateKey);return url+(qs.length?"&":"?")+"accessToken="+token};ImboUrl.prototype.toString=function(){return this.getUrl()};if(typeof module!=="undefined"){module.exports=ImboUrl}Imbo.Url=ImboUrl;return ImboUrl})(typeof Imbo!=="undefined"?Imbo:{});(function(Imbo,undef){var ImboClient=function(serverUrls,publicKey,privateKey){this.options={hosts:this.parseUrls(serverUrls),publicKey:publicKey,privateKey:privateKey}};var getErrorMessage=function(res){if(res&&res.body&&res.body.error){return res.body.error.message}else if(res&&res.headers){return res.headers["X-Imbo-Error-Internalcode"]||res.statusCode}else if(res){return res}return"Unknown error"};ImboClient.prototype.getImageIdentifier=function(image,callback){return Imbo.Compat.md5(image,callback)};ImboClient.prototype.getImageIdentifierFromString=function(image,callback){return Imbo.Compat.md5(image,callback,true)};ImboClient.prototype.getImageUrl=function(imageIdentifier){var host=this.getHostForImageIdentifier(imageIdentifier);return new Imbo.Url(host,this.options.publicKey,this.options.privateKey,imageIdentifier)};ImboClient.prototype.getResourceUrl=function(resourceIdentifier,path){return new Imbo.Url(this.options.hosts[0],this.options.publicKey,this.options.privateKey,resourceIdentifier,path)};ImboClient.prototype.getSignedResourceUrl=function(method,url,date){var timestamp=(date||new Date).toISOString().replace(/\.\d+Z$/,"Z");var signature=this.generateSignature(method,url.toString(),timestamp);var qs=url.toString().indexOf("?")>-1?"&":"?";qs+="signature="+encodeURIComponent(signature);qs+="&timestamp="+encodeURIComponent(timestamp);return url+qs};ImboClient.prototype.generateSignature=function(method,url,timestamp){var data=[method,url,this.options.publicKey,timestamp].join("|");var signature=Imbo.Compat.sha256(this.options.privateKey,data);return signature};ImboClient.prototype.getHostForImageIdentifier=function(imageIdentifier){var dec=parseInt(imageIdentifier.slice(0,2),16);return this.options.hosts[dec%this.options.hosts.length]};ImboClient.prototype.parseUrls=function(urls){if(typeof urls==="string"){urls=[urls]}var serverUrls=[];for(var i=0;i<urls.length;i++){serverUrls.push(urls[i].replace(/:80(\/|$)/,"$1").replace(/\/$/,""))}return serverUrls};ImboClient.prototype.headImage=function(imageIdentifier,callback){var url=this.getResourceUrl(imageIdentifier),undef;Imbo.Compat.request("HEAD",url.toString(),function(err,res){if(err){return callback(err,res)}else if(res.statusCode!==200){return callback(getErrorMessage(res),res)}return callback(undef,res)})};ImboClient.prototype.deleteImage=function(imgPath,callback){var self=this;self.getImageIdentifier(imgPath,function(err,imageIdentifier){if(err){return callback(err)}self.deleteImageByIdentifier(imageIdentifier,callback)})};ImboClient.prototype.deleteImageByIdentifier=function(imageIdentifier,callback){var url=this.getSignedResourceUrl("DELETE",this.getResourceUrl(imageIdentifier));Imbo.Compat.request("DELETE",url,function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};ImboClient.prototype.imageIdentifierExists=function(identifier,callback){this.headImage(identifier,function(err,res){if(err&&(err==404||res&&res.statusCode==404)){return callback(undef,false,identifier)}else if(err){return callback(err)}else if(res&&res.statusCode!=404&&res.statusCode!=200){return callback(getErrorMessage(res))}callback(undef,res.statusCode==200,identifier)})};ImboClient.prototype.imageExists=function(imgPath,callback){var self=this;self.getImageIdentifier(imgPath,function(err,imageIdentifier){if(err){return callback(err)}self.imageIdentifierExists(imageIdentifier,callback)})};ImboClient.prototype.addImageFromBlob=function(blob,callback){var self=this,onComplete=callback.complete||callback;var start=Date.now();self.getImageIdentifierFromString(blob,function(err,imageIdentifier){if(err){return onComplete(err)}var url=self.getSignedResourceUrl("PUT",self.getResourceUrl(imageIdentifier));Imbo.Compat.request("PUT",url,blob,{complete:function(err,res){var identifier=res&&res.body?res.body.imageIdentifier:imageIdentifier;if(err){return onComplete(err,identifier,res)}else if(res.statusCode!=201){return onComplete(getErrorMessage(res),identifier,res)}return onComplete(undef,identifier,res)},progress:callback.progress||null,uploadComplete:callback.uploadComplete||null})})};ImboClient.prototype.addImage=function(path,callback){var self=this;Imbo.Compat.getContents(path,function(err,data){if(err){return callback(err)}self.addImageFromBlob(data,callback)})};ImboClient.prototype.addImageFromUrl=function(url,callback){var self=this;Imbo.Compat.getContentsFromUrl(url,function(err,data){if(err){return callback(err)}self.addImageFromBlob(data,callback)})};ImboClient.prototype.getMetadata=function(imageIdentifier,callback){var url=this.getResourceUrl(imageIdentifier,"/meta");Imbo.Compat.request("GET",url.toString(),function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),null,res)}callback(undef,res.body,res)})};ImboClient.prototype.deleteMetadata=function(imageIdentifier,callback){var url=this.getSignedResourceUrl("DELETE",this.getResourceUrl(imageIdentifier,"/meta"));Imbo.Compat.request("DELETE",url,function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};ImboClient.prototype.editMetadata=function(imageIdentifier,data,callback){var url=this.getSignedResourceUrl("POST",this.getResourceUrl(imageIdentifier,"/meta"));Imbo.Compat.request("POST",url,data,function(err,res){if(err||res&&res.statusCode!=200&&res.statusCode!=201){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};Imbo.Client=ImboClient;return ImboClient})(Imbo);
+Imbo={Node:false,Version:"0.3.5"};(function(Imbo,undef){var faultylabs={};faultylabs.MD5=function(h){function c(s){var r=(s>>>0).toString(16);return"00000000".substr(0,8-r.length)+r}function b(t){var r=[];for(var s=0;s<t.length;s++){r=r.concat(o(t[s]))}return r}function q(s){var r=[];for(var t=0;t<8;t++){r.push(s&255);s=s>>>8}return r}function k(s,r){return s<<r&4294967295|s>>>32-r}function l(r,t,s){return r&t|~r&s}function i(r,t,s){return s&r|~s&t}function e(r,t,s){return r^t^s}function d(r,t,s){return t^(r|~s)}function n(r,s){return r[s+3]<<24|r[s+2]<<16|r[s+1]<<8|r[s]}function o(v){var r=[];for(var u=0;u<v.length;u++){if(v.charCodeAt(u)<=127){r.push(v.charCodeAt(u))}else{var t=encodeURIComponent(v.charAt(u)).substr(1).split("%");for(var s=0;s<t.length;s++){r.push(parseInt(t[s],16))}}}return r}function p(s,r,z,y){var x="";var w=0;var u=0;for(var v=3;v>=0;v--){u=arguments[v];w=u&255;u=u>>>8;w=w<<8;w=w|u&255;u=u>>>8;w=w<<8;w=w|u&255;u=u>>>8;w=w<<8;w=w|u;x=x+c(w)}return x}function j(t){var r=new Array(t.length);for(var s=0;s<t.length;s++){r[s]=t[s]}return r}var g=null;var m=null;if(typeof h=="string"){g=o(h)}else{if(h.constructor==Array){if(h.length===0){g=h}else{if(typeof h[0]=="string"){g=b(h)}else{if(typeof h[0]=="number"){g=h}else{m=typeof h[0]}}}}else{if(typeof ArrayBuffer!="undefined"){if(h instanceof ArrayBuffer){g=j(new Uint8Array(h))}else{if(h instanceof Uint8Array||h instanceof Int8Array){g=j(h)}else{if(h instanceof Uint32Array||h instanceof Int32Array||h instanceof Uint16Array||h instanceof Int16Array||h instanceof Float32Array||h instanceof Float64Array){g=j(new Uint8Array(h.buffer))}else{m=typeof h}}}}else{m=typeof h}}}if(m){alert("MD5 type mismatch, cannot process "+m)}function a(s,r){return 4294967295&s+r}return f();function f(){function u(H,I,F,E){var G=x;x=z;z=B;B=a(B,k(a(D,a(H,a(I,F))),E));D=G}var s=g.length;g.push(128);var v=g.length%64;if(v>56){for(var t=0;t<64-v;t++){g.push(0)}v=g.length%64}for(t=0;t<56-v;t++){g.push(0)}g=g.concat(q(s*8));var C=1732584193;var A=4023233417;var y=2562383102;var w=271733878;var D=0,B=0,z=0,x=0;for(t=0;t<g.length/64;t++){D=C;B=A;z=y;x=w;var r=t*64;u(l(B,z,x),3614090360,n(g,r),7);u(l(B,z,x),3905402710,n(g,r+4),12);u(l(B,z,x),606105819,n(g,r+8),17);u(l(B,z,x),3250441966,n(g,r+12),22);u(l(B,z,x),4118548399,n(g,r+16),7);u(l(B,z,x),1200080426,n(g,r+20),12);u(l(B,z,x),2821735955,n(g,r+24),17);u(l(B,z,x),4249261313,n(g,r+28),22);u(l(B,z,x),1770035416,n(g,r+32),7);u(l(B,z,x),2336552879,n(g,r+36),12);u(l(B,z,x),4294925233,n(g,r+40),17);u(l(B,z,x),2304563134,n(g,r+44),22);u(l(B,z,x),1804603682,n(g,r+48),7);u(l(B,z,x),4254626195,n(g,r+52),12);u(l(B,z,x),2792965006,n(g,r+56),17);u(l(B,z,x),1236535329,n(g,r+60),22);u(i(B,z,x),4129170786,n(g,r+4),5);u(i(B,z,x),3225465664,n(g,r+24),9);u(i(B,z,x),643717713,n(g,r+44),14);u(i(B,z,x),3921069994,n(g,r),20);u(i(B,z,x),3593408605,n(g,r+20),5);u(i(B,z,x),38016083,n(g,r+40),9);u(i(B,z,x),3634488961,n(g,r+60),14);u(i(B,z,x),3889429448,n(g,r+16),20);u(i(B,z,x),568446438,n(g,r+36),5);u(i(B,z,x),3275163606,n(g,r+56),9);u(i(B,z,x),4107603335,n(g,r+12),14);u(i(B,z,x),1163531501,n(g,r+32),20);u(i(B,z,x),2850285829,n(g,r+52),5);u(i(B,z,x),4243563512,n(g,r+8),9);u(i(B,z,x),1735328473,n(g,r+28),14);u(i(B,z,x),2368359562,n(g,r+48),20);u(e(B,z,x),4294588738,n(g,r+20),4);u(e(B,z,x),2272392833,n(g,r+32),11);u(e(B,z,x),1839030562,n(g,r+44),16);u(e(B,z,x),4259657740,n(g,r+56),23);u(e(B,z,x),2763975236,n(g,r+4),4);u(e(B,z,x),1272893353,n(g,r+16),11);u(e(B,z,x),4139469664,n(g,r+28),16);u(e(B,z,x),3200236656,n(g,r+40),23);u(e(B,z,x),681279174,n(g,r+52),4);u(e(B,z,x),3936430074,n(g,r),11);u(e(B,z,x),3572445317,n(g,r+12),16);u(e(B,z,x),76029189,n(g,r+24),23);u(e(B,z,x),3654602809,n(g,r+36),4);u(e(B,z,x),3873151461,n(g,r+48),11);u(e(B,z,x),530742520,n(g,r+60),16);u(e(B,z,x),3299628645,n(g,r+8),23);u(d(B,z,x),4096336452,n(g,r),6);u(d(B,z,x),1126891415,n(g,r+28),10);u(d(B,z,x),2878612391,n(g,r+56),15);u(d(B,z,x),4237533241,n(g,r+20),21);u(d(B,z,x),1700485571,n(g,r+48),6);u(d(B,z,x),2399980690,n(g,r+12),10);u(d(B,z,x),4293915773,n(g,r+40),15);u(d(B,z,x),2240044497,n(g,r+4),21);u(d(B,z,x),1873313359,n(g,r+32),6);u(d(B,z,x),4264355552,n(g,r+60),10);u(d(B,z,x),2734768916,n(g,r+24),15);u(d(B,z,x),1309151649,n(g,r+52),21);u(d(B,z,x),4149444226,n(g,r+16),6);u(d(B,z,x),3174756917,n(g,r+44),10);u(d(B,z,x),718787259,n(g,r+8),15);u(d(B,z,x),3951481745,n(g,r+36),21);C=a(C,D);A=a(A,B);y=a(y,z);w=a(w,x)}return p(w,y,A,C).toUpperCase()}};var CryptoJS=function(h,i){var e={},f=e.lib={},l=f.Base=function(){function a(){}return{extend:function(j){a.prototype=this;var d=new a;j&&d.mixIn(j);d.$super=this;return d},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var d in a)a.hasOwnProperty(d)&&(this[d]=a[d]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.$super.extend(this)}}}(),k=f.WordArray=l.extend({init:function(a,j){a=this.words=a||[];this.sigBytes=j!=i?j:4*a.length},toString:function(a){return(a||m).stringify(this)},concat:function(a){var j=this.words,d=a.words,c=this.sigBytes,a=a.sigBytes;this.clamp();if(c%4)for(var b=0;b<a;b++)j[c+b>>>2]|=(d[b>>>2]>>>24-8*(b%4)&255)<<24-8*((c+b)%4);else if(65535<d.length)for(b=0;b<a;b+=4)j[c+b>>>2]=d[b>>>2];else j.push.apply(j,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<32-8*(b%4);a.length=h.ceil(b/4)},clone:function(){var a=l.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*h.random()|0);return k.create(b,a)}}),o=e.enc={},m=o.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,2),16)<<24-4*(c%8);return k.create(d,b/2)}},q=o.Latin1={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return k.create(d,b)}},r=o.Utf8={stringify:function(a){try{return decodeURIComponent(escape(q.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data")}},parse:function(a){return q.parse(unescape(encodeURIComponent(a)))}},b=f.BufferedBlockAlgorithm=l.extend({reset:function(){this._data=k.create();this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,g=c/(4*e),g=a?h.ceil(g):h.max((g|0)-this._minBufferSize,0),a=g*e,c=h.min(4*a,c);if(a){for(var f=0;f<a;f+=e)this._doProcessBlock(d,f);f=d.splice(0,a);b.sigBytes-=c}return k.create(f,c)},clone:function(){var a=l.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});f.Hasher=b.extend({init:function(){this.reset()},reset:function(){b.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);this._doFinalize();return this._hash},clone:function(){var a=b.clone.call(this);a._hash=this._hash.clone();return a},blockSize:16,_createHelper:function(a){return function(b,d){return a.create(d).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return g.HMAC.create(a,d).finalize(b)}}});var g=e.algo={};return e}(Math);(function(h){var i=CryptoJS,e=i.lib,f=e.WordArray,e=e.Hasher,l=i.algo,k=[],o=[];(function(){function e(a){for(var b=h.sqrt(a),d=2;d<=b;d++)if(!(a%d))return!1;return!0}function f(a){return 4294967296*(a-(a|0))|0}for(var b=2,g=0;64>g;)e(b)&&(8>g&&(k[g]=f(h.pow(b,.5))),o[g]=f(h.pow(b,1/3)),g++),b++})();var m=[],l=l.SHA256=e.extend({_doReset:function(){this._hash=f.create(k.slice(0))},_doProcessBlock:function(e,f){for(var b=this._hash.words,g=b[0],a=b[1],j=b[2],d=b[3],c=b[4],h=b[5],l=b[6],k=b[7],n=0;64>n;n++){if(16>n)m[n]=e[f+n]|0;else{var i=m[n-15],p=m[n-2];m[n]=((i<<25|i>>>7)^(i<<14|i>>>18)^i>>>3)+m[n-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+m[n-16]}i=k+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&h^~c&l)+o[n]+m[n];p=((g<<30|g>>>2)^(g<<19|g>>>13)^(g<<10|g>>>22))+(g&a^g&j^a&j);k=l;l=h;h=c;c=d+i|0;d=j;j=a;a=g;g=i+p|0}b[0]=b[0]+g|0;b[1]=b[1]+a|0;b[2]=b[2]+j|0;b[3]=b[3]+d|0;b[4]=b[4]+c|0;b[5]=b[5]+h|0;b[6]=b[6]+l|0;b[7]=b[7]+k|0},_doFinalize:function(){var e=this._data,f=e.words,b=8*this._nDataBytes,g=8*e.sigBytes;f[g>>>5]|=128<<24-g%32;f[(g+64>>>9<<4)+15]=b;e.sigBytes=4*f.length;this._process()}});i.SHA256=e._createHelper(l);i.HmacSHA256=e._createHmacHelper(l)})(Math);(function(){var h=CryptoJS,i=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(e,f){e=this._hasher=e.create();"string"==typeof f&&(f=i.parse(f));var h=e.blockSize,k=4*h;f.sigBytes>k&&(f=e.finalize(f));for(var o=this._oKey=f.clone(),m=this._iKey=f.clone(),q=o.words,r=m.words,b=0;b<h;b++)q[b]^=1549556828,r[b]^=909522486;o.sigBytes=m.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var f=this._hasher,e=f.finalize(e);f.reset();return f.finalize(this._oKey.clone().concat(e))}})})();if(!window.BlobBuilder){window.BlobBuilder=window.WebKitBlobBuilder||window.MSBlobBuilder||window.MozBlobBuilder}if(typeof XMLHttpRequest.prototype.sendAsBinary!=="function"){XMLHttpRequest.prototype.sendAsBinary=function(text){var buffer=Imbo.Browser.getArrayBuffer(text);var data=null;try{if(typeof Blob!=="undefined"&&typeof DataView!=="undefined"){var dataView=new DataView(buffer);data=new Blob([dataView])}else if(typeof Blob!=="undefined"){data=new Blob([buffer])}}catch(e){data=null}if(!data){var bb=new BlobBuilder;bb.append(buffer);data=bb.getBlob()}this.send(data)}}Imbo.Browser={};Imbo.Browser.getArrayBuffer=function(text){var data=new ArrayBuffer(text.length);var ui8a=new Uint8Array(data,0);for(var i=0;i<text.length;i++)ui8a[i]=text.charCodeAt(i)&255;return data};Imbo.Browser.md5=function(buffer){if(typeof buffer!=="object"){buffer=Imbo.Browser.getArrayBuffer(buffer)}return faultylabs.MD5(buffer).toLowerCase()};Imbo.Browser.sha256=function(key,data){return CryptoJS.HmacSHA256(data,key).toString()};Imbo.Browser.getImageBlob=function(input,callback){if(input.constructor.name=="File"){return Imbo.Browser.getContentsFromFile(input,callback)}return callback(undef,input)};Imbo.Browser.getContentsFromFile=function(file,callback){var reader=new FileReader;reader.onload=function(e){return callback(undef,e.target.result)};reader.readAsBinaryString(file)};Imbo.Browser.getContentsFromUrl=function(url,callback){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.overrideMimeType("text/plain; charset=x-user-defined");xhr.onreadystatechange=function(){if(xhr.readyState===4){callback(undef,xhr.responseText)}};xhr.send(null)};Imbo.Browser.EtagCache={cache:{},get:function(url){return Imbo.Browser.EtagCache[url]},put:function(url,etag){Imbo.Browser.EtagCache[url]=etag}}})(Imbo);(function(env,undef){var headers={Accept:"application/json,image/*","User-Agent":"imboclient-js "+Imbo.Version+" ("+(env||navigator.userAgent)+")"};Imbo.Compat={sha256:function(key,data){if(Imbo.Node){return crypto.createHmac("sha256",key).update(data).digest("hex")}return Imbo.Browser.sha256(key,data)},md5:function(buffer,callback,isString){if(!Imbo.Node){if(buffer instanceof File){return Imbo.Browser.getContentsFromFile(buffer,function(err,data){Imbo.Compat.md5(data,callback)})}return callback(undef,Imbo.Browser.md5(buffer),{size:buffer.length})}},request:function(method,uri,data,callback){method=method.toUpperCase();if(!callback&&(data.complete||typeof data==="function")){callback=data;data=null}var onComplete=callback.complete||callback;var jsonRequest=data&&data.constructor&&data.constructor.name=="Object";var reqHeaders=method=="PUT"?Imbo.Compat.getPutHeaders(data?data.length:0):headers;if(jsonRequest){reqHeaders["Content-Type"]="application/json";data=JSON.stringify(data)}var xhr=new XMLHttpRequest,etag;xhr.onreadystatechange=function(){if(xhr.readyState===4&&xhr.status!==0&&onComplete){etag=xhr.getResponseHeader("etag");etag&&Imbo.Browser.EtagCache.put(uri,etag);onComplete(undef,Imbo.Compat.requestDone(xhr,jsonRequest))}};xhr.open(method,uri,true);xhr.setRequestHeader("Accept",headers.Accept);var etag=Imbo.Browser.EtagCache.get(uri);if(method=="GET"&&etag){xhr.setRequestHeader("If-None-Match",etag)}xhr.onerror=function(){onComplete("XMLHttpRequest failed - CORS disabled?")};if(callback.progress){xhr.upload.addEventListener("progress",callback.progress,false)}if(callback.uploadComplete){xhr.upload.addEventListener("load",callback.uploadComplete,false)}if(method=="PUT"){xhr.sendAsBinary(data)}else{xhr.send(data)}},requestDone:function(xhr,json){var resHeaders={"Content-Type":xhr.getResponseHeader("Content-Type"),"Last-Modified":xhr.getResponseHeader("Last-Modified"),"X-Imbo-Error-Internalcode":xhr.getResponseHeader("X-Imbo-Error-Internalcode")};for(var key in resHeaders){if(!resHeaders[key]){delete resHeaders[key]}}if(resHeaders["Content-Type"]=="application/json"){json=true}return{statusCode:xhr.status,body:json?JSON.parse(xhr.responseText):xhr.responseText,headers:resHeaders}},getPutHeaders:function(length){var putHeaders={};for(var key in headers){putHeaders[key]=headers[key]}if(length){putHeaders["Content-Length"]=length}return putHeaders},getContentsFromUrl:function(url,callback){if(!Imbo.Node){return Imbo.Browser.getContentsFromUrl(url,callback)}},getContents:function(file,callback){if(typeof file=="string"&&Imbo.Node){return fs.readFile(file,"binary",function(err,data){if(err&&err.code=="ENOENT"){return callback("File does not exist ("+err.path+")")}callback(err,data)})}else if(!Imbo.Node){return Imbo.Browser.getContentsFromFile(file,callback)}return callback("getContents() - not sure what to do with the passed file")}}})(typeof env==="undefined"?null:env);(function(Imbo,undef){Imbo.Compat=Imbo.Compat||require("./compat");var ImboUrl=function(baseUrl,publicKey,privateKey,imageIdentifier,path,queryString){this.transformations=[];this.baseUrl=baseUrl;this.publicKey=publicKey;this.privateKey=privateKey;this.imageIdentifier=imageIdentifier||"";this.path=path||"";this.queryString=queryString};ImboUrl.prototype.border=function(color,width,height){color=(color||"000000").replace(/^#/,"");width=parseInt(isNaN(width)?1:width,10);height=parseInt(isNaN(height)?1:height,10);return this.append("border:color="+color+",width="+width+",height="+height)};ImboUrl.prototype.compress=function(quality){quality=parseInt(quality,10);return this.append("compress:quality="+(quality?quality:75))};ImboUrl.prototype.convert=function(type){this.imageIdentifier=this.imageIdentifier.substr(0,32)+"."+type;return this};ImboUrl.prototype.gif=function(){return this.convert("gif")};ImboUrl.prototype.jpg=function(){return this.convert("jpg")};ImboUrl.prototype.png=function(){return this.convert("png")};ImboUrl.prototype.crop=function(x,y,width,height){return this.append("crop:x="+x+",y="+y+",width="+width+",height="+height)};ImboUrl.prototype.desaturate=function(){return this.append("desaturate")};ImboUrl.prototype.flipHorizontally=function(){return this.append("flipHorizontally")};ImboUrl.prototype.flipVertically=function(){return this.append("flipVertically")};ImboUrl.prototype.maxSize=function(width,height){var params=[];if(width){params.push("width="+parseInt(width,10))}if(height){params.push("height="+parseInt(height,10))}return this.append("maxSize:"+params.join(","))};ImboUrl.prototype.resize=function(width,height){var params=[];if(width){params.push("width="+parseInt(width,10))}if(height){params.push("height="+parseInt(height,10))}return this.append("resize:"+params.join(","))};ImboUrl.prototype.rotate=function(angle,bg){if(isNaN(angle)){return this}bg=(bg||"000000").replace(/^#/,"");return this.append("rotate:angle="+angle+",bg="+bg)};ImboUrl.prototype.sepia=function(threshold){threshold=parseInt(threshold,10);return this.append("sepia:threshold="+(threshold?threshold:80))};ImboUrl.prototype.thumbnail=function(width,height,fit){return this.append("thumbnail:width="+(width||50)+",height="+(height||50)+",fit="+(fit||"outbound"))};ImboUrl.prototype.transpose=function(){return this.append("transpose")};ImboUrl.prototype.transverse=function(){return this.append("transverse")};ImboUrl.prototype.reset=function(){this.imageIdentifier=this.imageIdentifier.substr(0,32);this.transformations=[];return this};ImboUrl.prototype.append=function(part){this.transformations.push(encodeURIComponent(part));return this};ImboUrl.prototype.getAccessToken=function(url){return Imbo.Compat.sha256(this.privateKey,url)};ImboUrl.prototype.getQueryString=function(){var query=this.queryString||"";if(this.transformations.length){query+=query.length?"&":"";query+="t[]="+this.transformations.reduce(function(query,element){return query+"&t[]="+element})}return query};ImboUrl.prototype.getUrl=function(){var url=this.baseUrl+"/users/"+this.publicKey;if(this.imageIdentifier||this.path){url=url+"/images/"+this.imageIdentifier+this.path}url=url.replace(/\/+$/,"");var qs=this.getQueryString();if(qs.length){url+="?"+qs}var token=this.getAccessToken(url,this.privateKey);return url+(url.indexOf("?")>-1?"&":"?")+"accessToken="+token};ImboUrl.prototype.toString=function(){return this.getUrl()};if(typeof module!=="undefined"){module.exports=ImboUrl}Imbo.Url=ImboUrl;return ImboUrl})(typeof Imbo!=="undefined"?Imbo:{});(function(Imbo,undef){var ImboQuery=function(){this.values={page:1,num:20,metadata:false,query:null,from:null,to:null}};ImboQuery.prototype.page=function(val){if(!val){return this.values.page}this.values.page=val;return this};ImboQuery.prototype.num=function(val){if(!val){return this.values.num}this.values.num=val;return this};ImboQuery.prototype.limit=ImboQuery.prototype.num;ImboQuery.prototype.metadata=function(val){if(typeof val==="undefined"){return this.values.metadata}this.values.metadata=!!val;return this};ImboQuery.prototype.query=function(val){if(!val){return this.values.query}this.values.query=val;return this};ImboQuery.prototype.from=function(val){if(!val){return this.values.from}this.values.from=val;return this};ImboQuery.prototype.to=function(val){if(!val){return this.values.to}this.values.to=val;return this};ImboQuery.prototype.toQueryString=function(){var params={},keys=["page","num","metadata","query","from","to"];for(var i=0;i<keys.length;i++){if(!!this.values[keys[i]]){params[keys[i]]=this.values[keys[i]]}}if(params.query){params.query=JSON.stringify(params.query)}if(params.from){params.from=Math.floor(params.from.getTime()/1e3)}if(params.to){params.to=Math.floor(params.to.getTime()/1e3)}var parts=[],key;for(key in params){parts.push(key+"="+encodeURIComponent(params[key]))}return parts.join("&")};ImboQuery.prototype.toString=ImboQuery.prototype.toQueryString;if(typeof module!=="undefined"){module.exports=ImboQuery}Imbo.Query=ImboQuery;return ImboQuery})(typeof Imbo!=="undefined"?Imbo:{});(function(Imbo,undef){var ImboClient=function(serverUrls,publicKey,privateKey){this.options={hosts:this.parseUrls(serverUrls),publicKey:publicKey,privateKey:privateKey}};var getErrorMessage=function(res){if(res&&res.body&&res.body.error){return res.body.error.message}else if(res&&res.headers){return res.headers["X-Imbo-Error-Internalcode"]||res.statusCode}else if(res){return res}return"Unknown error"};ImboClient.prototype.getImageIdentifier=function(image,callback){return Imbo.Compat.md5(image,callback)};ImboClient.prototype.getImageIdentifierFromString=function(image,callback){return Imbo.Compat.md5(image,callback,true)};ImboClient.prototype.getImageUrl=function(imageIdentifier){var host=this.getHostForImageIdentifier(imageIdentifier);return new Imbo.Url(host,this.options.publicKey,this.options.privateKey,imageIdentifier)};ImboClient.prototype.getImagesUrl=function(query){return this.getResourceUrl("","/",query?query.toString():null)};ImboClient.prototype.getUserUrl=function(){return this.getResourceUrl()};ImboClient.prototype.getResourceUrl=function(resourceIdentifier,path,query){return new Imbo.Url(this.options.hosts[0],this.options.publicKey,this.options.privateKey,resourceIdentifier,path,query)};ImboClient.prototype.getSignedResourceUrl=function(method,url,date){var timestamp=(date||new Date).toISOString().replace(/\.\d+Z$/,"Z");var signature=this.generateSignature(method,url.toString(),timestamp);var qs=url.toString().indexOf("?")>-1?"&":"?";qs+="signature="+encodeURIComponent(signature);qs+="&timestamp="+encodeURIComponent(timestamp);return url+qs};ImboClient.prototype.generateSignature=function(method,url,timestamp){var data=[method,url,this.options.publicKey,timestamp].join("|");var signature=Imbo.Compat.sha256(this.options.privateKey,data);return signature};ImboClient.prototype.getHostForImageIdentifier=function(imageIdentifier){var dec=parseInt(imageIdentifier.slice(0,2),16);return this.options.hosts[dec%this.options.hosts.length]};ImboClient.prototype.parseUrls=function(urls){if(typeof urls==="string"){urls=[urls]}var serverUrls=[];for(var i=0;i<urls.length;i++){serverUrls.push(urls[i].replace(/:80(\/|$)/,"$1").replace(/\/$/,""))}return serverUrls};ImboClient.prototype.headImage=function(imageIdentifier,cb){var callback=cb||function(){};var url=this.getResourceUrl(imageIdentifier),undef;Imbo.Compat.request("HEAD",url.toString(),function(err,res){if(err){return callback(err,res)}else if(res.statusCode!==200){return callback(getErrorMessage(res),res)}return callback(undef,res)})};ImboClient.prototype.deleteImage=function(imgPath,cb){var self=this,callback=cb||function(){};self.getImageIdentifier(imgPath,function(err,imageIdentifier){if(err){return callback(err)}self.deleteImageByIdentifier(imageIdentifier,callback)})};ImboClient.prototype.deleteImageByIdentifier=function(imageIdentifier,cb){var callback=cb||function(){};var url=this.getSignedResourceUrl("DELETE",this.getResourceUrl(imageIdentifier));Imbo.Compat.request("DELETE",url,function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};ImboClient.prototype.imageIdentifierExists=function(identifier,callback){this.headImage(identifier,function(err,res){if(err&&(err==404||res&&res.statusCode==404)){return callback(undef,false,identifier)}else if(err){return callback(err)}else if(res&&res.statusCode!=404&&res.statusCode!=200){return callback(getErrorMessage(res))}callback(undef,res.statusCode==200,identifier)})};ImboClient.prototype.imageExists=function(imgPath,callback){var self=this;self.getImageIdentifier(imgPath,function(err,imageIdentifier){if(err){return callback(err)}self.imageIdentifierExists(imageIdentifier,callback)})};ImboClient.prototype.addImageFromBlob=function(blob,cb){var callback=cb||function(){};var self=this,onComplete=callback.complete||callback;var start=Date.now();self.getImageIdentifierFromString(blob,function(err,imageIdentifier){if(err){return onComplete(err)}var url=self.getSignedResourceUrl("PUT",self.getResourceUrl(imageIdentifier));Imbo.Compat.request("PUT",url,blob,{complete:function(err,res){var identifier=res&&res.body?res.body.imageIdentifier:imageIdentifier;if(err){return onComplete(err,identifier,res)}else if(res.statusCode!=201){return onComplete(getErrorMessage(res),identifier,res)}return onComplete(undef,identifier,res)},progress:callback.progress||null,uploadComplete:callback.uploadComplete||null})})};ImboClient.prototype.addImage=function(path,cb){var self=this,callback=cb||function(){};Imbo.Compat.getContents(path,function(err,data){if(err){return callback(err)}self.addImageFromBlob(data,callback)})};ImboClient.prototype.addImageFromUrl=function(url,cb){var self=this,callback=cb||function(){};Imbo.Compat.getContentsFromUrl(url,function(err,data){if(err){return callback(err)}self.addImageFromBlob(data,callback)})};ImboClient.prototype.getUserInfo=function(callback){Imbo.Compat.request("GET",this.getUserUrl().toString(),function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),null,res)}callback(undef,res.body,res)})};ImboClient.prototype.getImages=function(callback,query){var url=this.getImagesUrl(query);Imbo.Compat.request("GET",url.toString(),function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),null,res)}callback(undef,res.body,res)})};ImboClient.prototype.getMetadata=function(imageIdentifier,cb){var url=this.getResourceUrl(imageIdentifier,"/meta");var callback=cb||function(){};Imbo.Compat.request("GET",url.toString(),function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),null,res)}callback(undef,res.body,res)})};ImboClient.prototype.deleteMetadata=function(imageIdentifier,cb){var url=this.getSignedResourceUrl("DELETE",this.getResourceUrl(imageIdentifier,"/meta"));var callback=cb||function(){};Imbo.Compat.request("DELETE",url,function(err,res){if(err||res&&res.statusCode!=200){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};ImboClient.prototype.editMetadata=function(imageIdentifier,data,cb){var url=this.getSignedResourceUrl("POST",this.getResourceUrl(imageIdentifier,"/meta"));var callback=cb||function(){};Imbo.Compat.request("POST",url,data,function(err,res){if(err||res&&res.statusCode!=200&&res.statusCode!=201){return callback(err||getErrorMessage(res),res)}callback(undef,res)})};Imbo.Client=ImboClient;return ImboClient})(Imbo);
View
12 lib/browser.js
@@ -108,4 +108,16 @@ Imbo = { Node: false, Version: '%Imbo.Version%' };
xhr.send(null);
};
+ Imbo.Browser.EtagCache = {
+ cache: {},
+
+ get: function(url) {
+ return Imbo.Browser.EtagCache[url];
+ },
+
+ put: function(url, etag) {
+ Imbo.Browser.EtagCache[url] = etag;
+ }
+ };
+
})(Imbo);
View
83 lib/client.js
@@ -49,8 +49,23 @@ if (typeof module !== 'undefined') {
return new Imbo.Url(host, this.options.publicKey, this.options.privateKey, imageIdentifier);
};
- ImboClient.prototype.getResourceUrl = function(resourceIdentifier, path) {
- return new Imbo.Url(this.options.hosts[0], this.options.publicKey, this.options.privateKey, resourceIdentifier, path);
+ ImboClient.prototype.getImagesUrl = function(query) {
+ return this.getResourceUrl('', '/', query ? query.toString() : null);
+ };
+
+ ImboClient.prototype.getUserUrl = function() {
+ return this.getResourceUrl();
+ };
+
+ ImboClient.prototype.getResourceUrl = function(resourceIdentifier, path, query) {
+ return new Imbo.Url(
+ this.options.hosts[0],
+ this.options.publicKey,
+ this.options.privateKey,
+ resourceIdentifier,
+ path,
+ query
+ );
};
ImboClient.prototype.getSignedResourceUrl = function(method, url, date) {
@@ -99,7 +114,8 @@ if (typeof module !== 'undefined') {
/**
* Image operations
*/
- ImboClient.prototype.headImage = function(imageIdentifier, callback) {
+ ImboClient.prototype.headImage = function(imageIdentifier, cb) {
+ var callback = cb || function() {};
var url = this.getResourceUrl(imageIdentifier), undef;
Imbo.Compat.request('HEAD', url.toString(), function(err, res) {
@@ -112,8 +128,8 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.deleteImage = function(imgPath, callback) {
- var self = this;
+ ImboClient.prototype.deleteImage = function(imgPath, cb) {
+ var self = this, callback = cb || function() {};
self.getImageIdentifier(imgPath, function(err, imageIdentifier) {
if (err) {
return callback(err);
@@ -123,7 +139,8 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.deleteImageByIdentifier = function(imageIdentifier, callback) {
+ ImboClient.prototype.deleteImageByIdentifier = function(imageIdentifier, cb) {
+ var callback = cb || function() {};
var url = this.getSignedResourceUrl('DELETE', this.getResourceUrl(imageIdentifier));
Imbo.Compat.request('DELETE', url, function(err, res) {
@@ -160,7 +177,8 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.addImageFromBlob = function(blob, callback) {
+ ImboClient.prototype.addImageFromBlob = function(blob, cb) {
+ var callback = cb || function() {};
var self = this, onComplete = callback.complete || callback;
var start = Date.now();
self.getImageIdentifierFromString(blob, function(err, imageIdentifier) {
@@ -191,11 +209,11 @@ if (typeof module !== 'undefined') {
/**
* Add a new image to the server (from filesystem)
*
- * @param {string|File} path Path to the local image, or an instance of File
- * @param {Function} callback Function to call when image has been uploaded
+ * @param {string|File} path Path to the local image, or an instance of File
+ * @param {Function} cb Function to call when image has been uploaded
*/
- ImboClient.prototype.addImage = function(path, callback) {
- var self = this;
+ ImboClient.prototype.addImage = function(path, cb) {
+ var self = this, callback = cb || function() {};
Imbo.Compat.getContents(path, function(err, data) {
if (err) {
return callback(err);
@@ -205,8 +223,8 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.addImageFromUrl = function(url, callback) {
- var self = this;
+ ImboClient.prototype.addImageFromUrl = function(url, cb) {
+ var self = this, callback = cb || function() {};
Imbo.Compat.getContentsFromUrl(url, function(err, data) {
if (err) {
return callback(err);
@@ -217,10 +235,41 @@ if (typeof module !== 'undefined') {
};
/**
+ * Fetch information for a given user/public key
+ */
+ ImboClient.prototype.getUserInfo = function(callback) {
+ Imbo.Compat.request('GET', this.getUserUrl().toString(), function(err, res) {
+ if (err || (res && res.statusCode != 200)) {
+ return callback(err || getErrorMessage(res), null, res);
+ }
+
+ callback(undef, res.body, res);
+ });
+ };
+
+ /**
+ * Fetch images
+ */
+ ImboClient.prototype.getImages = function(callback, query) {
+ // Build the complete URL
+ var url = this.getImagesUrl(query);
+
+ // Fetch the response
+ Imbo.Compat.request('GET', url.toString(), function(err, res) {
+ if (err || (res && res.statusCode != 200)) {
+ return callback(err || getErrorMessage(res), null, res);
+ }
+
+ callback(undef, res.body, res);
+ });
+ };
+
+ /**
* Metadata methods
*/
- ImboClient.prototype.getMetadata = function(imageIdentifier, callback) {
+ ImboClient.prototype.getMetadata = function(imageIdentifier, cb) {
var url = this.getResourceUrl(imageIdentifier, '/meta');
+ var callback = cb || function() {};
Imbo.Compat.request('GET', url.toString(), function(err, res) {
if (err || (res && res.statusCode != 200)) {
return callback(err || getErrorMessage(res), null, res);
@@ -230,8 +279,9 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.deleteMetadata = function(imageIdentifier, callback) {
+ ImboClient.prototype.deleteMetadata = function(imageIdentifier, cb) {
var url = this.getSignedResourceUrl('DELETE', this.getResourceUrl(imageIdentifier, '/meta'));
+ var callback = cb || function() {};
Imbo.Compat.request('DELETE', url, function(err, res) {
if (err || (res && res.statusCode != 200)) {
return callback(err || getErrorMessage(res), res);
@@ -241,8 +291,9 @@ if (typeof module !== 'undefined') {
});
};
- ImboClient.prototype.editMetadata = function(imageIdentifier, data, callback) {
+ ImboClient.prototype.editMetadata = function(imageIdentifier, data, cb) {
var url = this.getSignedResourceUrl('POST', this.getResourceUrl(imageIdentifier, '/meta'));
+ var callback = cb || function() {};
Imbo.Compat.request('POST', url, data, function(err, res) {
if (err || (res && res.statusCode != 200 && res.statusCode != 201)) {
return callback(err || getErrorMessage(res), res);
View
15 lib/compat.js
@@ -86,8 +86,8 @@ if (typeof module !== 'undefined') {
data = JSON.stringify(data);
}
+ // <Node>
if (Imbo.Node) {
- // <Node>
var options = {
'method' : method,
'uri' : uri,
@@ -104,18 +104,27 @@ if (typeof module !== 'undefined') {
}
onComplete(err, res, body);
});
- // </Node>
}
+ // </Node>
// Browser environment
- var xhr = new XMLHttpRequest();
+ var xhr = new XMLHttpRequest(), etag;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status !== 0 && onComplete) {
+ etag = xhr.getResponseHeader('etag');
+ etag && Imbo.Browser.EtagCache.put(uri, etag);
+
onComplete(undef, Imbo.Compat.requestDone(xhr, jsonRequest));
}
};
xhr.open(method, uri, true);
xhr.setRequestHeader('Accept', headers.Accept);
+
+ var etag = Imbo.Browser.EtagCache.get(uri);
+ if (method == 'GET' && etag) {
+ xhr.setRequestHeader('If-None-Match', etag);
+ }
+
xhr.onerror = function() {
onComplete('XMLHttpRequest failed - CORS disabled?');
};
View
1 lib/imbo.js
@@ -1,3 +1,4 @@
exports.Client = require('./client');
exports.Url = require('./url');
+exports.Query = require('./query');
exports.Version = require(__dirname + '/../package.json').version;
View
90 lib/query.js
@@ -0,0 +1,90 @@
+(function(Imbo, undef) {
+
+ var ImboQuery = function() {
+ this.values = {
+ page: 1,
+ num: 20,
+ metadata: false,
+ query: null,
+ from: null,
+ to: null
+ };
+ };
+
+ ImboQuery.prototype.page = function(val) {
+ if (!val) { return this.values.page; }
+ this.values.page = val;
+ return this;
+ };
+
+ ImboQuery.prototype.num = function(val) {
+ if (!val) { return this.values.num; }
+ this.values.num = val;
+ return this;
+ };
+
+ ImboQuery.prototype.limit = ImboQuery.prototype.num;
+
+ ImboQuery.prototype.metadata = function(val) {
+ if (typeof val === 'undefined') { return this.values.metadata; }
+ this.values.metadata = !!val;
+ return this;
+ };
+
+ ImboQuery.prototype.query = function(val) {
+ if (!val) { return this.values.query; }
+ this.values.query = val;
+ return this;
+ };
+
+ ImboQuery.prototype.from = function(val) {
+ if (!val) { return this.values.from; }
+ this.values.from = val;
+ return this;
+ };
+
+ ImboQuery.prototype.to = function(val) {
+ if (!val) { return this.values.to; }
+ this.values.to = val;
+ return this;
+ };
+
+ ImboQuery.prototype.toQueryString = function() {
+ // Retrieve query parameters, reduce params down to non-empty values
+ var params = {}, keys = ['page', 'num', 'metadata', 'query', 'from', 'to'];
+ for (var i = 0; i < keys.length; i++) {
+ if (!!this.values[keys[i]]) {
+ params[keys[i]] = this.values[keys[i]];
+ }
+ }
+
+ // JSON-encode metadata query, if present
+ if (params.query) {
+ params.query = JSON.stringify(params.query);
+ }
+
+ // Get timestamps from dates
+ if (params.from) {
+ params.from = Math.floor(params.from.getTime() / 1000);
+ }
+ if (params.to) {
+ params.to = Math.floor(params.to.getTime() / 1000);
+ }
+
+ // Build query string
+ var parts = [], key;
+ for (key in params) {
+ parts.push(key + '=' + encodeURIComponent(params[key]));
+ }
+ return parts.join('&');
+ };
+
+ ImboQuery.prototype.toString = ImboQuery.prototype.toQueryString;
+
+ if (typeof module !== 'undefined') {
+ module.exports = ImboQuery;
+ }
+
+ Imbo.Query = ImboQuery;
+ return ImboQuery;
+})(typeof Imbo !== 'undefined' ? Imbo : {});
View
24 lib/url.js
@@ -5,14 +5,15 @@
/**
* Imbo URL helper
*/
- function ImboUrl(baseUrl, publicKey, privateKey, imageIdentifier, path) {
+ var ImboUrl = function(baseUrl, publicKey, privateKey, imageIdentifier, path, queryString) {
this.transformations = [];
this.baseUrl = baseUrl;
this.publicKey = publicKey;
- this.imageIdentifier = imageIdentifier;
this.privateKey = privateKey;
+ this.imageIdentifier = imageIdentifier || '';
this.path = path || '';
- }
+ this.queryString = queryString;
+ };
ImboUrl.prototype.border = function(color, width, height) {
color = (color || '000000').replace(/^#/, '');
@@ -129,11 +130,12 @@
};
ImboUrl.prototype.getQueryString = function() {
- var query = '';
+ var query = this.queryString || '';
if (this.transformations.length) {
// We have some transformations. Generate a transformation key that will be sent to the
// server so the server can verify if the transformations are valid or not.
- query = 't[]=' + this.transformations.reduce(function(query, element) {
+ query += query.length ? '&' : '';
+ query += 't[]=' + this.transformations.reduce(function(query, element) {
return query + '&t[]=' + element;
});
}
@@ -142,15 +144,21 @@
};
ImboUrl.prototype.getUrl = function() {
- var url = this.baseUrl + '/users/' + this.publicKey + '/images/' + this.imageIdentifier + this.path;
- var qs = this.getQueryString();
+ var url = this.baseUrl + '/users/' + this.publicKey;
+ if (this.imageIdentifier || this.path) {
+ url = url + '/images/' + this.imageIdentifier + this.path;
+ }
+
+ url = url.replace(/\/+$/, '');
+
+ var qs = this.getQueryString();
if (qs.length) {
url += '?' + qs;
}
var token = this.getAccessToken(url, this.privateKey);
- return url + (qs.length ? '&' : '?') + 'accessToken=' + token;
+ return url + (url.indexOf('?') > -1 ? '&' : '?') + 'accessToken=' + token;
};
ImboUrl.prototype.toString = function() {
View
2 package.json
@@ -1,7 +1,7 @@
{
"name" : "imbo-client",
"description" : "An Imbo client for node.js and recent browsers",
- "version" : "0.3.2",
+ "version" : "0.3.5",
"author" : "Espen Hovlandsdal <rexxars@gmail.com>",
"contributors" : [],
"repository" : {
View
54 test/client.js
@@ -44,7 +44,31 @@ describe('Imbo.Client', function() {
it('should return something containing the image identifier', function() {
var url = client.getImageUrl(catMd5).toString();
- assert.equal(true, url.indexOf(catMd5) > 0, 'did not contain ' + catMd5);
+ assert.equal(true, url.indexOf(catMd5) > 0, url + ' did not contain ' + catMd5);
+ });
+ });
+
+ describe('#getImagesUrl', function() {
+ it('should return a ImboUrl-instance', function() {
+ var url = client.getImagesUrl();
+ assert.equal(true, url instanceof Imbo.Url, 'getImagesUrl did not return instance of ImboUrl');
+ });
+
+ it('should return the expected URL-string', function() {
+ var url = client.getImagesUrl().toString();
+ assert.equal('http://imbo/users/pub/images?accessToken=8b3a122984a9200c9d1a9cfa9f377aa2977e077b07398dc6c4bf574afabff851', url);
+ });
+ });
+
+ describe('#getUserUrl', function() {
+ it('should return a ImboUrl-instance', function() {
+ var url = client.getUserUrl();
+ assert.equal(true, url instanceof Imbo.Url, 'getUserUrl did not return instance of ImboUrl');
+ });
+
+ it('should return the expected URL-string', function() {
+ var url = client.getUserUrl().toString();
+ assert.equal('http://imbo/users/pub?accessToken=49d61296bd039ea36cb74597fb8ac51857f7fa8e77a42e72630cf03974abd2be', url);
});
});
@@ -281,6 +305,34 @@ describe('Imbo.Client', function() {
});
});
+ describe('#getUserInfo', function() {
+ it('should return an object of key => value data', function(done) {
+ mock.filteringPath(signatureCleaner)
+ .get('/users/pub')
+ .reply(200, JSON.stringify({ 'foo': 'bar' }), { 'Content-Type': 'application/json' });
+
+ client.getUserInfo(function(err, info, res) {
+ assert.equal(undef, err);
+ assert.equal('bar', info.foo);
+ assert.equal(200, res.statusCode);
+ done();
+ });
+ });
+
+ it('should return an error if the user does not exist', function(done) {
+ mock.filteringPath(signatureCleaner)
+ .get('/users/pub')
+ .reply(404, 'Not Found');
+
+ client.getUserInfo(function(err, body, res) {
+ assert.equal(404, err);
+ assert.equal(null, body);
+ assert.equal(404, res.statusCode);
+ done();
+ });
+ });
+ });
+
describe('#getMetadata', function() {
it('should return an object of key => value data', function(done) {
mock.filteringPath(signatureCleaner)
View
19 test/url.js
@@ -110,6 +110,20 @@ describe('Imbo.Url', function() {
});
});
+ describe('#maxSize', function() {
+ it('should return correct transformation', function() {
+ url.maxSize(320, 240).toString().should.include('?t[]=maxSize%3Awidth%3D320%2Cheight%3D240&accessToken=8b7e7f654a06c427285671c174349180686a6261aaf705b06bfe282e8b0a3b93');
+ });
+
+ it('should handle being passed only a width', function() {
+ url.maxSize(320).toString().should.include('?t[]=maxSize%3Awidth%3D320&accessToken=048eb874b111eafd3ed2ed8a858ac6e8a9f9835ce32483f1b3142ab3713a6c1f');
+ });
+
+ it('should handle being passed only a height', function() {
+ url.maxSize(null, 240).toString().should.include('?t[]=maxSize%3Aheight%3D240&accessToken=ce5f0c5faaab14157b7a1878cba765742e9ef78027bf3b720469e84f0e6fec49');
+ });
+ });
+
describe('#rotate', function() {
it('should return an unmodified url if angle is not a number', function() {
var original = url.toString();
@@ -187,6 +201,11 @@ describe('Imbo.Url', function() {
url.getQueryString().should.equal('');
});
+ it('should be able to construct query with existing params', function() {
+ var u = new Imbo.Url(baseUrl, pub, priv, catMd5, null, 'foo=bar&moo=tools');
+ u.transverse().getQueryString().should.equal('foo=bar&moo=tools&t[]=transverse');
+ });
+
/*
it('should include transformation key when there are only convert-transformations', function() {
//util.puts(url.png().getQueryString().toString());
Something went wrong with that request. Please try again.