From 78df698b5ee69a49d62800e22813944067e722a7 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Tue, 8 Oct 2013 15:10:06 -0300 Subject: [PATCH 01/20] Add bower file. --- bower.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 bower.json diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..ef80b8a --- /dev/null +++ b/bower.json @@ -0,0 +1,11 @@ +{ + "name" : "silverorange-jsOAuth", + "main" : [ + "dist/jsOAuth.js" + ], + "ignore" : [ + "build/", + "spec/", + "src" + ] +} From 49e29985bfd643a4420785c7e1f6a196bc56bab1 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Fri, 25 Oct 2013 23:12:59 -0300 Subject: [PATCH 02/20] Bump version number. --- Version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Version b/Version index 3336003..6b7259c 100644 --- a/Version +++ b/Version @@ -1 +1 @@ -1.3.7 +1.3.8so1 From 732857cfe6f79568c82e39affa9c0c239a817d10 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Fri, 25 Oct 2013 23:13:27 -0300 Subject: [PATCH 03/20] Add new data-structures for multi-param support. --- src/OAuth/Collection.js | 79 ----------------------------------------- src/OAuth/List.js | 57 +++++++++++++++++++++++++++++ src/OAuth/Param.js | 31 ++++++++++++++++ src/OAuth/ParamList.js | 69 +++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 79 deletions(-) delete mode 100644 src/OAuth/Collection.js create mode 100644 src/OAuth/List.js create mode 100644 src/OAuth/Param.js create mode 100644 src/OAuth/ParamList.js diff --git a/src/OAuth/Collection.js b/src/OAuth/Collection.js deleted file mode 100644 index 36bf393..0000000 --- a/src/OAuth/Collection.js +++ /dev/null @@ -1,79 +0,0 @@ - function Collection(obj) { - var args = arguments, args_callee = args.callee, args_length = args.length, - i, collection = this; - - if (!(this instanceof args_callee)) { - return new args_callee(obj); - } - - for(i in obj) { - if (obj.hasOwnProperty(i)) { - collection[i] = obj[i]; - } - } - - return collection; - } - - function Hash() {} - Hash.prototype = { - join: function(string){ - string = string || ''; - return this.values().join(string); - }, - keys: function(){ - var i, arr = [], self = this; - for (i in self) { - if (self.hasOwnProperty(i)) { - arr.push(i); - } - } - - return arr; - }, - values: function(){ - var i, arr = [], self = this; - for (i in self) { - if (self.hasOwnProperty(i)) { - arr.push(self[i]); - } - } - - return arr; - }, - shift: function(){throw 'not implemented';}, - unshift: function(){throw 'not implemented';}, - push: function(){throw 'not implemented';}, - pop: function(){throw 'not implemented';}, - sort: function(){throw 'not implemented';}, - - ksort: function(func){ - var self = this, keys = self.keys(), i, value, key; - - if (func == undefined) { - keys.sort(); - } else { - keys.sort(func); - } - - for (i = 0; i < keys.length; i++) { - key = keys[i]; - value = self[key]; - delete self[key]; - self[key] = value; - } - - return self; - }, - toObject: function () { - var obj = {}, i, self = this; - for (i in self) { - if (self.hasOwnProperty(i)) { - obj[i] = self[i]; - } - } - - return obj; - } - }; - Collection.prototype = new Hash; diff --git a/src/OAuth/List.js b/src/OAuth/List.js new file mode 100644 index 0000000..3cd1919 --- /dev/null +++ b/src/OAuth/List.js @@ -0,0 +1,57 @@ + function List() { + var self = this; + self.values = []; + } + + List.prototype = { + join: function(string) { + string = string || ''; + return this.values.join(string); + }, + concat: function(obj) { + var self = this; + + if (obj instanceof List) { + obj = obj.values; + } else if (!(obj instanceof Array)) { + throw 'Can only concat lists or arrays.'; + } + + this.values = this.values.concat(obj); + return this; + }, + copy: function() { + var i, value; + + var list = new List(); + + for (i = 0; i < this.values.length; i++) { + if (typeof this.values[i].copy === 'function') { + value = this.values[i].copy(); + } else { + value = this.values[i]; + } + list.push(value); + } + + return list; + }, + shift: function() { + return this.values.shift(); + }, + unshift: function() { + this.values.unshift(arguments); + return this; + }, + push: function() { + this.values.push(arguments); + return this; + }, + pop: function() { + return this.values.pop(); + }, + sort: function() { + this.values.sort(); + return this; + } + }; diff --git a/src/OAuth/Param.js b/src/OAuth/Param.js new file mode 100644 index 0000000..0b2637e --- /dev/null +++ b/src/OAuth/Param.js @@ -0,0 +1,31 @@ + function Param(name, value) { + var args = arguments, args_callee = args.callee, args_length = args.length, + i, queryparam = this; + + if (!(this instanceof args_callee)) { + return new args_callee(name, value); + } + + if (name instanceof Array && name.length === 2) { + queryparam.name = name[0] + ''; + queryparam.value = name[1] + ''; + } else if (name !== undefined) { + queryparam.name = name; + if (value === undefined) { + queryparam.value = ''; + } else { + queryparam.value = value; + } + } + + return queryparam; + } + + Param.prototype.copy = function() { + return new QueryParam(this.name, this.value); + }; + + Param.prototype.toString = function() { + var encode = OAuth.urlEncode; + return encode(this.name) + '=' + encode(this.value); + }; diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js new file mode 100644 index 0000000..52fbab3 --- /dev/null +++ b/src/OAuth/ParamList.js @@ -0,0 +1,69 @@ + function ParamList(arr) { + var args = arguments, args_callee = args.callee, i, paramlist = this; + + if (!(this instanceof args_callee)) { + return new args_callee(arr); + } + + if (arr instanceof ParamList) { + for (i = 0; i < arr.values.length; i++) { + paramlist.push(arr.values[i]); + } + } else if (arr instanceof Array) { + for (i = 0; i < arr.length; i++) { + if (arr[i] instanceof Array && arr[i].length === 2) { + paramlist.push(new Param(arr[i][0], arr[i][1])); + } + } + } + + return paramlist; + } + + // ParamList is a type of list So inherit + ParamList.prototype = new List(); + + ParamList.prototype.sort = function() { + // byte-order sorting of names and then values + this.values.sort(function(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + if (a.value < b.value) { + return -1; + } + if (a.value > b.value) { + return 1; + } + return 0; + }); + + return self; + }; + + ParamList.prototype.toString = function () { + var i, q_arr = [], ret = '', encode = OAuth.urlEncode; + + this.sort(); + + for (i = 0; i < this.values.length; i++) { + q_arr.push(this.values[i] + ''); + } + + if (q_arr.length > 0) { + ret = q_arr.join('&'); + } + + return ret; + }; + + ParamList.prototype.toArray = function() { + var q_arr = [], i; + for (i = 0; i < this.values.length; i++) { + q_arr.push([ this.values[i].name, this.values[i].value ]); + } + return q_arr; + }; From 0d94e3ce270ae8d708565d8de73b93a0943bb1c4 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Fri, 25 Oct 2013 23:15:08 -0300 Subject: [PATCH 04/20] Add files to build. --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 46a0785..c87c599 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,9 @@ COMMONJS_TEST_DIR = ${COMMONJS_DIR}/tests DEST_DIR = ${DIST_DIR} SRC_FILES = ${SRC_DIR}/start.js \ - ${SRC_DIR}/OAuth/Collection.js \ + ${SRC_DIR}/OAuth/List.js \ + ${SRC_DIR}/OAuth/Param.js \ + ${SRC_DIR}/OAuth/ParamList.js \ ${SRC_DIR}/OAuth/URI.js \ ${SRC_DIR}/OAuth/Consumer.js \ ${SRC_DIR}/OAuth/Request.js \ From c7a78db479153fd4ad7999ba8c33635ff6969e0f Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 11:04:17 -0300 Subject: [PATCH 05/20] Clean up list subclassing. --- src/OAuth/List.js | 4 ++-- src/OAuth/ParamList.js | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/OAuth/List.js b/src/OAuth/List.js index 3cd1919..a1d8ac7 100644 --- a/src/OAuth/List.js +++ b/src/OAuth/List.js @@ -1,9 +1,9 @@ function List() { - var self = this; - self.values = []; + this.values = []; } List.prototype = { + constructor: List, join: function(string) { string = string || ''; return this.values.join(string); diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js index 52fbab3..8591745 100644 --- a/src/OAuth/ParamList.js +++ b/src/OAuth/ParamList.js @@ -1,4 +1,6 @@ function ParamList(arr) { + ParamList.superclass.construct.call(this, arr); + var args = arguments, args_callee = args.callee, i, paramlist = this; if (!(this instanceof args_callee)) { @@ -22,6 +24,8 @@ // ParamList is a type of list So inherit ParamList.prototype = new List(); + ParamList.superclass = List.prototype; + ParamList.prototype.constructor = ParamList; ParamList.prototype.sort = function() { // byte-order sorting of names and then values From bf677979928dc8e9e0cddb6a6902497228c60bef Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 11:04:31 -0300 Subject: [PATCH 06/20] Make QueryString subclass ParamList. --- src/OAuth/URI.js | 82 +++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/src/OAuth/URI.js b/src/OAuth/URI.js index ab64a28..b9ef5b0 100644 --- a/src/OAuth/URI.js +++ b/src/OAuth/URI.js @@ -71,47 +71,14 @@ * * @param {Object} obj */ - function QueryString(obj){ - var args = arguments, args_callee = args.callee, args_length = args.length, - i, querystring = this, decode = OAuth.urlDecode; - - if (!(this instanceof args_callee)) { - return new args_callee(obj); - } - - if (obj != undefined) { - for (i in obj) { - if (obj.hasOwnProperty(i)) { - querystring[i] = obj[i]; - } - } - } - - return querystring; + function QueryString(arr) { + QueryString.superclass.construct.call(this, arr); } - // QueryString is a type of collection So inherit - QueryString.prototype = new Collection(); - QueryString.prototype.toString = function () { - var i, self = this, q_arr = [], ret = '', - val = '', encode = OAuth.urlEncode; - self.ksort(); // lexicographical byte value ordering of the keys - - for (i in self) { - if (self.hasOwnProperty(i)) { - if (i != undefined && self[i] != undefined) { - val = encode(i) + '=' + encode(self[i]); - q_arr.push(val); - } - } - } - - if (q_arr.length > 0) { - ret = q_arr.join('&'); - } - - return ret; - }; + // QueryString is a type of param list, so inherit + QueryString.prototype = new ParamList(); + QueryString.superclass = ParamList.prototype; + QueryString.prototype.constructor = QueryString; /** * @@ -121,12 +88,27 @@ var args = arguments, args_length = args.length, i, query_array, query_array_length, querystring = this, key_value, decode = OAuth.urlDecode; - if (args_length == 1) { + if (args_length === 1) { if (typeof query === 'object') { - // iterate - for (i in query) { - if (query.hasOwnProperty(i)) { - querystring[i] = decode(query[i]); + if (query instanceof Array) { + // iterate array + for (i = 0; i < query.length; i++) { + if (query[i] instanceof Array && query[i].length === 2) { + querystring.push( + new Param(query[i][0]), + new Param(query[i][1]) + ); + } + } + } else { + // iterate object + for (i in query) { + if (query.hasOwnProperty(i)) { + querystring.push( + new Param(i), + new Param(query[i]) + ); + } } } } else if (typeof query === 'string') { @@ -136,15 +118,21 @@ for (i = 0, query_array_length = query_array.length; i < query_array_length; i++) { // split on '=' to get key, value key_value = query_array[i].split('='); - if (key_value[0] != "") { - querystring[key_value[0]] = decode(key_value[1]); + if (key_value[0] !== '') { + querystring.push( + new Param(decode(key_value[0])), + new Param(decode(key_value[1])) + ); } } } } else { for (i = 0; i < args_length; i += 2) { // treat each arg as key, then value - querystring[args[i]] = decode(args[i+1]); + querystring.push( + new Param(args[i]), + new Param(args[i + 1]) + ); } } }; From 2391fd1f839d63df42f3c949795a74d262d9c19a Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 12:59:25 -0300 Subject: [PATCH 07/20] Use new list types to send requests for consumer. --- src/OAuth/Consumer.js | 139 ++++++++++++++++------------------------- src/OAuth/ParamList.js | 14 ++++- 2 files changed, 67 insertions(+), 86 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index 0a4b41e..4e276af 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -80,22 +80,20 @@ * @param options {object} * method {string} ['GET', 'POST', 'PUT', ...] * url {string} A valid http(s) url - * data {object} A key value paired object of data - * example: {'q':'foobar'} - * for GET this will append a query string - * headers {object} A key value paired object of additional headers + * data {ParamList} A list of of data. For GET this will append a query string. + * headers {ParamList} A list of additional headers. * success {function} callback for a sucessful request * failure {function} callback for a failed request */ this.request = function (options) { var method, url, data, headers, success, failure, xhr, i, headerParams, signatureMethod, signatureString, signature, - query = [], appendQueryString, signatureData = {}, params, withFile, urlString; + query = [], appendQueryString, signatureData = new ParamList(), params, withFile, urlString; method = options.method || 'GET'; url = URI(options.url); - data = options.data || {}; - headers = options.headers || {}; + data = (options.data) ? new ParamList(options.data) : new ParamList(); + headers = (options.headers) ? new ParamList(options.headers) : new ParamList(); success = options.success || function () {}; failure = options.failure || function () {}; @@ -159,24 +157,21 @@ } }; - headerParams = { - 'oauth_callback': oauth.callbackUrl, - 'oauth_consumer_key': oauth.consumerKey, - 'oauth_token': oauth.accessTokenKey, - 'oauth_signature_method': oauth.signatureMethod, - 'oauth_timestamp': getTimestamp(), - 'oauth_nonce': getNonce(), - 'oauth_verifier': oauth.verifier, - 'oauth_version': OAUTH_VERSION_1_0 - }; + headerParams = new ParamList([ + [ 'oauth_callback', oauth.callbackUrl ], + [ 'oauth_consumer_key', oauth.consumerKey ], + [ 'oauth_token', oauth.accessTokenKey ], + [ 'oauth_signature_method', oauth.signatureMethod ], + [ 'oauth_timestamp', getTimestamp() ], + [ 'oauth_nonce', getNonce() ], + [ 'oauth_verifier', oauth.verifier ], + [ 'oauth_version', OAUTH_VERSION_1_0 ] + ]); signatureMethod = oauth.signatureMethod; // Handle GET params first - params = url.query.toObject(); - for (i in params) { - signatureData[i] = params[i]; - } + signatureData.concat(url.query); // According to the OAuth spec // if data is transfered using @@ -184,9 +179,7 @@ // have to be signed: // http://www.mail-archive.com/oauth@googlegroups.com/msg01556.html if((!('Content-Type' in headers) || headers['Content-Type'] == 'application/x-www-form-urlencoded') && !withFile) { - for (i in data) { - signatureData[i] = data[i]; - } + signatureData.concat(data); } urlString = url.scheme + '://' + url.host + url.path; @@ -194,11 +187,11 @@ signature = OAuth.signatureMethod[signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString); - headerParams.oauth_signature = signature; + headerParams.push(new Param('oauth_signature', signature)); if (this.realm) { - headerParams['realm'] = this.realm; + headerParams.push(new Param('realm', this.realm)); } if (oauth.proxy) { @@ -213,32 +206,30 @@ url = URI(oauth.proxyUrl + url.path); } - if(appendQueryString || method == 'GET') { + if(appendQueryString || method === 'GET') { url.query.setQueryParams(data); query = null; } else if(!withFile){ - if (typeof data == 'string') { + // TODO: hmmmmm...... handle POST +/* if (typeof data === 'string') { query = data; if (!('Content-Type' in headers)) { headers['Content-Type'] = 'text/plain'; } - } else { - for(i in data) { - query.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(data[i] + '')); - } - query = query.sort().join('&'); + } else {*/ + query = data.copy().sort().join('&'); if (!('Content-Type' in headers)) { headers['Content-Type'] = 'application/x-www-form-urlencoded'; } - } +/* }*/ } else if(withFile) { // When using FormData multipart content type // is used by default and required header // is set to multipart/form-data etc query = new FormData(); - for(i in data) { - query.append(i, data[i]); + for (i = 0; i < data.values.length; i++) { + query.append(data.values[i].name, data.values[i].value); } } @@ -389,24 +380,30 @@ /** * Get a string of the parameters for the OAuth Authorization header * - * @param params {object} A key value paired object of data - * example: {'q':'foobar'} - * for GET this will append a query string + * @param params {ParamList} A list of data. */ function toHeaderString(params) { - var arr = [], i, realm; - - for (i in params) { - if (params[i] && params[i] !== undefined && params[i] !== '') { - if (i === 'realm') { - realm = i + '="' + params[i] + '"'; - } else { - arr.push(i + '="' + OAuth.urlEncode(params[i]+'') + '"'); - } + var list = new ParamList(), i, realm, encode = OAuth.urlEncode, arr = []; + + for (i = 0; i < params.values.length; i++) { + if (params.values[i].name === 'realm') { + realm = encode(params.values[i].name) + '="' + encode(params.values[i].value) + '"' + } else { + list.push(new Param( + params.values[i].name, + params.values[i].value + ); } } - arr.sort(); + list.sort(); + + // encode sorted list + for (i = 0; i < list.length; i++) { + arr.push(encode(list[i].name) + '="' + encode(list[i].value) + '"'); + } + + // add realm to start if (realm) { arr.unshift(realm); } @@ -419,50 +416,22 @@ * * @param method {string} ['GET', 'POST', 'PUT', ...] * @param url {string} A valid http(s) url - * @param header_params A key value paired object of additional headers - * @param query_params {object} A key value paired object of data - * example: {'q':'foobar'} - * for GET this will append a query string + * @param header_params {ParamList} List of additional headers + * @param query_params {ParamList} List of POST data or query parameters. */ function toSignatureBaseString(method, url, header_params, query_params) { - var arr = [], i, encode = OAuth.urlEncode; + var list = new ParamList(), i, encode = OAuth.urlEncode; - for (i in header_params) { - if (header_params[i] !== undefined && header_params[i] !== '') { - arr.push([OAuth.urlEncode(i), OAuth.urlEncode(header_params[i]+'')]); - } - } - - for (i in query_params) { - if (query_params[i] !== undefined && query_params[i] !== '') { - if (!header_params[i]) { - arr.push([encode(i), encode(query_params[i] + '')]); - } - } - } + list.concat(header_params); + list.concat(query_params); - arr = arr.sort(function(a, b) { - if (a[0] < b[0]) { - return -1; - } else if (a[0] > b[0]) { - return 1; - } else { - if (a[1] < b[1]) { - return -1; - } else if (a[1] > b[1]) { - return 1; - } else { - return 0; - } - } - }).map(function(el) { - return el.join("="); - }); + list.removeByName('oauth_signature'); + list.sort(); return [ method, encode(url), - encode(arr.join('&')) + encode(list.join('&')) ].join('&'); } diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js index 8591745..9a8ab86 100644 --- a/src/OAuth/ParamList.js +++ b/src/OAuth/ParamList.js @@ -45,7 +45,19 @@ return 0; }); - return self; + return this; + }; + + ParamList.prototye.removeByName = function(name) { + var i, length = this.values.length; + for (i = 0; i < length; i++) { + if (this.values[i].name === name) { + this.values.splice(i, 1); + i--; + length--; + } + } + return this; }; ParamList.prototype.toString = function () { From 032d25fcbf5e3e5b1e230ae538a30031359742a9 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 13:01:05 -0300 Subject: [PATCH 08/20] Fix parse error. --- src/OAuth/Consumer.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index 4e276af..785d92b 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -389,9 +389,11 @@ if (params.values[i].name === 'realm') { realm = encode(params.values[i].name) + '="' + encode(params.values[i].value) + '"' } else { - list.push(new Param( - params.values[i].name, - params.values[i].value + list.push( + new Param( + params.values[i].name, + params.values[i].value + ) ); } } From 66994c068e9127105424f3f0e9b2e2666d2fe95b Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 15:41:03 -0300 Subject: [PATCH 09/20] Fix variable names for Param constructor. --- src/OAuth/Param.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OAuth/Param.js b/src/OAuth/Param.js index 0b2637e..0da4c98 100644 --- a/src/OAuth/Param.js +++ b/src/OAuth/Param.js @@ -1,28 +1,28 @@ function Param(name, value) { var args = arguments, args_callee = args.callee, args_length = args.length, - i, queryparam = this; + i, param = this; if (!(this instanceof args_callee)) { return new args_callee(name, value); } if (name instanceof Array && name.length === 2) { - queryparam.name = name[0] + ''; - queryparam.value = name[1] + ''; + param.name = name[0] + ''; + param.value = name[1] + ''; } else if (name !== undefined) { - queryparam.name = name; + param.name = name; if (value === undefined) { - queryparam.value = ''; + param.value = ''; } else { - queryparam.value = value; + param.value = value; } } - return queryparam; + return param; } Param.prototype.copy = function() { - return new QueryParam(this.name, this.value); + return new Param(this.name, this.value); }; Param.prototype.toString = function() { From 451b06e9fb6daee03dd58a67270c194239fe9ecc Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 15:41:50 -0300 Subject: [PATCH 10/20] Enhancements to List and ParamList * Add List.each() method and use in List and Param List * Add List.length() method * Fix constructor superclass calls. --- src/OAuth/List.js | 35 +++++++++++++++++++++++--------- src/OAuth/ParamList.js | 46 +++++++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/OAuth/List.js b/src/OAuth/List.js index a1d8ac7..6c3609e 100644 --- a/src/OAuth/List.js +++ b/src/OAuth/List.js @@ -8,6 +8,9 @@ string = string || ''; return this.values.join(string); }, + length: function() { + return this.values.length; + }, concat: function(obj) { var self = this; @@ -21,18 +24,14 @@ return this; }, copy: function() { - var i, value; - var list = new List(); - for (i = 0; i < this.values.length; i++) { - if (typeof this.values[i].copy === 'function') { - value = this.values[i].copy(); - } else { - value = this.values[i]; + this.each(function(i, value) { + if (typeof value.copy === 'function') { + value = value.copy(); } list.push(value); - } + }); return list; }, @@ -40,11 +39,11 @@ return this.values.shift(); }, unshift: function() { - this.values.unshift(arguments); + this.values.unshift.apply(this.values, arguments); return this; }, push: function() { - this.values.push(arguments); + this.values.push.apply(this.values, arguments); return this; }, pop: function() { @@ -53,5 +52,21 @@ sort: function() { this.values.sort(); return this; + }, + getFirst: function() { + var value = null; + + if (this.length() > 0) { + value = this.values[0]; + } + + return value; + }, + each: function(callback) { + var i; + for (i = 0; i < this.length(); i++) { + callback(i, this.values[i]); + } + return this; } }; diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js index 9a8ab86..8993816 100644 --- a/src/OAuth/ParamList.js +++ b/src/OAuth/ParamList.js @@ -1,5 +1,5 @@ function ParamList(arr) { - ParamList.superclass.construct.call(this, arr); + ParamList.superclass.constructor.call(this, arr); var args = arguments, args_callee = args.callee, i, paramlist = this; @@ -27,6 +27,30 @@ ParamList.superclass = List.prototype; ParamList.prototype.constructor = ParamList; + ParamList.prototype.getByNameInsensitive = function(name) { + var list = new ParamList(); + + this.each(function(i, param) { + if (param.name.toLowerCase() === name.toLowerCase()) { + list.push(param); + } + }); + + return list; + }; + + ParamList.prototype.getByName = function(name) { + var list = new ParamList(); + + this.each(function(i, param) { + if (param.name === name) { + list.push(param); + } + }); + + return list; + }; + ParamList.prototype.sort = function() { // byte-order sorting of names and then values this.values.sort(function(a, b) { @@ -48,7 +72,7 @@ return this; }; - ParamList.prototye.removeByName = function(name) { + ParamList.prototype.removeByName = function(name) { var i, length = this.values.length; for (i = 0; i < length; i++) { if (this.values[i].name === name) { @@ -61,13 +85,13 @@ }; ParamList.prototype.toString = function () { - var i, q_arr = [], ret = '', encode = OAuth.urlEncode; + var q_arr = [], ret = ''; this.sort(); - for (i = 0; i < this.values.length; i++) { - q_arr.push(this.values[i] + ''); - } + this.each(function(i, param) { + q_arr.push(param + ''); + }); if (q_arr.length > 0) { ret = q_arr.join('&'); @@ -77,9 +101,11 @@ }; ParamList.prototype.toArray = function() { - var q_arr = [], i; - for (i = 0; i < this.values.length; i++) { - q_arr.push([ this.values[i].name, this.values[i].value ]); - } + var q_arr = []; + + this.each(function(i, param) { + q_arr.push([ param.name, param.value ]); + }); + return q_arr; }; From 1f3c0a469516afddc5e90c042c97aa312586af08 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 15:42:34 -0300 Subject: [PATCH 11/20] Fix constructor superclass call. --- src/OAuth/URI.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth/URI.js b/src/OAuth/URI.js index b9ef5b0..f97f157 100644 --- a/src/OAuth/URI.js +++ b/src/OAuth/URI.js @@ -72,7 +72,7 @@ * @param {Object} obj */ function QueryString(arr) { - QueryString.superclass.construct.call(this, arr); + QueryString.superclass.constructor.call(this, arr); } // QueryString is a type of param list, so inherit From 7fbb6f9ee6275642cd430f9f70fd7d0999585d94 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 15:43:06 -0300 Subject: [PATCH 12/20] More work to support multi-params. * use new list methods for iteration * fix request and response headers * ignore empty parameters --- src/OAuth/Consumer.js | 89 ++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index 785d92b..f8655d2 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -88,7 +88,8 @@ this.request = function (options) { var method, url, data, headers, success, failure, xhr, i, headerParams, signatureMethod, signatureString, signature, - query = [], appendQueryString, signatureData = new ParamList(), params, withFile, urlString; + query = [], appendQueryString, signatureData = new ParamList(), params, withFile, urlString, + contentType; method = options.method || 'GET'; url = URI(options.url); @@ -120,7 +121,7 @@ if (xhr.readyState === 4) { var regex = /^(.*?):\s*(.*?)\r?$/mg, requestHeaders = headers, - responseHeaders = {}, + responseHeaders = new ParamList(), responseHeadersString = '', match; @@ -129,23 +130,30 @@ while((match = regex.exec(responseHeadersString))) { responseHeaders[match[1]] = match[2]; } - } else if(!!xhr.getResponseHeaders) { + } else if (!!xhr.getResponseHeaders) { responseHeadersString = xhr.getResponseHeaders(); for (var i = 0, len = responseHeadersString.length; i < len; ++i) { - responseHeaders[responseHeadersString[i][0]] = responseHeadersString[i][1]; + responseHeaders.push( + new Param( + responseHeadersString[i][0], + responseHeadersString[i][1] + ) + ); } } var includeXML = false; - if ('Content-Type' in responseHeaders) - { - if (responseHeaders['Content-Type'] == 'text/xml') - { - includeXML = true; - } - + var contentType = responseHeaders.getByNameInsensitive('Content-Type').getFirst(); + if (contentType && contentType.value === 'text/xml') { + includeXML = true; } - var responseObject = {text: xhr.responseText, xml: (includeXML ? xhr.responseXML : ''), requestHeaders: requestHeaders, responseHeaders: responseHeaders}; + + var responseObject = { + text: xhr.responseText, + xml: (includeXML ? xhr.responseXML : ''), + requestHeaders: requestHeaders, + responseHeaders: responseHeaders + }; // we are powerless against 3xx redirects if((xhr.status >= 200 && xhr.status <= 226) || xhr.status == 304 || xhr.status === 0) { @@ -178,7 +186,8 @@ // multipart the POST data doesn't // have to be signed: // http://www.mail-archive.com/oauth@googlegroups.com/msg01556.html - if((!('Content-Type' in headers) || headers['Content-Type'] == 'application/x-www-form-urlencoded') && !withFile) { + contentType = headers.getByNameInsensitive('Content-Type').getFirst(); + if ((!contentType || contentType.value.toLowerCase() === 'application/x-www-form-urlencoded') && !withFile) { signatureData.concat(data); } @@ -189,8 +198,7 @@ headerParams.push(new Param('oauth_signature', signature)); - if (this.realm) - { + if (this.realm) { headerParams.push(new Param('realm', this.realm)); } @@ -218,8 +226,8 @@ } } else {*/ query = data.copy().sort().join('&'); - if (!('Content-Type' in headers)) { - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (!contentType) { + headers.push(new Param('Content-Type', 'application/x-www-form-urlencoded')); } /* }*/ @@ -237,9 +245,10 @@ xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams)); xhr.setRequestHeader('X-Requested-With','XMLHttpRequest'); - for (i in headers) { - xhr.setRequestHeader(i, headers[i]); - } + + headers.each(function(i, param) { + xhr.setRequestHeader(param.name, param.value); + }); xhr.send(query); }; @@ -385,25 +394,27 @@ function toHeaderString(params) { var list = new ParamList(), i, realm, encode = OAuth.urlEncode, arr = []; - for (i = 0; i < params.values.length; i++) { - if (params.values[i].name === 'realm') { - realm = encode(params.values[i].name) + '="' + encode(params.values[i].value) + '"' - } else { - list.push( - new Param( - params.values[i].name, - params.values[i].value - ) - ); + params.each(function(i, param) { + if (param.value !== '') { + if (param.name.toLowerCase() === 'realm') { + realm = encode(param.name) + '="' + encode(param.value) + '"' + } else { + list.push( + new Param( + param.name, + param.value + ) + ); + } } - } + }); list.sort(); // encode sorted list - for (i = 0; i < list.length; i++) { - arr.push(encode(list[i].name) + '="' + encode(list[i].value) + '"'); - } + list.each(function(i, param) { + arr.push(encode(param.name) + '="' + encode(param.value) + '"'); + }); // add realm to start if (realm) { @@ -422,7 +433,7 @@ * @param query_params {ParamList} List of POST data or query parameters. */ function toSignatureBaseString(method, url, header_params, query_params) { - var list = new ParamList(), i, encode = OAuth.urlEncode; + var list = new ParamList(), i, encode = OAuth.urlEncode, noEmpty = new ParamList(); list.concat(header_params); list.concat(query_params); @@ -430,10 +441,16 @@ list.removeByName('oauth_signature'); list.sort(); + list.each(function(i, param) { + if (param.value !== '') { + noEmpty.push(param); + } + }); + return [ method, encode(url), - encode(list.join('&')) + encode(noEmpty.join('&')) ].join('&'); } From f6ded6397d986b111432813be59d4b7e33857b87 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 15:52:54 -0300 Subject: [PATCH 13/20] Fix param constructors in setQueryString(). --- src/OAuth/URI.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/OAuth/URI.js b/src/OAuth/URI.js index f97f157..04ca5c1 100644 --- a/src/OAuth/URI.js +++ b/src/OAuth/URI.js @@ -95,8 +95,10 @@ for (i = 0; i < query.length; i++) { if (query[i] instanceof Array && query[i].length === 2) { querystring.push( - new Param(query[i][0]), - new Param(query[i][1]) + new Param( + query[i][0], + query[i][1] + ) ); } } @@ -105,8 +107,10 @@ for (i in query) { if (query.hasOwnProperty(i)) { querystring.push( - new Param(i), - new Param(query[i]) + new Param( + i, + query[i] + ) ); } } @@ -120,8 +124,10 @@ key_value = query_array[i].split('='); if (key_value[0] !== '') { querystring.push( - new Param(decode(key_value[0])), - new Param(decode(key_value[1])) + new Param( + decode(key_value[0]), + decode(key_value[1]) + ) ); } } @@ -130,8 +136,10 @@ for (i = 0; i < args_length; i += 2) { // treat each arg as key, then value querystring.push( - new Param(args[i]), - new Param(args[i + 1]) + new Param( + args[i], + args[i + 1] + ) ); } } From a0772b9022754c2c32dea289b8e732ae7de89172 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 16:24:00 -0300 Subject: [PATCH 14/20] Use each() to iterate in ParamList copy-constructor. --- src/OAuth/ParamList.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js index 8993816..b21df42 100644 --- a/src/OAuth/ParamList.js +++ b/src/OAuth/ParamList.js @@ -8,9 +8,9 @@ } if (arr instanceof ParamList) { - for (i = 0; i < arr.values.length; i++) { - paramlist.push(arr.values[i]); - } + arr.each(function(i, param) { + paramlist.push(param); + }); } else if (arr instanceof Array) { for (i = 0; i < arr.length; i++) { if (arr[i] instanceof Array && arr[i].length === 2) { From 7fd93e4f9e56597c8d0f6cff33edde114ba85d9a Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 16:24:20 -0300 Subject: [PATCH 15/20] Make copy() method keep the same class for copied list. --- src/OAuth/List.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth/List.js b/src/OAuth/List.js index 6c3609e..be0c785 100644 --- a/src/OAuth/List.js +++ b/src/OAuth/List.js @@ -24,7 +24,7 @@ return this; }, copy: function() { - var list = new List(); + var list = new this.constructor(); this.each(function(i, value) { if (typeof value.copy === 'function') { From 1ecea1dae17e9b59b77fd2bc7d0ce771e0638308 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 16:24:49 -0300 Subject: [PATCH 16/20] Use each() to iterate when building form data. --- src/OAuth/Consumer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index f8655d2..d40092c 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -236,9 +236,9 @@ // is used by default and required header // is set to multipart/form-data etc query = new FormData(); - for (i = 0; i < data.values.length; i++) { - query.append(data.values[i].name, data.values[i].value); - } + data.each(function(i, param) { + query.append(param.name, param.value); + }); } xhr.open(method, url+'', true); From 63a8b497e30a3418b97c11f6e955c5a686dbd120 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 28 Oct 2013 16:25:05 -0300 Subject: [PATCH 17/20] In setQueryString() copy existing param lists. --- src/OAuth/URI.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OAuth/URI.js b/src/OAuth/URI.js index 04ca5c1..7428585 100644 --- a/src/OAuth/URI.js +++ b/src/OAuth/URI.js @@ -102,6 +102,8 @@ ); } } + } else if (query instanceof ParamList) { + querystring = query.copy(); } else { // iterate object for (i in query) { From b4fee3c75e88a8af4fdf54e9a87b7cc27b0fba4c Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Wed, 30 Oct 2013 11:16:54 -0300 Subject: [PATCH 18/20] Fix hasFile implementation. --- src/OAuth/Consumer.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index d40092c..328b0eb 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -101,11 +101,15 @@ // According to the spec withFile = (function(){ var hasFile = false; - for(var name in data) { + + data.each(function(i, param)) { // Thanks to the FileAPI any file entry // has a fileName property - if(data[name] instanceof File || typeof data[name].fileName != 'undefined') hasFile = true; - } + if (param.value instanceof File || typeof param.value.fileName !== 'undefined') { + hasFile = true; + return true; + } + }); return hasFile; })(); From c854b512713e5ea939eb21912e421ceb18f35412 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Wed, 30 Oct 2013 12:20:04 -0300 Subject: [PATCH 19/20] Make List extend Array. * Use native forEach if available. * Update copy() to use correct constructor * Update concat() calls to assume a new object is returned --- src/OAuth/Consumer.js | 22 ++++----- src/OAuth/List.js | 102 +++++++++++++++++------------------------ src/OAuth/ParamList.js | 63 ++++++++++++------------- 3 files changed, 84 insertions(+), 103 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index 328b0eb..1e1133f 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -102,7 +102,7 @@ withFile = (function(){ var hasFile = false; - data.each(function(i, param)) { + data.forEach(function(param) { // Thanks to the FileAPI any file entry // has a fileName property if (param.value instanceof File || typeof param.value.fileName !== 'undefined') { @@ -183,7 +183,7 @@ signatureMethod = oauth.signatureMethod; // Handle GET params first - signatureData.concat(url.query); + signatureData = signatureData.concat(url.query); // According to the OAuth spec // if data is transfered using @@ -192,7 +192,7 @@ // http://www.mail-archive.com/oauth@googlegroups.com/msg01556.html contentType = headers.getByNameInsensitive('Content-Type').getFirst(); if ((!contentType || contentType.value.toLowerCase() === 'application/x-www-form-urlencoded') && !withFile) { - signatureData.concat(data); + signatureData = signatureData.concat(data); } urlString = url.scheme + '://' + url.host + url.path; @@ -240,7 +240,7 @@ // is used by default and required header // is set to multipart/form-data etc query = new FormData(); - data.each(function(i, param) { + data.forEach(function(param) { query.append(param.name, param.value); }); } @@ -250,7 +250,7 @@ xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams)); xhr.setRequestHeader('X-Requested-With','XMLHttpRequest'); - headers.each(function(i, param) { + headers.forEach(function(param) { xhr.setRequestHeader(param.name, param.value); }); @@ -398,7 +398,7 @@ function toHeaderString(params) { var list = new ParamList(), i, realm, encode = OAuth.urlEncode, arr = []; - params.each(function(i, param) { + params.forEach(function(param) { if (param.value !== '') { if (param.name.toLowerCase() === 'realm') { realm = encode(param.name) + '="' + encode(param.value) + '"' @@ -413,10 +413,8 @@ } }); - list.sort(); - // encode sorted list - list.each(function(i, param) { + list.sort().forEach(function(param) { arr.push(encode(param.name) + '="' + encode(param.value) + '"'); }); @@ -439,13 +437,11 @@ function toSignatureBaseString(method, url, header_params, query_params) { var list = new ParamList(), i, encode = OAuth.urlEncode, noEmpty = new ParamList(); - list.concat(header_params); - list.concat(query_params); + list = list.concat(header_params).concat(query_params); list.removeByName('oauth_signature'); - list.sort(); - list.each(function(i, param) { + list.sort().forEach(function(param) { if (param.value !== '') { noEmpty.push(param); } diff --git a/src/OAuth/List.js b/src/OAuth/List.js index be0c785..e37fc06 100644 --- a/src/OAuth/List.js +++ b/src/OAuth/List.js @@ -1,71 +1,55 @@ - function List() { - this.values = []; - } + function List() {} - List.prototype = { - constructor: List, - join: function(string) { - string = string || ''; - return this.values.join(string); - }, - length: function() { - return this.values.length; - }, - concat: function(obj) { - var self = this; + List.prototype = []; + List.superclass = Array.prototype; + List.prototype.constructor = List; - if (obj instanceof List) { - obj = obj.values; - } else if (!(obj instanceof Array)) { - throw 'Can only concat lists or arrays.'; + List.prototype.copy = function() { + var list = new this.constructor(); + + this.forEach(function(value, i) { + if (typeof value.copy === 'function') { + value = value.copy(); } + list.push(value); + }); - this.values = this.values.concat(obj); - return this; - }, - copy: function() { - var list = new this.constructor(); + return list; + }; + + List.prototype.concat = function() { + var list = this.copy(), i, j, len; - this.each(function(i, value) { - if (typeof value.copy === 'function') { - value = value.copy(); + for (var i = 0; i < arguments.length; i++) { + if (arguments[i] instanceof Array) { + for (j = 0, len = arguments[i].length; j < len; j++) { + list.push(arguments[i][j]); } - list.push(value); - }); + } else { + list.push(arguments[i]); + } + } - return list; - }, - shift: function() { - return this.values.shift(); - }, - unshift: function() { - this.values.unshift.apply(this.values, arguments); - return this; - }, - push: function() { - this.values.push.apply(this.values, arguments); - return this; - }, - pop: function() { - return this.values.pop(); - }, - sort: function() { - this.values.sort(); - return this; - }, - getFirst: function() { - var value = null; + return list; + }; - if (this.length() > 0) { - value = this.values[0]; - } + List.prototype.getFirst = function() { + var value = null; + + if (this.length > 0) { + value = this[0]; + } + + return value; + }; - return value; - }, - each: function(callback) { - var i; - for (i = 0; i < this.length(); i++) { - callback(i, this.values[i]); + if (typeof List.prototype.forEach !== 'function') { + List.prototype.forEach = function(callback, scope) { + var i, len; + for (i = 0, len = this.length; i < len; ++i) { + if (i in this) { + callback.call(scope, this[i], i, this); + } } return this; } diff --git a/src/OAuth/ParamList.js b/src/OAuth/ParamList.js index b21df42..40e61dc 100644 --- a/src/OAuth/ParamList.js +++ b/src/OAuth/ParamList.js @@ -8,7 +8,7 @@ } if (arr instanceof ParamList) { - arr.each(function(i, param) { + arr.forEach(function(param) { paramlist.push(param); }); } else if (arr instanceof Array) { @@ -28,9 +28,9 @@ ParamList.prototype.constructor = ParamList; ParamList.prototype.getByNameInsensitive = function(name) { - var list = new ParamList(); + var list = new this.constructor(); - this.each(function(i, param) { + this.forEach(function(param) { if (param.name.toLowerCase() === name.toLowerCase()) { list.push(param); } @@ -40,9 +40,9 @@ }; ParamList.prototype.getByName = function(name) { - var list = new ParamList(); + var list = new this.constructor(); - this.each(function(i, param) { + this.forEach(function(param) { if (param.name === name) { list.push(param); } @@ -51,32 +51,35 @@ return list; }; - ParamList.prototype.sort = function() { - // byte-order sorting of names and then values - this.values.sort(function(a, b) { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - if (a.value < b.value) { - return -1; - } - if (a.value > b.value) { - return 1; - } - return 0; - }); + ParamList.prototype.sort = function(fn) { - return this; + // default to byte-order sorting of names and then values + if (typeof fn === 'undefined') { + fn = function(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + if (a.value < b.value) { + return -1; + } + if (a.value > b.value) { + return 1; + } + return 0; + }; + } + + return ParamList.superclass.sort.call(this, fn); }; ParamList.prototype.removeByName = function(name) { - var i, length = this.values.length; + var i, length = this.length; for (i = 0; i < length; i++) { - if (this.values[i].name === name) { - this.values.splice(i, 1); + if (this[i].name === name) { + this.splice(i, 1); i--; length--; } @@ -87,9 +90,7 @@ ParamList.prototype.toString = function () { var q_arr = [], ret = ''; - this.sort(); - - this.each(function(i, param) { + this.sort().forEach(function(param) { q_arr.push(param + ''); }); @@ -100,10 +101,10 @@ return ret; }; - ParamList.prototype.toArray = function() { + ParamList.prototype.toJSON = function() { var q_arr = []; - this.each(function(i, param) { + this.forEach(function(param) { q_arr.push([ param.name, param.value ]); }); From 8e1622943f6c4bb750a94df6301069ba58afe771 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Wed, 30 Oct 2013 12:45:47 -0300 Subject: [PATCH 20/20] Handle POST strings properly. Parse token values properly. --- src/OAuth/Consumer.js | 57 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/OAuth/Consumer.js b/src/OAuth/Consumer.js index 1e1133f..3b61418 100644 --- a/src/OAuth/Consumer.js +++ b/src/OAuth/Consumer.js @@ -102,14 +102,16 @@ withFile = (function(){ var hasFile = false; - data.forEach(function(param) { - // Thanks to the FileAPI any file entry - // has a fileName property - if (param.value instanceof File || typeof param.value.fileName !== 'undefined') { - hasFile = true; - return true; - } - }); + if (data instanceof List) { + data.forEach(function(param) { + // Thanks to the FileAPI any file entry + // has a fileName property + if (param.value instanceof File || typeof param.value.fileName !== 'undefined') { + hasFile = true; + return true; + } + }); + } return hasFile; })(); @@ -132,7 +134,7 @@ if (!!xhr.getAllResponseHeaders) { responseHeadersString = xhr.getAllResponseHeaders(); while((match = regex.exec(responseHeadersString))) { - responseHeaders[match[1]] = match[2]; + responseHeaders.push(new Param(match[1], match[2])); } } else if (!!xhr.getResponseHeaders) { responseHeadersString = xhr.getResponseHeaders(); @@ -218,24 +220,22 @@ url = URI(oauth.proxyUrl + url.path); } - if(appendQueryString || method === 'GET') { + if (appendQueryString || method === 'GET') { url.query.setQueryParams(data); query = null; - } else if(!withFile){ - // TODO: hmmmmm...... handle POST -/* if (typeof data === 'string') { + } else if (!withFile){ + if (typeof data === 'string') { query = data; - if (!('Content-Type' in headers)) { - headers['Content-Type'] = 'text/plain'; + if (!contentType) { + headers.push(new Param('Content-Type', 'text/plain')); } - } else {*/ + } else { query = data.copy().sort().join('&'); if (!contentType) { headers.push(new Param('Content-Type', 'application/x-www-form-urlencoded')); } -/* }*/ - - } else if(withFile) { + } + } else if (withFile) { // When using FormData multipart content type // is used by default and required header // is set to multipart/form-data etc @@ -314,13 +314,14 @@ success(JSON.parse(data.text)); }, 'failure': failure, - 'headers': { - 'Content-Type': 'application/json' - } + 'headers': [ + [ 'Content-Type', 'application/json' ] + ] }); }, parseTokenRequest: function (tokenRequest, content_type) { + var obj; switch(content_type) { @@ -328,15 +329,23 @@ var token = tokenRequest.xml.getElementsByTagName('token'); var secret = tokenRequest.xml.getElementsByTagName('secret'); - obj[OAuth.urlDecode(token[0])] = OAuth.urlDecode(secret[0]); + obj = { + 'oauth_token' : OAuth.urlDecode(token[0]), + 'oauth_token_secret' : OAuth.urlDecode(secret[0]) + }; + break; default: - var i = 0, arr = tokenRequest.text.split('&'), len = arr.length, obj = {}; + var i = 0, arr = tokenRequest.text.split('&'), len = arr.length; + + obj = {}; for (; i < len; ++i) { var pair = arr[i].split('='); obj[OAuth.urlDecode(pair[0])] = OAuth.urlDecode(pair[1]); } + + break; }