1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview 7 * Simple utilities for making XHRs more pleasant. 8 */ 9 10'use strict'; 11 12/** @suppress {duplicate} */ 13var remoting = remoting || {}; 14 15/** Namespace for XHR functions */ 16/** @type {Object} */ 17remoting.xhr = remoting.xhr || {}; 18 19/** 20 * Takes an associative array of parameters and urlencodes it. 21 * 22 * @param {Object.<string>} paramHash The parameter key/value pairs. 23 * @return {string} URLEncoded version of paramHash. 24 */ 25remoting.xhr.urlencodeParamHash = function(paramHash) { 26 var paramArray = []; 27 for (var key in paramHash) { 28 paramArray.push(encodeURIComponent(key) + 29 '=' + encodeURIComponent(paramHash[key])); 30 } 31 if (paramArray.length > 0) { 32 return paramArray.join('&'); 33 } 34 return ''; 35}; 36 37/** 38 * Execute an XHR GET asynchronously. 39 * 40 * @param {string} url The base URL to GET, excluding parameters. 41 * @param {function(XMLHttpRequest):void} onDone The function to call on 42 * completion. 43 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 44 * either as an associative array, or a string. If it is a string, do 45 * not include the ? and be sure it is correctly URLEncoded. 46 * @param {Object.<string>=} opt_headers Additional headers to include on the 47 * request. 48 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 49 * XHR. 50 * @return {XMLHttpRequest} The request object. 51 */ 52remoting.xhr.get = function(url, onDone, opt_parameters, opt_headers, 53 opt_withCredentials) { 54 return remoting.xhr.doMethod('GET', url, onDone, opt_parameters, 55 opt_headers, opt_withCredentials); 56}; 57 58/** 59 * Execute an XHR POST asynchronously. 60 * 61 * @param {string} url The base URL to POST, excluding parameters. 62 * @param {function(XMLHttpRequest):void} onDone The function to call on 63 * completion. 64 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 65 * either as an associative array, or a string. If it is a string, be 66 * sure it is correctly URLEncoded. 67 * @param {Object.<string>=} opt_headers Additional headers to include on the 68 * request. 69 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 70 * XHR. 71 * @return {XMLHttpRequest} The request object. 72 */ 73remoting.xhr.post = function(url, onDone, opt_parameters, opt_headers, 74 opt_withCredentials) { 75 return remoting.xhr.doMethod('POST', url, onDone, opt_parameters, 76 opt_headers, opt_withCredentials); 77}; 78 79/** 80 * Execute an XHR DELETE asynchronously. 81 * 82 * @param {string} url The base URL to DELETE, excluding parameters. 83 * @param {function(XMLHttpRequest):void} onDone The function to call on 84 * completion. 85 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 86 * either as an associative array, or a string. If it is a string, be 87 * sure it is correctly URLEncoded. 88 * @param {Object.<string>=} opt_headers Additional headers to include on the 89 * request. 90 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 91 * XHR. 92 * @return {XMLHttpRequest} The request object. 93 */ 94remoting.xhr.remove = function(url, onDone, opt_parameters, opt_headers, 95 opt_withCredentials) { 96 return remoting.xhr.doMethod('DELETE', url, onDone, opt_parameters, 97 opt_headers, opt_withCredentials); 98}; 99 100/** 101 * Execute an XHR PUT asynchronously. 102 * 103 * @param {string} url The base URL to PUT, excluding parameters. 104 * @param {function(XMLHttpRequest):void} onDone The function to call on 105 * completion. 106 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 107 * either as an associative array, or a string. If it is a string, be 108 * sure it is correctly URLEncoded. 109 * @param {Object.<string>=} opt_headers Additional headers to include on the 110 * request. 111 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 112 * XHR. 113 * @return {XMLHttpRequest} The request object. 114 */ 115remoting.xhr.put = function(url, onDone, opt_parameters, opt_headers, 116 opt_withCredentials) { 117 return remoting.xhr.doMethod('PUT', url, onDone, opt_parameters, 118 opt_headers, opt_withCredentials); 119}; 120 121/** 122 * Execute an arbitrary HTTP method asynchronously. 123 * 124 * @param {string} methodName The HTTP method name, e.g. "GET", "POST" etc. 125 * @param {string} url The base URL, excluding parameters. 126 * @param {function(XMLHttpRequest):void} onDone The function to call on 127 * completion. 128 * @param {(string|Object.<string>)=} opt_parameters The request parameters, 129 * either as an associative array, or a string. If it is a string, be 130 * sure it is correctly URLEncoded. 131 * @param {Object.<string>=} opt_headers Additional headers to include on the 132 * request. 133 * @param {boolean=} opt_withCredentials Set the withCredentials flags in the 134 * XHR. 135 * @return {XMLHttpRequest} The XMLHttpRequest object. 136 */ 137remoting.xhr.doMethod = function(methodName, url, onDone, 138 opt_parameters, opt_headers, 139 opt_withCredentials) { 140 /** @type {XMLHttpRequest} */ 141 var xhr = new XMLHttpRequest(); 142 xhr.onreadystatechange = function() { 143 if (xhr.readyState != 4) { 144 return; 145 } 146 onDone(xhr); 147 }; 148 149 var parameterString = ''; 150 if (typeof(opt_parameters) === 'string') { 151 parameterString = opt_parameters; 152 } else if (typeof(opt_parameters) === 'object') { 153 parameterString = remoting.xhr.urlencodeParamHash(opt_parameters); 154 } else if (opt_parameters === undefined) { 155 // No problem here. Do nothing. 156 } else { 157 throw 'opt_parameters must be string or associated array.'; 158 } 159 160 var useBody = (methodName == 'POST') || (methodName == 'PUT'); 161 162 if (!useBody && parameterString != '') { 163 url = url + '?' + parameterString; 164 } 165 166 xhr.open(methodName, url, true); 167 if (methodName == 'POST' && 168 (typeof opt_headers !== 'object' || 169 typeof opt_headers['Content-type'] !== 'string')) { 170 xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 171 } 172 // Add in request headers. 173 if (typeof(opt_headers) === 'object') { 174 for (var key in opt_headers) { 175 xhr.setRequestHeader(key, opt_headers[key]); 176 } 177 } else if (opt_headers === undefined) { 178 // No problem here. Do nothing. 179 } else { 180 throw 'opt_headers must be associative array.'; 181 } 182 183 if (opt_withCredentials) { 184 xhr.withCredentials = true; 185 } 186 187 xhr.send(useBody ? parameterString : null); 188 return xhr; 189}; 190