avm99963 | 04def3e | 2016-11-27 22:53:05 +0100 | [diff] [blame] | 1 | (function(){
|
| 2 | /**
|
| 3 | * ajax is a encapsulated function that used to send data to server
|
| 4 | * asynchronously. It uses XMLHttpRequest object to send textual or binary
|
| 5 | * data through HTTP method GET, POST etc. It can custom request method,
|
| 6 | * request header. Response can be parsed automatically by MIME type of
|
| 7 | * response's Content-type, and it can handle success, error or progress event
|
| 8 | * in course of sending request and retrieving response.
|
| 9 | * @param {Object} option
|
| 10 | */
|
| 11 | function ajax(option) {
|
| 12 | if (arguments.length < 1 || option.constructor != Object)
|
| 13 | throw new Error('Bad parameter.');
|
| 14 | var url = option.url;
|
| 15 | var success = option.success;
|
| 16 | var complete = option.complete;
|
| 17 | if (!url || !(success || complete))
|
| 18 | throw new Error('Parameter url and success or complete are required.');
|
| 19 |
|
| 20 | var parameters = option.parameters || {};
|
| 21 | var method = option.method || 'GET';
|
| 22 | var status = option.status;
|
| 23 | var headers = option.headers || {};
|
| 24 | var data = option.data || null;
|
| 25 | var multipartData = option.multipartData;
|
| 26 | var queryString = constructQueryString(parameters);
|
| 27 |
|
| 28 | if (multipartData) {
|
| 29 | var boundary = multipartData.boundary || 'XMLHttpRequest2';
|
| 30 | method = 'POST';
|
| 31 | var multipartDataString;
|
| 32 | var contentType = headers['Content-Type'] || 'multipart/form-data';
|
| 33 | if (contentType.indexOf('multipart/form-data') == 0) {
|
| 34 | headers['Content-Type'] = 'multipart/form-data; boundary=' + boundary;
|
| 35 | multipartDataString = constructMultipartFormData(multipartData, boundary,
|
| 36 | parameters);
|
| 37 | } else if (contentType.indexOf('multipart/related') == 0) {
|
| 38 | headers['Content-Type'] = 'multipart/related; boundary=' + boundary;
|
| 39 | multipartDataString = constructMultipartRelatedData(boundary,
|
| 40 | multipartData.dataList);
|
| 41 | }
|
| 42 |
|
| 43 | data = constructBufferData(multipartDataString);
|
| 44 | } else {
|
| 45 | if (queryString)
|
| 46 | url += '?' + queryString;
|
| 47 | }
|
| 48 |
|
| 49 | var xhr = new XMLHttpRequest();
|
| 50 | xhr.open(method, url, true);
|
| 51 | xhr.onreadystatechange = function() {
|
| 52 | if (xhr.readyState == 4) {
|
| 53 | var statusCode = xhr.status;
|
| 54 | var parsedResponse = parseResponse(xhr);
|
| 55 | if (complete)
|
| 56 | complete(statusCode, parsedResponse);
|
| 57 | if (success && (statusCode == 200 || statusCode == 304)) {
|
| 58 | success(parsedResponse);
|
| 59 | } else if (status) {
|
| 60 | if (status[statusCode]) {
|
| 61 | // Call specified status code handler
|
| 62 | status[statusCode](parsedResponse);
|
| 63 | } else if (status['others']) {
|
| 64 | // Call others status code handler
|
| 65 | status['others'](parsedResponse, statusCode);
|
| 66 | }
|
| 67 | }
|
| 68 | }
|
| 69 | };
|
| 70 |
|
| 71 | // Handle request progress
|
| 72 | var progress = option.progress;
|
| 73 | if (progress) {
|
| 74 | xhr.upload.addEventListener('progress', function(e) {
|
| 75 | // lengthComputable return true when the length of the progress is known
|
| 76 | if (e.lengthComputable) {
|
| 77 | progress(e.loaded, e.total);
|
| 78 | }
|
| 79 | }, false);
|
| 80 | }
|
| 81 | // Set request header
|
| 82 | for (var headerKey in headers) {
|
| 83 | xhr.setRequestHeader(headerKey, headers[headerKey]);
|
| 84 | }
|
| 85 |
|
| 86 | xhr.send(data);
|
| 87 | }
|
| 88 |
|
| 89 | function constructQueryString(parameters) {
|
| 90 | var tmpParameter = [];
|
| 91 | for(var name in parameters) {
|
| 92 | var value = parameters[name];
|
| 93 | if (value.constructor == Array) {
|
| 94 | value.forEach(function(val) {
|
| 95 | tmpParameter.push(name + '=' + val);
|
| 96 | });
|
| 97 | } else {
|
| 98 | tmpParameter.push(name + '=' + value);
|
| 99 | }
|
| 100 | }
|
| 101 | return tmpParameter.join('&');
|
| 102 | }
|
| 103 |
|
| 104 | // Parse response data according to content type of response
|
| 105 | function parseResponse(xhr) {
|
| 106 | var ct = xhr.getResponseHeader("content-type");
|
| 107 | if (typeof ct == 'string') {
|
| 108 | if (ct.indexOf('xml') >= 0)
|
| 109 | return xhr.responseXML;
|
| 110 | else if (ct.indexOf('json') >= 0)
|
| 111 | return JSON.parse(xhr.responseText);
|
| 112 | }
|
| 113 | return xhr.responseText;
|
| 114 | }
|
| 115 |
|
| 116 | /**
|
| 117 | * Construct multipart/form-data formatted data string.
|
| 118 | * @param {Object} binaryData binary data
|
| 119 | * @param {String} boundary boundary of parts
|
| 120 | * @param {Object} otherParameters other text parameters
|
| 121 | */
|
| 122 | function constructMultipartFormData(binaryData, boundary, otherParameters) {
|
| 123 | var commonHeader = 'Content-Disposition: form-data; ';
|
| 124 | var data = [];
|
| 125 | for (var key in otherParameters) {
|
| 126 |
|
| 127 | // Add boundary of one header part
|
| 128 | data.push('--' + boundary + '\r\n');
|
| 129 |
|
| 130 | // Add same Content-Disposition information
|
| 131 | data.push(commonHeader);
|
| 132 | data.push('name="' + key + '"\r\n\r\n' + otherParameters[key] + '\r\n');
|
| 133 | }
|
| 134 |
|
| 135 | // Construct file data header
|
| 136 | data.push('--' + boundary + '\r\n');
|
| 137 | data.push(commonHeader);
|
| 138 |
|
| 139 | data.push('name="' + (binaryData.name || 'binaryfilename') + '"; ');
|
| 140 | data.push('filename=\"' + binaryData.value + '\"\r\n');
|
| 141 | data.push('Content-type: ' + binaryData.type + '\r\n\r\n');
|
| 142 | data.push(binaryData.data + '\r\n');
|
| 143 |
|
| 144 | data.push('--' + boundary + '--\r\n');
|
| 145 | return data.join('');
|
| 146 | }
|
| 147 |
|
| 148 | function constructBufferData(dataString, contentType) {
|
| 149 | var len = dataString.length;
|
| 150 |
|
| 151 | // Create a 8-bit unsigned integer ArrayBuffer view
|
| 152 | var data = new Uint8Array(len);
|
| 153 | for (var i = 0; i < len; i++) {
|
| 154 | data[i] = dataString.charCodeAt(i);
|
| 155 | }
|
| 156 |
|
| 157 | return data.buffer
|
| 158 | }
|
| 159 |
|
| 160 | function constructMultipartRelatedData(boundary, dataList) {
|
| 161 | var result = [];
|
| 162 | dataList.forEach(function(data) {
|
| 163 | result.push('--' + boundary + '\r\n');
|
| 164 | result.push('Content-Type: ' + data.contentType + '\r\n\r\n');
|
| 165 | result.push(data.data + '\r\n');
|
| 166 | });
|
| 167 | result.push('--' + boundary + '--\r\n');
|
| 168 | return result.join('');
|
| 169 | }
|
| 170 |
|
| 171 | ajax.encodeForBinary = function(string) {
|
| 172 | string = encodeURI(string).replace(/%([A-Z0-9]{2})/g, '%u00$1');
|
| 173 | return unescape(string);
|
| 174 | };
|
| 175 |
|
| 176 | ajax.convertEntityString = function(string) {
|
| 177 | var entitychars = ['<', '>', '&', '"', '\''];
|
| 178 | var entities = ['<', '>', '&', '"', '''];
|
| 179 | entitychars.forEach(function(character, index) {
|
| 180 | string = string.replace(character, entities[index]);
|
| 181 | });
|
| 182 | return string;
|
| 183 | };
|
| 184 | window.ajax = ajax;
|
| 185 | })(); |