;(function(global, S) {
var doc = global.document,
location = global.location;
function mix(target, source) {
var k, key;
for (key in source) {
if((k = source[key]) !== undefined) {
target[key] = k;
}
}
return target;
}
function merge(d, n) {
return mix(mix({}, d), n);
}
function isType(type) {
return function(obj) {
return {}.toString.call(obj) == '[object ' + type + ']';
}
}
var isObject = isType('Object'),
isArray = Array.isArray || isType('Array'),
isFunction = isType('Function');
function each(obj, iterator, context) {
var keys = Object.keys(obj), i, len;
for (i = 0, len = keys.length; i < len; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === false) {
return;
}
}
}
var jsonpID = 1,
TRUE = !0,
FALSE = !TRUE,
NULL = null,
ABORT = "abort",
SUCCESS = "success",
ERROR = "error",
EMPTY = "",
getScript = S.getScript,
noop = function() {};
var transports = {},
def = {
type: 'GET',
async: TRUE,
serializeArray: TRUE,
processData: TRUE,
beforeSend: noop,
success: noop,
error: noop,
complete: noop,
context: NULL,
accepts: {
script: 'text/javascript,application/javascript',
json: "application/json,text/json",
xml: 'application/xml,text/xml',
html: "text/html",
text: 'text/plain'
},
timeout: 0,
cache: TRUE
};
function presetConfig(cfg) {
if(!cfg.url) {
cfg.url = location.toString();
}
if (cfg.processData && isObject(cfg.data)) {
cfg.data = param(cfg.data, cfg.serializeArray)
}
cfg.type = cfg.type.toUpperCase();
if (cfg.data && cfg.type == 'GET') {
cfg.url = appendURL(cfg.url, cfg.data)
}
if (cfg.cache === FALSE) {
cfg.url = appendURL(cfg.url, 't=' + Date.now());
}
var testURL = /^([\w-]+:)?\/\/([^\/]+)/.test(cfg.url),
protocol = testURL ? RegExp.$1 : location.protocol;
cfg.local = protocol == 'file:';
cfg.context || (cfg.context = cfg);
return cfg;
}
function fireEvent(type, io) {
IO.fire(type, {io: io});
}
function IO(config) {
var self = this;
if (!(self instanceof IO)) {
return new IO(config);
}
var cfg = presetConfig(merge(def, config)),
timeout = cfg.timeout;
self.cfg = cfg;
fireEvent('start', self);
var dataType = cfg.dataType,
Transport = transports[dataType] || transports[EMPTY];
var transport = new Transport(self);
self.transport = transport;
var fnBeforeSend = cfg.beforeSend;
if(fnBeforeSend && fnBeforeSend.call(cfg.context, self, cfg) === false) {
self.abort();
return self;
}
fireEvent('send', self);
if(timeout > 0) {
self._timer = setTimeout(function() {
self.abort("timeout");
}, timeout * 1000);
}
try {
transport.send();
}catch(ex) {
self._complete(FALSE, ex.message);
}
return self;
}
mix(IO, S.Event.Target);
mix(IO.prototype, {
abort: function(s) {
this.transport.abort(s);
},
_complete: function(status, statusText) {
var self = this,
cfg = self.cfg,
context = cfg.context,
param = [self.responseData, statusText, self],
TYPE = status ? SUCCESS : ERROR,
COMPLETE = "complete";
if(self._end) return;
self._end = TRUE;
clearTimeout(self._timer);
cfg[TYPE].apply(context, param);
fireEvent(TYPE, self);
cfg[COMPLETE].apply(context, param);
fireEvent(COMPLETE, self);
}
});
function setTransport(name, fn) {
transports[name] = fn;
}
function appendURL(url, query) {
return (url + '&' + query).replace(/[&?]{1,2}/, '?');
}
var encode = encodeURIComponent;
function param(o, arr) {
var rt = [];
_serialize(rt, o, arr);
return rt.join("&");
}
function _serialize(rt, o, arr, k) {
var symbol = arr === true ? encode("[]") : EMPTY;
each(o, function(val, key) {
if(k) {
key = k + symbol;
}
if(isArray(val)) {
_serialize(rt, val, arr, key);
}else{
rt.push(key + "=" + encode(val));
}
});
}
var XHRNAME = "XMLHttpRequest",
reBlank = /^\s*$/;
function createXHR() {
return new global[XHRNAME]();
}
function xhrTransport(io) {
this.io = io;
}
mix(xhrTransport.prototype, {
_init: function() {
var self = this,
io = self.io,
cfg = io.cfg,
dataType = cfg.dataType,
mime = cfg.accepts[dataType],
baseHeaders = {},
xhr = createXHR();
io.getNativeXhr = function() {
return xhr;
};
if (!cfg.crossDomain) {
baseHeaders['X-Requested-With'] = XHRNAME;
}
if (mime) {
baseHeaders['Accept'] = mime;
if(xhr.overrideMimeType) {
if (mime.indexOf(',') > -1) {
mime = mime.split(',', 2)[0]
}
xhr.overrideMimeType(mime)
}
}
if (cfg.contentType || (cfg.data && cfg.type != 'GET')) {
baseHeaders['Content-Type'] = cfg.contentType ||
'application/x-www-form-urlencoded';
}
cfg.headers = merge(baseHeaders, cfg.headers || {})
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var result, error = FALSE;
if ((xhr.status >= 200 &&
xhr.status < 300) ||
xhr.status == 304 ||
(xhr.status == 0 && cfg.local)) {
if(xhr.responseURL){
var typeMatch = xhr.responseURL.match(/\.(json|xml)$/);
} else {
var typeMatch = cfg.url.match(/\.(json|xml)$/);
}
if(!dataType && typeMatch && typeMatch.length >= 2){
dataType = typeMatch[1];
} else {
dataType = dataType || mimeToDataType(xhr.getResponseHeader('Content-Type'));
}
result = io.responseText = xhr.responseText;
io.responseXML = xhr.responseXML;
try {
if (dataType == 'script') {
(1,eval)(result);
}else if(dataType == 'xml') {
result = xhr.responseXML;
}else if (dataType == 'json') {
result = reBlank.test(result) ? NULL : parseJSON(result);
}
} catch (e) { error = e }
io.responseData = result;
if (error) {
io._complete(FALSE, 'parsererror')
}else {
io._complete(TRUE, SUCCESS);
}
} else {
io._complete(FALSE, ERROR)
}
}
};
xhr.open(cfg.type, cfg.url, cfg.async);
each(cfg.headers, function(v, k) {
xhr.setRequestHeader(k, v);
});
xhr.send(cfg.data ? cfg.data : NULL);
},
abort: function(statusText) {
var self = this,
xhr = self.xhr,
io = self.io;
if(xhr) {
xhr.onreadystatechange = noop;
xhr.abort();
}
io._complete(FALSE, statusText || ABORT);
},
send: function() {
this._init();
}
});
setTransport(EMPTY, xhrTransport);
var regMimeType = /^(?:text|application)\/(json|javascript|xml|html)/i;
function mimeToDataType(mime) {
var result = mime && regMimeType.test(mime),
type = result ? RegExp.$1 : "text";
return type === "javascript" ? "script" : type;
}
function parseJSON(text) {
return JSON.parse(text);
}
function ScriptTransport(io) {
this.io = io;
}
mix(ScriptTransport.prototype, {
abort: function(statusText) {
this._end(FALSE, statusText || ABORT)
},
_end: function(status, statusText) {
var self = this,
script = self.script,
io = self.io,
gvar = self._globalVar;
global[gvar] = function() {
delete global[gvar];
};
if(script) {
script.src = NULL;
script.onload = script.onerror = noop;
script.parentNode.removeChild(script);
}
io._complete(status, statusText);
},
send: function() {
var self = this,
io = self.io,
cfg = io.cfg,
callbackName = cfg.jsonp || "callback",
methodName = cfg.jsonpCallback || "jsonp"+jsonpID ++;
self._globalVar = methodName;
var url = appendURL(cfg.url, callbackName + "=" + methodName);
global[methodName] = function(data){
io.responseData = data;
self._end(TRUE, SUCCESS);
};
self.script = getScript(url, {
charset: cfg.scriptCharset,
error: function() {
self._end(FALSE, ERROR);
}
});
}
});
setTransport("jsonp", ScriptTransport);
function factory(t, dt) {
return function(url, data, callback, dataType, type) {
if (isFunction(data)) {
dataType = callback;
callback = data;
data = NULL;
}
return IO({
type: t || type,
url: url,
data: data,
success: callback,
dataType: dt || dataType
});
};
}