/**
* @ignore
* base for xhr and subdomain
* @author yiminghe@gmail.com
*/
KISSY.add('ajax/xhr-transport-base', function (S, io) {
var OK_CODE = 200,
win = S.Env.host,
// http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx
_XDomainRequest = S.UA.ie > 7 && win['XDomainRequest'],
NO_CONTENT_CODE = 204,
NOT_FOUND_CODE = 404,
NO_CONTENT_CODE2 = 1223,
XhrTransportBase = {
proto: {}
}, lastModifiedCached = {},
eTagCached = {};
io.__lastModifiedCached = lastModifiedCached;
io.__eTagCached = eTagCached;
function createStandardXHR(_, refWin) {
try {
return new (refWin || win)['XMLHttpRequest']();
} catch (e) {
S.log('createStandardXHR error: ' + _);
}
return undefined;
}
function createActiveXHR(_, refWin) {
try {
return new (refWin || win)['ActiveXObject']('Microsoft.XMLHTTP');
} catch (e) {
S.log('createActiveXHR error: ' + _);
}
return undefined;
}
XhrTransportBase.nativeXhr = win['ActiveXObject'] ? function (crossDomain, refWin) {
if (crossDomain && _XDomainRequest) {
return new _XDomainRequest();
}
// ie7 XMLHttpRequest 不能访问本地文件
return !io.isLocal && createStandardXHR(crossDomain, refWin) ||
createActiveXHR(crossDomain, refWin);
} : createStandardXHR;
XhrTransportBase._XDomainRequest = _XDomainRequest;
function isInstanceOfXDomainRequest(xhr) {
return _XDomainRequest && (xhr instanceof _XDomainRequest);
}
function getIfModifiedKey(c) {
var ifModified = c.ifModified,
ifModifiedKey;
if (ifModified) {
ifModifiedKey = c.uri;
if (c.cache === false) {
ifModifiedKey = ifModifiedKey.clone();
// remove random timestamp
// random timestamp is forced to fetch code file from server
ifModifiedKey.query.remove('_ksTS');
}
ifModifiedKey = ifModifiedKey.toString();
}
return ifModifiedKey;
}
S.mix(XhrTransportBase.proto, {
sendInternal: function () {
var self = this,
io = self.io,
c = io.config,
nativeXhr = self.nativeXhr,
type = c.type,
async = c.async,
username,
crossDomain = c.crossDomain,
mimeType = io.mimeType,
requestHeaders = io.requestHeaders || {},
url = io._getUrlForSend(),
xhrFields,
ifModifiedKey = getIfModifiedKey(c),
cacheValue,
i;
if (ifModifiedKey) {
// if ajax want a conditional load
// (response status is 304 and responseText is null)
// u need to set if-modified-since manually!
// or else
// u will always get response status 200 and full responseText
// which is also conditional load but process transparently by browser
if (cacheValue = lastModifiedCached[ifModifiedKey]) {
requestHeaders['If-Modified-Since'] = cacheValue;
}
if (cacheValue = eTagCached[ifModifiedKey]) {
requestHeaders['If-None-Match'] = cacheValue;
}
}
if (username = c['username']) {
nativeXhr.open(type, url, async, username, c.password)
} else {
nativeXhr.open(type, url, async);
}
if (xhrFields = c['xhrFields']) {
for (i in xhrFields) {
nativeXhr[ i ] = xhrFields[ i ];
}
}
// Override mime type if supported
if (mimeType && nativeXhr.overrideMimeType) {
nativeXhr.overrideMimeType(mimeType);
}
// yui3 and jquery both have
if (!crossDomain && !requestHeaders['X-Requested-With']) {
requestHeaders[ 'X-Requested-With' ] = 'XMLHttpRequest';
}
try {
// 跨域时,不能设,否则请求变成
// OPTIONS /xhr/r.php HTTP/1.1
if (!crossDomain) {
for (i in requestHeaders) {
nativeXhr.setRequestHeader(i, requestHeaders[ i ]);
}
}
} catch (e) {
S.log('setRequestHeader in xhr error: ');
S.log(e);
}
nativeXhr.send(c.hasContent && c.data || null);
if (!async || nativeXhr.readyState == 4) {
self._callback();
} else {
// _XDomainRequest 单独的回调机制
if (isInstanceOfXDomainRequest(nativeXhr)) {
nativeXhr.onload = function () {
nativeXhr.readyState = 4;
nativeXhr.status = 200;
self._callback();
};
nativeXhr.onerror = function () {
nativeXhr.readyState = 4;
nativeXhr.status = 500;
self._callback();
};
} else {
nativeXhr.onreadystatechange = function () {
self._callback();
};
}
}
},
// 由 io.abort 调用,自己不可以调用 io.abort
abort: function () {
this._callback(0, 1);
},
_callback: function (event, abort) {
// Firefox throws exceptions when accessing properties
// of an xhr when a network error occurred
// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
var self = this,
nativeXhr = self.nativeXhr,
io = self.io,
ifModifiedKey,
lastModified,
eTag,
statusText,
xml,
c = io.config;
try {
//abort or complete
if (abort || nativeXhr.readyState == 4) {
// ie6 ActiveObject 设置不恰当属性导致出错
if (isInstanceOfXDomainRequest(nativeXhr)) {
nativeXhr.onerror = S.noop;
nativeXhr.onload = S.noop;
} else {
// ie6 ActiveObject 只能设置,不能读取这个属性,否则出错!
nativeXhr.onreadystatechange = S.noop;
}
if (abort) {
// 完成以后 abort 不要调用
if (nativeXhr.readyState !== 4) {
nativeXhr.abort();
}
} else {
ifModifiedKey = getIfModifiedKey(c);
var status = nativeXhr.status;
// _XDomainRequest 不能获取响应头
if (!isInstanceOfXDomainRequest(nativeXhr)) {
io.responseHeadersString = nativeXhr.getAllResponseHeaders();
}
if (ifModifiedKey) {
lastModified = nativeXhr.getResponseHeader('Last-Modified');
eTag = nativeXhr.getResponseHeader('ETag');
// if u want to set if-modified-since manually
// u need to save last-modified after the first request
if (lastModified) {
lastModifiedCached[ifModifiedKey] = lastModified;
}
if (eTag) {
eTagCached[eTag] = eTag;
}
}
xml = nativeXhr.responseXML;
// Construct response list
if (xml && xml.documentElement /* #4958 */) {
io.responseXML = xml;
}
io.responseText = nativeXhr.responseText;
// Firefox throws an exception when accessing
// statusText for faulty cross-domain requests
try {
statusText = nativeXhr.statusText;
} catch (e) {
S.log('xhr statusText error: ');
S.log(e);
// We normalize with Webkit giving an empty statusText
statusText = '';
}
// Filter status for non standard behaviors
// If the request is local and we have data: assume a success
// (success with no data won't get notified, that's the best we
// can do given current implementations)
if (!status && io.isLocal && !c.crossDomain) {
status = io.responseText ? OK_CODE : NOT_FOUND_CODE;
// IE - #1450: sometimes returns 1223 when it should be 204
} else if (status === NO_CONTENT_CODE2) {
status = NO_CONTENT_CODE;
}
io._ioReady(status, statusText);
}
}
} catch (firefoxAccessException) {
nativeXhr.onreadystatechange = S.noop;
if (!abort) {
io._ioReady(-1, firefoxAccessException);
}
}
}
});
return XhrTransportBase;
}, {
requires: ['./base']
});