Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / src / adapter / core / ext-base-ajax.js
1 /*!
2  * Ext JS Library 3.1.1
3  * Copyright(c) 2006-2010 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /*\r
8 * Portions of this file are based on pieces of Yahoo User Interface Library\r
9 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.\r
10 * YUI licensed under the BSD License:\r
11 * http://developer.yahoo.net/yui/license.txt\r
12 */\r
13 Ext.lib.Ajax = function() {     \r
14     var activeX = ['MSXML2.XMLHTTP.3.0',\r
15                    'MSXML2.XMLHTTP',\r
16                    'Microsoft.XMLHTTP'],\r
17         CONTENTTYPE = 'Content-Type';\r
18                    \r
19     // private\r
20     function setHeader(o) {\r
21         var conn = o.conn,\r
22             prop;\r
23         \r
24         function setTheHeaders(conn, headers){\r
25             for (prop in headers) {\r
26                 if (headers.hasOwnProperty(prop)) {\r
27                     conn.setRequestHeader(prop, headers[prop]);\r
28                 }\r
29             }   \r
30         }       \r
31         \r
32         if (pub.defaultHeaders) {\r
33             setTheHeaders(conn, pub.defaultHeaders);\r
34         }\r
35 \r
36         if (pub.headers) {\r
37             setTheHeaders(conn, pub.headers);\r
38             delete pub.headers;                \r
39         }\r
40     }    \r
41     \r
42     // private\r
43     function createExceptionObject(tId, callbackArg, isAbort, isTimeout) {          \r
44         return {\r
45             tId : tId,\r
46             status : isAbort ? -1 : 0,\r
47             statusText : isAbort ? 'transaction aborted' : 'communication failure',\r
48             isAbort: isAbort,\r
49             isTimeout: isTimeout,\r
50             argument : callbackArg\r
51         };\r
52     }  \r
53     \r
54     // private \r
55     function initHeader(label, value) {         \r
56         (pub.headers = pub.headers || {})[label] = value;                       \r
57     }\r
58     \r
59     // private\r
60     function createResponseObject(o, callbackArg) {\r
61         var headerObj = {},\r
62             headerStr,              \r
63             conn = o.conn,\r
64             t,\r
65             s;\r
66 \r
67         try {\r
68             headerStr = o.conn.getAllResponseHeaders();   \r
69             Ext.each(headerStr.replace(/\r\n/g, '\n').split('\n'), function(v){\r
70                 t = v.indexOf(':');\r
71                 if(t >= 0){\r
72                     s = v.substr(0, t).toLowerCase();\r
73                     if(v.charAt(t + 1) == ' '){\r
74                         ++t;\r
75                     }\r
76                     headerObj[s] = v.substr(t + 1);\r
77                 }\r
78             });\r
79         } catch(e) {}\r
80                     \r
81         return {\r
82             tId : o.tId,\r
83             status : conn.status,\r
84             statusText : conn.statusText,\r
85             getResponseHeader : function(header){return headerObj[header.toLowerCase()];},\r
86             getAllResponseHeaders : function(){return headerStr},\r
87             responseText : conn.responseText,\r
88             responseXML : conn.responseXML,\r
89             argument : callbackArg\r
90         };\r
91     }\r
92     \r
93     // private\r
94     function releaseObject(o) {\r
95         o.conn = null;\r
96         o = null;\r
97     }        \r
98     \r
99     // private\r
100     function handleTransactionResponse(o, callback, isAbort, isTimeout) {\r
101         if (!callback) {\r
102             releaseObject(o);\r
103             return;\r
104         }\r
105 \r
106         var httpStatus, responseObject;\r
107 \r
108         try {\r
109             if (o.conn.status !== undefined && o.conn.status != 0) {\r
110                 httpStatus = o.conn.status;\r
111             }\r
112             else {\r
113                 httpStatus = 13030;\r
114             }\r
115         }\r
116         catch(e) {\r
117             httpStatus = 13030;\r
118         }\r
119 \r
120         if ((httpStatus >= 200 && httpStatus < 300) || (Ext.isIE && httpStatus == 1223)) {\r
121             responseObject = createResponseObject(o, callback.argument);\r
122             if (callback.success) {\r
123                 if (!callback.scope) {\r
124                     callback.success(responseObject);\r
125                 }\r
126                 else {\r
127                     callback.success.apply(callback.scope, [responseObject]);\r
128                 }\r
129             }\r
130         }\r
131         else {\r
132             switch (httpStatus) {\r
133                 case 12002:\r
134                 case 12029:\r
135                 case 12030:\r
136                 case 12031:\r
137                 case 12152:\r
138                 case 13030:\r
139                     responseObject = createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false), isTimeout);\r
140                     if (callback.failure) {\r
141                         if (!callback.scope) {\r
142                             callback.failure(responseObject);\r
143                         }\r
144                         else {\r
145                             callback.failure.apply(callback.scope, [responseObject]);\r
146                         }\r
147                     }\r
148                     break;\r
149                 default:\r
150                     responseObject = createResponseObject(o, callback.argument);\r
151                     if (callback.failure) {\r
152                         if (!callback.scope) {\r
153                             callback.failure(responseObject);\r
154                         }\r
155                         else {\r
156                             callback.failure.apply(callback.scope, [responseObject]);\r
157                         }\r
158                     }\r
159             }\r
160         }\r
161 \r
162         releaseObject(o);\r
163         responseObject = null;\r
164     }  \r
165     \r
166     // private\r
167     function handleReadyState(o, callback){\r
168     callback = callback || {};\r
169         var conn = o.conn,\r
170             tId = o.tId,\r
171             poll = pub.poll,\r
172             cbTimeout = callback.timeout || null;\r
173 \r
174         if (cbTimeout) {\r
175             pub.timeout[tId] = setTimeout(function() {\r
176                 pub.abort(o, callback, true);\r
177             }, cbTimeout);\r
178         }\r
179 \r
180         poll[tId] = setInterval(\r
181             function() {\r
182                 if (conn && conn.readyState == 4) {\r
183                     clearInterval(poll[tId]);\r
184                     poll[tId] = null;\r
185 \r
186                     if (cbTimeout) {\r
187                         clearTimeout(pub.timeout[tId]);\r
188                         pub.timeout[tId] = null;\r
189                     }\r
190 \r
191                     handleTransactionResponse(o, callback);\r
192                 }\r
193             },\r
194             pub.pollInterval);\r
195     }\r
196     \r
197     // private\r
198     function asyncRequest(method, uri, callback, postData) {\r
199         var o = getConnectionObject() || null;\r
200 \r
201         if (o) {\r
202             o.conn.open(method, uri, true);\r
203 \r
204             if (pub.useDefaultXhrHeader) {                    \r
205                 initHeader('X-Requested-With', pub.defaultXhrHeader);\r
206             }\r
207 \r
208             if(postData && pub.useDefaultHeader && (!pub.headers || !pub.headers[CONTENTTYPE])){\r
209                 initHeader(CONTENTTYPE, pub.defaultPostHeader);\r
210             }\r
211 \r
212             if (pub.defaultHeaders || pub.headers) {\r
213                 setHeader(o);\r
214             }\r
215 \r
216             handleReadyState(o, callback);\r
217             o.conn.send(postData || null);\r
218         }\r
219         return o;\r
220     }\r
221     \r
222     // private\r
223     function getConnectionObject() {\r
224         var o;          \r
225 \r
226         try {\r
227             if (o = createXhrObject(pub.transactionId)) {\r
228                 pub.transactionId++;\r
229             }\r
230         } catch(e) {\r
231         } finally {\r
232             return o;\r
233         }\r
234     }\r
235        \r
236     // private\r
237     function createXhrObject(transactionId) {\r
238         var http;\r
239             \r
240         try {\r
241             http = new XMLHttpRequest();                \r
242         } catch(e) {\r
243             for (var i = 0; i < activeX.length; ++i) {              \r
244                 try {\r
245                     http = new ActiveXObject(activeX[i]);                        \r
246                     break;\r
247                 } catch(e) {}\r
248             }\r
249         } finally {\r
250             return {conn : http, tId : transactionId};\r
251         }\r
252     }\r
253          \r
254     var pub = {\r
255         request : function(method, uri, cb, data, options) {\r
256             if(options){\r
257                 var me = this,              \r
258                     xmlData = options.xmlData,\r
259                     jsonData = options.jsonData,\r
260                     hs;\r
261                     \r
262                 Ext.applyIf(me, options);           \r
263                 \r
264                 if(xmlData || jsonData){\r
265                     hs = me.headers;\r
266                     if(!hs || !hs[CONTENTTYPE]){\r
267                         initHeader(CONTENTTYPE, xmlData ? 'text/xml' : 'application/json');\r
268                     }\r
269                     data = xmlData || (!Ext.isPrimitive(jsonData) ? Ext.encode(jsonData) : jsonData);\r
270                 }\r
271             }                       \r
272             return asyncRequest(method || options.method || "POST", uri, cb, data);\r
273         },\r
274 \r
275         serializeForm : function(form) {\r
276             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,\r
277                 hasSubmit = false,\r
278                 encoder = encodeURIComponent,\r
279                 element,\r
280                 options, \r
281                 name, \r
282                 val,                \r
283                 data = '',\r
284                 type;\r
285                 \r
286             Ext.each(fElements, function(element) {                 \r
287                 name = element.name;                 \r
288                 type = element.type;\r
289                 \r
290                 if (!element.disabled && name){\r
291                     if(/select-(one|multiple)/i.test(type)) {\r
292                         Ext.each(element.options, function(opt) {\r
293                             if (opt.selected) {\r
294                                 data += String.format("{0}={1}&", encoder(name), encoder((opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttribute('value') !== null) ? opt.value : opt.text));\r
295                             }                               \r
296                         });\r
297                     } else if(!/file|undefined|reset|button/i.test(type)) {\r
298                             if(!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)){\r
299                                 \r
300                                 data += encoder(name) + '=' + encoder(element.value) + '&';                     \r
301                                 hasSubmit = /submit/i.test(type);    \r
302                             }                       \r
303                     } \r
304                 }\r
305             });            \r
306             return data.substr(0, data.length - 1);\r
307         },\r
308         \r
309         useDefaultHeader : true,\r
310         defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',\r
311         useDefaultXhrHeader : true,\r
312         defaultXhrHeader : 'XMLHttpRequest',        \r
313         poll : {},\r
314         timeout : {},\r
315         pollInterval : 50,\r
316         transactionId : 0,\r
317         \r
318 //  This is never called - Is it worth exposing this?               \r
319 //          setProgId : function(id) {\r
320 //              activeX.unshift(id);\r
321 //          },\r
322 \r
323 //  This is never called - Is it worth exposing this?   \r
324 //          setDefaultPostHeader : function(b) {\r
325 //              this.useDefaultHeader = b;\r
326 //          },\r
327         \r
328 //  This is never called - Is it worth exposing this?   \r
329 //          setDefaultXhrHeader : function(b) {\r
330 //              this.useDefaultXhrHeader = b;\r
331 //          },\r
332 \r
333 //  This is never called - Is it worth exposing this?           \r
334 //          setPollingInterval : function(i) {\r
335 //              if (typeof i == 'number' && isFinite(i)) {\r
336 //                  this.pollInterval = i;\r
337 //              }\r
338 //          },\r
339         \r
340 //  This is never called - Is it worth exposing this?\r
341 //          resetDefaultHeaders : function() {\r
342 //              this.defaultHeaders = null;\r
343 //          },\r
344     \r
345             abort : function(o, callback, isTimeout) {\r
346                 var me = this,\r
347                     tId = o.tId,\r
348                     isAbort = false;\r
349                 \r
350                 if (me.isCallInProgress(o)) {\r
351                     o.conn.abort();\r
352                     clearInterval(me.poll[tId]);\r
353                     me.poll[tId] = null;\r
354                     clearTimeout(pub.timeout[tId]);\r
355                     me.timeout[tId] = null;\r
356                     \r
357                     handleTransactionResponse(o, callback, (isAbort = true), isTimeout);                \r
358                 }\r
359                 return isAbort;\r
360             },\r
361     \r
362             isCallInProgress : function(o) {\r
363                 // if there is a connection and readyState is not 0 or 4\r
364                 return o.conn && !{0:true,4:true}[o.conn.readyState];           \r
365             }\r
366         };\r
367         return pub;\r
368     }();