var ApiClient = new Class({ Extends: Request, options: { // Default option values for the base class 'headers': {'Accept':'application/json'}, 'method': 'post', 'evalScripts': true, // Igloo specific options below: 'queryparams': null, // get added to the URL (can be used in POST and GET requests) 'postdata': null, // passed to the Request object later as the 'data' option 'sessionKey': Cookie.read('iglooauth').replace("\{","").replace("\}",""), // used in creating the signature 'secretKey': Cookie.read('igloojs').replace("\{","").replace("\}",""), // used in creating the signature 'sendimmediately': true, // immediately send the request once the ApiClient object is created, no more .send(), thanks Ian! 'debug': false, // this can be set to true in development to help with debugging // These api values are combined to create the Request url 'apibaseurl': document.location.protocol + '//' + document.location.host, 'apipath': '/.api/api.svc/', 'apimethod': null }, initialize: function(options){ this.parent(options); this.apiurl = this.options.apibaseurl; this.apiurl += this.options.apipath; this.apiurl += this.options.apimethod; // this is the querystring in the apiurl. This is one ingredient in the crypted sig var apiquerystring = new Hash({'apisecure':'on'}); if( this.options.queryparams ){ apiquerystring.combine(this.options.queryparams); } this.apiurl += "?" + Hash.toQueryString(apiquerystring); // OK now we have a very subtle problem. // The API expects queryparams containing a "tilde" ~ to be encoded as %7e // however our handydandy encodeURIComponent() function does not encode tildes. // thus we have to do it to each item in the queryparams before it's assembled into the querystring. // we can do this with a simple text replacement because delimiiters are known not to include tildes. // be wary that there may be other cases besides tilde this.apiurl = this.apiurl.replace('~','%7e'); if( this.options.sendimmediately ){ this.send(); } }, send: function(query_string){ // the user can set 'sendimmediately':false and call the send method passing in a query string like "save=username&name=John" if( typeof(query_string) === 'string' ){ this.setOptions({ postdata: query_string.parseQueryString() }); } /* interesting little glitch: if the method is POST and postdata is empty, firefox doesn't send a content-length header. This causes problems deeper in the system. So here we will add some dummy data */ if(this.options.method == 'post'){ if(this.options.postdata == null){ this.options.postdata = { 'post':'empty' } } } if(this.options.debug){ alert('### debug mode is on\n\n' + 'apiurl = '+this.apiurl+'\n\npostdata = '+Hash.toQueryString(this.options.postdata)); } // sign the request just before it's sent var sig = this.signRequest(); // start with the same URL we used to generate the signature (baseurl, path, method, queryparams, apisecure) var requesturl = this.apiurl; // add a qmark or an amp requesturl += (requesturl.indexOf('?') == -1)?'?':'&'; // add the other ingredients and stir requesturl += 'sessionKey='+this.options.sessionKey; requesturl += '&nonce='+this.nonce; requesturl += '&signature='+encodeURIComponent(sig); if(this.options.debug){ alert('### debug mode is on\n\n' + 'requesturl = '+requesturl+'\n\npostdata = '+Hash.toQueryString(this.options.postdata)); } // update the options with the final information needed by Request this.setOptions({ 'url': requesturl, 'data': this.options.postdata }); this.parent(); }, signRequest: function(){ // right at the moment of sending, generate a new nonce. // it mustn't be defined in the initialize method, because then it uses the same nonce every time // this way the same api object can be used more than once for repeated requests var nonce = this.uniquenonce(); // create the string, as described here -- http://developers.iglooteams.com/wiki/api_signature // this string has the format: // method : uri sessionKey nonce (but without the spaces) var str = this.options.method.toUpperCase(); str += ":"; str += this.apiurl; str += this.options.sessionKey; str += nonce; // if posting data, append the postdata to the string str += this.options.postdata?Hash.toQueryString(this.options.postdata):''; // encode the strings into arrays of bytes using a UTF encoder var utf8str = Utils.Utf8.encode(str); var utf8secret = Utils.Utf8.encode(this.options.secretKey); if(this.options.debug){alert('secret\n\n'+utf8secret);} // Generate the signature with the HMAC-SHA1 algorithm using the UTF8 encoded string and the API secret key var cryp = new Crypt.Sha1.Hmac({ 'b64pad': '=', 'output': 'b64' }); var sig = cryp.encrypt(utf8secret,utf8str); if(this.options.debug){alert('utf8str\n\n'+utf8str);} if(this.options.debug){alert('sig\n\n'+sig);} // hooray! we have the signature. Now let's build the AJAX Request URL return sig; }, uniquenonce: function(){ this.nonce = this.nonce || 1; this.Date = this.Date || new Date(); var seed = this.Date.getSeconds() + this.nonce; this.nonce = Math.floor( Math.random(seed)*1000000000 ) + 1; return this.nonce; } });