(function(global) { // the default timeout for xhr connections // this is set long at 2 minutes Gitana.HTTP_TIMEOUT = 120000; Gitana.Http = Base.extend( /** @lends Gitana.Http.prototype */ { /** * @constructs * * @class Gitana.Http */ constructor: function() { /////////////////////////////////////////////////////////////////////////////////////// // // PRIVILEDGED METHODS // this.invoke = function(options) { var method = options.method || 'GET'; var url = options.url; //var data = options.data || {}; var data = options.data; var headers = options.headers || {}; var success = options.success || function () {}; var failure = options.failure || function () {}; // make sure that all responses come back as JSON if they can (instead of XML) //headers["Accept"] = "application/json,*/*;q=0.8"; headers["Accept"] = "application/json"; // ensure that CSRF token is applied (if available) // the csrf token var csrfToken = Gitana.CSRF_TOKEN; if (!csrfToken) { // if we were not explicitly provided the token, look it up from a cookie // NOTE: this only works in the browser for (var t = 0; t < Gitana.CSRF_COOKIE_NAMES.length; t++) { var cookieName = Gitana.CSRF_COOKIE_NAMES[t]; var cookieValue = Gitana.readCookie(cookieName); if (cookieValue) { csrfToken = cookieValue; break; } } } if (csrfToken) { headers[Gitana.CSRF_HEADER_NAME] = csrfToken; } // XHR_CACHE_FN if (typeof(Gitana.XHR_CACHE_FN) !== "undefined" && Gitana.XHR_CACHE_FN !== null) { var responseObject = Gitana.XHR_CACHE_FN({ method: method, url: url, headers: headers }); if (responseObject) { success(responseObject); return; } } var xhr = Gitana.Http.Request(); xhr.withCredentials = true; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { var regex = /^(.*?):\s*(.*?)\r?$/mg, requestHeaders = headers, responseHeaders = {}, responseHeadersString = '', match; if (!!xhr.getAllResponseHeaders) { responseHeadersString = xhr.getAllResponseHeaders(); while((match = regex.exec(responseHeadersString))) { responseHeaders[match[1]] = match[2]; } } else if(!!xhr.getResponseHeaders) { responseHeadersString = xhr.getResponseHeaders(); for (var i = 0, len = responseHeadersString.length; i < len; ++i) { responseHeaders[responseHeadersString[i][0]] = responseHeadersString[i][1]; } } var includeXML = false; if ('Content-Type' in responseHeaders) { if (responseHeaders['Content-Type'] == 'text/xml') { includeXML = true; } } var responseObject = { text: xhr.responseText, xml: (includeXML ? xhr.responseXML : ''), requestHeaders: requestHeaders, responseHeaders: responseHeaders }; // handle the response if (xhr.status === 0) { // not handled } if ((xhr.status >= 200 && xhr.status <= 226) || xhr.status == 304) { // XHR_CACHE_FN if (typeof(Gitana.XHR_CACHE_FN) !== "undefined" && Gitana.XHR_CACHE_FN !== null) { Gitana.XHR_CACHE_FN({ method: method, url: url, headers: headers }, responseObject); } // ok success(responseObject, xhr); } else if (xhr.status >= 400 && xhr.status !== 0) { // everything what is 400 and above is a failure code failure(responseObject, xhr); } else if (xhr.status >= 300 && xhr.status <= 303) { // some kind of redirect, probably to a login server // indicates missing access token? failure(responseObject, xhr); } } }; xhr.open(method, url, true); xhr.timeout = Gitana.HTTP_TIMEOUT; xhr.ontimeout = function () { failure({ "timeout": true }, xhr); }; xhr.setRequestHeader('X-Requested-With','XMLHttpRequest'); for (var header in headers) { xhr.setRequestHeader(header, headers[header]); } try { xhr.send(data); } catch (e) { console.log(e); } }; }, /** * Performs an HTTP call. * * @param options */ request: function(options) { return this.invoke(options); } }); Gitana.Http.toQueryString = function(params) { var queryString = ""; if (params) { for (var k in params) { if (queryString.length > 0) { queryString += "&"; } var val = null; if (params[k]) { val = params[k]; // url encode val = Gitana.Http.URLEncode(val); } if (val) { queryString += k + "=" + val; } } } return queryString; }; Gitana.Http.Request = function() { var XHR; if (typeof global.Titanium !== 'undefined' && typeof global.Titanium.Network.createHTTPClient != 'undefined') { XHR = global.Titanium.Network.createHTTPClient(); } else if (typeof require !== 'undefined') { // CommonJS require try { var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; XHR = new XMLHttpRequest(); } catch (e) { XHR = new global.XMLHttpRequest(); } } else { // W3C XHR = new global.XMLHttpRequest(); } return XHR; }; var Hash = function() {}; 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; } }; var Collection = function(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; }; Collection.prototype = new Hash(); Gitana.Http.URI = function(url) { var args = arguments, args_callee = args.callee, parsed_uri, scheme, host, port, path, query, anchor, parser = /^([^:\/?#]+?:\/\/)*([^\/:?#]*)?(:[^\/?#]*)*([^?#]*)(\?[^#]*)?(#(.*))*/, uri = this; if (!(this instanceof args_callee)) { return new args_callee(url); } uri.scheme = ''; uri.host = ''; uri.port = ''; uri.path = ''; uri.query = new Gitana.Http.QueryString(); uri.anchor = ''; if (url !== null) { parsed_uri = url.match(parser); scheme = parsed_uri[1]; host = parsed_uri[2]; port = parsed_uri[3]; path = parsed_uri[4]; query = parsed_uri[5]; anchor = parsed_uri[6]; scheme = (scheme !== undefined) ? scheme.replace('://', '').toLowerCase() : 'http'; port = (port ? port.replace(':', '') : (scheme === 'https' ? '443' : '80')); // correct the scheme based on port number scheme = (scheme == 'http' && port === '443' ? 'https' : scheme); query = query ? query.replace('?', '') : ''; anchor = anchor ? anchor.replace('#', '') : ''; // Fix the host name to include port if non-standard ports were given if ((scheme === 'https' && port !== '443') || (scheme === 'http' && port !== '80')) { host = host + ':' + port; } uri.scheme = scheme; uri.host = host; uri.port = port; uri.path = path || '/'; uri.query.setQueryParams(query); uri.anchor = anchor || ''; } }; Gitana.Http.URI.prototype = { scheme: '', host: '', port: '', path: '', query: '', anchor: '', toString: function () { var self = this, query = self.query + ''; return self.scheme + '://' + self.host + self.path + (query != '' ? '?' + query : '') + (self.anchor !== '' ? '#' + self.anchor : ''); } }; Gitana.Http.QueryString = function(obj) { var args = arguments, args_callee = args.callee, args_length = args.length, i, querystring = this; 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; }; // QueryString is a type of collection So inherit Gitana.Http.QueryString.prototype = new Collection(); Gitana.Http.QueryString.prototype.toString = function () { var i, self = this, q_arr = [], ret = '', val = '', encode = Gitana.Http.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; }; Gitana.Http.QueryString.prototype.setQueryParams = function (query) { var args = arguments, args_length = args.length, i, query_array, query_array_length, querystring = this, key_value; if (args_length == 1) { if (typeof query === 'object') { // iterate for (i in query) { if (query.hasOwnProperty(i)) { querystring[i] = query[i]; } } } else if (typeof query === 'string') { // split string on '&' query_array = query.split('&'); // iterate over each of the array items 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('='); querystring[key_value[0]] = key_value[1]; } } } else { for (i = 0; i < arg_length; i += 2) { // treat each arg as key, then value querystring[args[i]] = args[i+1]; } } }; Gitana.Http.URLEncode = function(string) { function hex(code) { var hex = code.toString(16).toUpperCase(); if (hex.length < 2) { hex = 0 + hex; } return '%' + hex; } if (!string) { return ''; } string = string + ''; var reserved_chars = /[ \r\n!*"'();:@&=+$,\/?%#\[\]<>{}|`^\\\u0080-\uffff]/, str_len = string.length, i, string_arr = string.split(''), c; for (i = 0; i < str_len; i++) { c = string_arr[i].match(reserved_chars); if (c) { c = c[0].charCodeAt(0); if (c < 128) { string_arr[i] = hex(c); } else if (c < 2048) { string_arr[i] = hex(192+(c>>6)) + hex(128+(c&63)); } else if (c < 65536) { string_arr[i] = hex(224+(c>>12)) + hex(128+((c>>6)&63)) + hex(128+(c&63)); } else if (c < 2097152) { string_arr[i] = hex(240+(c>>18)) + hex(128+((c>>12)&63)) + hex(128+((c>>6)&63)) + hex(128+(c&63)); } } } return string_arr.join(''); }; Gitana.Http.URLDecode = function (string) { if (!string) { return ''; } return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) { return String.fromCharCode(parseInt(match.replace('%', ''), 16)); }); }; }(this));