1 /**
  2  * @fileOverview base for xhr and subdomain
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("ajax/XhrTransportBase", function (S, io) {
  6     var OK_CODE = 200,
  7         win = S.Env.host,
  8         // http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx
  9         _XDomainRequest = win['XDomainRequest'],
 10         NO_CONTENT_CODE = 204,
 11         NOT_FOUND_CODE = 404,
 12         NO_CONTENT_CODE2 = 1223,
 13         XhrTransportBase = {
 14             proto:{}
 15         };
 16 
 17     function createStandardXHR(_, refWin) {
 18         try {
 19             return new (refWin || win)['XMLHttpRequest']();
 20         } catch (e) {
 21             //S.log("createStandardXHR error");
 22         }
 23         return undefined;
 24     }
 25 
 26     function createActiveXHR(_, refWin) {
 27         try {
 28             return new (refWin || win)['ActiveXObject']("Microsoft.XMLHTTP");
 29         } catch (e) {
 30             S.log("createActiveXHR error");
 31         }
 32         return undefined;
 33     }
 34 
 35     XhrTransportBase.nativeXhr = win['ActiveXObject'] ? function (crossDomain, refWin) {
 36         if (crossDomain && _XDomainRequest) {
 37             return new _XDomainRequest();
 38         }
 39         // ie7 XMLHttpRequest 不能访问本地文件
 40         return !io.isLocal && createStandardXHR(crossDomain, refWin) || createActiveXHR(crossDomain, refWin);
 41     } : createStandardXHR;
 42 
 43     function isInstanceOfXDomainRequest(xhr) {
 44         return _XDomainRequest && (xhr instanceof _XDomainRequest);
 45     }
 46 
 47     S.mix(XhrTransportBase.proto, {
 48         sendInternal:function () {
 49 
 50             var self = this,
 51                 xhrObj = self.xhrObj,
 52                 c = xhrObj.config;
 53 
 54             var nativeXhr = self.nativeXhr,
 55                 xhrFields,
 56                 i;
 57 
 58             if (c['username']) {
 59                 nativeXhr.open(c.type, c.url, c.async, c['username'], c.password)
 60             } else {
 61                 nativeXhr.open(c.type, c.url, c.async);
 62             }
 63 
 64             if (xhrFields = c['xhrFields']) {
 65                 for (i in xhrFields) {
 66                     if (xhrFields.hasOwnProperty(i)) {
 67                         nativeXhr[ i ] = xhrFields[ i ];
 68                     }
 69                 }
 70             }
 71 
 72             // Override mime type if supported
 73             if (xhrObj.mimeType && nativeXhr.overrideMimeType) {
 74                 nativeXhr.overrideMimeType(xhrObj.mimeType);
 75             }
 76             // yui3 and jquery both have
 77             if (!c.crossDomain && !xhrObj.requestHeaders["X-Requested-With"]) {
 78                 xhrObj.requestHeaders[ "X-Requested-With" ] = "XMLHttpRequest";
 79             }
 80             try {
 81                 // 跨域时,不能设,否则请求变成
 82                 // OPTIONS /xhr/r.php HTTP/1.1
 83                 if (!c.crossDomain) {
 84                     for (i in xhrObj.requestHeaders) {
 85                         nativeXhr.setRequestHeader(i, xhrObj.requestHeaders[ i ]);
 86                     }
 87                 }
 88             } catch (e) {
 89                 S.log("setRequestHeader in xhr error : ");
 90                 S.log(e);
 91             }
 92 
 93             nativeXhr.send(c.hasContent && c.data || null);
 94 
 95             if (!c.async || nativeXhr.readyState == 4) {
 96                 self._callback();
 97             } else {
 98                 // _XDomainRequest 单独的回调机制
 99                 if (isInstanceOfXDomainRequest(nativeXhr)) {
100                     nativeXhr.onload = function () {
101                         nativeXhr.readyState = 4;
102                         nativeXhr.status = 200;
103                         self._callback();
104                     };
105                     nativeXhr.onerror = function () {
106                         nativeXhr.readyState = 4;
107                         nativeXhr.status = 500;
108                         self._callback();
109                     };
110                 } else {
111                     nativeXhr.onreadystatechange = function () {
112                         self._callback();
113                     };
114                 }
115             }
116         },
117         // 由 xhrObj.abort 调用,自己不可以调用 xhrObj.abort
118         abort:function () {
119             this._callback(0, 1);
120         },
121 
122         _callback:function (event, abort) {
123             // Firefox throws exceptions when accessing properties
124             // of an xhr when a network error occured
125             // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
126             try {
127                 var self = this,
128                     nativeXhr = self.nativeXhr,
129                     xhrObj = self.xhrObj,
130                     c = xhrObj.config;
131                 //abort or complete
132                 if (abort || nativeXhr.readyState == 4) {
133 
134                     // ie6 ActiveObject 设置不恰当属性导致出错
135                     if (isInstanceOfXDomainRequest(nativeXhr)) {
136                         nativeXhr.onerror = S.noop;
137                         nativeXhr.onload = S.noop;
138                     } else {
139                         // ie6 ActiveObject 只能设置,不能读取这个属性,否则出错!
140                         nativeXhr.onreadystatechange = S.noop;
141                     }
142 
143                     if (abort) {
144                         // 完成以后 abort 不要调用
145                         if (nativeXhr.readyState !== 4) {
146                             nativeXhr.abort();
147                         }
148                     } else {
149                         var status = nativeXhr.status;
150 
151                         // _XDomainRequest 不能获取响应头
152                         if (!isInstanceOfXDomainRequest(nativeXhr)) {
153                             xhrObj.responseHeadersString = nativeXhr.getAllResponseHeaders();
154                         }
155 
156                         var xml = nativeXhr.responseXML;
157 
158                         // Construct response list
159                         if (xml && xml.documentElement /* #4958 */) {
160                             xhrObj.responseXML = xml;
161                         }
162                         xhrObj.responseText = nativeXhr.responseText;
163 
164                         // Firefox throws an exception when accessing
165                         // statusText for faulty cross-domain requests
166                         try {
167                             var statusText = nativeXhr.statusText;
168                         } catch (e) {
169                             S.log("xhr statustext error : ");
170                             S.log(e);
171                             // We normalize with Webkit giving an empty statusText
172                             statusText = "";
173                         }
174 
175                         // Filter status for non standard behaviors
176                         // If the request is local and we have data: assume a success
177                         // (success with no data won't get notified, that's the best we
178                         // can do given current implementations)
179                         if (!status && io.isLocal && !c.crossDomain) {
180                             status = xhrObj.responseText ? OK_CODE : NOT_FOUND_CODE;
181                             // IE - #1450: sometimes returns 1223 when it should be 204
182                         } else if (status === NO_CONTENT_CODE2) {
183                             status = NO_CONTENT_CODE;
184                         }
185 
186                         xhrObj._xhrReady(status, statusText);
187                     }
188                 }
189             } catch (firefoxAccessException) {
190                 nativeXhr.onreadystatechange = S.noop;
191                 if (!abort) {
192                     xhrObj._xhrReady(-1, firefoxAccessException);
193                 }
194             }
195         }
196     });
197 
198     return XhrTransportBase;
199 }, {
200     requires:['./base']
201 });